Merge "Add BluetoothDevice class and test to androidx.bluetooth package" into androidx-main
diff --git a/activity/activity/api/current.txt b/activity/activity/api/current.txt
index ee25f24..efb5f4a 100644
--- a/activity/activity/api/current.txt
+++ b/activity/activity/api/current.txt
@@ -21,6 +21,7 @@
     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, int, android.content.Intent?);
     method @CallSuper public void onMultiWindowModeChanged(boolean);
@@ -50,12 +51,19 @@
     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 setUp(androidx.activity.ComponentActivity, optional androidx.activity.SystemBarStyle statusBarStyle, optional androidx.activity.SystemBarStyle navigationBarStyle);
+    method public static void setUp(androidx.activity.ComponentActivity, optional androidx.activity.SystemBarStyle statusBarStyle);
+    method public static void setUp(androidx.activity.ComponentActivity);
+  }
+
   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);
@@ -103,6 +111,19 @@
     property public abstract androidx.activity.OnBackPressedDispatcher onBackPressedDispatcher;
   }
 
+  public final class SystemBarStyle {
+    method public static androidx.activity.SystemBarStyle auto(@ColorInt int lightScrim, @ColorInt int darkScrim);
+    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 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);
diff --git a/activity/activity/api/public_plus_experimental_current.txt b/activity/activity/api/public_plus_experimental_current.txt
index ee25f24..efb5f4a 100644
--- a/activity/activity/api/public_plus_experimental_current.txt
+++ b/activity/activity/api/public_plus_experimental_current.txt
@@ -21,6 +21,7 @@
     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, int, android.content.Intent?);
     method @CallSuper public void onMultiWindowModeChanged(boolean);
@@ -50,12 +51,19 @@
     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 setUp(androidx.activity.ComponentActivity, optional androidx.activity.SystemBarStyle statusBarStyle, optional androidx.activity.SystemBarStyle navigationBarStyle);
+    method public static void setUp(androidx.activity.ComponentActivity, optional androidx.activity.SystemBarStyle statusBarStyle);
+    method public static void setUp(androidx.activity.ComponentActivity);
+  }
+
   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);
@@ -103,6 +111,19 @@
     property public abstract androidx.activity.OnBackPressedDispatcher onBackPressedDispatcher;
   }
 
+  public final class SystemBarStyle {
+    method public static androidx.activity.SystemBarStyle auto(@ColorInt int lightScrim, @ColorInt int darkScrim);
+    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 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);
diff --git a/activity/activity/api/restricted_current.txt b/activity/activity/api/restricted_current.txt
index 1210c28..600cec5 100644
--- a/activity/activity/api/restricted_current.txt
+++ b/activity/activity/api/restricted_current.txt
@@ -20,6 +20,7 @@
     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, int, android.content.Intent?);
     method @CallSuper public void onMultiWindowModeChanged(boolean);
@@ -49,12 +50,19 @@
     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 setUp(androidx.activity.ComponentActivity, optional androidx.activity.SystemBarStyle statusBarStyle, optional androidx.activity.SystemBarStyle navigationBarStyle);
+    method public static void setUp(androidx.activity.ComponentActivity, optional androidx.activity.SystemBarStyle statusBarStyle);
+    method public static void setUp(androidx.activity.ComponentActivity);
+  }
+
   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);
@@ -102,6 +110,19 @@
     property public abstract androidx.activity.OnBackPressedDispatcher onBackPressedDispatcher;
   }
 
+  public final class SystemBarStyle {
+    method public static androidx.activity.SystemBarStyle auto(@ColorInt int lightScrim, @ColorInt int darkScrim);
+    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 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);
diff --git a/activity/activity/build.gradle b/activity/activity/build.gradle
index f2b29f7..6bcedcb5 100644
--- a/activity/activity/build.gradle
+++ b/activity/activity/build.gradle
@@ -22,7 +22,7 @@
     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.2.1")
+    implementation("androidx.profileinstaller:profileinstaller:1.3.0")
     implementation("androidx.tracing:tracing:1.0.0")
     api(libs.kotlinStdlib)
 
diff --git a/activity/activity/src/androidTest/java/androidx/activity/EdgeToEdgeTest.kt b/activity/activity/src/androidTest/java/androidx/activity/EdgeToEdgeTest.kt
new file mode 100644
index 0000000..bd3a8a8
--- /dev/null
+++ b/activity/activity/src/androidTest/java/androidx/activity/EdgeToEdgeTest.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.activity
+
+import android.graphics.Color
+import android.os.Build
+import androidx.core.view.WindowInsetsControllerCompat
+import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.testutils.withActivity
+import androidx.testutils.withUse
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class EdgeToEdgeTest {
+
+    @Test
+    fun setUpAuto() {
+        withUse(ActivityScenario.launch(ComponentActivity::class.java)) {
+            withActivity {
+                setUpEdgeToEdge()
+                val view = window.decorView
+                if (Build.VERSION.SDK_INT >= 29) {
+                    assertThat(window.statusBarColor).isEqualTo(Color.TRANSPARENT)
+                    assertThat(window.navigationBarColor).isEqualTo(Color.TRANSPARENT)
+                    WindowInsetsControllerCompat(window, view).run {
+                        assertThat(isAppearanceLightStatusBars).isTrue()
+                        assertThat(isAppearanceLightNavigationBars).isTrue()
+                    }
+                } else if (Build.VERSION.SDK_INT >= 26) {
+                    assertThat(window.statusBarColor).isEqualTo(Color.TRANSPARENT)
+                    assertThat(window.navigationBarColor).isEqualTo(DefaultLightScrim)
+                    WindowInsetsControllerCompat(window, view).run {
+                        assertThat(isAppearanceLightStatusBars).isTrue()
+                        assertThat(isAppearanceLightNavigationBars).isTrue()
+                    }
+                } else if (Build.VERSION.SDK_INT >= 23) {
+                    assertThat(window.statusBarColor).isEqualTo(Color.TRANSPARENT)
+                    assertThat(window.navigationBarColor).isEqualTo(DefaultDarkScrim)
+                    WindowInsetsControllerCompat(window, view).run {
+                        assertThat(isAppearanceLightStatusBars).isTrue()
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    fun setUpDark() {
+        withUse(ActivityScenario.launch(ComponentActivity::class.java)) {
+            withActivity {
+                setUpEdgeToEdge(
+                    statusBarStyle = SystemBarStyle.dark(Color.DKGRAY),
+                    navigationBarStyle = SystemBarStyle.dark(Color.DKGRAY)
+                )
+                val view = window.decorView
+                if (Build.VERSION.SDK_INT >= 26) {
+                    assertThat(window.statusBarColor).isEqualTo(Color.DKGRAY)
+                    assertThat(window.navigationBarColor).isEqualTo(Color.DKGRAY)
+                    WindowInsetsControllerCompat(window, view).run {
+                        assertThat(isAppearanceLightStatusBars).isFalse()
+                        assertThat(isAppearanceLightNavigationBars).isFalse()
+                    }
+                } else if (Build.VERSION.SDK_INT >= 23) {
+                    assertThat(window.statusBarColor).isEqualTo(Color.DKGRAY)
+                    assertThat(window.navigationBarColor).isEqualTo(Color.DKGRAY)
+                    WindowInsetsControllerCompat(window, view).run {
+                        assertThat(isAppearanceLightStatusBars).isFalse()
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    fun setUpLight() {
+        withUse(ActivityScenario.launch(ComponentActivity::class.java)) {
+            withActivity {
+                setUpEdgeToEdge(
+                    statusBarStyle = SystemBarStyle.light(Color.CYAN, Color.DKGRAY),
+                    navigationBarStyle = SystemBarStyle.light(Color.CYAN, Color.DKGRAY),
+                )
+                val view = window.decorView
+                if (Build.VERSION.SDK_INT >= 26) {
+                    assertThat(window.statusBarColor).isEqualTo(Color.CYAN)
+                    assertThat(window.navigationBarColor).isEqualTo(Color.CYAN)
+                    WindowInsetsControllerCompat(window, view).run {
+                        assertThat(isAppearanceLightStatusBars).isTrue()
+                        assertThat(isAppearanceLightNavigationBars).isTrue()
+                    }
+                } else if (Build.VERSION.SDK_INT >= 23) {
+                    assertThat(window.statusBarColor).isEqualTo(Color.CYAN)
+                    assertThat(window.navigationBarColor).isEqualTo(Color.DKGRAY)
+                    WindowInsetsControllerCompat(window, view).run {
+                        assertThat(isAppearanceLightStatusBars).isTrue()
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/activity/activity/src/main/java/androidx/activity/ComponentActivity.java b/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
index 7e3e172..0028456 100644
--- a/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
+++ b/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
@@ -459,14 +459,14 @@
 
     @Override
     public void setContentView(@LayoutRes int layoutResID) {
-        initViewTreeOwners();
+        initializeViewTreeOwners();
         mReportFullyDrawnExecutor.viewCreated(getWindow().getDecorView());
         super.setContentView(layoutResID);
     }
 
     @Override
     public void setContentView(@SuppressLint({"UnknownNullness", "MissingNullability"}) View view) {
-        initViewTreeOwners();
+        initializeViewTreeOwners();
         mReportFullyDrawnExecutor.viewCreated(getWindow().getDecorView());
         super.setContentView(view);
     }
@@ -475,7 +475,7 @@
     public void setContentView(@SuppressLint({"UnknownNullness", "MissingNullability"}) View view,
             @SuppressLint({"UnknownNullness", "MissingNullability"})
                     ViewGroup.LayoutParams params) {
-        initViewTreeOwners();
+        initializeViewTreeOwners();
         mReportFullyDrawnExecutor.viewCreated(getWindow().getDecorView());
         super.setContentView(view, params);
     }
@@ -484,14 +484,17 @@
     public void addContentView(@SuppressLint({"UnknownNullness", "MissingNullability"}) View view,
             @SuppressLint({"UnknownNullness", "MissingNullability"})
                     ViewGroup.LayoutParams params) {
-        initViewTreeOwners();
+        initializeViewTreeOwners();
         mReportFullyDrawnExecutor.viewCreated(getWindow().getDecorView());
         super.addContentView(view, params);
     }
 
-    private void initViewTreeOwners() {
-        // Set the view tree owners before setting the content view so that the inflation process
-        // and attach listeners will see them already present
+    /**
+     * Sets the view tree owners before setting the content view so that the
+     * inflation process and attach listeners will see them already present.
+     */
+    @CallSuper
+    public void initializeViewTreeOwners() {
         ViewTreeLifecycleOwner.set(getWindow().getDecorView(), this);
         ViewTreeViewModelStoreOwner.set(getWindow().getDecorView(), this);
         ViewTreeSavedStateRegistryOwner.set(getWindow().getDecorView(), this);
diff --git a/activity/activity/src/main/java/androidx/activity/ComponentDialog.kt b/activity/activity/src/main/java/androidx/activity/ComponentDialog.kt
index 2a2059c..7f66ab7 100644
--- a/activity/activity/src/main/java/androidx/activity/ComponentDialog.kt
+++ b/activity/activity/src/main/java/androidx/activity/ComponentDialog.kt
@@ -100,26 +100,31 @@
     }
 
     override fun setContentView(layoutResID: Int) {
-        initViewTreeOwners()
+        initializeViewTreeOwners()
         super.setContentView(layoutResID)
     }
 
     override fun setContentView(view: View) {
-        initViewTreeOwners()
+        initializeViewTreeOwners()
         super.setContentView(view)
     }
 
     override fun setContentView(view: View, params: ViewGroup.LayoutParams?) {
-        initViewTreeOwners()
+        initializeViewTreeOwners()
         super.setContentView(view, params)
     }
 
     override fun addContentView(view: View, params: ViewGroup.LayoutParams?) {
-        initViewTreeOwners()
+        initializeViewTreeOwners()
         super.addContentView(view, params)
     }
 
-    private fun initViewTreeOwners() {
+    /**
+     * Sets the view tree owners before setting the content view so that the
+     * inflation process and attach listeners will see them already present.
+     */
+    @CallSuper
+    open fun initializeViewTreeOwners() {
         window!!.decorView.setViewTreeLifecycleOwner(this)
         window!!.decorView.setViewTreeOnBackPressedDispatcherOwner(this)
         window!!.decorView.setViewTreeSavedStateRegistryOwner(this)
diff --git a/activity/activity/src/main/java/androidx/activity/EdgeToEdge.kt b/activity/activity/src/main/java/androidx/activity/EdgeToEdge.kt
new file mode 100644
index 0000000..837544a
--- /dev/null
+++ b/activity/activity/src/main/java/androidx/activity/EdgeToEdge.kt
@@ -0,0 +1,282 @@
+/*
+ * 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("EdgeToEdge")
+
+package androidx.activity
+
+import android.app.UiModeManager
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.graphics.Color
+import android.os.Build
+import android.view.View
+import android.view.Window
+import android.view.WindowManager
+import androidx.annotation.ColorInt
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresApi
+import androidx.annotation.VisibleForTesting
+import androidx.core.view.WindowCompat
+import androidx.core.view.WindowInsetsControllerCompat
+
+// The light scrim color used in the platform API 29+
+// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/com/android/internal/policy/DecorView.java;drc=6ef0f022c333385dba2c294e35b8de544455bf19;l=142
+@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+internal val DefaultLightScrim = Color.argb(0xe6, 0xFF, 0xFF, 0xFF)
+
+// The dark scrim color used in the platform.
+// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/res/res/color/system_bar_background_semi_transparent.xml
+// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/res/remote_color_resources_res/values/colors.xml;l=67
+@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+internal val DefaultDarkScrim = Color.argb(0x80, 0x1b, 0x1b, 0x1b)
+
+private var Impl: EdgeToEdgeImpl? = null
+
+/**
+ * Sets up edge-to-edge display for this [ComponentActivity].
+ *
+ * To set it up with the default style, call this method in your Activity's onCreate method:
+ * ```
+ *     override fun onCreate(savedInstanceState: Bundle?) {
+ *         setUpEdgeToEdge()
+ *         super.onCreate(savedInstanceState)
+ *         ...
+ *     }
+ * ```
+ *
+ * The default style configures the system bars with a transparent background when contrast can be
+ * enforced by the system (API 29 or above). On older platforms (which only have 3-button/2-button
+ * navigation modes), an equivalent scrim is applied to ensure contrast with the system bars.
+ *
+ * See [SystemBarStyle] for more customization options.
+ *
+ * @param statusBarStyle The [SystemBarStyle] for the status bar.
+ * @param navigationBarStyle The [SystemBarStyle] for the navigation bar.
+ */
+@JvmName("setUp")
+@JvmOverloads
+fun ComponentActivity.setUpEdgeToEdge(
+    statusBarStyle: SystemBarStyle = SystemBarStyle.auto(Color.TRANSPARENT, Color.TRANSPARENT),
+    navigationBarStyle: SystemBarStyle = SystemBarStyle.auto(DefaultLightScrim, DefaultDarkScrim)
+) {
+    val view = window.decorView
+    val statusBarIsDark = statusBarStyle.isDark(view.resources)
+    val navigationBarIsDark = navigationBarStyle.isDark(view.resources)
+    val impl = Impl ?: if (Build.VERSION.SDK_INT >= 29) {
+        EdgeToEdgeApi29()
+    } else if (Build.VERSION.SDK_INT >= 26) {
+        EdgeToEdgeApi26()
+    } else if (Build.VERSION.SDK_INT >= 23) {
+        EdgeToEdgeApi23()
+    } else if (Build.VERSION.SDK_INT >= 21) {
+        EdgeToEdgeApi21()
+    } else {
+        EdgeToEdgeBase()
+    }.also { Impl = it }
+    impl.setUp(
+        statusBarStyle, navigationBarStyle, window, view, statusBarIsDark, navigationBarIsDark
+    )
+}
+
+/**
+ * The style for the status bar or the navigation bar used in [setUpEdgeToEdge].
+ */
+class SystemBarStyle private constructor(
+    private val lightScrim: Int,
+    internal val darkScrim: Int,
+    internal val nightMode: Int
+) {
+
+    companion object {
+
+        /**
+         * Creates a new instance of [SystemBarStyle]. This style detects the dark mode
+         * automatically.
+         * - On API level 29 and above, the bar will be transparent in the gesture navigation mode.
+         *   If this is used for the navigation bar, it will have the scrim automatically applied
+         *   by the system in the 3-button navigation mode. _Note that neither of the specified
+         *   colors are used_. If you really want a custom color on these API levels, use [dark] or
+         *   [light].
+         * - On API level 28 and below, the bar will be one of the specified scrim colors depending
+         *   on the dark mode.
+         * @param lightScrim The scrim color to be used for the background when the app is in light
+         * mode.
+         * @param darkScrim The scrim color to be used for the background when the app is in dark
+         * mode. This is also used on devices where the system icon color is always light.
+         */
+        @JvmStatic
+        fun auto(@ColorInt lightScrim: Int, @ColorInt darkScrim: Int): SystemBarStyle {
+            return SystemBarStyle(lightScrim, darkScrim, UiModeManager.MODE_NIGHT_AUTO)
+        }
+
+        /**
+         * Creates a new instance of [SystemBarStyle]. This style consistently applies the specified
+         * scrim color regardless of the system navigation mode.
+         *
+         * @param scrim The scrim color to be used for the background. It is expected to be dark
+         * for the contrast against the light system icons.
+         */
+        @JvmStatic
+        fun dark(@ColorInt scrim: Int): SystemBarStyle {
+            return SystemBarStyle(scrim, scrim, UiModeManager.MODE_NIGHT_YES)
+        }
+
+        /**
+         * Creates a new instance of [SystemBarStyle]. This style consistently applies the specified
+         * scrim color regardless of the system navigation mode.
+         *
+         * @param scrim The scrim color to be used for the background. It is expected to be light
+         * for the contrast against the dark system icons.
+         * @param darkScrim The scrim color to be used for the background on devices where the
+         * system icon color is always light. It is expected to be dark.
+         */
+        @JvmStatic
+        fun light(@ColorInt scrim: Int, @ColorInt darkScrim: Int): SystemBarStyle {
+            return SystemBarStyle(scrim, darkScrim, UiModeManager.MODE_NIGHT_NO)
+        }
+    }
+
+    internal fun isDark(resources: Resources): Boolean {
+        return when (nightMode) {
+            UiModeManager.MODE_NIGHT_YES -> true
+            UiModeManager.MODE_NIGHT_NO -> false
+            else -> (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) ==
+                Configuration.UI_MODE_NIGHT_YES
+        }
+    }
+
+    internal fun getScrim(isDark: Boolean) = if (isDark) darkScrim else lightScrim
+
+    internal fun getScrimWithEnforcedContrast(isDark: Boolean): Int {
+        return when {
+            nightMode == UiModeManager.MODE_NIGHT_AUTO -> Color.TRANSPARENT
+            isDark -> darkScrim
+            else -> lightScrim
+        }
+    }
+}
+
+private interface EdgeToEdgeImpl {
+
+    fun setUp(
+        statusBarStyle: SystemBarStyle,
+        navigationBarStyle: SystemBarStyle,
+        window: Window,
+        view: View,
+        statusBarIsDark: Boolean,
+        navigationBarIsDark: Boolean
+    )
+}
+
+private class EdgeToEdgeBase : EdgeToEdgeImpl {
+
+    override fun setUp(
+        statusBarStyle: SystemBarStyle,
+        navigationBarStyle: SystemBarStyle,
+        window: Window,
+        view: View,
+        statusBarIsDark: Boolean,
+        navigationBarIsDark: Boolean
+    ) {
+        // No edge-to-edge before SDK 21.
+    }
+}
+
+@RequiresApi(21)
+private class EdgeToEdgeApi21 : EdgeToEdgeImpl {
+
+    @Suppress("DEPRECATION")
+    @DoNotInline
+    override fun setUp(
+        statusBarStyle: SystemBarStyle,
+        navigationBarStyle: SystemBarStyle,
+        window: Window,
+        view: View,
+        statusBarIsDark: Boolean,
+        navigationBarIsDark: Boolean
+    ) {
+        WindowCompat.setDecorFitsSystemWindows(window, false)
+        window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
+        window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
+    }
+}
+
+@RequiresApi(23)
+private class EdgeToEdgeApi23 : EdgeToEdgeImpl {
+
+    @DoNotInline
+    override fun setUp(
+        statusBarStyle: SystemBarStyle,
+        navigationBarStyle: SystemBarStyle,
+        window: Window,
+        view: View,
+        statusBarIsDark: Boolean,
+        navigationBarIsDark: Boolean
+    ) {
+        WindowCompat.setDecorFitsSystemWindows(window, false)
+        window.statusBarColor = statusBarStyle.getScrim(statusBarIsDark)
+        window.navigationBarColor = navigationBarStyle.darkScrim
+        WindowInsetsControllerCompat(window, view).isAppearanceLightStatusBars = !statusBarIsDark
+    }
+}
+
+@RequiresApi(26)
+private class EdgeToEdgeApi26 : EdgeToEdgeImpl {
+
+    @DoNotInline
+    override fun setUp(
+        statusBarStyle: SystemBarStyle,
+        navigationBarStyle: SystemBarStyle,
+        window: Window,
+        view: View,
+        statusBarIsDark: Boolean,
+        navigationBarIsDark: Boolean
+    ) {
+        WindowCompat.setDecorFitsSystemWindows(window, false)
+        window.statusBarColor = statusBarStyle.getScrim(statusBarIsDark)
+        window.navigationBarColor = navigationBarStyle.getScrim(navigationBarIsDark)
+        WindowInsetsControllerCompat(window, view).run {
+            isAppearanceLightStatusBars = !statusBarIsDark
+            isAppearanceLightNavigationBars = !navigationBarIsDark
+        }
+    }
+}
+
+@RequiresApi(29)
+private class EdgeToEdgeApi29 : EdgeToEdgeImpl {
+
+    @DoNotInline
+    override fun setUp(
+        statusBarStyle: SystemBarStyle,
+        navigationBarStyle: SystemBarStyle,
+        window: Window,
+        view: View,
+        statusBarIsDark: Boolean,
+        navigationBarIsDark: Boolean
+    ) {
+        WindowCompat.setDecorFitsSystemWindows(window, false)
+        window.statusBarColor = statusBarStyle.getScrimWithEnforcedContrast(statusBarIsDark)
+        window.navigationBarColor =
+            navigationBarStyle.getScrimWithEnforcedContrast(navigationBarIsDark)
+        window.isStatusBarContrastEnforced = false
+        window.isNavigationBarContrastEnforced =
+            navigationBarStyle.nightMode == UiModeManager.MODE_NIGHT_AUTO
+        WindowInsetsControllerCompat(window, view).run {
+            isAppearanceLightStatusBars = !statusBarIsDark
+            isAppearanceLightNavigationBars = !navigationBarIsDark
+        }
+    }
+}
diff --git a/activity/integration-tests/testapp/build.gradle b/activity/integration-tests/testapp/build.gradle
index 364e39f..7799f8e 100644
--- a/activity/integration-tests/testapp/build.gradle
+++ b/activity/integration-tests/testapp/build.gradle
@@ -31,6 +31,8 @@
     implementation(project(":activity:activity-ktx"))
     implementation(projectOrArtifact(":lifecycle:lifecycle-runtime-ktx"))
     androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-common"))
+    implementation("androidx.appcompat:appcompat:1.6.0")
+    implementation("androidx.core:core-splashscreen:1.0.0")
     androidTestImplementation(libs.kotlinStdlib)
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
diff --git a/activity/integration-tests/testapp/src/main/AndroidManifest.xml b/activity/integration-tests/testapp/src/main/AndroidManifest.xml
index 8a6e622..5aacb12 100644
--- a/activity/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/activity/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -31,6 +31,17 @@
             android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
             android:exported="true"
             android:supportsPictureInPicture="true" />
+        <activity
+            android:name="androidx.activity.integration.testapp.EdgeToEdgeActivity"
+            android:exported="true"
+            android:label="EdgeToEdgeActivity"
+            android:theme="@style/Theme.EdgeToEdge.SplashScreen"
+            android:windowSoftInputMode="adjustResize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
 
         <provider
             android:name="androidx.core.content.FileProvider"
@@ -46,4 +57,6 @@
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
 
+    <uses-sdk tools:overrideLibrary="androidx.core.splashscreen" />
+
 </manifest>
diff --git a/activity/integration-tests/testapp/src/main/java/androidx/activity/integration/testapp/EdgeToEdgeActivity.kt b/activity/integration-tests/testapp/src/main/java/androidx/activity/integration/testapp/EdgeToEdgeActivity.kt
new file mode 100644
index 0000000..1f5d11a
--- /dev/null
+++ b/activity/integration-tests/testapp/src/main/java/androidx/activity/integration/testapp/EdgeToEdgeActivity.kt
@@ -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.activity.integration.testapp
+
+import android.app.Dialog
+import android.graphics.Color
+import android.os.Build
+import android.os.Bundle
+import android.view.View
+import androidx.activity.SystemBarStyle
+import androidx.activity.setUpEdgeToEdge
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.app.AppCompatDelegate
+import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
+import androidx.fragment.app.DialogFragment
+
+class EdgeToEdgeActivity : AppCompatActivity(R.layout.edge_to_edge_activity) {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        if (Build.VERSION.SDK_INT >= 21) {
+            installSplashScreen()
+        }
+        setUpEdgeToEdge()
+        super.onCreate(savedInstanceState)
+        findViewById<View>(R.id.default_config).setOnClickListener {
+            // The default style.
+            // API 29+: Transparent on gesture nav, Auto scrim on 3-button nav (same as default).
+            // API 26-28: Transparent status. Light or dark scrim on nav.
+            // API 23-25: Transparent status. Dark scrim on nav.
+            // API 21,22: Dark scrim (system default).
+            setUpEdgeToEdge()
+        }
+        findViewById<View>(R.id.custom_config).setOnClickListener {
+            // API 29+: Transparent on gesture nav, Auto scrim on 3-button nav (same as default).
+            // API 23-28: Yellow bars.
+            // API 21,22: Dark scrim (system default).
+            val style = SystemBarStyle.auto(
+                lightScrim = Color.argb(0x64, 0xff, 0xeb, 0x3b),
+                darkScrim = Color.argb(0x64, 0x4a, 0x14, 0x8c)
+            )
+            setUpEdgeToEdge(statusBarStyle = style, navigationBarStyle = style)
+        }
+        findViewById<View>(R.id.transparent_config).setOnClickListener {
+            // API 23+: Transparent regardless of the nav mode.
+            // API 21,22: Dark scrim (system default).
+            val style = SystemBarStyle.dark(Color.TRANSPARENT)
+            setUpEdgeToEdge(statusBarStyle = style, navigationBarStyle = style)
+        }
+        findViewById<View>(R.id.purple_config).setOnClickListener {
+            // API 23+: Purple.
+            // API 21,22: Dark scrim (system default).
+            val style = SystemBarStyle.dark(
+                scrim = Color.argb(0x64, 0x4a, 0x14, 0x8c)
+            )
+            setUpEdgeToEdge(statusBarStyle = style, navigationBarStyle = style)
+        }
+        findViewById<View>(R.id.yellow_config).setOnClickListener {
+            // API 23+: Yellow.
+            // API 21,22: Dark scrim (system default).
+            val style = SystemBarStyle.light(
+                scrim = Color.argb(0x64, 0xff, 0xeb, 0x3b),
+                darkScrim = Color.rgb(0xf5, 0x7f, 0x17)
+            )
+            setUpEdgeToEdge(statusBarStyle = style, navigationBarStyle = style)
+        }
+        findViewById<View>(R.id.light_mode).setOnClickListener { setDarkMode(false) }
+        findViewById<View>(R.id.dark_mode).setOnClickListener { setDarkMode(true) }
+        findViewById<View>(R.id.show_dialog).setOnClickListener {
+            EdgeToEdgeDialogFragment().show(supportFragmentManager, null)
+        }
+    }
+
+    private fun setDarkMode(darkMode: Boolean) {
+        AppCompatDelegate.setDefaultNightMode(
+            if (darkMode) AppCompatDelegate.MODE_NIGHT_YES else AppCompatDelegate.MODE_NIGHT_NO
+        )
+    }
+}
+
+class EdgeToEdgeDialogFragment : DialogFragment() {
+
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        return AlertDialog.Builder(requireContext())
+            .setTitle("Demo Dialog")
+            .setMessage("Hello, world!")
+            .setPositiveButton(android.R.string.ok, { dialog, _ ->
+                dialog.dismiss()
+            })
+            .create()
+    }
+}
diff --git a/activity/integration-tests/testapp/src/main/res/layout/edge_to_edge_activity.xml b/activity/integration-tests/testapp/src/main/res/layout/edge_to_edge_activity.xml
new file mode 100644
index 0000000..47e6cd6
--- /dev/null
+++ b/activity/integration-tests/testapp/src/main/res/layout/edge_to_edge_activity.xml
@@ -0,0 +1,100 @@
+<?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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/root"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <View
+        android:id="@+id/red"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:background="#f00" />
+
+    <View
+        android:id="@+id/blue"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_gravity="end|bottom"
+        android:background="#00f" />
+
+    <LinearLayout
+        android:id="@+id/content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_margin="64dp"
+        android:orientation="vertical">
+
+        <Button
+            android:id="@+id/default_config"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="default" />
+
+        <Button
+            android:id="@+id/custom_config"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="custom" />
+
+        <Button
+            android:id="@+id/transparent_config"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="transparent" />
+
+        <Button
+            android:id="@+id/purple_config"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="purple" />
+
+        <Button
+            android:id="@+id/yellow_config"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="yellow" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="32dp"
+            android:orientation="horizontal">
+
+            <Button
+                android:id="@+id/light_mode"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="light mode" />
+
+            <Button
+                android:id="@+id/dark_mode"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="dark mode" />
+
+        </LinearLayout>
+
+        <Button
+            android:id="@+id/show_dialog"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="32dp"
+            android:text="show dialog" />
+
+    </LinearLayout>
+
+</FrameLayout>
diff --git a/activity/integration-tests/testapp/src/main/res/values-v21/themes.xml b/activity/integration-tests/testapp/src/main/res/values-v21/themes.xml
new file mode 100644
index 0000000..d905b18
--- /dev/null
+++ b/activity/integration-tests/testapp/src/main/res/values-v21/themes.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<resources>
+    <style name="Theme.EdgeToEdge.SplashScreen" parent="Theme.SplashScreen">
+        <item name="postSplashScreenTheme">@style/Theme.EdgeToEdge</item>
+    </style>
+    <style name="Theme.EdgeToEdge" parent="@style/Theme.AppCompat.DayNight.NoActionBar" />
+</resources>
diff --git a/activity/integration-tests/testapp/src/main/res/values/themes.xml b/activity/integration-tests/testapp/src/main/res/values/themes.xml
new file mode 100644
index 0000000..8f4a6a4
--- /dev/null
+++ b/activity/integration-tests/testapp/src/main/res/values/themes.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<resources>
+    <style name="Theme.EdgeToEdge.SplashScreen" parent="@style/Theme.AppCompat.DayNight.NoActionBar" />
+</resources>
diff --git a/ads/OWNERS b/ads/OWNERS
deleted file mode 100644
index 3720323..0000000
--- a/ads/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 807287
-chaohuiw@google.com
-hanxu@google.com
diff --git a/ads/ads-identifier-benchmark/build.gradle b/ads/ads-identifier-benchmark/build.gradle
deleted file mode 100644
index 527e2f5..0000000
--- a/ads/ads-identifier-benchmark/build.gradle
+++ /dev/null
@@ -1,47 +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.
- */
-
-plugins {
-    id("AndroidXPlugin")
-    id("com.android.library")
-    id("androidx.benchmark")
-}
-
-dependencies {
-    androidTestImplementation(project(":benchmark:benchmark-junit4"))
-    androidTestImplementation(project(":ads:ads-identifier-common"))
-    androidTestImplementation(project(":ads:ads-identifier"))
-    androidTestImplementation(project(":ads:ads-identifier-provider"))
-    androidTestImplementation(project(":ads:ads-identifier-testing"))
-    androidTestImplementation("androidx.work:work-runtime:2.7.0")
-    androidTestImplementation(libs.junit)
-    androidTestImplementation(libs.multidex)
-    androidTestImplementation(libs.truth)
-    androidTestImplementation(libs.testExtJunit)
-    androidTestImplementation(libs.testCore)
-    androidTestImplementation(libs.testRunner)
-    androidTestImplementation(libs.testRules)
-    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy)
-}
-
-android {
-    namespace "androidx.ads.identifier.benchmark"
-
-    defaultConfig {
-        multiDexEnabled true
-    }
-}
-
diff --git a/ads/ads-identifier-benchmark/lint-baseline.xml b/ads/ads-identifier-benchmark/lint-baseline.xml
deleted file mode 100644
index bbd1a88..0000000
--- a/ads/ads-identifier-benchmark/lint-baseline.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?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">
-
-    <issue
-        id="BanThreadSleep"
-        message="Uses Thread.sleep()"
-        errorLine1="                    Thread.sleep(millis);"
-        errorLine2="                           ~~~~~">
-        <location
-            file="src/androidTest/java/androidx/ads/identifier/benchmark/AdvertisingIdBenchmark.java"/>
-    </issue>
-
-</issues>
diff --git a/ads/ads-identifier-benchmark/src/androidTest/AndroidManifest.xml b/ads/ads-identifier-benchmark/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index fc0c987..0000000
--- a/ads/ads-identifier-benchmark/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest
-    xmlns:android="http://schemas.android.com/apk/res/android">
-    <application
-        android:name="androidx.ads.identifier.benchmark.AdsIdentifierBenchmarkApplication">
-        <!-- enable profiling by shell for non-intrusive profiling tools -->
-        <profileable android:shell="true"/>
-    </application>
-</manifest>
diff --git a/ads/ads-identifier-benchmark/src/androidTest/java/androidx/ads/identifier/benchmark/AdsIdentifierBenchmarkApplication.java b/ads/ads-identifier-benchmark/src/androidTest/java/androidx/ads/identifier/benchmark/AdsIdentifierBenchmarkApplication.java
deleted file mode 100644
index 9e71e40..0000000
--- a/ads/ads-identifier-benchmark/src/androidTest/java/androidx/ads/identifier/benchmark/AdsIdentifierBenchmarkApplication.java
+++ /dev/null
@@ -1,30 +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.ads.identifier.benchmark;
-
-import android.app.Application;
-
-@SuppressWarnings("deprecation")
-public class AdsIdentifierBenchmarkApplication extends Application {
-    @Override
-    public void onCreate() {
-        super.onCreate();
-
-        androidx.ads.identifier.provider.AdvertisingIdProviderManager.registerProviderCallable(
-                SampleAdvertisingIdProvider::new);
-    }
-}
diff --git a/ads/ads-identifier-benchmark/src/androidTest/java/androidx/ads/identifier/benchmark/AdvertisingIdBenchmark.java b/ads/ads-identifier-benchmark/src/androidTest/java/androidx/ads/identifier/benchmark/AdvertisingIdBenchmark.java
deleted file mode 100644
index 5323750..0000000
--- a/ads/ads-identifier-benchmark/src/androidTest/java/androidx/ads/identifier/benchmark/AdvertisingIdBenchmark.java
+++ /dev/null
@@ -1,248 +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.ads.identifier.benchmark;
-
-import static androidx.ads.identifier.AdvertisingIdUtils.GET_AD_ID_ACTION;
-import static androidx.ads.identifier.benchmark.SampleAdvertisingIdProvider.DUMMY_AD_ID;
-import static androidx.ads.identifier.testing.MockPackageManagerHelper.createServiceResolveInfo;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-
-import androidx.ads.identifier.provider.internal.AdvertisingIdService;
-import androidx.ads.identifier.testing.MockPackageManagerHelper;
-import androidx.annotation.NonNull;
-import androidx.benchmark.BenchmarkState;
-import androidx.benchmark.junit4.BenchmarkRule;
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.LargeTest;
-import androidx.work.OneTimeWorkRequest;
-import androidx.work.WorkManager;
-import androidx.work.Worker;
-import androidx.work.WorkerParameters;
-
-import com.google.common.collect.Lists;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.MoreExecutors;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.lang.reflect.Method;
-import java.util.concurrent.CountDownLatch;
-
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-@SuppressWarnings("deprecation")
-public class AdvertisingIdBenchmark {
-
-    private static final int CONCURRENCY_NUM = 10;
-    private static final String SERVICE_NAME = AdvertisingIdService.class.getName();
-
-    @Rule
-    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
-
-    private Context mContext;
-
-    @Before
-    public void setUp() throws Exception {
-        Context applicationContext = ApplicationProvider.getApplicationContext();
-
-        mContext = mockContext(applicationContext);
-    }
-
-    private static Context mockContext(Context context) throws Exception {
-        MockPackageManagerHelper mockPackageManagerHelper = new MockPackageManagerHelper();
-        mockPackageManagerHelper.mockQueryGetAdIdServices(Lists.newArrayList(
-                createServiceResolveInfo(context.getPackageName(), SERVICE_NAME)));
-
-        return new ContextWrapper(context) {
-            @Override
-            public Context getApplicationContext() {
-                return this;
-            }
-
-            @Override
-            public PackageManager getPackageManager() {
-                return mockPackageManagerHelper.getMockPackageManager();
-            }
-        };
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        clearAdvertisingIdConnection();
-        stopAdvertisingIdService();
-    }
-
-    private void clearAdvertisingIdConnection() throws Exception {
-        Method method = androidx.ads.identifier.AdvertisingIdClient.class.getDeclaredMethod(
-                "clearConnectionClient");
-        method.setAccessible(true);
-        method.invoke(null);
-    }
-
-    private void stopAdvertisingIdService() {
-        Intent serviceIntent = new Intent(GET_AD_ID_ACTION);
-        serviceIntent.setClassName(mContext.getPackageName(), SERVICE_NAME);
-        mContext.stopService(serviceIntent);
-    }
-
-    @Test
-    public void getAdvertisingIdInfo() throws Exception {
-        final BenchmarkState state = mBenchmarkRule.getState();
-        while (state.keepRunning()) {
-            CountDownLatch countDownLatch = new CountDownLatch(1);
-            getAdvertisingIdInfoListenableFuture(countDownLatch);
-            countDownLatch.await();
-        }
-    }
-
-    private void getAdvertisingIdInfoListenableFuture(CountDownLatch countDownLatch) {
-        ListenableFuture<androidx.ads.identifier.AdvertisingIdInfo>
-                advertisingIdInfoListenableFuture =
-                androidx.ads.identifier.AdvertisingIdClient.getAdvertisingIdInfo(mContext);
-        Futures.addCallback(advertisingIdInfoListenableFuture,
-                new FutureCallback<androidx.ads.identifier.AdvertisingIdInfo>() {
-                    @Override
-                    public void onSuccess(
-                            androidx.ads.identifier.AdvertisingIdInfo advertisingIdInfo) {
-                        assertThat(advertisingIdInfo.getId()).isEqualTo(DUMMY_AD_ID);
-                        countDownLatch.countDown();
-                    }
-
-                    @Override
-                    public void onFailure(@NonNull Throwable throwable) {
-                        throw new RuntimeException(throwable);
-                    }
-                }, MoreExecutors.directExecutor());
-    }
-
-    @Test
-    public void getAdvertisingIdInfo_worker() throws Exception {
-        WorkManager workManager = WorkManager.getInstance(mContext);
-        workManager.cancelAllWork();
-        final BenchmarkState state = mBenchmarkRule.getState();
-        while (state.keepRunning()) {
-            workManager.enqueue(OneTimeWorkRequest.from(GetAdInfoWorker.class)).getResult().get();
-        }
-    }
-
-    /** Get the Advertising ID on a worker thread. */
-    public static class GetAdInfoWorker extends Worker {
-        public GetAdInfoWorker(@NonNull Context context, @NonNull WorkerParameters params) {
-            super(context, params);
-        }
-
-        @NonNull
-        @Override
-        public Result doWork() {
-            try {
-                Context context = mockContext(getApplicationContext());
-                androidx.ads.identifier.AdvertisingIdInfo advertisingIdInfo =
-                        androidx.ads.identifier.AdvertisingIdClient.getAdvertisingIdInfo(
-                                context).get();
-                assertThat(advertisingIdInfo.getId()).isEqualTo(DUMMY_AD_ID);
-            } catch (Exception e) {
-                return Result.failure();
-            }
-            return Result.success();
-        }
-    }
-
-    @Test
-    @SuppressWarnings("deprecation") /* AsyncTask */
-    public void getAdvertisingIdInfo_asyncTask() throws Exception {
-        final BenchmarkState state = mBenchmarkRule.getState();
-        while (state.keepRunning()) {
-            androidx.ads.identifier.AdvertisingIdInfo advertisingIdInfo =
-                    new android.os.AsyncTask<Void, Void,
-                            androidx.ads.identifier.AdvertisingIdInfo>() {
-                        @Override
-                        protected androidx.ads.identifier.AdvertisingIdInfo doInBackground(
-                                Void... voids) {
-                            try {
-                                return androidx.ads.identifier.AdvertisingIdClient
-                                        .getAdvertisingIdInfo(mContext).get();
-                            } catch (Exception e) {
-                                throw new RuntimeException(e);
-                            }
-                        }
-                    }.execute().get();
-            assertThat(advertisingIdInfo.getId()).isEqualTo(DUMMY_AD_ID);
-        }
-    }
-
-    @Test
-    public void getAdvertisingIdInfo_thread() throws Exception {
-        final BenchmarkState state = mBenchmarkRule.getState();
-        while (state.keepRunning()) {
-            Thread thread = new Thread(() -> {
-                try {
-                    androidx.ads.identifier.AdvertisingIdInfo advertisingIdInfo =
-                            androidx.ads.identifier.AdvertisingIdClient.getAdvertisingIdInfo(
-                                    mContext).get();
-                    assertThat(advertisingIdInfo.getId()).isEqualTo(DUMMY_AD_ID);
-                } catch (Exception e) {
-                    throw new RuntimeException(e);
-                }
-            });
-            thread.start();
-            thread.join();
-        }
-    }
-
-    @Test
-    public void getAdvertisingIdInfo_concurrency() throws Exception {
-        getAdvertisingIdInfo_concurrencyWithDelay(0);
-    }
-
-    @Test
-    public void getAdvertisingIdInfo_concurrencyWithDelay1Millis() throws Exception {
-        getAdvertisingIdInfo_concurrencyWithDelay(1);
-    }
-
-    @Test
-    public void getAdvertisingIdInfo_concurrencyWithDelay10Millis() throws Exception {
-        getAdvertisingIdInfo_concurrencyWithDelay(10);
-    }
-
-    private void getAdvertisingIdInfo_concurrencyWithDelay(long millis) throws Exception {
-        final BenchmarkState state = mBenchmarkRule.getState();
-        while (state.keepRunning()) {
-            CountDownLatch countDownLatch = new CountDownLatch(CONCURRENCY_NUM);
-            for (int i = 0; i < CONCURRENCY_NUM; i++) {
-                if (millis != 0) {
-                    Thread.sleep(millis);
-                }
-
-                getAdvertisingIdInfoListenableFuture(countDownLatch);
-            }
-            countDownLatch.await();
-        }
-    }
-}
diff --git a/ads/ads-identifier-benchmark/src/androidTest/java/androidx/ads/identifier/benchmark/SampleAdvertisingIdProvider.java b/ads/ads-identifier-benchmark/src/androidTest/java/androidx/ads/identifier/benchmark/SampleAdvertisingIdProvider.java
deleted file mode 100644
index b53b1be..0000000
--- a/ads/ads-identifier-benchmark/src/androidTest/java/androidx/ads/identifier/benchmark/SampleAdvertisingIdProvider.java
+++ /dev/null
@@ -1,38 +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.ads.identifier.benchmark;
-
-import androidx.annotation.NonNull;
-
-/** An example Advertising ID Provider which always returns same ID. */
-@SuppressWarnings("deprecation")
-public class SampleAdvertisingIdProvider implements
-        androidx.ads.identifier.provider.AdvertisingIdProvider {
-
-    static final String DUMMY_AD_ID = "308f629d-c857-4026-8b62-7bdd71caaaaa";
-
-    @NonNull
-    @Override
-    public String getId() {
-        return DUMMY_AD_ID;
-    }
-
-    @Override
-    public boolean isLimitAdTrackingEnabled() {
-        return false;
-    }
-}
diff --git a/ads/ads-identifier-benchmark/src/main/AndroidManifest.xml b/ads/ads-identifier-benchmark/src/main/AndroidManifest.xml
deleted file mode 100644
index cc0959e..0000000
--- a/ads/ads-identifier-benchmark/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<manifest />
diff --git a/ads/ads-identifier-common/api/restricted_current.txt b/ads/ads-identifier-common/api/restricted_current.txt
deleted file mode 100644
index e5c37ac..0000000
--- a/ads/ads-identifier-common/api/restricted_current.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-// Signature format: 4.0
-package androidx.ads.identifier {
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class AdvertisingIdUtils {
-    method public static java.util.List<android.content.pm.ServiceInfo!> getAdvertisingIdProviderServices(android.content.pm.PackageManager);
-    method public static android.content.pm.ServiceInfo? selectServiceByPriority(java.util.List<android.content.pm.ServiceInfo!>, android.content.pm.PackageManager);
-    field public static final String GET_AD_ID_ACTION = "androidx.ads.identifier.provider.GET_AD_ID";
-  }
-
-}
-
diff --git a/ads/ads-identifier-common/build.gradle b/ads/ads-identifier-common/build.gradle
deleted file mode 100644
index c753038..0000000
--- a/ads/ads-identifier-common/build.gradle
+++ /dev/null
@@ -1,49 +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.
- */
-
-import androidx.build.Publish
-
-plugins {
-    id("AndroidXPlugin")
-    id("com.android.library")
-}
-
-dependencies {
-    api("androidx.annotation:annotation:1.1.0")
-
-    testImplementation(project(":ads:ads-identifier-testing"))
-    testImplementation(libs.testRunner)
-    testImplementation(libs.junit)
-    testImplementation(libs.truth)
-    testImplementation(libs.mockitoCore4)
-    testImplementation(libs.robolectric)
-}
-
-android {
-    buildFeatures {
-        aidl = true
-    }
-    testOptions.unitTests.includeAndroidResources = true
-    namespace "androidx.ads.identifier.common"
-}
-
-androidx {
-    name = "AndroidX Ads Identifier Common"
-    publish = Publish.SNAPSHOT_AND_RELEASE
-    mavenVersion = LibraryVersions.ADS_IDENTIFIER
-    inceptionYear = "2019"
-    description = "AndroidX Ads Identifier Common"
-}
diff --git a/ads/ads-identifier-common/lint-baseline.xml b/ads/ads-identifier-common/lint-baseline.xml
deleted file mode 100644
index 6230722..0000000
--- a/ads/ads-identifier-common/lint-baseline.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?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">
-
-    <issue
-        id="PrivateConstructorForUtilityClass"
-        message="Utility class is missing private constructor"
-        errorLine1="public class AdvertisingIdUtils {"
-        errorLine2="             ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/ads/identifier/AdvertisingIdUtils.java"/>
-    </issue>
-
-</issues>
diff --git a/ads/ads-identifier-common/src/main/AndroidManifest.xml b/ads/ads-identifier-common/src/main/AndroidManifest.xml
deleted file mode 100644
index d2c9474..0000000
--- a/ads/ads-identifier-common/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
-</manifest>
diff --git a/ads/ads-identifier-common/src/main/aidl/androidx/ads/identifier/provider/IAdvertisingIdService.aidl b/ads/ads-identifier-common/src/main/aidl/androidx/ads/identifier/provider/IAdvertisingIdService.aidl
deleted file mode 100644
index c5bdc98..0000000
--- a/ads/ads-identifier-common/src/main/aidl/androidx/ads/identifier/provider/IAdvertisingIdService.aidl
+++ /dev/null
@@ -1,13 +0,0 @@
-package androidx.ads.identifier.provider;
-
-/**
- * The Advertising ID service used to communicate between an Advertising ID Provider and the
- * developer library.
- *
- * <p>The Advertising ID is a resettable identifier used for ads purpose.
- * @hide
- */
-interface IAdvertisingIdService {
-    String getId() = 0;
-    boolean isLimitAdTrackingEnabled() = 1;
-}
diff --git a/ads/ads-identifier-common/src/main/java/androidx/ads/identifier/AdvertisingIdUtils.java b/ads/ads-identifier-common/src/main/java/androidx/ads/identifier/AdvertisingIdUtils.java
deleted file mode 100644
index 6423a69..0000000
--- a/ads/ads-identifier-common/src/main/java/androidx/ads/identifier/AdvertisingIdUtils.java
+++ /dev/null
@@ -1,176 +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.ads.identifier;
-
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.os.Build;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Internal utilities for Advertising ID.
- *
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class AdvertisingIdUtils {
-
-    /**
-     * The Intent action used to identify an Advertising ID Provider. The Advertising ID Provider
-     * Service should declare this as an intent-filter, so that clients can find it.
-     */
-    public static final String GET_AD_ID_ACTION = "androidx.ads.identifier.provider.GET_AD_ID";
-
-    /**
-     * The permission used to indicate which Advertising ID Provider should be used in case there
-     * are multiple Advertising ID Providers on the device. Device manufacturer (OEM) should only
-     * grant this permission to the designated Advertising ID Provider.
-     */
-    @VisibleForTesting
-    static final String HIGH_PRIORITY_PERMISSION = "androidx.ads.identifier.provider.HIGH_PRIORITY";
-
-    AdvertisingIdUtils() {
-    }
-
-    /**
-     * Retrieves a list of all Advertising ID Providers' services on this device.
-     *
-     * <p>This is achieved by looking up which services can handle {@link #GET_AD_ID_ACTION}
-     * intent action.
-     * <p>Only system-level providers will be returned.
-     */
-    @NonNull
-    @SuppressWarnings({"MixedMutabilityReturnType", "deprecation"})
-    public static List<ServiceInfo> getAdvertisingIdProviderServices(
-            @NonNull PackageManager packageManager) {
-        Intent intent = new Intent(GET_AD_ID_ACTION);
-
-        List<ResolveInfo> resolveInfos =
-                packageManager.queryIntentServices(intent,
-                        Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
-                                ? PackageManager.MATCH_SYSTEM_ONLY : 0);
-        if (resolveInfos == null || resolveInfos.isEmpty()) {
-            return Collections.emptyList();
-        }
-        List<ServiceInfo> systemLevelServiceInfos = new ArrayList<>();
-        for (ResolveInfo resolveInfo : resolveInfos) {
-            ServiceInfo serviceInfo = resolveInfo.serviceInfo;
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
-                    || isSystemByApplicationInfo(serviceInfo.packageName, packageManager)) {
-                systemLevelServiceInfos.add(serviceInfo);
-            }
-        }
-        return systemLevelServiceInfos;
-    }
-
-    @SuppressWarnings("deprecation")
-    private static boolean isSystemByApplicationInfo(
-            @NonNull String packageName, @NonNull PackageManager packageManager) {
-        try {
-            ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0);
-            return (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
-        } catch (PackageManager.NameNotFoundException ignored) {
-            // Ignore this provider if name not found.
-            return false;
-        }
-    }
-
-    /**
-     * Selects the Service of an Advertising ID Provider which should be used by developer
-     * library when requesting an Advertising ID.
-     *
-     * <p>Note: This method should only be used with the {@link ServiceInfo}s from
-     * {@link #getAdvertisingIdProviderServices} method, this currently means that only
-     * system-level Providers will be selected.
-     * <p>It will return the same Advertising ID Provider for all apps which use the developer
-     * library, using this priority:
-     * <ol>
-     * <li>Providers with {@link #HIGH_PRIORITY_PERMISSION} permission
-     * <li>Other Providers
-     * </ol>
-     * <p>If there are ties in any of the above categories, it will use this priority:
-     * <ol>
-     * <li>First app by earliest install time ({@link PackageInfo#firstInstallTime})
-     * <li>First app by package name alphabetically sorted
-     * </ol>
-     *
-     * @return null if the input {@code serviceInfos} is null or empty, or non of the input
-     * package is found.
-     */
-    @Nullable
-    @SuppressWarnings("deprecation")
-    public static ServiceInfo selectServiceByPriority(
-            @NonNull List<ServiceInfo> serviceInfos, @NonNull PackageManager packageManager) {
-        if (serviceInfos.isEmpty()) {
-            return null;
-        }
-        ServiceInfo selectedServiceInfo = null;
-        PackageInfo selectedPackageInfo = null;
-        for (ServiceInfo serviceInfo : serviceInfos) {
-            PackageInfo packageInfo;
-            try {
-                packageInfo =
-                        packageManager.getPackageInfo(
-                                serviceInfo.packageName, PackageManager.GET_PERMISSIONS);
-            } catch (PackageManager.NameNotFoundException ignored) {
-                // Ignore this provider if name not found.
-                continue;
-            }
-            if (selectedPackageInfo == null
-                    || hasHigherPriority(packageInfo, selectedPackageInfo)) {
-                selectedServiceInfo = serviceInfo;
-                selectedPackageInfo = packageInfo;
-            }
-        }
-        return selectedServiceInfo;
-    }
-
-    private static boolean hasHigherPriority(PackageInfo candidate, PackageInfo currentHighest) {
-        boolean isCandidateRequestHighPriority = isRequestHighPriority(candidate);
-        boolean isCurrentHighestRequestHighPriority = isRequestHighPriority(currentHighest);
-        if (isCandidateRequestHighPriority != isCurrentHighestRequestHighPriority) {
-            return isCandidateRequestHighPriority;
-        }
-        if (candidate.firstInstallTime != currentHighest.firstInstallTime) {
-            return candidate.firstInstallTime < currentHighest.firstInstallTime;
-        }
-        return candidate.packageName.compareTo(currentHighest.packageName) < 0;
-    }
-
-    private static boolean isRequestHighPriority(PackageInfo packageInfo) {
-        if (packageInfo.requestedPermissions == null) {
-            return false;
-        }
-        for (String permission : packageInfo.requestedPermissions) {
-            if (HIGH_PRIORITY_PERMISSION.equals(permission)) {
-                return true;
-            }
-        }
-        return false;
-    }
-}
diff --git a/ads/ads-identifier-common/src/test/java/androidx/ads/identifier/AdvertisingIdUtilsTest.java b/ads/ads-identifier-common/src/test/java/androidx/ads/identifier/AdvertisingIdUtilsTest.java
deleted file mode 100644
index 50f7933..0000000
--- a/ads/ads-identifier-common/src/test/java/androidx/ads/identifier/AdvertisingIdUtilsTest.java
+++ /dev/null
@@ -1,149 +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.ads.identifier;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
-
-import com.google.common.collect.Lists;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.internal.DoNotInstrument;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-@RunWith(RobolectricTestRunner.class)
-@DoNotInstrument
-public class AdvertisingIdUtilsTest {
-
-    private PackageManager mPackageManager;
-
-    @Before
-    public void setUp() {
-        mPackageManager = mock(PackageManager.class);
-    }
-
-    @Test
-    public void selectServiceByPriority() throws Exception {
-        List<ServiceInfo> serviceInfos = Lists.newArrayList(
-                createServiceInfo("c.normal.1", false, 1),
-                createServiceInfo("y.normal.0", false, 0),
-                createServiceInfo("x.normal.0", false, 0),
-                createServiceInfo("z.high.2", true, 2));
-
-        List<String> priorityList = getPriorityList(serviceInfos);
-
-        assertThat(priorityList).containsExactly(
-                "z.high.2",
-                "x.normal.0",
-                "y.normal.0",
-                "c.normal.1"
-        ).inOrder();
-    }
-
-    @Test
-    public void selectServiceByPriority_firstInstallTime() throws Exception {
-        List<ServiceInfo> serviceInfos = Lists.newArrayList(
-                createServiceInfo("com.a", false, 2),
-                createServiceInfo("com.b", false, 9),
-                createServiceInfo("com.c", false, 7),
-                createServiceInfo("com.d", false, 10),
-                createServiceInfo("com.e", false, 0));
-
-        List<String> priorityList = getPriorityList(serviceInfos);
-
-        assertThat(priorityList).containsExactly(
-                "com.e",
-                "com.a",
-                "com.c",
-                "com.b",
-                "com.d"
-        ).inOrder();
-    }
-
-    @Test
-    public void selectServiceByPriority_packageName() throws Exception {
-        List<ServiceInfo> serviceInfos = Lists.newArrayList(
-                createServiceInfo("com.abc.id", false, 0),
-                createServiceInfo("com.abc", false, 0),
-                createServiceInfo("org.example", false, 0),
-                createServiceInfo("com.abcde", false, 0),
-                createServiceInfo("com.abcde_id", false, 0));
-
-        List<String> priorityList = getPriorityList(serviceInfos);
-
-        assertThat(priorityList).containsExactly(
-                "com.abc",
-                "com.abc.id",
-                "com.abcde",
-                "com.abcde_id",
-                "org.example"
-        ).inOrder();
-    }
-
-    private List<String> getPriorityList(List<ServiceInfo> serviceInfos) {
-        List<String> result = new ArrayList<>();
-        while (serviceInfos.size() > 0) {
-            final ServiceInfo serviceInfo =
-                    AdvertisingIdUtils.selectServiceByPriority(serviceInfos, mPackageManager);
-
-            result.add(serviceInfo.packageName);
-
-            serviceInfos.remove(serviceInfo);
-        }
-        return result;
-    }
-
-    @Test
-    public void selectServiceByPriority_inputEmpty() {
-        ServiceInfo serviceInfo = AdvertisingIdUtils.selectServiceByPriority(
-                Collections.emptyList(), mPackageManager);
-
-        assertThat(serviceInfo).isNull();
-    }
-
-    @SuppressWarnings("deprecation")
-    private ServiceInfo createServiceInfo(String packageName, boolean requestHighPriority,
-            long firstInstallTime) throws Exception {
-        PackageInfo packageInfo = new PackageInfo();
-        packageInfo.packageName = packageName;
-        if (requestHighPriority) {
-            packageInfo.requestedPermissions =
-                    new String[]{AdvertisingIdUtils.HIGH_PRIORITY_PERMISSION};
-        }
-        packageInfo.firstInstallTime = firstInstallTime;
-
-        when(mPackageManager.getPackageInfo(eq(packageName), eq(PackageManager.GET_PERMISSIONS)))
-                .thenReturn(packageInfo);
-
-        ServiceInfo serviceInfo = mock(ServiceInfo.class);
-        serviceInfo.packageName = packageName;
-        return serviceInfo;
-    }
-}
diff --git a/ads/ads-identifier-common/src/test/resources/robolectric.properties b/ads/ads-identifier-common/src/test/resources/robolectric.properties
deleted file mode 100644
index 7946f01..0000000
--- a/ads/ads-identifier-common/src/test/resources/robolectric.properties
+++ /dev/null
@@ -1 +0,0 @@
-# robolectric properties
\ No newline at end of file
diff --git a/ads/ads-identifier-provider/api/current.txt b/ads/ads-identifier-provider/api/current.txt
deleted file mode 100644
index 19e5667..0000000
--- a/ads/ads-identifier-provider/api/current.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-// Signature format: 4.0
-package androidx.ads.identifier.provider {
-
-  @Deprecated public interface AdvertisingIdProvider {
-    method @Deprecated public String getId();
-    method @Deprecated public boolean isLimitAdTrackingEnabled();
-  }
-
-  @Deprecated @com.google.auto.value.AutoValue @com.google.auto.value.AutoValue.CopyAnnotations public abstract class AdvertisingIdProviderInfo {
-    method @Deprecated public abstract String getPackageName();
-    method @Deprecated public abstract android.content.Intent? getSettingsIntent();
-    method @Deprecated public abstract boolean isHighestPriority();
-  }
-
-  @Deprecated public class AdvertisingIdProviderManager {
-    method @Deprecated public static java.util.List<androidx.ads.identifier.provider.AdvertisingIdProviderInfo!> getAdvertisingIdProviders(android.content.Context);
-    method @Deprecated public static void registerProviderCallable(java.util.concurrent.Callable<androidx.ads.identifier.provider.AdvertisingIdProvider!>);
-  }
-
-}
-
diff --git a/ads/ads-identifier-provider/api/public_plus_experimental_current.txt b/ads/ads-identifier-provider/api/public_plus_experimental_current.txt
deleted file mode 100644
index 19e5667..0000000
--- a/ads/ads-identifier-provider/api/public_plus_experimental_current.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-// Signature format: 4.0
-package androidx.ads.identifier.provider {
-
-  @Deprecated public interface AdvertisingIdProvider {
-    method @Deprecated public String getId();
-    method @Deprecated public boolean isLimitAdTrackingEnabled();
-  }
-
-  @Deprecated @com.google.auto.value.AutoValue @com.google.auto.value.AutoValue.CopyAnnotations public abstract class AdvertisingIdProviderInfo {
-    method @Deprecated public abstract String getPackageName();
-    method @Deprecated public abstract android.content.Intent? getSettingsIntent();
-    method @Deprecated public abstract boolean isHighestPriority();
-  }
-
-  @Deprecated public class AdvertisingIdProviderManager {
-    method @Deprecated public static java.util.List<androidx.ads.identifier.provider.AdvertisingIdProviderInfo!> getAdvertisingIdProviders(android.content.Context);
-    method @Deprecated public static void registerProviderCallable(java.util.concurrent.Callable<androidx.ads.identifier.provider.AdvertisingIdProvider!>);
-  }
-
-}
-
diff --git a/ads/ads-identifier-provider/api/restricted_current.txt b/ads/ads-identifier-provider/api/restricted_current.txt
deleted file mode 100644
index 19e5667..0000000
--- a/ads/ads-identifier-provider/api/restricted_current.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-// Signature format: 4.0
-package androidx.ads.identifier.provider {
-
-  @Deprecated public interface AdvertisingIdProvider {
-    method @Deprecated public String getId();
-    method @Deprecated public boolean isLimitAdTrackingEnabled();
-  }
-
-  @Deprecated @com.google.auto.value.AutoValue @com.google.auto.value.AutoValue.CopyAnnotations public abstract class AdvertisingIdProviderInfo {
-    method @Deprecated public abstract String getPackageName();
-    method @Deprecated public abstract android.content.Intent? getSettingsIntent();
-    method @Deprecated public abstract boolean isHighestPriority();
-  }
-
-  @Deprecated public class AdvertisingIdProviderManager {
-    method @Deprecated public static java.util.List<androidx.ads.identifier.provider.AdvertisingIdProviderInfo!> getAdvertisingIdProviders(android.content.Context);
-    method @Deprecated public static void registerProviderCallable(java.util.concurrent.Callable<androidx.ads.identifier.provider.AdvertisingIdProvider!>);
-  }
-
-}
-
diff --git a/ads/ads-identifier-provider/build.gradle b/ads/ads-identifier-provider/build.gradle
deleted file mode 100644
index 395eea7..0000000
--- a/ads/ads-identifier-provider/build.gradle
+++ /dev/null
@@ -1,52 +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.
- */
-
-import androidx.build.Publish
-
-plugins {
-    id("AndroidXPlugin")
-    id("com.android.library")
-}
-
-dependencies {
-    api("androidx.annotation:annotation:1.1.0")
-    implementation("androidx.core:core:1.1.0")
-    implementation(libs.autoValueAnnotations)
-    annotationProcessor(libs.autoValue)
-
-    implementation(project(":ads:ads-identifier-common"))
-
-    androidTestImplementation(project(":ads:ads-identifier-testing"))
-    androidTestImplementation(libs.junit)
-    androidTestImplementation(libs.truth)
-    androidTestImplementation(libs.testExtJunit)
-    androidTestImplementation(libs.testCore)
-    androidTestImplementation(libs.testRunner)
-    androidTestImplementation(libs.testRules)
-    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy)
-}
-
-androidx {
-    name = "AndroidX Ads Identifier Provider"
-    publish = Publish.SNAPSHOT_AND_RELEASE
-    mavenVersion = LibraryVersions.ADS_IDENTIFIER
-    inceptionYear = "2019"
-    description = "AndroidX Ads Identifier Provider"
-}
-
-android {
-    namespace "androidx.ads.identifier.provider"
-}
diff --git a/ads/ads-identifier-provider/integration-tests/testapp/build.gradle b/ads/ads-identifier-provider/integration-tests/testapp/build.gradle
deleted file mode 100644
index a03737b..0000000
--- a/ads/ads-identifier-provider/integration-tests/testapp/build.gradle
+++ /dev/null
@@ -1,33 +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.
- */
-plugins {
-    id("AndroidXPlugin")
-    id("com.android.application")
-}
-
-android {
-    defaultConfig {
-        applicationId "androidx.ads.identifier.provider.testapp"
-        minSdkVersion 14
-    }
-    namespace "androidx.ads.identifier.provider.testapp"
-}
-
-dependencies {
-    implementation(project(":ads:ads-identifier-provider"))
-    implementation("androidx.recyclerview:recyclerview:1.0.0")
-    api("androidx.annotation:annotation:1.1.0")
-}
diff --git a/ads/ads-identifier-provider/integration-tests/testapp/lint-baseline.xml b/ads/ads-identifier-provider/integration-tests/testapp/lint-baseline.xml
deleted file mode 100644
index 0160194..0000000
--- a/ads/ads-identifier-provider/integration-tests/testapp/lint-baseline.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?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">
-
-    <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="    protected void onCreate(Bundle savedInstanceState) {"
-        errorLine2="                            ~~~~~~">
-        <location
-            file="src/main/java/androidx/ads/identifier/provider/testapp/AdsIdentifierProviderActivity.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 void listProviders(View view) {"
-        errorLine2="                              ~~~~">
-        <location
-            file="src/main/java/androidx/ads/identifier/provider/testapp/AdsIdentifierProviderActivity.java"/>
-    </issue>
-
-</issues>
diff --git a/ads/ads-identifier-provider/integration-tests/testapp/src/main/AndroidManifest.xml b/ads/ads-identifier-provider/integration-tests/testapp/src/main/AndroidManifest.xml
deleted file mode 100644
index ac86787..0000000
--- a/ads/ads-identifier-provider/integration-tests/testapp/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools">
-
-    <application
-        android:name=".AdsIdentifierProviderApplication"
-        android:allowBackup="false"
-        android:label="@string/app_name"
-        tools:ignore="GoogleAppIndexingWarning,MissingApplicationIcon">
-
-        <activity
-            android:name=".AdsIdentifierProviderActivity"
-            android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-
-</manifest>
diff --git a/ads/ads-identifier-provider/integration-tests/testapp/src/main/java/androidx/ads/identifier/provider/testapp/AdsIdentifierProviderActivity.java b/ads/ads-identifier-provider/integration-tests/testapp/src/main/java/androidx/ads/identifier/provider/testapp/AdsIdentifierProviderActivity.java
deleted file mode 100644
index ebdad4d..0000000
--- a/ads/ads-identifier-provider/integration-tests/testapp/src/main/java/androidx/ads/identifier/provider/testapp/AdsIdentifierProviderActivity.java
+++ /dev/null
@@ -1,126 +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.ads.identifier.provider.testapp;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.TextView;
-
-import androidx.ads.identifier.provider.AdvertisingIdProviderInfo;
-import androidx.ads.identifier.provider.AdvertisingIdProviderManager;
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Simple activity as an Advertising ID Provider.
- */
-public class AdsIdentifierProviderActivity extends Activity {
-
-    private Adapter mAdapter;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_ads_identifier_provider);
-
-        RecyclerView recyclerView = findViewById(R.id.recycler_view);
-        recyclerView.setLayoutManager(new LinearLayoutManager(this));
-
-        mAdapter = new Adapter();
-        recyclerView.setAdapter(mAdapter);
-    }
-
-    /** Lists all the Advertising ID Providers. */
-    public void listProviders(View view) {
-        List<AdvertisingIdProviderInfo> allAdIdProviders =
-                AdvertisingIdProviderManager.getAdvertisingIdProviders(this);
-
-        TextView textView = findViewById(R.id.main_text);
-        textView.setText("There are " + allAdIdProviders.size() + " provider(s) on the device.\n");
-
-        mAdapter.setAdvertisingIdProviderInfoList(allAdIdProviders);
-    }
-
-    static class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {
-
-        private List<AdvertisingIdProviderInfo> mAdvertisingIdProviderInfoList;
-
-        static class ViewHolder extends RecyclerView.ViewHolder {
-            @NonNull
-            TextView mTextView;
-            @NonNull
-            Button mButton;
-
-            ViewHolder(@NonNull View view) {
-                super(view);
-                mTextView = view.findViewById(R.id.text);
-                mButton = view.findViewById(R.id.button);
-            }
-        }
-
-        Adapter() {
-            mAdvertisingIdProviderInfoList = Collections.emptyList();
-        }
-
-        void setAdvertisingIdProviderInfoList(
-                List<AdvertisingIdProviderInfo> advertisingIdProviderInfoList) {
-            mAdvertisingIdProviderInfoList = advertisingIdProviderInfoList;
-            notifyDataSetChanged();
-        }
-
-        @NonNull
-        @Override
-        public Adapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
-            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
-            View view = inflater.inflate(R.layout.provider_info, parent, false);
-            return new ViewHolder(view);
-        }
-
-        @Override
-        public void onBindViewHolder(@NonNull Adapter.ViewHolder holder, int position) {
-            AdvertisingIdProviderInfo providerInfo = mAdvertisingIdProviderInfoList.get(position);
-            TextView textView = holder.mTextView;
-            textView.setText("Package name: " + providerInfo.getPackageName() + "\n");
-
-            textView.append("Settings UI intent: " + providerInfo.getSettingsIntent() + "\n");
-            if (providerInfo.getSettingsIntent() != null) {
-                holder.mButton.setClickable(true);
-                holder.mButton.setOnClickListener(view -> {
-                    view.getContext().startActivity(providerInfo.getSettingsIntent());
-                });
-            } else {
-                holder.mButton.setClickable(false);
-                textView.append(textView.getResources().getString(R.string.empty_settings_warning));
-            }
-
-            textView.append("Is highest priority: " + providerInfo.isHighestPriority() + "\n");
-        }
-
-        @Override
-        public int getItemCount() {
-            return mAdvertisingIdProviderInfoList.size();
-        }
-    }
-}
diff --git a/ads/ads-identifier-provider/integration-tests/testapp/src/main/java/androidx/ads/identifier/provider/testapp/AdsIdentifierProviderApplication.java b/ads/ads-identifier-provider/integration-tests/testapp/src/main/java/androidx/ads/identifier/provider/testapp/AdsIdentifierProviderApplication.java
deleted file mode 100644
index 77e2fea..0000000
--- a/ads/ads-identifier-provider/integration-tests/testapp/src/main/java/androidx/ads/identifier/provider/testapp/AdsIdentifierProviderApplication.java
+++ /dev/null
@@ -1,35 +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.ads.identifier.provider.testapp;
-
-import android.app.Application;
-
-import androidx.ads.identifier.provider.AdvertisingIdProviderManager;
-
-/**
- * This will show you how to make your own Advertising ID Provider for providing the developer
- * library an Advertising ID when requested by apps.
- */
-public class AdsIdentifierProviderApplication extends Application {
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-
-        AdvertisingIdProviderManager.registerProviderCallable(SampleAdvertisingIdProvider::new);
-    }
-}
diff --git a/ads/ads-identifier-provider/integration-tests/testapp/src/main/java/androidx/ads/identifier/provider/testapp/SampleAdvertisingIdProvider.java b/ads/ads-identifier-provider/integration-tests/testapp/src/main/java/androidx/ads/identifier/provider/testapp/SampleAdvertisingIdProvider.java
deleted file mode 100644
index fc5c2af..0000000
--- a/ads/ads-identifier-provider/integration-tests/testapp/src/main/java/androidx/ads/identifier/provider/testapp/SampleAdvertisingIdProvider.java
+++ /dev/null
@@ -1,35 +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.ads.identifier.provider.testapp;
-
-import androidx.ads.identifier.provider.AdvertisingIdProvider;
-import androidx.annotation.NonNull;
-
-/** An example Advertising ID Provider which always returns same ID. */
-public class SampleAdvertisingIdProvider implements AdvertisingIdProvider {
-
-    @NonNull
-    @Override
-    public String getId() {
-        return "308f629d-c857-4026-8b62-7bdd71caaaaa";
-    }
-
-    @Override
-    public boolean isLimitAdTrackingEnabled() {
-        return false;
-    }
-}
diff --git a/ads/ads-identifier-provider/integration-tests/testapp/src/main/res/layout/activity_ads_identifier_provider.xml b/ads/ads-identifier-provider/integration-tests/testapp/src/main/res/layout/activity_ads_identifier_provider.xml
deleted file mode 100644
index 9c36015..0000000
--- a/ads/ads-identifier-provider/integration-tests/testapp/src/main/res/layout/activity_ads_identifier_provider.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical"
-    tools:context="androidx.ads.identifier.provider.testapp.AdsIdentifierProviderActivity">
-
-    <LinearLayout
-        style="?android:attr/buttonBarStyle"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal">
-
-        <Button
-            style="?android:attr/buttonBarButtonStyle"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:onClick="listProviders"
-            android:text="@string/list_providers" />
-    </LinearLayout>
-
-    <TextView
-        android:id="@+id/main_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="" />
-
-    <androidx.recyclerview.widget.RecyclerView
-        android:id="@+id/recycler_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:scrollbars="vertical"
-        tools:listitem="@layout/provider_info" />
-</LinearLayout>
diff --git a/ads/ads-identifier-provider/integration-tests/testapp/src/main/res/layout/provider_info.xml b/ads/ads-identifier-provider/integration-tests/testapp/src/main/res/layout/provider_info.xml
deleted file mode 100644
index 1f433ae..0000000
--- a/ads/ads-identifier-provider/integration-tests/testapp/src/main/res/layout/provider_info.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="horizontal">
-
-    <TextView
-        android:id="@+id/text"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_weight="1" />
-
-    <Button
-        android:id="@+id/button"
-        android:layout_width="100dp"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:text="@string/settings" />
-</LinearLayout>
\ No newline at end of file
diff --git a/ads/ads-identifier-provider/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml b/ads/ads-identifier-provider/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
deleted file mode 100644
index b738e32..0000000
--- a/ads/ads-identifier-provider/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<resources>
-    <string name="app_name">Ad ID Provider</string>
-    <string name="list_providers">List Providers</string>
-    <string name="settings">Settings</string>
-    <string name="empty_settings_warning">*WARNING*: This provider should mark its settings activity
-        with the intent androidx.ads.identifier.provider.OPEN_SETTINGS.\n</string>
-</resources>
\ No newline at end of file
diff --git a/ads/ads-identifier-provider/src/androidTest/AndroidManifest.xml b/ads/ads-identifier-provider/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 7c37821..0000000
--- a/ads/ads-identifier-provider/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<!--
-  Copyright (C) 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License
-  -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
-</manifest>
diff --git a/ads/ads-identifier-provider/src/androidTest/java/androidx/ads/identifier/provider/AdvertisingIdProviderManagerTest.java b/ads/ads-identifier-provider/src/androidTest/java/androidx/ads/identifier/provider/AdvertisingIdProviderManagerTest.java
deleted file mode 100644
index b08c910..0000000
--- a/ads/ads-identifier-provider/src/androidTest/java/androidx/ads/identifier/provider/AdvertisingIdProviderManagerTest.java
+++ /dev/null
@@ -1,237 +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.ads.identifier.provider;
-
-import static androidx.ads.identifier.testing.MockPackageManagerHelper.createActivityResolveInfo;
-import static androidx.ads.identifier.testing.MockPackageManagerHelper.createServiceResolveInfo;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-
-import androidx.ads.identifier.testing.MockPackageManagerHelper;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import com.google.common.collect.Lists;
-import com.google.common.truth.Correspondence;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.Callable;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@SuppressWarnings("deprecation")
-public class AdvertisingIdProviderManagerTest {
-
-    private static final Correspondence<AdvertisingIdProviderInfo, AdvertisingIdProviderInfo>
-            PROVIDER_INFO_EQUALITY = Correspondence.from(
-            AdvertisingIdProviderManagerTest::isProviderInfoEqual, "is equivalent to");
-
-    private MockPackageManagerHelper mMockPackageManagerHelper = new MockPackageManagerHelper();
-
-    private Context mContext;
-
-    @Before
-    public void setUp() {
-        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
-
-        mContext = new ContextWrapper(context) {
-            @Override
-            public PackageManager getPackageManager() {
-                return mMockPackageManagerHelper.getMockPackageManager();
-            }
-        };
-    }
-
-    @Test
-    public void getAllAdIdProviders_onlySelf() throws Exception {
-        mMockPackageManagerHelper.mockQueryGetAdIdServices(
-                Lists.newArrayList(createServiceResolveInfo(mContext.getPackageName())));
-
-        assertThat(AdvertisingIdProviderManager.getAdvertisingIdProviders(mContext))
-                .comparingElementsUsing(PROVIDER_INFO_EQUALITY)
-                .containsExactly(
-                        AdvertisingIdProviderInfo.builder()
-                                .setPackageName(mContext.getPackageName())
-                                .setHighestPriority(true)
-                                .build());
-    }
-
-    @Test
-    public void getAllAdIdProviders_noProvider() {
-        assertThat(AdvertisingIdProviderManager.getAdvertisingIdProviders(mContext)).isEmpty();
-    }
-
-    @Test
-    public void getAllAdIdProviders() throws Exception {
-        mMockPackageManagerHelper.mockQueryGetAdIdServices(
-                Lists.newArrayList(
-                        createServiceResolveInfo(mContext.getPackageName()),
-                        createServiceResolveInfo("com.a")));
-
-        assertThat(AdvertisingIdProviderManager.getAdvertisingIdProviders(mContext))
-                .comparingElementsUsing(PROVIDER_INFO_EQUALITY)
-                .containsExactly(
-                        AdvertisingIdProviderInfo.builder()
-                                .setPackageName(mContext.getPackageName())
-                                .setHighestPriority(true)
-                                .build(),
-                        AdvertisingIdProviderInfo.builder().setPackageName("com.a").build());
-    }
-
-    @Test
-    public void getAllAdIdProviders_withOpenIntent() throws Exception {
-        mMockPackageManagerHelper.mockQueryGetAdIdServices(
-                Lists.newArrayList(
-                        createServiceResolveInfo(mContext.getPackageName()),
-                        createServiceResolveInfo("com.a")));
-
-        mMockPackageManagerHelper.mockQueryOpenSettingsActivities(
-                Lists.newArrayList(
-                        createActivityResolveInfo(mContext.getPackageName(), "Activity"),
-                        createActivityResolveInfo("com.a", "A")));
-
-        assertThat(AdvertisingIdProviderManager.getAdvertisingIdProviders(mContext))
-                .comparingElementsUsing(PROVIDER_INFO_EQUALITY)
-                .containsExactly(
-                        AdvertisingIdProviderInfo.builder()
-                                .setPackageName(mContext.getPackageName())
-                                .setSettingsIntent(new Intent(
-                                        androidx.ads.identifier.provider
-                                                .AdvertisingIdProviderManager.OPEN_SETTINGS_ACTION)
-                                        .setClassName(mContext.getPackageName(), "Activity"))
-                                .setHighestPriority(true)
-                                .build(),
-                        AdvertisingIdProviderInfo.builder()
-                                .setPackageName("com.a")
-                                .setSettingsIntent(new Intent(
-                                        androidx.ads.identifier.provider
-                                                .AdvertisingIdProviderManager.OPEN_SETTINGS_ACTION)
-                                        .setClassName("com.a", "A"))
-                                .build());
-    }
-
-    @Test
-    public void getAllAdIdProviders_twoOtherProviders() throws Exception {
-        mMockPackageManagerHelper.mockQueryGetAdIdServices(
-                Lists.newArrayList(
-                        createServiceResolveInfo(mContext.getPackageName()),
-                        createServiceResolveInfo("com.a"),
-                        createServiceResolveInfo("com.b")));
-
-        mMockPackageManagerHelper.mockQueryOpenSettingsActivities(
-                Lists.newArrayList(
-                        createActivityResolveInfo(mContext.getPackageName(), "Activity"),
-                        createActivityResolveInfo("com.a", "A")));
-
-        assertThat(AdvertisingIdProviderManager.getAdvertisingIdProviders(mContext))
-                .comparingElementsUsing(PROVIDER_INFO_EQUALITY)
-                .containsExactly(
-                        AdvertisingIdProviderInfo.builder()
-                                .setPackageName(mContext.getPackageName())
-                                .setSettingsIntent(new Intent(
-                                        androidx.ads.identifier.provider
-                                                .AdvertisingIdProviderManager.OPEN_SETTINGS_ACTION)
-                                        .setClassName(mContext.getPackageName(), "Activity"))
-                                .setHighestPriority(true)
-                                .build(),
-                        AdvertisingIdProviderInfo.builder()
-                                .setPackageName("com.a")
-                                .setSettingsIntent(new Intent(
-                                        androidx.ads.identifier.provider
-                                                .AdvertisingIdProviderManager.OPEN_SETTINGS_ACTION)
-                                        .setClassName("com.a", "A"))
-                                .build(),
-                        AdvertisingIdProviderInfo.builder()
-                                .setPackageName("com.b")
-                                .build());
-    }
-
-    @Test
-    public void getAllAdIdProviders_extraOpenIntent() throws Exception {
-        mMockPackageManagerHelper.mockQueryGetAdIdServices(
-                Lists.newArrayList(
-                        createServiceResolveInfo(mContext.getPackageName()),
-                        createServiceResolveInfo("com.a")));
-
-        mMockPackageManagerHelper.mockQueryOpenSettingsActivities(
-                Lists.newArrayList(
-                        createActivityResolveInfo(mContext.getPackageName(), "Activity"),
-                        createActivityResolveInfo("com.a", "A"),
-                        createActivityResolveInfo("com.b", "B")));
-
-        assertThat(AdvertisingIdProviderManager.getAdvertisingIdProviders(mContext))
-                .comparingElementsUsing(PROVIDER_INFO_EQUALITY)
-                .containsExactly(
-                        AdvertisingIdProviderInfo.builder()
-                                .setPackageName(mContext.getPackageName())
-                                .setSettingsIntent(new Intent(
-                                        androidx.ads.identifier.provider
-                                                .AdvertisingIdProviderManager.OPEN_SETTINGS_ACTION)
-                                        .setClassName(mContext.getPackageName(), "Activity"))
-                                .setHighestPriority(true)
-                                .build(),
-                        AdvertisingIdProviderInfo.builder()
-                                .setPackageName("com.a")
-                                .setSettingsIntent(new Intent(
-                                        androidx.ads.identifier.provider
-                                                .AdvertisingIdProviderManager.OPEN_SETTINGS_ACTION)
-                                        .setClassName("com.a", "A"))
-                                .build());
-    }
-
-    @Test
-    public void registerProviderCallable() {
-        Callable<AdvertisingIdProvider> providerCallable = () -> null;
-
-        AdvertisingIdProviderManager.registerProviderCallable(providerCallable);
-
-        assertThat(AdvertisingIdProviderManager.getProviderCallable())
-                .isSameInstanceAs(providerCallable);
-    }
-
-    @Test(expected = NullPointerException.class)
-    public void registerProviderCallable_null() {
-        AdvertisingIdProviderManager.registerProviderCallable(null);
-    }
-
-    @Test
-    public void clearProviderCallable() {
-        Callable<AdvertisingIdProvider> providerCallable = () -> null;
-
-        AdvertisingIdProviderManager.registerProviderCallable(providerCallable);
-        AdvertisingIdProviderManager.clearProviderCallable();
-
-        assertThat(AdvertisingIdProviderManager.getProviderCallable()).isNull();
-    }
-
-    private static boolean isProviderInfoEqual(
-            AdvertisingIdProviderInfo actual, AdvertisingIdProviderInfo expected) {
-        return actual.getPackageName().equals(expected.getPackageName())
-                && (actual.getSettingsIntent() == null ? expected.getSettingsIntent() == null
-                : actual.getSettingsIntent().filterEquals(expected.getSettingsIntent()))
-                && actual.isHighestPriority() == expected.isHighestPriority();
-    }
-}
diff --git a/ads/ads-identifier-provider/src/androidTest/java/androidx/ads/identifier/provider/internal/AdvertisingIdServiceTest.java b/ads/ads-identifier-provider/src/androidTest/java/androidx/ads/identifier/provider/internal/AdvertisingIdServiceTest.java
deleted file mode 100644
index e34d9e8..0000000
--- a/ads/ads-identifier-provider/src/androidTest/java/androidx/ads/identifier/provider/internal/AdvertisingIdServiceTest.java
+++ /dev/null
@@ -1,166 +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.ads.identifier.provider.internal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-
-import androidx.ads.identifier.AdvertisingIdUtils;
-import androidx.ads.identifier.provider.IAdvertisingIdService;
-import androidx.annotation.NonNull;
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.util.concurrent.BlockingDeque;
-import java.util.concurrent.LinkedBlockingDeque;
-import java.util.concurrent.TimeUnit;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@SuppressWarnings("deprecation")
-public class AdvertisingIdServiceTest {
-    private static final String TESTING_AD_ID = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
-
-    @Rule
-    public MockitoRule mMockitoRule = MockitoJUnit.rule();
-
-    private Context mContext;
-    private Intent mIntent;
-    private ServiceConnection mServiceConnection;
-
-    @Before
-    public void setUp() {
-        androidx.ads.identifier.provider.AdvertisingIdProviderManager.clearProviderCallable();
-
-        mContext = ApplicationProvider.getApplicationContext();
-
-        mIntent = new Intent(AdvertisingIdUtils.GET_AD_ID_ACTION);
-        mIntent.setClassName(mContext.getPackageName(), AdvertisingIdService.class.getName());
-    }
-
-    @After
-    public void tearDown() {
-        if (mServiceConnection != null) {
-            mContext.unbindService(mServiceConnection);
-        }
-        mContext.stopService(mIntent);
-    }
-
-    private IAdvertisingIdService getService() throws InterruptedException {
-        BlockingDeque<IAdvertisingIdService> blockingDeque = new LinkedBlockingDeque<>();
-        mServiceConnection = new ServiceConnection() {
-            @Override
-            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
-                blockingDeque.add(IAdvertisingIdService.Stub.asInterface(iBinder));
-            }
-
-            @Override
-            public void onServiceDisconnected(ComponentName componentName) {
-            }
-        };
-        mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
-        return blockingDeque.poll(1, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void getId() throws Exception {
-        androidx.ads.identifier.provider.AdvertisingIdProviderManager.registerProviderCallable(
-                () -> new MockAdvertisingIdProvider(TESTING_AD_ID, true));
-
-        IAdvertisingIdService service = getService();
-
-        assertThat(service.getId()).isEqualTo(TESTING_AD_ID);
-        assertThat(service.isLimitAdTrackingEnabled()).isEqualTo(true);
-    }
-
-    @Test(expected = RuntimeException.class)
-    public void getId_providerThrowsException() throws Exception {
-        androidx.ads.identifier.provider.AdvertisingIdProviderManager.registerProviderCallable(
-                () -> {
-                    MockAdvertisingIdProvider mockAdvertisingIdProvider =
-                            new MockAdvertisingIdProvider(TESTING_AD_ID, true);
-                    mockAdvertisingIdProvider.mGetIdThrowsException = true;
-                    return mockAdvertisingIdProvider;
-                });
-
-        IAdvertisingIdService service = getService();
-        service.getId();
-    }
-
-    private static class MockAdvertisingIdProvider implements
-            androidx.ads.identifier.provider.AdvertisingIdProvider {
-        private final String mId;
-        private final boolean mLimitAdTrackingEnabled;
-        boolean mGetIdThrowsException = false;
-
-        MockAdvertisingIdProvider(String id, boolean limitAdTrackingEnabled) {
-            mId = id;
-            mLimitAdTrackingEnabled = limitAdTrackingEnabled;
-        }
-
-        @NonNull
-        @Override
-        public String getId() {
-            if (mGetIdThrowsException) {
-                throw new RuntimeException();
-            }
-            return mId;
-        }
-
-        @Override
-        public boolean isLimitAdTrackingEnabled() {
-            return mLimitAdTrackingEnabled;
-        }
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void getId_providerNotRegistered() {
-        AdvertisingIdService.getAdvertisingIdProvider();
-    }
-
-    @Test(expected = RuntimeException.class)
-    public void getId_providerCallableThrowsException() {
-        androidx.ads.identifier.provider.AdvertisingIdProviderManager.registerProviderCallable(
-                () -> {
-                    throw new Exception();
-                });
-
-        AdvertisingIdService.getAdvertisingIdProvider();
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void getId_providerCallableReturnsNull() {
-        androidx.ads.identifier.provider.AdvertisingIdProviderManager.registerProviderCallable(
-                () -> null);
-
-        AdvertisingIdService.getAdvertisingIdProvider();
-    }
-}
diff --git a/ads/ads-identifier-provider/src/main/AndroidManifest.xml b/ads/ads-identifier-provider/src/main/AndroidManifest.xml
deleted file mode 100644
index da35f1a..0000000
--- a/ads/ads-identifier-provider/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools">
-
-    <application>
-        <service
-            android:name=".internal.AdvertisingIdService"
-            android:enabled="true"
-            android:exported="true"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService">
-            <intent-filter>
-                <action android:name="androidx.ads.identifier.provider.GET_AD_ID" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-            <meta-data
-                android:name="instantapps.clients.allowed"
-                android:value="true" />
-        </service>
-    </application>
-</manifest>
diff --git a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProvider.java b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProvider.java
deleted file mode 100644
index 91728a9..0000000
--- a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProvider.java
+++ /dev/null
@@ -1,41 +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.ads.identifier.provider;
-
-import androidx.annotation.NonNull;
-
-/**
- * The class for the AndroidX Advertising ID Provider that should provide the resettable ID and
- * LAT preference should implement this interface.
- *
- * See {@link AdvertisingIdProviderManager} for more details.
- *
- * <p>Note: The implementation of this interface must be completely thread-safe.
- *
- * @deprecated Use the
- * <a href="https://developers.google.com/android/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient">
- * Advertising ID API that's available as part of Google Play Services</a> instead of this library.
- */
-@Deprecated
-public interface AdvertisingIdProvider {
-    /** Retrieves the Advertising ID. */
-    @NonNull
-    String getId();
-
-    /** Retrieves whether the user has chosen to limit ad tracking (ads personalization). */
-    boolean isLimitAdTrackingEnabled();
-}
diff --git a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProviderInfo.java b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProviderInfo.java
deleted file mode 100644
index b3094695..0000000
--- a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProviderInfo.java
+++ /dev/null
@@ -1,87 +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.ads.identifier.provider;
-
-import android.content.Intent;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.google.auto.value.AutoValue;
-
-/**
- * A {@link AdvertisingIdProviderInfo} represents the information about an Advertising ID Provider
- * installed on the device.
- *
- * <p>Used in cases when there are multiple Advertising ID Providers on the device. See
- * {@link AdvertisingIdProviderManager#getAdvertisingIdProviders} for more details.
- *
- * @deprecated Use the
- * <a href="https://developers.google.com/android/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient">
- * Advertising ID API that's available as part of Google Play Services</a> instead of this library.
- */
-@Deprecated
-@AutoValue
-@AutoValue.CopyAnnotations
-public abstract class AdvertisingIdProviderInfo {
-
-    // Create a no-args constructor so it doesn't appear in current.txt
-    AdvertisingIdProviderInfo() {
-    }
-
-    /** Retrieves the Advertising ID Provider package name. */
-    @NonNull
-    public abstract String getPackageName();
-
-    /**
-     * Retrieves the {@link Intent} to open the Advertising ID settings page for a given
-     * Advertising ID Provider.
-     *
-     * <p>This page should allow the user to reset Advertising IDs and change Limit Advertising
-     * Tracking preference.
-     */
-    @Nullable
-    public abstract Intent getSettingsIntent();
-
-    /**
-     * Retrieves whether the provider has the highest priority among all the providers for the
-     * developer library, meaning its provided ID will be used.
-     */
-    public abstract boolean isHighestPriority();
-
-    /** Create a {@link Builder}. */
-    static Builder builder() {
-        return new AutoValue_AdvertisingIdProviderInfo.Builder().setHighestPriority(false);
-    }
-
-    /** The builder for {@link AdvertisingIdProviderInfo}. */
-    @AutoValue.Builder
-    abstract static class Builder {
-
-        // Create a no-args constructor so it doesn't appear in current.txt
-        Builder() {
-        }
-
-        abstract Builder setPackageName(String packageName);
-
-        abstract Builder setSettingsIntent(Intent settingsIntent);
-
-        abstract Builder setHighestPriority(boolean highestPriority);
-
-        abstract AdvertisingIdProviderInfo build();
-    }
-}
diff --git a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProviderManager.java b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProviderManager.java
deleted file mode 100644
index 844e7f3..0000000
--- a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProviderManager.java
+++ /dev/null
@@ -1,164 +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.ads.identifier.provider;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-
-import androidx.ads.identifier.AdvertisingIdUtils;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.VisibleForTesting;
-import androidx.core.util.Preconditions;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Callable;
-
-/**
- * The AdvertisingIdProviderManager will be used by an Advertising ID Provider to register the
- * provider implementation or retrieve all the Advertising ID Providers on the device.
- *
- * This package contains an implementation of the Advertising ID Provider Service that supports
- * IAdvertisingIdService.aidl for communication with the developer library, allowing you to
- * easily make your own Advertising ID Provider. Simply do the following:
- * <ol>
- * <li>Implement the {@link AdvertisingIdProvider} interface in the provider library. Developer apps
- * will be interacting with the provider through this programmatic interface.
- * <li>Register the implementation by calling {@link #registerProviderCallable} within the
- * provider’s {@link android.app.Application#onCreate} callback.
- * <li>Register the Advertising Id settings UI with the intent filter
- * "androidx.ads.identifier.provider.OPEN_SETTINGS".
- * </ol>
- *
- * @deprecated Use the
- * <a href="https://developers.google.com/android/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient">
- * Advertising ID API that's available as part of Google Play Services</a> instead of this library.
- */
-@Deprecated
-public class AdvertisingIdProviderManager {
-
-    @VisibleForTesting
-    static final String OPEN_SETTINGS_ACTION = "androidx.ads.identifier.provider.OPEN_SETTINGS";
-
-    private static Callable<AdvertisingIdProvider> sProviderCallable = null;
-
-    private AdvertisingIdProviderManager() {
-    }
-
-    /**
-     * Registers the {@link Callable} to create an instance of {@link AdvertisingIdProvider}.
-     *
-     * <p>This is used to lazy load the {@link AdvertisingIdProvider} when the Service is started.
-     * <p>This {@link Callable} will be called within the library's built-in Advertising ID
-     * Service's {@link android.app.Service#onCreate} method.
-     * <p>Provider could call this method to register the implementation in
-     * {@link android.app.Application#onCreate}, which is before
-     * {@link android.app.Service#onCreate} has been called.
-     */
-    public static void registerProviderCallable(
-            @NonNull Callable<AdvertisingIdProvider> providerCallable) {
-        sProviderCallable = Preconditions.checkNotNull(providerCallable);
-    }
-
-    /**
-     * Gets the {@link Callable} to create the Advertising ID Provider.
-     *
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    @Nullable
-    public static Callable<AdvertisingIdProvider> getProviderCallable() {
-        return sProviderCallable;
-    }
-
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    @VisibleForTesting
-    public static void clearProviderCallable() {
-        sProviderCallable = null;
-    }
-
-    /**
-     * Retrieves a list of all the Advertising ID Providers' information on this device, including
-     * self and other providers which is based on the AndroidX Advertising ID Provider library.
-     *
-     * <p>This method helps one Advertising ID Provider find other providers. One usage of this is
-     * to link to other providers' settings activity from one provider's settings activity, so the
-     * user of the device can manager all the providers' settings together.
-     */
-    @NonNull
-    @SuppressWarnings("MixedMutabilityReturnType")
-    public static List<AdvertisingIdProviderInfo> getAdvertisingIdProviders(
-            @NonNull Context context) {
-        PackageManager packageManager = context.getPackageManager();
-        List<ServiceInfo> serviceInfos =
-                AdvertisingIdUtils.getAdvertisingIdProviderServices(packageManager);
-        if (serviceInfos.isEmpty()) {
-            return Collections.emptyList();
-        }
-
-        Map<String, String> activityMap = getOpenSettingsActivities(packageManager);
-        ServiceInfo highestPriorityServiceInfo =
-                AdvertisingIdUtils.selectServiceByPriority(serviceInfos, packageManager);
-
-        List<AdvertisingIdProviderInfo> providerInfos = new ArrayList<>();
-        for (ServiceInfo serviceInfo : serviceInfos) {
-            String packageName = serviceInfo.packageName;
-
-            AdvertisingIdProviderInfo.Builder builder =
-                    AdvertisingIdProviderInfo.builder()
-                            .setPackageName(packageName)
-                            .setHighestPriority(serviceInfo == highestPriorityServiceInfo);
-            String activityName = activityMap.get(packageName);
-            if (activityName != null) {
-                builder.setSettingsIntent(
-                        new Intent(OPEN_SETTINGS_ACTION).setClassName(packageName, activityName));
-            }
-            providerInfos.add(builder.build());
-        }
-        return providerInfos;
-    }
-
-    /**
-     * Retrieves a {@link Map} from package name to settings activity name.
-     *
-     * <p>This is achieved by looking up which activities can handle {@link #OPEN_SETTINGS_ACTION}
-     * intent action.
-     */
-    @SuppressWarnings({"MixedMutabilityReturnType", "deprecation"})
-    private static Map<String, String> getOpenSettingsActivities(PackageManager packageManager) {
-        Intent settingsIntent = new Intent(OPEN_SETTINGS_ACTION);
-        List<ResolveInfo> settingsResolveInfos = packageManager.queryIntentActivities(
-                settingsIntent, 0);
-        if (settingsResolveInfos.isEmpty()) {
-            return Collections.emptyMap();
-        }
-        Map<String, String> activityMap = new HashMap<>();
-        for (ResolveInfo settingsResolveInfo : settingsResolveInfos) {
-            ActivityInfo settingsActivityInfo = settingsResolveInfo.activityInfo;
-            activityMap.put(settingsActivityInfo.packageName, settingsActivityInfo.name);
-        }
-        return activityMap;
-    }
-}
diff --git a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/internal/AdvertisingIdAidlServiceImpl.java b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/internal/AdvertisingIdAidlServiceImpl.java
deleted file mode 100644
index ccb84ecb..0000000
--- a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/internal/AdvertisingIdAidlServiceImpl.java
+++ /dev/null
@@ -1,44 +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.ads.identifier.provider.internal;
-
-import androidx.ads.identifier.provider.IAdvertisingIdService;
-
-/**
- * The implementation of the IAdvertisingIdService.aidl which retrieves values from
- * {@link androidx.ads.identifier.provider.AdvertisingIdProvider} and replies to the client.
- */
-@SuppressWarnings("deprecation")
-class AdvertisingIdAidlServiceImpl extends IAdvertisingIdService.Stub {
-
-    private final androidx.ads.identifier.provider.AdvertisingIdProvider mProvider;
-
-    AdvertisingIdAidlServiceImpl(
-            androidx.ads.identifier.provider.AdvertisingIdProvider advertisingIdProvider) {
-        mProvider = advertisingIdProvider;
-    }
-
-    @Override
-    public String getId() {
-        return mProvider.getId();
-    }
-
-    @Override
-    public boolean isLimitAdTrackingEnabled() {
-        return mProvider.isLimitAdTrackingEnabled();
-    }
-}
diff --git a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/internal/AdvertisingIdService.java b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/internal/AdvertisingIdService.java
deleted file mode 100644
index 4edeb4d..0000000
--- a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/internal/AdvertisingIdService.java
+++ /dev/null
@@ -1,67 +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.ads.identifier.provider.internal;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-
-import java.util.concurrent.Callable;
-
-/**
- * The internal service of AndroidX Advertising ID Provider library to provide the Advertising ID.
- */
-@SuppressWarnings("deprecation")
-public class AdvertisingIdService extends Service {
-
-    private AdvertisingIdAidlServiceImpl mAdvertisingIdAidlServiceImpl;
-
-    @Override
-    public void onCreate() {
-        mAdvertisingIdAidlServiceImpl =
-                new AdvertisingIdAidlServiceImpl(getAdvertisingIdProvider());
-    }
-
-    @Override
-    @NonNull
-    public IBinder onBind(@NonNull Intent intent) {
-        return mAdvertisingIdAidlServiceImpl;
-    }
-
-    @VisibleForTesting
-    @NonNull
-    static androidx.ads.identifier.provider.AdvertisingIdProvider getAdvertisingIdProvider() {
-        Callable<androidx.ads.identifier.provider.AdvertisingIdProvider> providerCallable =
-                androidx.ads.identifier.provider.AdvertisingIdProviderManager.getProviderCallable();
-        if (providerCallable == null) {
-            throw new IllegalStateException("Advertising ID Provider not registered.");
-        }
-        androidx.ads.identifier.provider.AdvertisingIdProvider advertisingIdProvider;
-        try {
-            advertisingIdProvider = providerCallable.call();
-        } catch (Exception e) {
-            throw new RuntimeException("Could not fetch the Advertising ID Provider.", e);
-        }
-        if (advertisingIdProvider == null) {
-            throw new IllegalArgumentException("Fetched Advertising ID Provider is null.");
-        }
-        return advertisingIdProvider;
-    }
-}
diff --git a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/internal/package-info.java b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/internal/package-info.java
deleted file mode 100644
index 2b8c7ca..0000000
--- a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/internal/package-info.java
+++ /dev/null
@@ -1,24 +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.
- */
-
-/**
- */
-@RestrictTo(LIBRARY)
-package androidx.ads.identifier.provider.internal;
-
-import static androidx.annotation.RestrictTo.Scope.LIBRARY;
-
-import androidx.annotation.RestrictTo;
diff --git a/ads/ads-identifier-testing/build.gradle b/ads/ads-identifier-testing/build.gradle
deleted file mode 100644
index b1a7b7a..0000000
--- a/ads/ads-identifier-testing/build.gradle
+++ /dev/null
@@ -1,39 +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.
- */
-
-import androidx.build.LibraryType
-
-plugins {
-    id("AndroidXPlugin")
-    id("com.android.library")
-}
-
-dependencies {
-    implementation(project(":ads:ads-identifier-common"))
-    api("androidx.annotation:annotation:1.1.0")
-    api(libs.mockitoCore, excludes.bytebuddy)
-}
-
-android {
-    lintOptions {
-        disable "InvalidPackage" // Lint is unhappy about mockito package
-    }
-    namespace "androidx.ads.identifier.testing"
-}
-
-androidx {
-    type = LibraryType.INTERNAL_TEST_LIBRARY
-}
diff --git a/ads/ads-identifier-testing/src/main/AndroidManifest.xml b/ads/ads-identifier-testing/src/main/AndroidManifest.xml
deleted file mode 100644
index cc0959e..0000000
--- a/ads/ads-identifier-testing/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<manifest />
diff --git a/ads/ads-identifier-testing/src/main/java/androidx/ads/identifier/testing/MockPackageManagerHelper.java b/ads/ads-identifier-testing/src/main/java/androidx/ads/identifier/testing/MockPackageManagerHelper.java
deleted file mode 100644
index 1c0b28c..0000000
--- a/ads/ads-identifier-testing/src/main/java/androidx/ads/identifier/testing/MockPackageManagerHelper.java
+++ /dev/null
@@ -1,139 +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.ads.identifier.testing;
-
-import static androidx.ads.identifier.AdvertisingIdUtils.GET_AD_ID_ACTION;
-
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.when;
-
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.os.Build;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-
-/**
- * Testing utilities for Advertising ID.
- *
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class MockPackageManagerHelper {
-
-    private static final String OPEN_SETTINGS_ACTION =
-            "androidx.ads.identifier.provider.OPEN_SETTINGS";
-
-    @Mock
-    private PackageManager mMockPackageManager;
-
-    public MockPackageManagerHelper() {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    @NonNull
-    public PackageManager getMockPackageManager() {
-        return mMockPackageManager;
-    }
-
-    /** Mocks the {@link PackageManager#queryIntentServices(Intent, int)}. */
-    @SuppressWarnings("deprecation")
-    public void mockQueryGetAdIdServices(@NonNull List<ResolveInfo> resolveInfos) throws Exception {
-        boolean supportMatchSystemOnly = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
-        when(mMockPackageManager.queryIntentServices(hasAction(GET_AD_ID_ACTION),
-                eq(supportMatchSystemOnly ? PackageManager.MATCH_SYSTEM_ONLY : 0)))
-                .thenReturn(resolveInfos);
-        for (ResolveInfo resolveInfo : resolveInfos) {
-            String packageName = resolveInfo.serviceInfo.packageName;
-            if (!supportMatchSystemOnly) {
-                ApplicationInfo applicationInfo = new ApplicationInfo();
-                applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
-                when(mMockPackageManager.getApplicationInfo(packageName, 0))
-                        .thenReturn(applicationInfo);
-            }
-            PackageInfo packageInfo = new PackageInfo();
-            packageInfo.packageName = packageName;
-            when(mMockPackageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS))
-                    .thenReturn(packageInfo);
-        }
-    }
-
-    /** Mocks the {@link PackageManager#queryIntentActivities(Intent, int)}. */
-    @SuppressWarnings("deprecation")
-    public void mockQueryOpenSettingsActivities(@NonNull List<ResolveInfo> resolveInfos) {
-        when(mMockPackageManager.queryIntentActivities(hasAction(OPEN_SETTINGS_ACTION), eq(0)))
-                .thenReturn(resolveInfos);
-    }
-
-    private static Intent hasAction(final String action) {
-        return argThat(new ArgumentMatcher<Intent>() {
-            @Override
-            public boolean matches(Intent intent) {
-                return intent != null && action.equals(intent.getAction());
-            }
-        });
-    }
-
-    /**
-     * Creates a {@link ResolveInfo} which contains a {@link ServiceInfo} with given package name.
-     */
-    @NonNull
-    public static ResolveInfo createServiceResolveInfo(@NonNull String packageName) {
-        ResolveInfo resolveInfo = new ResolveInfo();
-        resolveInfo.serviceInfo = new ServiceInfo();
-        resolveInfo.serviceInfo.packageName = packageName;
-        return resolveInfo;
-    }
-
-    /**
-     * Creates a {@link ResolveInfo} which contains a {@link ServiceInfo} with given package name
-     * and service name.
-     */
-    @NonNull
-    public static ResolveInfo createServiceResolveInfo(
-            @NonNull String packageName, @NonNull String serviceName) {
-        ResolveInfo resolveInfo = createServiceResolveInfo(packageName);
-        resolveInfo.serviceInfo.name = serviceName;
-        return resolveInfo;
-    }
-
-    /**
-     * Creates a {@link ResolveInfo} which contains a {@link ActivityInfo} with given package
-     * name and activity name.
-     */
-    @NonNull
-    public static ResolveInfo createActivityResolveInfo(
-            @NonNull String packageName, @NonNull String activityName) {
-        ResolveInfo resolveInfo = new ResolveInfo();
-        resolveInfo.activityInfo = new ActivityInfo();
-        resolveInfo.activityInfo.packageName = packageName;
-        resolveInfo.activityInfo.name = activityName;
-        return resolveInfo;
-    }
-}
diff --git a/ads/ads-identifier/api/api_lint.ignore b/ads/ads-identifier/api/api_lint.ignore
deleted file mode 100644
index a514159..0000000
--- a/ads/ads-identifier/api/api_lint.ignore
+++ /dev/null
@@ -1,3 +0,0 @@
-// Baseline format: 1.0
-AsyncSuffixFuture: androidx.ads.identifier.AdvertisingIdClient#getAdvertisingIdInfo(android.content.Context):
-    Methods returning com.google.common.util.concurrent.ListenableFuture should have a suffix *Async to reserve unmodified name for a suspend function
diff --git a/ads/ads-identifier/api/current.txt b/ads/ads-identifier/api/current.txt
deleted file mode 100644
index 41188c1..0000000
--- a/ads/ads-identifier/api/current.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-// Signature format: 4.0
-package androidx.ads.identifier {
-
-  @Deprecated public class AdvertisingIdClient {
-    method @Deprecated public static com.google.common.util.concurrent.ListenableFuture<androidx.ads.identifier.AdvertisingIdInfo!> getAdvertisingIdInfo(android.content.Context);
-    method @Deprecated public static boolean isAdvertisingIdProviderAvailable(android.content.Context);
-  }
-
-  @Deprecated @com.google.auto.value.AutoValue @com.google.auto.value.AutoValue.CopyAnnotations public abstract class AdvertisingIdInfo {
-    method @Deprecated public abstract String getId();
-    method @Deprecated public abstract String getProviderPackageName();
-    method @Deprecated public abstract boolean isLimitAdTrackingEnabled();
-  }
-
-  @Deprecated public class AdvertisingIdNotAvailableException extends java.lang.Exception {
-    ctor @Deprecated public AdvertisingIdNotAvailableException(String);
-    ctor @Deprecated public AdvertisingIdNotAvailableException(String, Throwable);
-  }
-
-}
-
diff --git a/ads/ads-identifier/api/public_plus_experimental_current.txt b/ads/ads-identifier/api/public_plus_experimental_current.txt
deleted file mode 100644
index 41188c1..0000000
--- a/ads/ads-identifier/api/public_plus_experimental_current.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-// Signature format: 4.0
-package androidx.ads.identifier {
-
-  @Deprecated public class AdvertisingIdClient {
-    method @Deprecated public static com.google.common.util.concurrent.ListenableFuture<androidx.ads.identifier.AdvertisingIdInfo!> getAdvertisingIdInfo(android.content.Context);
-    method @Deprecated public static boolean isAdvertisingIdProviderAvailable(android.content.Context);
-  }
-
-  @Deprecated @com.google.auto.value.AutoValue @com.google.auto.value.AutoValue.CopyAnnotations public abstract class AdvertisingIdInfo {
-    method @Deprecated public abstract String getId();
-    method @Deprecated public abstract String getProviderPackageName();
-    method @Deprecated public abstract boolean isLimitAdTrackingEnabled();
-  }
-
-  @Deprecated public class AdvertisingIdNotAvailableException extends java.lang.Exception {
-    ctor @Deprecated public AdvertisingIdNotAvailableException(String);
-    ctor @Deprecated public AdvertisingIdNotAvailableException(String, Throwable);
-  }
-
-}
-
diff --git a/ads/ads-identifier/api/restricted_current.txt b/ads/ads-identifier/api/restricted_current.txt
deleted file mode 100644
index 41188c1..0000000
--- a/ads/ads-identifier/api/restricted_current.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-// Signature format: 4.0
-package androidx.ads.identifier {
-
-  @Deprecated public class AdvertisingIdClient {
-    method @Deprecated public static com.google.common.util.concurrent.ListenableFuture<androidx.ads.identifier.AdvertisingIdInfo!> getAdvertisingIdInfo(android.content.Context);
-    method @Deprecated public static boolean isAdvertisingIdProviderAvailable(android.content.Context);
-  }
-
-  @Deprecated @com.google.auto.value.AutoValue @com.google.auto.value.AutoValue.CopyAnnotations public abstract class AdvertisingIdInfo {
-    method @Deprecated public abstract String getId();
-    method @Deprecated public abstract String getProviderPackageName();
-    method @Deprecated public abstract boolean isLimitAdTrackingEnabled();
-  }
-
-  @Deprecated public class AdvertisingIdNotAvailableException extends java.lang.Exception {
-    ctor @Deprecated public AdvertisingIdNotAvailableException(String);
-    ctor @Deprecated public AdvertisingIdNotAvailableException(String, Throwable);
-  }
-
-}
-
diff --git a/ads/ads-identifier/build.gradle b/ads/ads-identifier/build.gradle
deleted file mode 100644
index 90bd660..0000000
--- a/ads/ads-identifier/build.gradle
+++ /dev/null
@@ -1,54 +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.
- */
-
-import androidx.build.Publish
-
-plugins {
-    id("AndroidXPlugin")
-    id("com.android.library")
-}
-
-dependencies {
-    api("androidx.annotation:annotation:1.1.0")
-    implementation("androidx.core:core:1.1.0")
-    implementation(libs.autoValueAnnotations)
-    annotationProcessor(libs.autoValue)
-    api(libs.guavaListenableFuture)
-    implementation("androidx.concurrent:concurrent-futures:1.0.0")
-
-    implementation(project(":ads:ads-identifier-common"))
-
-    androidTestImplementation(project(":ads:ads-identifier-testing"))
-    androidTestImplementation(libs.junit)
-    androidTestImplementation(libs.truth)
-    androidTestImplementation(libs.testExtJunit)
-    androidTestImplementation(libs.testCore)
-    androidTestImplementation(libs.testRunner)
-    androidTestImplementation(libs.testRules)
-    androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy)
-}
-
-androidx {
-    name = "AndroidX Ads Identifier"
-    publish = Publish.SNAPSHOT_AND_RELEASE
-    mavenVersion = LibraryVersions.ADS_IDENTIFIER
-    inceptionYear = "2019"
-    description = "AndroidX Ads Identifier"
-}
-
-android {
-    namespace "androidx.ads.identifier"
-}
diff --git a/ads/ads-identifier/integration-tests/testapp/build.gradle b/ads/ads-identifier/integration-tests/testapp/build.gradle
deleted file mode 100644
index 6a99797..0000000
--- a/ads/ads-identifier/integration-tests/testapp/build.gradle
+++ /dev/null
@@ -1,34 +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.
- */
-
-plugins {
-    id("AndroidXPlugin")
-    id("com.android.application")
-}
-
-android {
-    defaultConfig {
-        applicationId "androidx.ads.identifier.testapp"
-        minSdkVersion 14
-    }
-    namespace "androidx.ads.identifier.testapp"
-}
-
-dependencies {
-    implementation(project(":ads:ads-identifier"))
-    implementation(project(":ads:ads-identifier-common"))
-    implementation(libs.guavaAndroid)
-}
diff --git a/ads/ads-identifier/integration-tests/testapp/lint-baseline.xml b/ads/ads-identifier/integration-tests/testapp/lint-baseline.xml
deleted file mode 100644
index eabfd5a..0000000
--- a/ads/ads-identifier/integration-tests/testapp/lint-baseline.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<?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">
-
-    <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="    protected void onCreate(Bundle savedInstanceState) {"
-        errorLine2="                            ~~~~~~">
-        <location
-            file="src/main/java/androidx/ads/identifier/testapp/AdsIdentifierActivity.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 void getId(View view) {"
-        errorLine2="                      ~~~~">
-        <location
-            file="src/main/java/androidx/ads/identifier/testapp/AdsIdentifierActivity.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 void getIdSync(View view) {"
-        errorLine2="                          ~~~~">
-        <location
-            file="src/main/java/androidx/ads/identifier/testapp/AdsIdentifierActivity.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 void isProviderAvailable(View view) {"
-        errorLine2="                                    ~~~~">
-        <location
-            file="src/main/java/androidx/ads/identifier/testapp/AdsIdentifierActivity.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 void listProvider(View view) {"
-        errorLine2="                             ~~~~">
-        <location
-            file="src/main/java/androidx/ads/identifier/testapp/AdsIdentifierActivity.java"/>
-    </issue>
-
-</issues>
diff --git a/ads/ads-identifier/integration-tests/testapp/src/main/AndroidManifest.xml b/ads/ads-identifier/integration-tests/testapp/src/main/AndroidManifest.xml
deleted file mode 100644
index 6e3275e..0000000
--- a/ads/ads-identifier/integration-tests/testapp/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools">
-
-    <application
-        android:allowBackup="false"
-        android:label="@string/app_name"
-        tools:ignore="GoogleAppIndexingWarning,MissingApplicationIcon">
-        <activity
-            android:name=".AdsIdentifierActivity"
-            android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-
-</manifest>
\ No newline at end of file
diff --git a/ads/ads-identifier/integration-tests/testapp/src/main/java/androidx/ads/identifier/testapp/AdsIdentifierActivity.java b/ads/ads-identifier/integration-tests/testapp/src/main/java/androidx/ads/identifier/testapp/AdsIdentifierActivity.java
deleted file mode 100644
index 2f575fd..0000000
--- a/ads/ads-identifier/integration-tests/testapp/src/main/java/androidx/ads/identifier/testapp/AdsIdentifierActivity.java
+++ /dev/null
@@ -1,146 +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.ads.identifier.testapp;
-
-import android.app.Activity;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
-import android.os.Bundle;
-import android.text.format.DateFormat;
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.ads.identifier.AdvertisingIdClient;
-import androidx.ads.identifier.AdvertisingIdInfo;
-import androidx.ads.identifier.AdvertisingIdUtils;
-
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.MoreExecutors;
-
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.ExecutionException;
-
-/**
- * Simple activity as an ads identifier developer.
- */
-public class AdsIdentifierActivity extends Activity {
-
-    private static final String HIGH_PRIORITY_PERMISSION =
-            "androidx.ads.identifier.provider.HIGH_PRIORITY";
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_ads_identifier);
-    }
-
-    /** Gets Advertising ID. */
-    public void getId(View view) {
-        TextView textView = findViewById(R.id.text);
-        ListenableFuture<AdvertisingIdInfo> advertisingIdInfoListenableFuture =
-                AdvertisingIdClient.getAdvertisingIdInfo(getApplicationContext());
-        Futures.addCallback(advertisingIdInfoListenableFuture,
-                new FutureCallback<AdvertisingIdInfo>() {
-                    @Override
-                    public void onSuccess(AdvertisingIdInfo advertisingIdInfo) {
-                        runOnUiThread(() -> textView.setText(advertisingIdInfo.toString()));
-                    }
-
-                    @Override
-                    public void onFailure(Throwable throwable) {
-                        runOnUiThread(() -> textView.setText(throwable.toString()));
-                    }
-                }, MoreExecutors.directExecutor());
-    }
-
-    /** Gets Advertising ID synchronously. */
-    public void getIdSync(View view) {
-        TextView textView = findViewById(R.id.text);
-        new Thread(() -> {
-            AdvertisingIdInfo advertisingIdInfo;
-            try {
-                advertisingIdInfo =
-                        AdvertisingIdClient.getAdvertisingIdInfo(getApplicationContext()).get();
-            } catch (ExecutionException e) {
-                Throwable cause = e.getCause() != null ? e.getCause() : e;
-                runOnUiThread(() -> textView.setText(cause.toString()));
-                return;
-
-            } catch (InterruptedException e) {
-                Thread.currentThread().interrupt();
-                runOnUiThread(() -> textView.setText(e.toString()));
-                return;
-            }
-            runOnUiThread(() -> textView.setText(advertisingIdInfo.toString()));
-        }).start();
-    }
-
-    /** Checks is provider available. */
-    public void isProviderAvailable(View view) {
-        TextView textView = findViewById(R.id.text);
-        boolean isAvailable = AdvertisingIdClient.isAdvertisingIdProviderAvailable(this);
-        textView.setText(String.valueOf(isAvailable));
-    }
-
-    /** Lists all the providers. */
-    @SuppressWarnings("deprecation")
-    public void listProvider(View view) {
-        TextView textView = findViewById(R.id.text);
-        textView.setText("Services:\n");
-
-        List<ServiceInfo> serviceInfos =
-                AdvertisingIdUtils.getAdvertisingIdProviderServices(getPackageManager());
-        for (ServiceInfo serviceInfo : serviceInfos) {
-            PackageInfo packageInfo;
-            try {
-                packageInfo = getPackageManager().getPackageInfo(serviceInfo.packageName,
-                        PackageManager.GET_PERMISSIONS);
-            } catch (PackageManager.NameNotFoundException e) {
-                continue;
-            }
-            show(textView, packageInfo);
-        }
-    }
-
-    private void show(TextView textView, PackageInfo packageInfo) {
-        textView.append(String.format(Locale.US, "%s\nFLAG_SYSTEM:%d\n",
-                packageInfo.packageName,
-                packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM));
-        textView.append(String.format(Locale.US, "isRequestHighPriority:%s\n",
-                isRequestHighPriority(packageInfo.requestedPermissions)));
-        textView.append(String.format(Locale.US, "firstInstallTime:%s\n",
-                DateFormat.format("yyyy-MM-dd HH:mm:ss", packageInfo.firstInstallTime)));
-        textView.append("\n");
-    }
-
-    private static boolean isRequestHighPriority(String[] array) {
-        if (array == null) {
-            return false;
-        }
-        for (String permission : array) {
-            if (HIGH_PRIORITY_PERMISSION.equals(permission)) {
-                return true;
-            }
-        }
-        return false;
-    }
-}
diff --git a/ads/ads-identifier/integration-tests/testapp/src/main/res/layout/activity_ads_identifier.xml b/ads/ads-identifier/integration-tests/testapp/src/main/res/layout/activity_ads_identifier.xml
deleted file mode 100644
index 0f4f3ea..0000000
--- a/ads/ads-identifier/integration-tests/testapp/src/main/res/layout/activity_ads_identifier.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical"
-    tools:context="androidx.ads.identifier.testapp.AdsIdentifierActivity">
-
-    <LinearLayout
-        style="?android:attr/buttonBarStyle"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal">
-
-        <Button
-            style="?android:attr/buttonBarButtonStyle"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:onClick="getId"
-            android:text="@string/get_ad_id" />
-
-        <Button
-            style="?android:attr/buttonBarButtonStyle"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:onClick="getIdSync"
-            android:text="@string/get_ad_id_sync" />
-    </LinearLayout>
-
-    <LinearLayout
-        style="?android:attr/buttonBarStyle"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal">
-
-        <Button
-            style="?android:attr/buttonBarButtonStyle"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:onClick="isProviderAvailable"
-            android:text="@string/is_provider_available" />
-
-        <Button
-            style="?android:attr/buttonBarButtonStyle"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:onClick="listProvider"
-            android:text="@string/list_provider" />
-    </LinearLayout>
-
-    <TextView
-        android:id="@+id/text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="" />
-</LinearLayout>
diff --git a/ads/ads-identifier/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml b/ads/ads-identifier/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
deleted file mode 100644
index 4d7dca0..0000000
--- a/ads/ads-identifier/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<resources>
-    <string name="app_name">Ad ID</string>
-    <string name="get_ad_id">Get Ad ID</string>
-    <string name="get_ad_id_sync">Get Ad ID (Sync)</string>
-    <string name="list_provider">List Providers</string>
-    <string name="is_provider_available">Is Provider Available</string>
-</resources>
\ No newline at end of file
diff --git a/ads/ads-identifier/lint-baseline.xml b/ads/ads-identifier/lint-baseline.xml
deleted file mode 100644
index 2284798..0000000
--- a/ads/ads-identifier/lint-baseline.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<?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">
-
-    <issue
-        id="BanThreadSleep"
-        message="Uses Thread.sleep()"
-        errorLine1="        Thread.sleep(20000);"
-        errorLine2="               ~~~~~">
-        <location
-            file="src/androidTest/java/androidx/ads/identifier/AdvertisingIdClientTest.java"/>
-    </issue>
-
-    <issue
-        id="BanThreadSleep"
-        message="Uses Thread.sleep()"
-        errorLine1="        Thread.sleep(11000);"
-        errorLine2="               ~~~~~">
-        <location
-            file="src/androidTest/java/androidx/ads/identifier/AdvertisingIdClientTest.java"/>
-    </issue>
-
-    <issue
-        id="BanThreadSleep"
-        message="Uses Thread.sleep()"
-        errorLine1="        Thread.sleep(20000);"
-        errorLine2="               ~~~~~">
-        <location
-            file="src/androidTest/java/androidx/ads/identifier/AdvertisingIdClientTest.java"/>
-    </issue>
-
-    <issue
-        id="BanThreadSleep"
-        message="Uses Thread.sleep()"
-        errorLine1="        Thread.sleep(20000);"
-        errorLine2="               ~~~~~">
-        <location
-            file="src/androidTest/java/androidx/ads/identifier/AdvertisingIdClientTest.java"/>
-    </issue>
-
-    <issue
-        id="BanThreadSleep"
-        message="Uses Thread.sleep()"
-        errorLine1="        Thread.sleep(11000);"
-        errorLine2="               ~~~~~">
-        <location
-            file="src/androidTest/java/androidx/ads/identifier/AdvertisingIdClientTest.java"/>
-    </issue>
-
-</issues>
diff --git a/ads/ads-identifier/src/androidTest/AndroidManifest.xml b/ads/ads-identifier/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index 453fdfb..0000000
--- a/ads/ads-identifier/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<!--
-  Copyright (C) 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License
-  -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools">
-
-    <application>
-        <service
-            android:name="androidx.ads.identifier.MockAdvertisingIdService"
-            android:enabled="true"
-            android:exported="true"
-            android:process=":test"
-            tools:ignore="ExportedService">
-            <intent-filter>
-                <action android:name="androidx.ads.identifier.provider.GET_AD_ID" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </service>
-
-        <service
-            android:name="androidx.ads.identifier.MockAdvertisingIdThrowsNpeService"
-            android:enabled="true"
-            android:exported="true"
-            android:process=":test"
-            tools:ignore="ExportedService">
-            <intent-filter>
-                <action android:name="androidx.ads.identifier.provider.GET_AD_ID" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </service>
-    </application>
-</manifest>
diff --git a/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/AdvertisingIdClientTest.java b/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/AdvertisingIdClientTest.java
deleted file mode 100644
index 164c2b2..0000000
--- a/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/AdvertisingIdClientTest.java
+++ /dev/null
@@ -1,198 +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.ads.identifier;
-
-import static androidx.ads.identifier.AdvertisingIdUtils.GET_AD_ID_ACTION;
-import static androidx.ads.identifier.MockAdvertisingIdService.TESTING_AD_ID;
-import static androidx.ads.identifier.testing.MockPackageManagerHelper.createServiceResolveInfo;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.fail;
-
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-
-import androidx.ads.identifier.testing.MockPackageManagerHelper;
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.LargeTest;
-
-import com.google.common.collect.Lists;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Collections;
-import java.util.concurrent.ExecutionException;
-
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-@SuppressWarnings("deprecation")
-public class AdvertisingIdClientTest {
-    private static final String MOCK_SERVICE_NAME = MockAdvertisingIdService.class.getName();
-    private static final String MOCK_THROWS_NPE_SERVICE_NAME =
-            MockAdvertisingIdThrowsNpeService.class.getName();
-
-    private MockPackageManagerHelper mMockPackageManagerHelper = new MockPackageManagerHelper();
-
-    private Context mContext;
-
-    @Before
-    public void setUp() throws Exception {
-        Context applicationContext = ApplicationProvider.getApplicationContext();
-
-        mContext = new ContextWrapper(applicationContext) {
-            @Override
-            public Context getApplicationContext() {
-                return this;
-            }
-
-            @Override
-            public PackageManager getPackageManager() {
-                return mMockPackageManagerHelper.getMockPackageManager();
-            }
-        };
-
-        mMockPackageManagerHelper.mockQueryGetAdIdServices(Lists.newArrayList(
-                createServiceResolveInfo(mContext.getPackageName(), MOCK_SERVICE_NAME)));
-    }
-
-    @After
-    public void tearDown() {
-        AdvertisingIdClient.clearConnectionClient();
-
-        Intent serviceIntent = new Intent(GET_AD_ID_ACTION);
-        serviceIntent.setClassName(mContext.getPackageName(), MOCK_SERVICE_NAME);
-        mContext.stopService(serviceIntent);
-
-        Intent npeServiceIntent = new Intent(GET_AD_ID_ACTION);
-        npeServiceIntent.setClassName(mContext.getPackageName(), MOCK_THROWS_NPE_SERVICE_NAME);
-        mContext.stopService(npeServiceIntent);
-    }
-
-    @Test
-    public void getAdvertisingIdInfo() throws Exception {
-        AdvertisingIdInfo info = AdvertisingIdClient.getAdvertisingIdInfo(mContext).get();
-
-        assertThat(info).isEqualTo(AdvertisingIdInfo.builder()
-                .setId(TESTING_AD_ID)
-                .setLimitAdTrackingEnabled(true)
-                .setProviderPackageName(mContext.getPackageName())
-                .build());
-    }
-
-    public void getAdvertisingIdInfo_noProvider() throws Exception {
-        mMockPackageManagerHelper.mockQueryGetAdIdServices(Collections.<ResolveInfo>emptyList());
-
-        try {
-            AdvertisingIdClient.getAdvertisingIdInfo(mContext).get();
-        } catch (ExecutionException e) {
-            assertThat(e).hasCauseThat().isInstanceOf(AdvertisingIdNotAvailableException.class);
-            return;
-        }
-        fail("Expected ExecutionException");
-    }
-
-    @Test
-    public void getAdvertisingIdInfo_serviceThrowsNullPointerException() throws Exception {
-        mMockPackageManagerHelper.mockQueryGetAdIdServices(Lists.newArrayList(
-                createServiceResolveInfo(mContext.getPackageName(), MOCK_THROWS_NPE_SERVICE_NAME)));
-
-        try {
-            AdvertisingIdClient.getAdvertisingIdInfo(mContext).get();
-        } catch (ExecutionException e) {
-            assertThat(e).hasCauseThat().isInstanceOf(AdvertisingIdNotAvailableException.class);
-            return;
-        }
-        fail("Expected ExecutionException");
-    }
-
-    @Test
-    public void getAdvertisingIdInfo_getTwice() throws Exception {
-        AdvertisingIdInfo info1 = AdvertisingIdClient.getAdvertisingIdInfo(mContext).get();
-        AdvertisingIdInfo info2 = AdvertisingIdClient.getAdvertisingIdInfo(mContext).get();
-
-        AdvertisingIdInfo expected = AdvertisingIdInfo.builder()
-                .setId(TESTING_AD_ID)
-                .setLimitAdTrackingEnabled(true)
-                .setProviderPackageName(mContext.getPackageName())
-                .build();
-        assertThat(info1).isEqualTo(expected);
-        assertThat(info2).isEqualTo(expected);
-    }
-
-    @Test
-    public void notConnectedAtBeginning() {
-        assertThat(AdvertisingIdClient.isConnected()).isFalse();
-    }
-
-    @Test
-    public void scheduleAutoDisconnect() throws Exception {
-        AdvertisingIdClient.getAdvertisingIdInfo(mContext).get();
-        assertThat(AdvertisingIdClient.isConnected()).isTrue();
-
-        Thread.sleep(20000);
-        assertThat(AdvertisingIdClient.isConnected()).isTrue();
-
-        Thread.sleep(11000);
-        assertThat(AdvertisingIdClient.isConnected()).isFalse();
-    }
-
-    @Test
-    public void scheduleAutoDisconnect_extend() throws Exception {
-        AdvertisingIdClient.getAdvertisingIdInfo(mContext).get();
-        assertThat(AdvertisingIdClient.isConnected()).isTrue();
-
-        Thread.sleep(20000);
-        assertThat(AdvertisingIdClient.isConnected()).isTrue();
-        AdvertisingIdClient.getAdvertisingIdInfo(mContext).get();
-
-        Thread.sleep(20000);
-        assertThat(AdvertisingIdClient.isConnected()).isTrue();
-
-        Thread.sleep(11000);
-        assertThat(AdvertisingIdClient.isConnected()).isFalse();
-    }
-
-    @Test
-    public void isAdvertisingIdProviderAvailable() {
-        assertThat(AdvertisingIdClient.isAdvertisingIdProviderAvailable(mContext)).isTrue();
-    }
-
-    @Test
-    public void isAdvertisingIdProviderAvailable_noProvider() throws Exception {
-        mMockPackageManagerHelper.mockQueryGetAdIdServices(Collections.<ResolveInfo>emptyList());
-
-        assertThat(AdvertisingIdClient.isAdvertisingIdProviderAvailable(mContext)).isFalse();
-    }
-
-    @Test
-    public void isAdvertisingIdProviderAvailable_twoProviders() throws Exception {
-        mMockPackageManagerHelper.mockQueryGetAdIdServices(Lists.newArrayList(
-                createServiceResolveInfo("com.a", "A"),
-                createServiceResolveInfo("com.b", "B")));
-
-        assertThat(AdvertisingIdClient.isAdvertisingIdProviderAvailable(mContext)).isTrue();
-    }
-
-}
diff --git a/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/MockAdvertisingIdService.java b/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/MockAdvertisingIdService.java
deleted file mode 100644
index 691a37c..0000000
--- a/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/MockAdvertisingIdService.java
+++ /dev/null
@@ -1,58 +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.ads.identifier;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-import androidx.ads.identifier.provider.IAdvertisingIdService;
-import androidx.annotation.Nullable;
-
-/**
- * Provide a mock for {@link androidx.ads.identifier.provider.IAdvertisingIdService}.
- * To be used in unit tests.
- */
-public class MockAdvertisingIdService extends Service {
-
-    public static final String TESTING_AD_ID = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
-
-    private MockAdvertisingIdServiceImpl mAdvertisingIdServiceImpl;
-
-    @Override
-    public void onCreate() {
-        mAdvertisingIdServiceImpl = new MockAdvertisingIdServiceImpl();
-    }
-
-    @Nullable
-    @Override
-    public IBinder onBind(Intent intent) {
-        return mAdvertisingIdServiceImpl;
-    }
-
-    private static class MockAdvertisingIdServiceImpl extends IAdvertisingIdService.Stub {
-        @Override
-        public String getId() {
-            return TESTING_AD_ID;
-        }
-
-        @Override
-        public boolean isLimitAdTrackingEnabled() {
-            return true;
-        }
-    }
-}
diff --git a/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/MockAdvertisingIdThrowsNpeService.java b/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/MockAdvertisingIdThrowsNpeService.java
deleted file mode 100644
index 3ed2714..0000000
--- a/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/MockAdvertisingIdThrowsNpeService.java
+++ /dev/null
@@ -1,56 +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.ads.identifier;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-import androidx.ads.identifier.provider.IAdvertisingIdService;
-import androidx.annotation.Nullable;
-
-/**
- * Provide a mock for {@link IAdvertisingIdService} which always throw {@link NullPointerException}.
- * To be used in unit tests.
- */
-public class MockAdvertisingIdThrowsNpeService extends Service {
-
-    private MockAdvertisingIdServiceImpl mAdvertisingIdServiceImpl;
-
-    @Override
-    public void onCreate() {
-        mAdvertisingIdServiceImpl = new MockAdvertisingIdServiceImpl();
-    }
-
-    @Nullable
-    @Override
-    public IBinder onBind(Intent intent) {
-        return mAdvertisingIdServiceImpl;
-    }
-
-    private static class MockAdvertisingIdServiceImpl extends IAdvertisingIdService.Stub {
-        @Override
-        public String getId() {
-            throw new NullPointerException();
-        }
-
-        @Override
-        public boolean isLimitAdTrackingEnabled() {
-            throw new NullPointerException();
-        }
-    }
-}
diff --git a/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/internal/HoldingConnectionClientTest.java b/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/internal/HoldingConnectionClientTest.java
deleted file mode 100644
index 17557a45..0000000
--- a/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/internal/HoldingConnectionClientTest.java
+++ /dev/null
@@ -1,167 +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.ads.identifier.internal;
-
-import static androidx.ads.identifier.AdvertisingIdUtils.GET_AD_ID_ACTION;
-import static androidx.ads.identifier.MockAdvertisingIdService.TESTING_AD_ID;
-import static androidx.ads.identifier.testing.MockPackageManagerHelper.createServiceResolveInfo;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-
-import androidx.ads.identifier.MockAdvertisingIdService;
-import androidx.ads.identifier.provider.IAdvertisingIdService;
-import androidx.ads.identifier.testing.MockPackageManagerHelper;
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.LargeTest;
-
-import com.google.common.collect.Lists;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-import java.util.concurrent.TimeoutException;
-
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-@SuppressWarnings("deprecation")
-public class HoldingConnectionClientTest {
-    private static final String MOCK_SERVICE_NAME = MockAdvertisingIdService.class.getName();
-
-    private MockPackageManagerHelper mMockPackageManagerHelper = new MockPackageManagerHelper();
-
-    private Context mContext;
-
-    private HoldingConnectionClient mClient;
-
-    @Before
-    public void setUp() throws Exception {
-        MockHoldingConnectionClient.sGetServiceConnectionThrowException = false;
-        MockHoldingConnectionClient.sGetServiceFromConnectionThrowInterruptedException = false;
-
-        Context applicationContext = ApplicationProvider.getApplicationContext();
-
-        mContext = new ContextWrapper(applicationContext) {
-            @Override
-            public Context getApplicationContext() {
-                return this;
-            }
-
-            @Override
-            public PackageManager getPackageManager() {
-                return mMockPackageManagerHelper.getMockPackageManager();
-            }
-        };
-
-        mMockPackageManagerHelper.mockQueryGetAdIdServices(Lists.newArrayList(
-                createServiceResolveInfo(mContext.getPackageName(), MOCK_SERVICE_NAME)));
-
-        mClient = new HoldingConnectionClient(mContext);
-    }
-
-    @After
-    public void tearDown() {
-        mClient.finish();
-
-        Intent serviceIntent = new Intent(GET_AD_ID_ACTION);
-        serviceIntent.setClassName(mContext.getPackageName(), MOCK_SERVICE_NAME);
-        mContext.stopService(serviceIntent);
-    }
-
-    @Test
-    public void connectedAtBeginning() {
-        assertThat(mClient.isConnected()).isTrue();
-    }
-
-    @Test
-    public void finish() {
-        mClient.finish();
-
-        assertThat(mClient.isConnected()).isFalse();
-    }
-
-    @Test
-    public void getService() throws Exception {
-        assertThat(mClient.getIdService().getId()).isEqualTo(TESTING_AD_ID);
-    }
-
-    @Test
-    public void getPackageName() {
-        assertThat(mClient.getPackageName()).isEqualTo(mContext.getPackageName());
-    }
-
-    @Test(expected = TimeoutException.class)
-    public void getServiceWithPackageName_connectionTimeout() throws Exception {
-        new MockHoldingConnectionClient(mContext);
-    }
-
-    @Test(expected = InterruptedException.class)
-    public void getServiceWithPackageName_interrupted() throws Exception {
-        MockHoldingConnectionClient.sGetServiceFromConnectionThrowInterruptedException = true;
-
-        new MockHoldingConnectionClient(mContext);
-    }
-
-    @Test(expected = IOException.class)
-    public void getServiceWithPackageName_connectionFailed() throws Exception {
-        MockHoldingConnectionClient.sGetServiceConnectionThrowException = true;
-
-        new MockHoldingConnectionClient(mContext);
-    }
-
-    private static class MockHoldingConnectionClient extends HoldingConnectionClient {
-
-        static boolean sGetServiceConnectionThrowException = false;
-        static boolean sGetServiceFromConnectionThrowInterruptedException = false;
-
-        MockHoldingConnectionClient(Context context)
-                throws InterruptedException, TimeoutException,
-                androidx.ads.identifier.AdvertisingIdNotAvailableException,
-                IOException {
-            super(context);
-        }
-
-        @Override
-        BlockingServiceConnection getServiceConnection(ComponentName componentName)
-                throws IOException {
-            if (sGetServiceConnectionThrowException) {
-                throw new IOException();
-            }
-
-            // This connection does not bind to any service, so it always timeout.
-            return new BlockingServiceConnection();
-        }
-
-        @Override
-        IAdvertisingIdService getIdServiceFromConnection()
-                throws TimeoutException, InterruptedException {
-            if (sGetServiceFromConnectionThrowInterruptedException) {
-                throw new InterruptedException();
-            }
-            return super.getIdServiceFromConnection();
-        }
-    }
-}
diff --git a/ads/ads-identifier/src/main/AndroidManifest.xml b/ads/ads-identifier/src/main/AndroidManifest.xml
deleted file mode 100644
index 0d0975d..0000000
--- a/ads/ads-identifier/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
-</manifest>
\ No newline at end of file
diff --git a/ads/ads-identifier/src/main/java/androidx/ads/identifier/AdvertisingIdClient.java b/ads/ads-identifier/src/main/java/androidx/ads/identifier/AdvertisingIdClient.java
deleted file mode 100644
index e2ab65d..0000000
--- a/ads/ads-identifier/src/main/java/androidx/ads/identifier/AdvertisingIdClient.java
+++ /dev/null
@@ -1,293 +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.ads.identifier;
-
-import android.content.Context;
-import android.os.RemoteException;
-
-import androidx.ads.identifier.internal.HoldingConnectionClient;
-import androidx.ads.identifier.provider.IAdvertisingIdService;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-import androidx.annotation.WorkerThread;
-import androidx.concurrent.futures.CallbackToFutureAdapter;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.io.IOException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * Client for retrieving Advertising ID related info from an AndroidX ID Provider installed on
- * the device.
- *
- * <p>Typical usage would be:
- * <ol>
- * <li>Call {@link #isAdvertisingIdProviderAvailable} to make sure there is an Advertising ID
- * Provider available.
- * <li>Call {@link #getAdvertisingIdInfo} to get Advertising ID info (the Advertising ID and LAT
- * setting).
- * </ol>
- *
- * @deprecated Use the
- * <a href="https://developers.google.com/android/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient">
- * Advertising ID API that's available as part of Google Play Services</a> instead of this library.
- */
-@Deprecated
-public class AdvertisingIdClient {
-
-    /**
-     * Amount of time to wait before timing out when trying to get the ID info from the
-     * Provider. Including the binding service time and the remote calling time.
-     */
-    private static final long TIMEOUT_SECONDS = 20;
-
-    private static final long AUTO_DISCONNECT_SECONDS = 30;
-
-    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-    static final ExecutorService QUERY_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
-
-    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-    static final ScheduledExecutorService SCHEDULED_EXECUTOR_SERVICE =
-            Executors.newSingleThreadScheduledExecutor();
-
-    private static final Object sLock = new Object();
-
-    /**
-     * The client holding connection which can be reused if connected.
-     *
-     * <p>This value will only be set at 2 places at production when setup new connection or auto
-     * disconnect timeout happen, and 1 place at testing when clear connection.
-     * <p>There could be multiple connection clients in corner cases, but each of them will be
-     * auto disconnect eventually.
-     * <p>Each connection client has a last connection ID field, which ties to the connection
-     * client and also indicates the status of connection. See {@link HoldingConnectionClient}'s
-     * mLastConnectionId filed for details.
-     * <p>Each get ID instance will get a pair of connection client and connection ID (which ties
-     * to the connection client) first, then use this pair to schedule an auto disconnection at
-     * {@link #AUTO_DISCONNECT_SECONDS} later.
-     */
-    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-    @NonNull
-    static final AtomicReference<HoldingConnectionClient> sConnectionClient =
-            new AtomicReference<>(null);
-
-    @AutoValue
-    @AutoValue.CopyAnnotations
-    @SuppressWarnings("deprecation")
-    abstract static class ConnectionPair {
-        @NonNull
-        abstract HoldingConnectionClient getConnectionClient();
-
-        abstract long getConnectionId();
-
-        @NonNull
-        static ConnectionPair of(HoldingConnectionClient connectionClient, long connectionId) {
-            return new AutoValue_AdvertisingIdClient_ConnectionPair(connectionClient, connectionId);
-        }
-    }
-
-    private AdvertisingIdClient() {
-    }
-
-    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-    @WorkerThread
-    @NonNull
-    static ConnectionPair getConnection(Context context)
-            throws IOException, AdvertisingIdNotAvailableException, TimeoutException,
-            InterruptedException {
-        ConnectionPair connectionPair = tryConnect();
-        if (connectionPair == null) {
-            synchronized (sLock) {
-                connectionPair = tryConnect();
-                if (connectionPair == null) {
-                    HoldingConnectionClient connectionClient = new HoldingConnectionClient(context);
-                    sConnectionClient.set(connectionClient);
-                    connectionPair = ConnectionPair.of(connectionClient, 0);
-                }
-            }
-        }
-        return connectionPair;
-    }
-
-    @Nullable
-    private static ConnectionPair tryConnect() {
-        HoldingConnectionClient connectionClient = sConnectionClient.get();
-        if (connectionClient != null) {
-            long connectionId = connectionClient.askConnectionId();
-            if (connectionId >= 0) {
-                return ConnectionPair.of(connectionClient, connectionId);
-            }
-        }
-        return null;
-    }
-
-    /** Returns the Advertising ID info as {@link AdvertisingIdInfo}. */
-    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-    @VisibleForTesting
-    @WorkerThread
-    @NonNull
-    static AdvertisingIdInfo getIdInfo(HoldingConnectionClient connectionClient)
-            throws IOException, AdvertisingIdNotAvailableException {
-        IAdvertisingIdService service = connectionClient.getIdService();
-
-        try {
-            String id = service.getId();
-            if (id == null || id.trim().isEmpty()) {
-                throw new AdvertisingIdNotAvailableException(
-                        "Advertising ID Provider does not returns an Advertising ID.");
-            }
-            return AdvertisingIdInfo.builder()
-                    .setId(id)
-                    .setProviderPackageName(connectionClient.getPackageName())
-                    .setLimitAdTrackingEnabled(service.isLimitAdTrackingEnabled())
-                    .build();
-        } catch (RemoteException e) {
-            throw new IOException("Remote exception", e);
-        } catch (RuntimeException e) {
-            throw new AdvertisingIdNotAvailableException(
-                    "Advertising ID Provider throws a exception.", e);
-        }
-    }
-
-    @VisibleForTesting
-    static void clearConnectionClient() {
-        sConnectionClient.set(null);
-    }
-
-    @VisibleForTesting
-    static boolean isConnected() {
-        HoldingConnectionClient connectionClient = sConnectionClient.get();
-        return connectionClient != null && connectionClient.isConnected();
-    }
-
-    /**
-     * Checks whether there is any Advertising ID Provider installed on the device.
-     *
-     * <p>This method does a quick check for the Advertising ID providers.
-     * <p>Note: Even if this method returns true, there is still a possibility that the
-     * {@link #getAdvertisingIdInfo(Context)} method throws an exception for some reason.
-     *
-     * @param context Current {@link Context} (such as the current {@link android.app.Activity}).
-     * @return whether there is an Advertising ID Provider available on the device.
-     */
-    public static boolean isAdvertisingIdProviderAvailable(@NonNull Context context) {
-        return !AdvertisingIdUtils.getAdvertisingIdProviderServices(context.getPackageManager())
-                .isEmpty();
-    }
-
-    /**
-     * Retrieves the user's Advertising ID info.
-     *
-     * <p>When multiple Advertising ID Providers are installed on the device, this method will
-     * always return the Advertising ID information from same Advertising ID Provider for all
-     * apps which use this library, using following priority:
-     * <ol>
-     * <li>System-level providers with "androidx.ads.identifier.provider.HIGH_PRIORITY" permission
-     * <li>Other system-level providers
-     * </ol>
-     * <p>If there are ties in any of the above categories, it will use this priority:
-     * <ol>
-     * <li>First app by earliest install time
-     * ({@link android.content.pm.PackageInfo#firstInstallTime})
-     * <li>First app by package name alphabetically sorted
-     * </ol>
-     *
-     * @param context Current {@link Context} (such as the current {@link android.app.Activity}).
-     * @return A {@link ListenableFuture} that will be fulfilled with a {@link AdvertisingIdInfo}
-     * which contains the user's Advertising ID info, or rejected with the following exceptions,
-     * <ul>
-     * <li><b>IOException</b> signaling connection to Advertising ID Providers failed.
-     * <li><b>AdvertisingIdNotAvailableException</b> indicating Advertising ID is not available,
-     * like no Advertising ID Provider found or provider does not return an Advertising ID.
-     * <li><b>TimeoutException</b> indicating timeout period (20s) has expired.
-     * <li><b>InterruptedException</b> indicating the current thread has been interrupted.
-     * </ul>
-     */
-    @NonNull
-    public static ListenableFuture<AdvertisingIdInfo> getAdvertisingIdInfo(
-            @NonNull Context context) {
-        final Context applicationContext = context.getApplicationContext();
-
-        return CallbackToFutureAdapter.getFuture(
-                new CallbackToFutureAdapter.Resolver<AdvertisingIdInfo>() {
-                    @Override
-                    public Object attachCompleter(
-                            @NonNull CallbackToFutureAdapter.Completer<AdvertisingIdInfo>
-                                    completer) {
-                        submitAdvertisingIdInfoTask(applicationContext, completer);
-                        return "getAdvertisingIdInfo";
-                    }
-                });
-    }
-
-    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-    static void submitAdvertisingIdInfoTask(
-            final Context applicationContext,
-            @NonNull final CallbackToFutureAdapter.Completer<AdvertisingIdInfo> completer) {
-        final Future<?> getIdInfoFuture = QUERY_EXECUTOR_SERVICE.submit(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    ConnectionPair connectionPair = getConnection(applicationContext);
-                    scheduleAutoDisconnect(connectionPair);
-                    completer.set(getIdInfo(connectionPair.getConnectionClient()));
-                } catch (IOException | AdvertisingIdNotAvailableException | TimeoutException
-                        | InterruptedException e) {
-                    completer.setException(e);
-                }
-            }
-        });
-        scheduleTimeoutCheck(getIdInfoFuture, completer);
-    }
-
-    @SuppressWarnings("FutureReturnValueIgnored")
-    private static void scheduleTimeoutCheck(
-            final Future<?> getIdInfoFuture,
-            @NonNull final CallbackToFutureAdapter.Completer<AdvertisingIdInfo> completer) {
-        SCHEDULED_EXECUTOR_SERVICE.schedule(new Runnable() {
-            @Override
-            public void run() {
-                if (!getIdInfoFuture.isDone()) {
-                    completer.setException(new TimeoutException());
-                    getIdInfoFuture.cancel(true);
-                }
-            }
-        }, TIMEOUT_SECONDS, TimeUnit.SECONDS);
-    }
-
-    @SuppressWarnings({"WeakerAccess", "FutureReturnValueIgnored"}) /* synthetic accessor */
-    static void scheduleAutoDisconnect(final ConnectionPair connectionPair) {
-        SCHEDULED_EXECUTOR_SERVICE.schedule(new Runnable() {
-            @Override
-            public void run() {
-                HoldingConnectionClient connectionClient = connectionPair.getConnectionClient();
-                if (connectionClient.tryFinish(connectionPair.getConnectionId())) {
-                    sConnectionClient.compareAndSet(connectionClient, null);
-                }
-            }
-        }, AUTO_DISCONNECT_SECONDS, TimeUnit.SECONDS);
-    }
-}
diff --git a/ads/ads-identifier/src/main/java/androidx/ads/identifier/AdvertisingIdInfo.java b/ads/ads-identifier/src/main/java/androidx/ads/identifier/AdvertisingIdInfo.java
deleted file mode 100644
index 1e6380d..0000000
--- a/ads/ads-identifier/src/main/java/androidx/ads/identifier/AdvertisingIdInfo.java
+++ /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.
- */
-
-package androidx.ads.identifier;
-
-import androidx.annotation.NonNull;
-
-import com.google.auto.value.AutoValue;
-
-/**
- * Advertising ID Information.
- * Includes both the Advertising ID and the limit ad tracking setting.
- *
- * @deprecated Use the
- * <a href="https://developers.google.com/android/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient">
- * Advertising ID API that's available as part of Google Play Services</a> instead of this library.
- */
-@Deprecated
-@AutoValue
-@AutoValue.CopyAnnotations
-public abstract class AdvertisingIdInfo {
-
-    // Create a no-args constructor so it doesn't appear in current.txt
-    AdvertisingIdInfo() {
-    }
-
-    /** Retrieves the Advertising ID. */
-    @NonNull
-    public abstract String getId();
-
-    /** Retrieves the Advertising ID provider package name. */
-    @NonNull
-    public abstract String getProviderPackageName();
-
-    /** Retrieves whether the user has set Limit Advertising Tracking. */
-    public abstract boolean isLimitAdTrackingEnabled();
-
-    /** Create a {@link Builder}. */
-    static Builder builder() {
-        return new AutoValue_AdvertisingIdInfo.Builder();
-    }
-
-    /** The builder for {@link AdvertisingIdInfo}. */
-    @AutoValue.Builder
-    abstract static class Builder {
-
-        // Create a no-args constructor so it doesn't appear in current.txt
-        Builder() {
-        }
-
-        abstract Builder setId(String id);
-
-        abstract Builder setProviderPackageName(String providerPackageName);
-
-        abstract Builder setLimitAdTrackingEnabled(boolean limitAdTrackingEnabled);
-
-        abstract AdvertisingIdInfo build();
-    }
-}
diff --git a/ads/ads-identifier/src/main/java/androidx/ads/identifier/AdvertisingIdNotAvailableException.java b/ads/ads-identifier/src/main/java/androidx/ads/identifier/AdvertisingIdNotAvailableException.java
deleted file mode 100644
index 3bb057d..0000000
--- a/ads/ads-identifier/src/main/java/androidx/ads/identifier/AdvertisingIdNotAvailableException.java
+++ /dev/null
@@ -1,37 +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.ads.identifier;
-
-import androidx.annotation.NonNull;
-
-/**
- * Indicates an AndroidX Advertising ID is not available.
- *
- * @deprecated Use the
- * <a href="https://developers.google.com/android/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient">
- * Advertising ID API that's available as part of Google Play Services</a> instead of this library.
- */
-@Deprecated
-public class AdvertisingIdNotAvailableException extends Exception {
-    public AdvertisingIdNotAvailableException(@NonNull String message) {
-        super(message);
-    }
-
-    public AdvertisingIdNotAvailableException(@NonNull String message, @NonNull Throwable cause) {
-        super(message, cause);
-    }
-}
diff --git a/ads/ads-identifier/src/main/java/androidx/ads/identifier/internal/HoldingConnectionClient.java b/ads/ads-identifier/src/main/java/androidx/ads/identifier/internal/HoldingConnectionClient.java
deleted file mode 100644
index 330cde5..0000000
--- a/ads/ads-identifier/src/main/java/androidx/ads/identifier/internal/HoldingConnectionClient.java
+++ /dev/null
@@ -1,232 +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.ads.identifier.internal;
-
-import static androidx.ads.identifier.AdvertisingIdUtils.GET_AD_ID_ACTION;
-
-import android.app.Service;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
-import android.os.IBinder;
-
-import androidx.ads.identifier.AdvertisingIdUtils;
-import androidx.ads.identifier.provider.IAdvertisingIdService;
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-import androidx.annotation.WorkerThread;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicLong;
-
-/** A client which keeps the ServiceConnection to the {@link IAdvertisingIdService}. */
-@SuppressWarnings("deprecation")
-public class HoldingConnectionClient {
-
-    private static final long SERVICE_CONNECTION_TIMEOUT_SECONDS = 10;
-
-    private final Context mContext;
-
-    @NonNull
-    private final BlockingServiceConnection mConnection;
-
-    @NonNull
-    private final String mPackageName;
-
-    @NonNull
-    private final IAdvertisingIdService mIdService;
-
-    /**
-     * The last connection ID which assign to the users of this client.
-     *
-     * <p>This also indicates the connection status, >= 0 indicates this client is connected,
-     * otherwise this client has already been disconnected.
-     * <p>It helps to synchronize between the usages of this client and auto disconnection task by
-     * using this single atomic, which supports 3 kinds of atomic operations:
-     * <ul>
-     *     <li>Checks whether this client is connected, if yes, increment and get a connection ID.
-     *     <li>When an auto disconnect task is due, it compares its connection ID to this value, if
-     *     same, unbind the service and sets this atomic to {@link Long#MIN_VALUE}.
-     *     <li>When this client's connection has lost and
-     *     {@link BlockingServiceConnection#onServiceDisconnected} is called, unbind the service
-     *     and sets this atomic to {@link Long#MIN_VALUE}.
-     * </ul>
-     * <p>This ID is monotonically increasing, except when this client is disconnected, this ID
-     * sets to {@link Long#MIN_VALUE}.
-     */
-    private final AtomicLong mLastConnectionId = new AtomicLong(0);
-
-    @WorkerThread
-    public HoldingConnectionClient(@NonNull Context context)
-            throws androidx.ads.identifier.AdvertisingIdNotAvailableException, IOException,
-            TimeoutException,
-            InterruptedException {
-        mContext = context;
-        ComponentName componentName = getProviderComponentName(mContext);
-        mConnection = getServiceConnection(componentName);
-        mIdService = getIdServiceFromConnection();
-        mPackageName = componentName.getPackageName();
-    }
-
-    /** Gets the connected {@link IAdvertisingIdService}. */
-    @NonNull
-    public IAdvertisingIdService getIdService() {
-        return mIdService;
-    }
-
-    /** Gets the connected service's package name. */
-    @NonNull
-    public String getPackageName() {
-        return mPackageName;
-    }
-
-    /** Gets whether the client is connected to the {@link IAdvertisingIdService}. */
-    public boolean isConnected() {
-        return mLastConnectionId.get() >= 0;
-    }
-
-    /**
-     * Gets a connection ID before using this client which prevents race condition with the auto
-     * disconnection task.
-     *
-     * @return connection ID, >= 0 indicates this client is connected, otherwise this client has
-     * already been disconnected.
-     */
-    public long askConnectionId() {
-        return mLastConnectionId.incrementAndGet();
-    }
-
-    /**
-     * Closes the connection to the Advertising ID Provider Service.
-     *
-     * <p>Note: If the connection has already been closed, does nothing.
-     */
-    void finish() {
-        if (mLastConnectionId.getAndSet(Long.MIN_VALUE) >= 0) {
-            mContext.unbindService(mConnection);
-        }
-    }
-
-    /**
-     * Tries to close the connection to the Advertising ID Provider Service if no one is using the
-     * client.
-     *
-     * @return true if this client is disconnected after this method returns.
-     */
-    public boolean tryFinish(long connectionId) {
-        if (mLastConnectionId.compareAndSet(connectionId, Long.MIN_VALUE)) {
-            mContext.unbindService(mConnection);
-            return true;
-        }
-        return !isConnected();
-    }
-
-    private static ComponentName getProviderComponentName(Context context)
-            throws androidx.ads.identifier.AdvertisingIdNotAvailableException {
-        PackageManager packageManager = context.getPackageManager();
-        List<ServiceInfo> serviceInfos =
-                AdvertisingIdUtils.getAdvertisingIdProviderServices(packageManager);
-        ServiceInfo serviceInfo =
-                AdvertisingIdUtils.selectServiceByPriority(serviceInfos, packageManager);
-        if (serviceInfo == null) {
-            throw new androidx.ads.identifier.AdvertisingIdNotAvailableException(
-                    "No compatible AndroidX Advertising ID Provider available.");
-        }
-        return new ComponentName(serviceInfo.packageName, serviceInfo.name);
-    }
-
-    /**
-     * Retrieves BlockingServiceConnection which must be unbound after use.
-     *
-     * @throws IOException when unable to bind service successfully.
-     */
-    @VisibleForTesting
-    BlockingServiceConnection getServiceConnection(ComponentName componentName) throws IOException {
-        Intent intent = new Intent(GET_AD_ID_ACTION);
-        intent.setComponent(componentName);
-
-        BlockingServiceConnection bsc = new BlockingServiceConnection();
-        if (mContext.bindService(intent, bsc, Service.BIND_AUTO_CREATE)) {
-            return bsc;
-        } else {
-            throw new IOException("Connection failure");
-        }
-    }
-
-    /**
-     * Gets the {@link IAdvertisingIdService} from the blocking queue. This should wait until
-     * {@link ServiceConnection#onServiceConnected} event with a
-     * {@link #SERVICE_CONNECTION_TIMEOUT_SECONDS} second timeout.
-     *
-     * @throws TimeoutException     if connection timeout period has expired.
-     * @throws InterruptedException if connection has been interrupted before connected.
-     */
-    @VisibleForTesting
-    @WorkerThread
-    IAdvertisingIdService getIdServiceFromConnection()
-            throws TimeoutException, InterruptedException {
-        // Block until the bind is complete, or timeout period is over.
-        return IAdvertisingIdService.Stub.asInterface(mConnection.getServiceWithTimeout());
-    }
-
-    /**
-     * A one-time use ServiceConnection that facilitates waiting for the bind to complete and the
-     * passing of the IBinder from the callback thread to the waiting thread.
-     */
-    class BlockingServiceConnection implements ServiceConnection {
-        // Facilitates passing of the IBinder across threads
-        private final BlockingQueue<IBinder> mBlockingQueue = new LinkedBlockingQueue<>();
-
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            mBlockingQueue.add(service);
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            finish();
-        }
-
-        /**
-         * Blocks until the bind is complete with a timeout and returns the bound IBinder. This must
-         * only be called once.
-         *
-         * @return the IBinder of the bound service
-         * @throws InterruptedException if the current thread is interrupted while waiting for
-         *                              the bind
-         * @throws TimeoutException     if the timeout period has elapsed
-         */
-        @WorkerThread
-        @NonNull
-        IBinder getServiceWithTimeout() throws InterruptedException, TimeoutException {
-            IBinder binder =
-                    mBlockingQueue.poll(SERVICE_CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS);
-            if (binder == null) {
-                throw new TimeoutException("Timed out waiting for the service connection");
-            }
-            return binder;
-        }
-    }
-}
diff --git a/ads/ads-identifier/src/main/java/androidx/ads/identifier/internal/package-info.java b/ads/ads-identifier/src/main/java/androidx/ads/identifier/internal/package-info.java
deleted file mode 100644
index e9ad310..0000000
--- a/ads/ads-identifier/src/main/java/androidx/ads/identifier/internal/package-info.java
+++ /dev/null
@@ -1,25 +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.
- */
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-package androidx.ads.identifier.internal;
-
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import androidx.annotation.RestrictTo;
diff --git a/appactions/interaction/OWNERS b/appactions/interaction/OWNERS
index 7dbb814..1f59937 100644
--- a/appactions/interaction/OWNERS
+++ b/appactions/interaction/OWNERS
@@ -1,4 +1,5 @@
 # Bug component: 1292315
 aliibrahim@google.com
+dennistwo@google.com
 jaazm@google.com
-mkucharski@google.com
\ No newline at end of file
+mkucharski@google.com
diff --git a/appactions/interaction/interaction-capabilities-communication/build.gradle b/appactions/interaction/interaction-capabilities-communication/build.gradle
index d73076d..9de46bb 100644
--- a/appactions/interaction/interaction-capabilities-communication/build.gradle
+++ b/appactions/interaction/interaction-capabilities-communication/build.gradle
@@ -25,10 +25,15 @@
 dependencies {
     api(libs.kotlinStdlib)
     implementation("androidx.annotation:annotation:1.1.0")
+    implementation(project(":appactions:interaction:interaction-capabilities-core"))
 }
 
 android {
     namespace "androidx.appactions.interaction.capabilities.communication"
+    defaultConfig {
+        // TODO(b/266649259): lower minSdk version once Optional is removed.
+        minSdkVersion 33
+    }
 }
 
 androidx {
diff --git a/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateCall.kt b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateCall.kt
new file mode 100644
index 0000000..0aea3b2
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateCall.kt
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.communication
+
+import androidx.appactions.interaction.capabilities.core.ActionCapability
+import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
+import androidx.appactions.interaction.capabilities.core.CapabilityFactory
+import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
+import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty
+import androidx.appactions.interaction.capabilities.core.task.impl.AbstractTaskUpdater
+import androidx.appactions.interaction.capabilities.core.values.Call
+import androidx.appactions.interaction.capabilities.core.values.Call.CallFormat
+import androidx.appactions.interaction.capabilities.core.values.GenericErrorStatus
+import androidx.appactions.interaction.capabilities.core.values.properties.Participant
+import androidx.appactions.interaction.capabilities.core.values.SuccessStatus
+import androidx.appactions.interaction.proto.ParamValue
+import androidx.appactions.interaction.protobuf.Struct
+import androidx.appactions.interaction.protobuf.Value
+import java.util.Optional
+
+private const val CAPABILITY_NAME: String = "actions.intent.CREATE_CALL"
+
+private val ACTION_SPEC =
+    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+        .setDescriptor(CreateCall.Property::class.java)
+        .setArgument(CreateCall.Argument::class.java, CreateCall.Argument::Builder)
+        .setOutput(CreateCall.Output::class.java)
+        .bindOptionalParameter(
+            "call.callFormat",
+            { property -> Optional.ofNullable(property.callFormat) },
+            CreateCall.Argument.Builder::setCallFormat,
+            TypeConverters::toCallFormat,
+            TypeConverters::toEntity
+        )
+        .bindRepeatedParameter(
+            "call.participant",
+            { property -> Optional.ofNullable(property.participant) },
+            CreateCall.Argument.Builder::setParticipantList,
+            ParticipantValue.FROM_PARAM_VALUE,
+            TypeConverters::toEntity
+        )
+        .bindOptionalOutput(
+            "call",
+            { output -> Optional.ofNullable(output.call) },
+            TypeConverters::toParamValue
+        )
+        .bindOptionalOutput(
+            "executionStatus",
+            { output -> Optional.ofNullable(output.executionStatus) },
+            CreateCall.ExecutionStatus::toParamValue
+        )
+        .build()
+
+@CapabilityFactory(name = CAPABILITY_NAME)
+class CreateCall private constructor() {
+    class CapabilityBuilder :
+        CapabilityBuilderBase<
+            CapabilityBuilder, Property, Argument, Output, Confirmation, TaskUpdater, Session
+        >(ACTION_SPEC) {
+        override fun build(): ActionCapability {
+            super.setProperty(Property.Builder().build())
+            // TODO(b/268369632): No-op remove empty property builder after Property is removed.
+            super.setProperty(Property.Builder().build())
+            return super.build()
+        }
+    }
+
+    // TODO(b/268369632): Remove Property from public capability APIs.
+    class Property
+    internal constructor(
+        val callFormat: TypeProperty<CallFormat>?,
+        val participant: TypeProperty<Participant>?
+    ) {
+        override fun toString(): String {
+            return "Property(callFormat=$callFormat, participant=$participant)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass != other?.javaClass) return false
+
+            other as Property
+
+            if (callFormat != other.callFormat) return false
+            if (participant != other.participant) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            var result = callFormat.hashCode()
+            result = 31 * result + participant.hashCode()
+            return result
+        }
+
+        class Builder {
+            private var callFormat: TypeProperty<CallFormat>? = null
+
+            private var participant: TypeProperty<Participant>? = null
+
+            fun setCallFormat(callFormat: TypeProperty<CallFormat>): Builder = apply {
+                this.callFormat = callFormat
+            }
+
+            fun build(): Property = Property(callFormat, participant)
+        }
+    }
+
+    class Argument
+    internal constructor(
+        val callFormat: CallFormat?,
+        val participantList: List<ParticipantValue>,
+    ) {
+        override fun toString(): String {
+            return "Argument(callFormat=$callFormat, participantList=$participantList)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass != other?.javaClass) return false
+
+            other as Argument
+
+            if (callFormat != other.callFormat) return false
+            if (participantList != other.participantList) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            var result = callFormat.hashCode()
+            result = 31 * result + participantList.hashCode()
+            return result
+        }
+
+        class Builder : BuilderOf<Argument> {
+            private var callFormat: CallFormat? = null
+            private var participantList: List<ParticipantValue> = mutableListOf()
+
+            fun setCallFormat(callFormat: CallFormat): Builder = apply {
+                this.callFormat = callFormat
+            }
+
+            fun setParticipantList(participantList: List<ParticipantValue>): Builder = apply {
+                this.participantList = participantList
+            }
+
+            override fun build(): Argument = Argument(callFormat, participantList)
+        }
+    }
+
+    class Output internal constructor(val call: Call?, val executionStatus: ExecutionStatus?) {
+        override fun toString(): String {
+            return "Output(call=$call, executionStatus=$executionStatus)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass != other?.javaClass) return false
+
+            other as Output
+
+            if (call != other.call) return false
+            if (executionStatus != other.executionStatus) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            var result = call.hashCode()
+            result = 31 * result + executionStatus.hashCode()
+            return result
+        }
+
+        class Builder {
+            private var call: Call? = null
+            private var executionStatus: ExecutionStatus? = null
+
+            fun setCall(call: Call): Builder = apply { this.call = call }
+
+            fun setExecutionStatus(executionStatus: ExecutionStatus): Builder = apply {
+                this.executionStatus = executionStatus
+            }
+
+            fun build(): Output = Output(call, executionStatus)
+        }
+    }
+
+    class ExecutionStatus {
+        private var successStatus: SuccessStatus? = null
+        private var genericErrorStatus: GenericErrorStatus? = null
+
+        constructor(successStatus: SuccessStatus) {
+            this.successStatus = successStatus
+        }
+
+        constructor(genericErrorStatus: GenericErrorStatus) {
+            this.genericErrorStatus = genericErrorStatus
+        }
+
+        internal fun toParamValue(): ParamValue {
+            var status = ""
+            if (successStatus != null) {
+                status = successStatus.toString()
+            }
+            if (genericErrorStatus != null) {
+                status = genericErrorStatus.toString()
+            }
+            val value: Value = Value.newBuilder().setStringValue(status).build()
+            return ParamValue.newBuilder()
+                .setStructValue(
+                    Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build()
+                )
+                .build()
+        }
+    }
+
+    class Confirmation internal constructor()
+
+    class TaskUpdater internal constructor() : AbstractTaskUpdater()
+
+    sealed interface Session : BaseSession<Argument, Output>
+}
diff --git a/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateMessage.kt b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateMessage.kt
new file mode 100644
index 0000000..873b7ff
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/CreateMessage.kt
@@ -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.
+ */
+
+package androidx.appactions.interaction.capabilities.communication
+
+import androidx.appactions.interaction.capabilities.core.ActionCapability
+import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
+import androidx.appactions.interaction.capabilities.core.CapabilityFactory
+import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
+import androidx.appactions.interaction.capabilities.core.impl.converters.PropertyConverter
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
+import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
+import androidx.appactions.interaction.capabilities.core.properties.StringValue
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty
+import androidx.appactions.interaction.capabilities.core.task.impl.AbstractTaskUpdater
+import androidx.appactions.interaction.capabilities.core.values.GenericErrorStatus
+import androidx.appactions.interaction.capabilities.core.values.Message
+import androidx.appactions.interaction.capabilities.core.values.properties.Recipient
+import androidx.appactions.interaction.capabilities.core.values.SuccessStatus
+import androidx.appactions.interaction.proto.ParamValue
+import androidx.appactions.interaction.protobuf.Struct
+import androidx.appactions.interaction.protobuf.Value
+import java.util.Optional
+
+private const val CAPABILITY_NAME: String = "actions.intent.CREATE_MESSAGE"
+
+private val ACTION_SPEC =
+    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+        .setDescriptor(CreateMessage.Property::class.java)
+        .setArgument(CreateMessage.Argument::class.java, CreateMessage.Argument::Builder)
+        .setOutput(CreateMessage.Output::class.java)
+        .bindRepeatedParameter(
+            "message.recipient",
+            { property -> Optional.ofNullable(property.recipient) },
+            CreateMessage.Argument.Builder::setRecipientList,
+            RecipientValue.FROM_PARAM_VALUE,
+            TypeConverters::toEntity
+        )
+        .bindOptionalParameter(
+            "message.text",
+            { property -> Optional.ofNullable(property.messageText) },
+            CreateMessage.Argument.Builder::setMessageText,
+            TypeConverters::toStringValue,
+            PropertyConverter::stringValueToProto
+        )
+        .bindOptionalOutput(
+            "message",
+            { output -> Optional.ofNullable(output.message) },
+            TypeConverters::toParamValue
+        )
+        .bindOptionalOutput(
+            "executionStatus",
+            { output -> Optional.ofNullable(output.executionStatus) },
+            CreateMessage.ExecutionStatus::toParamValue
+        )
+        .build()
+
+@CapabilityFactory(name = CAPABILITY_NAME)
+class CreateMessage private constructor() {
+    class CapabilityBuilder :
+        CapabilityBuilderBase<
+            CapabilityBuilder, Property, Argument, Output, Confirmation, TaskUpdater, Session
+        >(ACTION_SPEC) {
+        override fun build(): ActionCapability {
+            super.setProperty(Property.Builder().build())
+            // TODO(b/268369632): No-op remove empty property builder after Property is removed.
+            super.setProperty(Property.Builder().build())
+            return super.build()
+        }
+    }
+
+    // TODO(b/268369632): Remove Property from public capability APIs.
+    class Property
+    internal constructor(
+        val recipient: TypeProperty<Recipient>?,
+        val messageText: TypeProperty<StringValue>?
+    ) {
+        override fun toString(): String {
+            return "Property(recipient=$recipient, messageText=$messageText)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass != other?.javaClass) return false
+
+            other as Property
+
+            if (recipient != other.recipient) return false
+            if (messageText != other.messageText) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            var result = recipient.hashCode()
+            result = 31 * result + messageText.hashCode()
+            return result
+        }
+
+        class Builder {
+            private var recipient: TypeProperty<Recipient>? = null
+            private var messageText: TypeProperty<StringValue>? = null
+
+            fun setRecipient(recipient: TypeProperty<Recipient>): Builder = apply {
+                this.recipient = recipient
+            }
+
+            fun setMessageText(messageText: TypeProperty<StringValue>): Builder = apply {
+                this.messageText = messageText
+            }
+
+            fun build(): Property = Property(recipient, messageText)
+        }
+    }
+
+    class Argument
+    internal constructor(val recipientList: List<RecipientValue>, val messageText: String?) {
+        override fun toString(): String {
+            return "Argument(recipient=$recipientList, messageTextList=$messageText)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass != other?.javaClass) return false
+
+            other as Argument
+
+            if (recipientList != other.recipientList) return false
+            if (messageText != other.messageText) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            var result = recipientList.hashCode()
+            result = 31 * result + messageText.hashCode()
+            return result
+        }
+
+        class Builder : BuilderOf<Argument> {
+            private var recipientList: List<RecipientValue> = mutableListOf()
+            private var messageText: String? = null
+
+            fun setRecipientList(recipientList: List<RecipientValue>): Builder = apply {
+                this.recipientList = recipientList
+            }
+
+            fun setMessageText(messageTextList: String): Builder = apply {
+                this.messageText = messageTextList
+            }
+
+            override fun build(): Argument = Argument(recipientList, messageText)
+        }
+    }
+
+    class Output
+    internal constructor(val message: Message?, val executionStatus: ExecutionStatus?) {
+        override fun toString(): String {
+            return "Output(call=$message, executionStatus=$executionStatus)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass != other?.javaClass) return false
+
+            other as Output
+
+            if (message != other.message) return false
+            if (executionStatus != other.executionStatus) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            var result = message.hashCode()
+            result = 31 * result + executionStatus.hashCode()
+            return result
+        }
+
+        class Builder {
+            private var message: Message? = null
+            private var executionStatus: ExecutionStatus? = null
+
+            fun setMessage(message: Message): Builder = apply { this.message = message }
+
+            fun setExecutionStatus(executionStatus: ExecutionStatus): Builder = apply {
+                this.executionStatus = executionStatus
+            }
+
+            fun build(): Output = Output(message, executionStatus)
+        }
+    }
+
+    class ExecutionStatus {
+        private var successStatus: SuccessStatus? = null
+        private var genericErrorStatus: GenericErrorStatus? = null
+
+        constructor(successStatus: SuccessStatus) {
+            this.successStatus = successStatus
+        }
+
+        constructor(genericErrorStatus: GenericErrorStatus) {
+            this.genericErrorStatus = genericErrorStatus
+        }
+
+        internal fun toParamValue(): ParamValue {
+            var status = ""
+            if (successStatus != null) {
+                status = successStatus.toString()
+            }
+            if (genericErrorStatus != null) {
+                status = genericErrorStatus.toString()
+            }
+            val value: Value = Value.newBuilder().setStringValue(status).build()
+            return ParamValue.newBuilder()
+                .setStructValue(
+                    Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build()
+                )
+                .build()
+        }
+    }
+
+    class Confirmation internal constructor()
+
+    class TaskUpdater internal constructor() : AbstractTaskUpdater()
+
+    sealed interface Session : BaseSession<Argument, Output>
+}
diff --git a/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/ParticipantValue.kt b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/ParticipantValue.kt
new file mode 100644
index 0000000..5e3dcf4
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/ParticipantValue.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.appactions.interaction.capabilities.communication
+
+import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
+import androidx.appactions.interaction.capabilities.core.impl.converters.UnionTypeSpec
+import androidx.appactions.interaction.capabilities.core.values.SearchAction
+import androidx.appactions.interaction.capabilities.core.values.properties.Participant
+
+class ParticipantValue private constructor(
+    val asParticipant: Participant?,
+    val asParticipantFilter: SearchAction<Participant>?,
+) {
+    constructor(participant: Participant) : this(participant, null)
+
+    // TODO(b/268071906) add ParticipantFilter type to SearchAction
+    constructor(participantFilter: SearchAction<Participant>) : this(null, participantFilter)
+
+    companion object {
+        private val TYPE_SPEC = UnionTypeSpec.Builder<ParticipantValue>()
+            .bindMemberType(
+                memberGetter = ParticipantValue::asParticipant,
+                ctor = { ParticipantValue(it) },
+                typeSpec = TypeConverters.PARTICIPANT_TYPE_SPEC,
+            )
+            .bindMemberType(
+                memberGetter = ParticipantValue::asParticipantFilter,
+                ctor = { ParticipantValue(it) },
+                typeSpec = TypeConverters.createSearchActionTypeSpec(
+                    TypeConverters.PARTICIPANT_TYPE_SPEC,
+                ),
+            )
+            .build()
+
+        internal val FROM_PARAM_VALUE = ParamValueConverter {
+            TYPE_SPEC.fromStruct(it.getStructValue())
+        }
+    }
+}
diff --git a/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/RecipientValue.kt b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/RecipientValue.kt
new file mode 100644
index 0000000..637f6b5
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/RecipientValue.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.communication
+
+import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
+import androidx.appactions.interaction.capabilities.core.impl.converters.UnionTypeSpec
+import androidx.appactions.interaction.capabilities.core.values.SearchAction
+import androidx.appactions.interaction.capabilities.core.values.properties.Recipient
+
+class RecipientValue private constructor(
+    val asRecipient: Recipient?,
+    val asRecipientFilter: SearchAction<Recipient>?,
+) {
+    constructor(recipient: Recipient) : this(recipient, null)
+
+    // TODO(b/268071906) add RecipientFilter type to SearchAction
+    constructor(recipientFilter: SearchAction<Recipient>) : this(null, recipientFilter)
+
+    companion object {
+        private val TYPE_SPEC = UnionTypeSpec.Builder<RecipientValue>()
+            .bindMemberType(
+                memberGetter = RecipientValue::asRecipient,
+                ctor = { RecipientValue(it) },
+                typeSpec = TypeConverters.RECIPIENT_TYPE_SPEC,
+            )
+            .bindMemberType(
+                memberGetter = RecipientValue::asRecipientFilter,
+                ctor = { RecipientValue(it) },
+                typeSpec = TypeConverters.createSearchActionTypeSpec(
+                    TypeConverters.RECIPIENT_TYPE_SPEC,
+                ),
+            )
+            .build()
+
+        internal val FROM_PARAM_VALUE = ParamValueConverter {
+            TYPE_SPEC.fromStruct(it.getStructValue())
+        }
+    }
+}
diff --git a/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/package-info.java b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/package-info.java
index 4c3216d..5d9654e 100644
--- a/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/package-info.java
+++ b/appactions/interaction/interaction-capabilities-communication/src/main/java/androidx/appactions/interaction/capabilities/communication/package-info.java
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+/** @hide */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 package androidx.appactions.interaction.capabilities.communication;
 
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/properties/Attendee.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/properties/Attendee.kt
new file mode 100644
index 0000000..6934635
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/properties/Attendee.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.builtintypes.properties
+
+import androidx.appactions.builtintypes.types.Person
+
+/**
+ * Represents the value of the union property: http://schema.org/attendee, currently it only can
+ * contain {@link Person}.
+ */
+class Attendee(person: Person) {
+    @get:JvmName("asPerson")
+    val asPerson: Person? = person
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/properties/CallFormat.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/properties/CallFormat.kt
new file mode 100644
index 0000000..e1b072d
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/properties/CallFormat.kt
@@ -0,0 +1,17 @@
+
+package androidx.appactions.builtintypes.properties
+
+class CallFormat private constructor(
+    val asText: String?,
+    val asCanonicalValue: CanonicalValue?,
+) {
+
+    constructor(text: String) : this(asText = text, asCanonicalValue = null)
+
+    constructor(canonicalValue: CanonicalValue) : this(
+        asText = null,
+        asCanonicalValue = canonicalValue
+    )
+
+    abstract class CanonicalValue internal constructor(val textValue: String)
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/properties/Participant.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/properties/Participant.kt
new file mode 100644
index 0000000..f044bbf
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/properties/Participant.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.builtintypes.properties
+
+import androidx.appactions.builtintypes.types.Person
+
+/**
+ * Represents the value of the union property: http://schema.org/participant, currently it only can
+ * contain {@link Person}.
+ */
+class Participant(person: Person) {
+    @get:JvmName("asPerson")
+    val asPerson: Person? = person
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/properties/Recipient.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/properties/Recipient.kt
new file mode 100644
index 0000000..186878a
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/properties/Recipient.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.builtintypes.properties
+
+import androidx.appactions.builtintypes.types.Person
+
+/**
+ * Represents the value of the union property: http://schema.org/recipient, currently it only can
+ * contain {@link Person}.
+ */
+class Recipient(person: Person) {
+    @get:JvmName("asPerson")
+    val asPerson: Person? = person
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/properties/Text.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/properties/Text.kt
new file mode 100644
index 0000000..4b1e628
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/properties/Text.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.appactions.builtintypes.properties
+
+class Text(asText: String) {
+    @get:JvmName("asText")
+    val asText: String = asText
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/ActionAlreadyInProgress.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/ActionAlreadyInProgress.kt
new file mode 100644
index 0000000..88279c8
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/ActionAlreadyInProgress.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.builtintypes.types
+
+import androidx.appactions.builtintypes.properties.Name
+
+interface ActionAlreadyInProgress : Thing {
+    override fun toBuilder(): Builder<*>
+
+    companion object {
+        @JvmStatic
+        fun Builder(): Builder<*> = ActionAlreadyInProgressBuilderImpl()
+    }
+
+    interface Builder<Self : Builder<Self>> : Thing.Builder<Self> {
+        override fun build(): ActionAlreadyInProgress
+    }
+}
+
+private class ActionAlreadyInProgressBuilderImpl :
+    ActionAlreadyInProgress.Builder<ActionAlreadyInProgressBuilderImpl> {
+
+    private var identifier: String? = null
+    private var name: Name? = null
+
+    override fun build() = ActionAlreadyInProgressImpl(identifier, name)
+
+    override fun setIdentifier(text: String?): ActionAlreadyInProgressBuilderImpl =
+        apply { identifier = text }
+
+    override fun setName(text: String): ActionAlreadyInProgressBuilderImpl =
+        apply { name = Name(text) }
+
+    override fun setName(name: Name?): ActionAlreadyInProgressBuilderImpl =
+        apply { this.name = name }
+
+    override fun clearName(): ActionAlreadyInProgressBuilderImpl = apply { name = null }
+}
+
+private class ActionAlreadyInProgressImpl(
+    override val identifier: String?,
+    override val name: Name?
+) :
+    ActionAlreadyInProgress {
+    override fun toBuilder(): ActionAlreadyInProgress.Builder<*> =
+        ActionAlreadyInProgressBuilderImpl().setIdentifier(identifier).setName(name)
+
+    override fun toString(): String = "ActionAlreadyInProgress"
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/ActionNotInProgress.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/ActionNotInProgress.kt
new file mode 100644
index 0000000..e5eee22d
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/ActionNotInProgress.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.builtintypes.types
+
+import androidx.appactions.builtintypes.properties.Name
+
+interface ActionNotInProgress : Thing {
+    override fun toBuilder(): Builder<*>
+
+    companion object {
+        @JvmStatic
+        fun Builder(): Builder<*> = ActionNotInProgressBuilderImpl()
+    }
+
+    interface Builder<Self : Builder<Self>> : Thing.Builder<Self> {
+        override fun build(): ActionNotInProgress
+    }
+}
+
+private class ActionNotInProgressBuilderImpl :
+    ActionNotInProgress.Builder<ActionNotInProgressBuilderImpl> {
+
+    private var identifier: String? = null
+    private var name: Name? = null
+
+    override fun build() = ActionNotInProgressImpl(identifier, name)
+
+    override fun setIdentifier(text: String?): ActionNotInProgressBuilderImpl =
+        apply { identifier = text }
+
+    override fun setName(text: String): ActionNotInProgressBuilderImpl = apply { name = Name(text) }
+
+    override fun setName(name: Name?): ActionNotInProgressBuilderImpl = apply { this.name = name }
+
+    override fun clearName(): ActionNotInProgressBuilderImpl = apply { name = null }
+}
+
+private class ActionNotInProgressImpl(override val identifier: String?, override val name: Name?) :
+    ActionNotInProgress {
+    override fun toBuilder(): ActionNotInProgress.Builder<*> =
+        ActionNotInProgressBuilderImpl().setIdentifier(identifier).setName(name)
+
+    override fun toString(): String = "ActionNotInProgress"
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/CalendarEvent.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/CalendarEvent.kt
index abd0fe1..e5cf454 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/CalendarEvent.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/CalendarEvent.kt
@@ -20,7 +20,7 @@
 import androidx.appactions.builtintypes.properties.EndDate
 import androidx.appactions.builtintypes.properties.Name
 import androidx.appactions.builtintypes.properties.StartDate
-import androidx.appactions.interaction.capabilities.core.values.properties.Attendee
+import androidx.appactions.builtintypes.properties.Attendee
 import java.time.LocalDate
 
 interface CalendarEvent : Thing {
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/Call.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/Call.kt
new file mode 100644
index 0000000..2567dea
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/Call.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.builtintypes.types
+
+import androidx.appactions.builtintypes.properties.CallFormat
+import androidx.appactions.builtintypes.properties.Name
+import androidx.appactions.builtintypes.properties.Participant
+
+interface Call : Thing {
+    val callFormat: CallFormat?
+    val participantList: List<Participant>
+    override fun toBuilder(): Builder<*>
+
+    companion object {
+        @JvmStatic
+        fun Builder(): Builder<*> = CallBuilderImpl()
+    }
+
+    object CanonicalValue {
+        class CallFormat private constructor(textValue: String) :
+            androidx.appactions.builtintypes.properties.CallFormat.CanonicalValue(textValue) {
+            companion object {
+                @JvmField
+                val Audio = CallFormat("Audio")
+
+                @JvmField
+                val Video = CallFormat("Video")
+            }
+        }
+    }
+
+    interface Builder<Self : Builder<Self>> : Thing.Builder<Self> {
+        fun setCallFormat(callFormat: CallFormat?): Self
+        fun setCallFormat(canonicalValue: CanonicalValue.CallFormat): Self
+        fun setCallFormat(text: String): Self
+        fun addParticipant(person: Person): Self
+        fun addParticipant(participant: Participant): Self
+        fun addAllParticipant(value: Iterable<Participant>): Self
+
+        override fun build(): Call
+    }
+}
+
+private class CallBuilderImpl : Call.Builder<CallBuilderImpl> {
+
+    private var identifier: String? = null
+    private var name: Name? = null
+    private var callFormat: CallFormat? = null
+    private var participantList = mutableListOf<Participant>()
+
+    override fun build() = CallImpl(identifier, name, callFormat, participantList.toList())
+
+    override fun setCallFormat(callFormat: CallFormat?): CallBuilderImpl =
+        apply { this.callFormat = callFormat }
+
+    override fun setCallFormat(canonicalValue: Call.CanonicalValue.CallFormat): CallBuilderImpl =
+        apply {
+            this.callFormat = CallFormat(canonicalValue)
+        }
+
+    override fun setCallFormat(text: String): CallBuilderImpl = apply {
+        this.callFormat = CallFormat(text)
+    }
+
+    override fun addParticipant(person: Person): CallBuilderImpl = apply {
+        participantList.add(Participant(person))
+    }
+
+    override fun addParticipant(participant: Participant): CallBuilderImpl = apply {
+        participantList.add(participant)
+    }
+
+    override fun addAllParticipant(value: Iterable<Participant>): CallBuilderImpl = apply {
+        participantList.addAll(value)
+    }
+
+    override fun setIdentifier(text: String?): CallBuilderImpl = apply { identifier = text }
+
+    override fun setName(text: String): CallBuilderImpl = apply { name = Name(text) }
+
+    override fun setName(name: Name?): CallBuilderImpl = apply { this.name = name }
+
+    override fun clearName(): CallBuilderImpl = apply { name = null }
+}
+
+private class CallImpl(
+    override val identifier: String?,
+    override val name: Name?,
+    override val callFormat: CallFormat?,
+    override val participantList: List<Participant>
+) : Call {
+    override fun toBuilder(): Call.Builder<*> =
+        CallBuilderImpl()
+            .setIdentifier(identifier)
+            .setName(name)
+            .setCallFormat(callFormat)
+            .addAllParticipant(participantList)
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/CreativeWork.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/CreativeWork.kt
new file mode 100644
index 0000000..3d340ea
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/CreativeWork.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.builtintypes.types
+
+import androidx.appactions.builtintypes.properties.Name
+import androidx.appactions.builtintypes.properties.Text
+
+interface CreativeWork : Thing {
+    val text: Text?
+
+    override fun toBuilder(): Thing.Builder<*>
+
+    companion object {
+        @JvmStatic
+        fun Builder(): Thing.Builder<*> = CreativeWorkBuilderImpl()
+    }
+
+    interface Builder<Self : Builder<Self>> : Thing.Builder<Self> {
+        fun setText(text: Text?): Self
+        fun setText(text: String): Self
+
+        override fun build(): CreativeWork
+    }
+}
+
+private class CreativeWorkBuilderImpl : CreativeWork.Builder<CreativeWorkBuilderImpl> {
+    private var identifier: String? = null
+    private var name: Name? = null
+    private var text: Text? = null
+
+    override fun build() = CreateWorkImpl(text, identifier, name)
+
+    override fun setText(text: Text?): CreativeWorkBuilderImpl = apply { this.text = text }
+
+    override fun setText(text: String): CreativeWorkBuilderImpl = apply { this.text = Text(text) }
+
+    override fun setIdentifier(text: String?): CreativeWorkBuilderImpl = apply {
+        identifier = text
+    }
+
+    override fun setName(text: String): CreativeWorkBuilderImpl = apply { name = Name(text) }
+    override fun setName(name: Name?): CreativeWorkBuilderImpl = apply {
+        this.name = name
+    }
+
+    override fun clearName(): CreativeWorkBuilderImpl = apply { name = null }
+}
+
+private class CreateWorkImpl(
+    override val text: Text?,
+    override val identifier: String?,
+    override val name: Name?
+) : CreativeWork {
+    override fun toBuilder(): CreativeWork.Builder<*> =
+        CreativeWorkBuilderImpl()
+            .setIdentifier(identifier)
+            .setName(name)
+            .setText(text)
+}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/GenericErrorStatus.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/GenericErrorStatus.kt
new file mode 100644
index 0000000..236cf9e
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/GenericErrorStatus.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.builtintypes.types
+
+import androidx.appactions.builtintypes.properties.Name
+
+interface GenericErrorStatus : Thing {
+    override fun toBuilder(): Builder<*>
+
+    companion object {
+        @JvmStatic
+        fun Builder(): Builder<*> = GenericErrorStatusBuilderImpl()
+    }
+
+    interface Builder<Self : Builder<Self>> : Thing.Builder<Self> {
+        override fun build(): GenericErrorStatus
+    }
+}
+
+private class GenericErrorStatusBuilderImpl :
+    GenericErrorStatus.Builder<GenericErrorStatusBuilderImpl> {
+
+    private var identifier: String? = null
+    private var name: Name? = null
+
+    override fun build() = GenericErrorStatusImpl(identifier, name)
+
+    override fun setIdentifier(text: String?): GenericErrorStatusBuilderImpl =
+        apply { identifier = text }
+
+    override fun setName(text: String): GenericErrorStatusBuilderImpl = apply { name = Name(text) }
+
+    override fun setName(name: Name?): GenericErrorStatusBuilderImpl = apply { this.name = name }
+
+    override fun clearName(): GenericErrorStatusBuilderImpl = apply { name = null }
+}
+
+private class GenericErrorStatusImpl(override val identifier: String?, override val name: Name?) :
+    GenericErrorStatus {
+    override fun toBuilder(): GenericErrorStatus.Builder<*> =
+        GenericErrorStatusBuilderImpl().setIdentifier(identifier).setName(name)
+
+    override fun toString(): String = "GenericErrorStatus"
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/Message.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/Message.kt
new file mode 100644
index 0000000..2b1de7c
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/Message.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.builtintypes.types
+
+import androidx.appactions.builtintypes.properties.Name
+import androidx.appactions.builtintypes.properties.Recipient
+import androidx.appactions.builtintypes.properties.Text
+
+interface Message : Thing, CreativeWork {
+    val recipientList: List<Recipient>
+    override fun toBuilder(): Builder<*>
+
+    companion object {
+        @JvmStatic
+        fun Builder(): Builder<*> = MessageBuilderImpl()
+    }
+
+    interface Builder<Self : Builder<Self>> : Thing.Builder<Self>, CreativeWork.Builder<Self> {
+        fun addRecipient(person: Person): Self
+        fun addRecipient(recipient: Recipient): Self
+        fun addAllRecipient(value: Iterable<Recipient>): Self
+
+        override fun build(): Message
+    }
+}
+
+private class MessageBuilderImpl : Message.Builder<MessageBuilderImpl> {
+
+    private var identifier: String? = null
+    private var name: Name? = null
+    private var text: Text? = null
+    private var recipientList = mutableListOf<Recipient>()
+
+    override fun build() = MessageImpl(identifier, name, text, recipientList.toList())
+
+    override fun addRecipient(person: Person): MessageBuilderImpl = apply {
+        recipientList.add(Recipient(person))
+    }
+
+    override fun addRecipient(recipient: Recipient): MessageBuilderImpl = apply {
+        recipientList.add(recipient)
+    }
+
+    override fun addAllRecipient(value: Iterable<Recipient>): MessageBuilderImpl = apply {
+        recipientList.addAll(value)
+    }
+
+    override fun setIdentifier(text: String?): MessageBuilderImpl = apply { identifier = text }
+
+    override fun setName(text: String): MessageBuilderImpl = apply { name = Name(text) }
+
+    override fun setName(name: Name?): MessageBuilderImpl = apply { this.name = name }
+
+    override fun clearName(): MessageBuilderImpl = apply { name = null }
+
+    override fun setText(text: Text?): MessageBuilderImpl = apply { this.text = text }
+
+    override fun setText(text: String): MessageBuilderImpl = apply { this.text = Text(text) }
+}
+
+private class MessageImpl(
+    override val identifier: String?,
+    override val name: Name?,
+    override val text: Text?,
+    override val recipientList: List<Recipient>
+) : Message {
+    override fun toBuilder(): Message.Builder<*> =
+        MessageBuilderImpl()
+            .setIdentifier(identifier)
+            .setName(name)
+            .setText(text)
+            .addAllRecipient(recipientList)
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/NoInternetConnection.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/NoInternetConnection.kt
new file mode 100644
index 0000000..bd890cc
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/NoInternetConnection.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.builtintypes.types
+
+import androidx.appactions.builtintypes.properties.Name
+
+interface NoInternetConnection : Thing {
+    override fun toBuilder(): Builder<*>
+
+    companion object {
+        @JvmStatic
+        fun Builder(): Builder<*> = NoInternetConnectionBuilderImpl()
+    }
+
+    interface Builder<Self : Builder<Self>> : Thing.Builder<Self> {
+        override fun build(): NoInternetConnection
+    }
+}
+
+private class NoInternetConnectionBuilderImpl :
+    NoInternetConnection.Builder<NoInternetConnectionBuilderImpl> {
+
+    private var identifier: String? = null
+    private var name: Name? = null
+
+    override fun build() = NoInternetConnectionImpl(identifier, name)
+
+    override fun setIdentifier(text: String?): NoInternetConnectionBuilderImpl =
+        apply { identifier = text }
+
+    override fun setName(text: String): NoInternetConnectionBuilderImpl =
+        apply { name = Name(text) }
+
+    override fun setName(name: Name?): NoInternetConnectionBuilderImpl = apply { this.name = name }
+
+    override fun clearName(): NoInternetConnectionBuilderImpl = apply { name = null }
+}
+
+private class NoInternetConnectionImpl(override val identifier: String?, override val name: Name?) :
+    NoInternetConnection {
+    override fun toBuilder(): NoInternetConnection.Builder<*> =
+        NoInternetConnectionBuilderImpl().setIdentifier(identifier).setName(name)
+
+    override fun toString(): String = "NoInternetConnection"
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/Person.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/Person.kt
new file mode 100644
index 0000000..3e1105e
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/Person.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.builtintypes.types
+
+import androidx.appactions.builtintypes.properties.Name
+
+interface Person : Thing {
+    val email: String?
+    val telephone: String?
+
+    override fun toBuilder(): Builder<*>
+
+    companion object {
+        @JvmStatic
+        fun Builder(): Builder<*> = PersonBuilderImpl()
+    }
+
+    interface Builder<Self : Builder<Self>> : Thing.Builder<Builder<Self>> {
+        fun setEmail(email: String?): Self
+        fun setTelephone(telephone: String?): Self
+
+        override fun build(): Person
+    }
+}
+
+private class PersonBuilderImpl : Person.Builder<PersonBuilderImpl> {
+
+    private var identifier: String? = null
+    private var name: Name? = null
+    private var email: String? = null
+    private var telephone: String? = null
+
+    override fun setEmail(email: String?): PersonBuilderImpl = apply { this.email = email }
+
+    override fun setTelephone(telephone: String?): PersonBuilderImpl =
+        apply { this.telephone = telephone }
+
+    override fun build() = PersonImpl(identifier, name, email, telephone)
+
+    override fun setIdentifier(text: String?): PersonBuilderImpl = apply { identifier = text }
+
+    override fun setName(text: String): PersonBuilderImpl = apply { name = Name(text) }
+
+    override fun setName(name: Name?): PersonBuilderImpl = apply { this.name = name }
+
+    override fun clearName(): PersonBuilderImpl = apply { name = null }
+}
+
+private class PersonImpl(
+    override val identifier: String?,
+    override val name: Name?,
+    override val email: String?,
+    override val telephone: String?
+) : Person {
+    override fun toBuilder(): Person.Builder<*> =
+        PersonBuilderImpl()
+            .setIdentifier(identifier)
+            .setName(name)
+            .setEmail(email)
+            .setTelephone(telephone)
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/SuccessStatus.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/SuccessStatus.kt
new file mode 100644
index 0000000..834eba0
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/builtintypes/types/SuccessStatus.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.builtintypes.types
+
+import androidx.appactions.builtintypes.properties.Name
+
+interface SuccessStatus : Thing {
+    override fun toBuilder(): Builder<*>
+
+    companion object {
+        @JvmStatic
+        fun Builder(): Builder<*> = SuccessStatusBuilderImpl()
+    }
+
+    interface Builder<Self : Builder<Self>> : Thing.Builder<Self> {
+        override fun build(): SuccessStatus
+    }
+}
+
+private class SuccessStatusBuilderImpl :
+    SuccessStatus.Builder<SuccessStatusBuilderImpl> {
+
+    private var identifier: String? = null
+    private var name: Name? = null
+
+    override fun build() = SuccessStatusImpl(identifier, name)
+
+    override fun setIdentifier(text: String?): SuccessStatusBuilderImpl =
+        apply { identifier = text }
+
+    override fun setName(text: String): SuccessStatusBuilderImpl = apply { name = Name(text) }
+
+    override fun setName(name: Name?): SuccessStatusBuilderImpl = apply { this.name = name }
+
+    override fun clearName(): SuccessStatusBuilderImpl = apply { name = null }
+}
+
+private class SuccessStatusImpl(override val identifier: String?, override val name: Name?) :
+    SuccessStatus {
+    override fun toBuilder(): SuccessStatus.Builder<*> =
+        SuccessStatusBuilderImpl().setIdentifier(identifier).setName(name)
+
+    override fun toString(): String = "SuccessStatus"
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/BaseSession.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/BaseSession.kt
index f7561eb..3e6d9d7 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/BaseSession.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/BaseSession.kt
@@ -16,7 +16,8 @@
 
 package androidx.appactions.interaction.capabilities.core
 
-import androidx.appactions.interaction.capabilities.core.impl.concurrent.convertToListenableFuture
+import androidx.appactions.interaction.capabilities.core.impl.concurrent.Futures
+import androidx.concurrent.futures.await
 import com.google.common.util.concurrent.ListenableFuture
 
 /** Base interface for Session of all verticals. */
@@ -31,21 +32,21 @@
     /**
      * Called when all arguments are finalized.
      *
-     * @param argument the Argument instance containing data for fulfillment.
-     * @return an ExecutionResult instance.
+     * @param argument the [ArgumentT] instance containing data for fulfillment.
+     * @return an [ExecutionResult] instance.
      */
     suspend fun onFinish(argument: ArgumentT): ExecutionResult<OutputT> {
-        throw NotImplementedError()
+        return onFinishAsync(argument).await()
     }
 
     /**
      * Called when all arguments are finalized.
      *
      * @param argument the Argument instance containing data for fulfillment.
-     * @return a ListenableFuture containing an ExecutionResult instance.
+     * @return a [ListenableFuture] containing an [ExecutionResult] instance.
      */
     fun onFinishAsync(argument: ArgumentT): ListenableFuture<ExecutionResult<OutputT>> {
-        return convertToListenableFuture("onFinish") { onFinish(argument) }
+        return Futures.immediateFuture(ExecutionResult.getDefaultInstance())
     }
 
     /** Implement any cleanup logic. This method is called some time after the session finishes. */
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/CapabilityFactory.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/CapabilityFactory.kt
new file mode 100644
index 0000000..9ccee6e
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/CapabilityFactory.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core
+
+import androidx.annotation.RestrictTo
+
+/** Annotates a class for creating some ActionCapability.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@Target(AnnotationTarget.CLASS)
+@Retention(AnnotationRetention.RUNTIME)
+annotation class CapabilityFactory(val name: String)
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/entity/EntityLookupCandidate.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/entity/EntityLookupCandidate.kt
new file mode 100644
index 0000000..e1734cb
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/entity/EntityLookupCandidate.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.entity
+
+/** The candidate of the lookup results, including the entity object. */
+class EntityLookupCandidate<T> internal constructor(
+    val candidate: T
+) {
+
+    override fun toString(): String {
+        return "EntityLookupCandidate(cadidate=$candidate)"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as EntityLookupCandidate<*>
+
+        if (candidate != other.candidate) return false
+        return true
+    }
+
+    override fun hashCode(): Int {
+        return candidate.hashCode()
+    }
+
+    /** Builder class for [EntityLookupCandidate]. */
+    class Builder<T> {
+        private var candidate: T? = null
+        fun setCandidate(candidate: T): Builder<T> = apply { this.candidate = candidate }
+        fun build(): EntityLookupCandidate<T> = EntityLookupCandidate(
+            requireNotNull(candidate) { "Entity lookup candidate must be set." })
+    }
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/entity/EntityLookupRequest.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/entity/EntityLookupRequest.kt
new file mode 100644
index 0000000..457fb11
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/entity/EntityLookupRequest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.entity
+
+import androidx.appactions.interaction.capabilities.core.values.SearchAction
+import androidx.appactions.interaction.protobuf.ByteString
+
+/** The class for the request of the entity lookup. */
+class EntityLookupRequest<T> internal constructor(
+    val searchAction: SearchAction<T>,
+    val pageSize: Int?,
+    val pageToken: ByteString?,
+) {
+    override fun toString(): String {
+        return "EntityLookupRequest(" +
+            "searchAction=$searchAction, " +
+            "pageSize=$pageSize, " +
+            "pageToken=$pageToken)"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as EntityLookupRequest<*>
+
+        if (searchAction != other.searchAction) return false
+        if (pageSize != other.pageSize) return false
+        if (pageToken != other.pageToken) return false
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = searchAction.hashCode()
+        result = 31 * result + pageSize.hashCode()
+        result = 31 * result + pageToken.hashCode()
+        return result
+    }
+
+    /** Builder class for EntityLookupRequest. */
+    class Builder<T> {
+        private var searchAction: SearchAction<T>? = null
+        private var pageSize: Int? = null
+        private var pageToken: ByteString? = null
+
+        fun setSearchAction(searchAction: SearchAction<T>) =
+            apply { this.searchAction = searchAction }
+
+        fun setPageSize(pageSize: Int) = apply { this.pageSize = pageSize }
+
+        fun setPageToken(pageToken: ByteString) = apply { this.pageToken = pageToken }
+
+        fun build() = EntityLookupRequest(
+            requireNotNull(searchAction) { "Search action must be set." },
+            pageSize,
+            pageToken
+        )
+    }
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/entity/EntityLookupResponse.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/entity/EntityLookupResponse.kt
new file mode 100644
index 0000000..e271cc5
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/entity/EntityLookupResponse.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.appactions.interaction.capabilities.core.entity
+
+import androidx.annotation.IntDef
+import androidx.appactions.interaction.protobuf.ByteString
+
+/** The class for the response of the entity lookup. */
+class EntityLookupResponse<T> internal constructor(
+    val candidateList: List<EntityLookupCandidate<T>>,
+    @property:EntityLookupStatus val status: Int,
+    val nextPageToken: ByteString?,
+) {
+    override fun toString(): String {
+        return "EntityLookupResponse(" +
+            "candidateList=$candidateList, " +
+            "status=$status, " +
+            "nextPageToken=$nextPageToken)"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as EntityLookupResponse<*>
+
+        if (candidateList != other.candidateList) return false
+        if (status != other.status) return false
+        if (nextPageToken != other.nextPageToken) return false
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = candidateList.hashCode()
+        result = 31 * result + status.hashCode()
+        result = 31 * result + nextPageToken.hashCode()
+        return result
+    }
+
+    /** Builder class for [Entity]. */
+    class Builder<T> {
+        private var candidateList: List<EntityLookupCandidate<T>> = listOf()
+
+        @property:EntityLookupStatus
+        private var status: Int = EntityLookupResponse.SUCCESS
+        private var nextPageToken: ByteString? = null
+        fun setCandidateList(candidateList: List<EntityLookupCandidate<T>>): Builder<T> = apply {
+            this.candidateList = candidateList
+        }
+
+        fun setStatus(status: @EntityLookupStatus Int): Builder<T> =
+            apply {
+                this.status = status
+            }
+
+        fun setNextPageToken(nextPageToken: ByteString): Builder<T> = apply {
+            this.nextPageToken = nextPageToken
+        }
+
+        fun build() = EntityLookupResponse<T>(candidateList, status, nextPageToken)
+    }
+
+    companion object {
+
+        const val SUCCESS: Int = 0
+        const val CANCELED: Int = 1
+        const val INVALID_PAGE_TOKEN: Int = 2
+        const val TIMEOUT: Int = 3
+        const val UNKNOWN_ERROR: Int = 4
+    }
+
+    // IntDef enum for lookup status.
+    @Target(
+        AnnotationTarget.PROPERTY,
+        AnnotationTarget.LOCAL_VARIABLE,
+        AnnotationTarget.VALUE_PARAMETER,
+        AnnotationTarget.TYPE
+    )
+    @Retention(AnnotationRetention.SOURCE)
+    @IntDef(
+        value = [
+            SUCCESS,
+            CANCELED,
+            INVALID_PAGE_TOKEN,
+            TIMEOUT,
+            UNKNOWN_ERROR,
+        ]
+    )
+    annotation class EntityLookupStatus
+}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/entity/EntityProvider.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/entity/EntityProvider.kt
new file mode 100644
index 0000000..fd45ab2
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/entity/EntityProvider.kt
@@ -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.appactions.interaction.capabilities.core.entity
+
+import androidx.annotation.RestrictTo
+import androidx.appactions.interaction.capabilities.core.impl.converters.SearchActionConverter
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeSpec
+import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException
+import androidx.appactions.interaction.capabilities.core.values.SearchAction
+import androidx.appactions.interaction.capabilities.core.values.Thing
+import androidx.appactions.interaction.proto.Entity
+import androidx.appactions.interaction.proto.GroundingRequest
+import androidx.appactions.interaction.proto.GroundingResponse
+
+/**
+ * EntityProvider could provide candidates for assistant's search actions.
+ *
+ * <p>Use abstract classes within the library to create instances of the {@link EntityProvider}.
+ */
+abstract class EntityProvider<T : Thing> internal constructor(private val typeSpec: TypeSpec<T>) {
+    /**
+     * Unique identifier for this EntityFilter. Must match the shortcuts.xml declaration, which allows
+     * different filters to be assigned to types on a per-BII basis.
+     */
+    abstract fun getId(): String
+
+    /**
+     * Executes the entity lookup.
+     *
+     * @param request The request includes e.g. entity, search metadata, etc.
+     */
+    abstract fun lookup(request: EntityLookupRequest<T>): EntityLookupResponse<T>
+
+    /**
+     * Internal method to lookup untyped entity, which will be used by service library to handle
+     * {@link GroundingRequest}.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    fun lookupInternal(request: GroundingRequest): GroundingResponse {
+        val converter: SearchActionConverter<T> =
+            TypeConverters.createSearchActionConverter(this.typeSpec)
+        val searchAction: SearchAction<T> = try {
+            converter.toSearchAction(request.request.searchAction)
+        } catch (e: StructConversionException) {
+            return createResponse(GroundingResponse.Status.INVALID_ENTITY_ARGUMENT)
+        }
+        val lookupRequest = EntityLookupRequest.Builder<T>()
+            .setSearchAction(searchAction)
+            .setPageSize(request.request.pageSize)
+            .setPageToken(request.request.pageToken)
+            .build()
+        val response = lookup(lookupRequest)
+        @EntityLookupResponse.EntityLookupStatus val status: Int = response.status
+        return if (status == EntityLookupResponse.SUCCESS) {
+            createResponse(response)
+        } else createResponse(convertStatus(status))
+    }
+
+    private fun createResponse(status: GroundingResponse.Status): GroundingResponse {
+        return GroundingResponse.newBuilder()
+            .setResponse(GroundingResponse.Response.newBuilder().setStatus(status))
+            .build()
+    }
+
+    private fun createResponse(response: EntityLookupResponse<T>): GroundingResponse {
+        val builder =
+            GroundingResponse.Response.newBuilder().setStatus(GroundingResponse.Status.SUCCESS)
+        for (candidate in response.candidateList) {
+            builder.addCandidates(
+                GroundingResponse.Candidate.newBuilder()
+                    .setGroundedEntity(
+                        Entity.newBuilder().setValue(typeSpec.toStruct(candidate.candidate))
+                    )
+                    .build()
+            )
+        }
+        return GroundingResponse.newBuilder().setResponse(builder.build()).build()
+    }
+
+    private fun convertStatus(
+        @EntityLookupResponse.EntityLookupStatus status: Int
+    ): GroundingResponse.Status {
+        return when (status) {
+            EntityLookupResponse.CANCELED -> GroundingResponse.Status.CANCELED
+            EntityLookupResponse.INVALID_PAGE_TOKEN -> GroundingResponse.Status.INVALID_PAGE_TOKEN
+            EntityLookupResponse.TIMEOUT -> GroundingResponse.Status.TIMEOUT
+            else -> GroundingResponse.Status.DEFAULT_UNKNOWN
+        }
+    }
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/entity/package-info.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/entity/package-info.java
new file mode 100644
index 0000000..1d8cf70
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/entity/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/** @hide */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+package androidx.appactions.interaction.capabilities.core.entity;
+
+import androidx.annotation.RestrictTo;
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/FulfillmentResult.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/FulfillmentResult.kt
new file mode 100644
index 0000000..c73f75b
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/FulfillmentResult.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.appactions.interaction.capabilities.core.impl
+
+import androidx.annotation.RestrictTo
+import androidx.appactions.interaction.proto.FulfillmentResponse
+
+/**
+ * A union type between FulfillmentResponse proto and ErrorStatusInternal value
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class FulfillmentResult(
+    val fulfillmentResponse: FulfillmentResponse?,
+    val errorStatus: ErrorStatusInternal?,
+) {
+    constructor(
+        fulfillmentResponse: FulfillmentResponse,
+    ) : this(fulfillmentResponse, null)
+
+    constructor(
+        errorStatus: ErrorStatusInternal,
+    ) : this(null, errorStatus)
+
+    fun applyToCallback(callback: CallbackInternal) {
+        if (fulfillmentResponse != null) {
+            callback.onSuccess(fulfillmentResponse)
+        } else {
+            callback.onError(errorStatus!!)
+        }
+    }
+}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilitySession.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilitySession.kt
index 6888593..dff7e78 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilitySession.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilitySession.kt
@@ -19,13 +19,14 @@
 import androidx.annotation.RestrictTo
 import androidx.appactions.interaction.capabilities.core.ActionExecutorAsync
 import androidx.appactions.interaction.capabilities.core.ExecutionResult
-import androidx.appactions.interaction.capabilities.core.impl.concurrent.FutureCallback
-import androidx.appactions.interaction.capabilities.core.impl.concurrent.Futures
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
 import androidx.appactions.interaction.proto.AppActionsContext.AppDialogState
-import androidx.appactions.interaction.proto.FulfillmentRequest.Fulfillment.FulfillmentValue
 import androidx.appactions.interaction.proto.FulfillmentResponse
 import androidx.appactions.interaction.proto.ParamValue
+import androidx.concurrent.futures.await
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
 
 /**
  * ActionCapabilitySession implementation for executing single-turn fulfillment requests.
@@ -36,9 +37,10 @@
 internal class SingleTurnCapabilitySession<
     ArgumentT,
     OutputT,
-    >(
-    val actionSpec: ActionSpec<*, ArgumentT, OutputT>,
-    val actionExecutorAsync: ActionExecutorAsync<ArgumentT, OutputT>,
+>(
+    private val actionSpec: ActionSpec<*, ArgumentT, OutputT>,
+    private val actionExecutorAsync: ActionExecutorAsync<ArgumentT, OutputT>,
+    private val scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
 ) : ActionCapabilitySession {
     override val state: AppDialogState
         get() {
@@ -63,38 +65,25 @@
         callback: CallbackInternal,
     ) {
         val paramValuesMap: Map<String, List<ParamValue>> =
-            argumentsWrapper.paramValues.entries.associate {
-                    entry: Map.Entry<String, List<FulfillmentValue>> ->
-                Pair(
-                    entry.key,
-                    entry.value.mapNotNull { fulfillmentValue: FulfillmentValue ->
-                        fulfillmentValue.getValue()
-                    },
-                )
-            }
+            argumentsWrapper.paramValues.mapValues { entry -> entry.value.mapNotNull { it.value } }
         val argument = actionSpec.buildArgument(paramValuesMap)
-        Futures.addCallback(
-            actionExecutorAsync.execute(argument),
-            object : FutureCallback<ExecutionResult<OutputT>> {
-                override fun onSuccess(executionResult: ExecutionResult<OutputT>) {
-                    callback.onSuccess(convertToFulfillmentResponse(executionResult))
-                }
-
-                override fun onFailure(t: Throwable) {
-                    callback.onError(ErrorStatusInternal.CANCELLED)
-                }
-            },
-            Runnable::run,
-        )
+        scope.launch {
+            try {
+                val output = actionExecutorAsync.execute(argument).await()
+                callback.onSuccess(convertToFulfillmentResponse(output))
+            } catch (t: Throwable) {
+                callback.onError(ErrorStatusInternal.CANCELLED)
+            }
+        }
     }
 
     /** Converts typed {@link ExecutionResult} to {@link FulfillmentResponse} proto. */
-    internal fun convertToFulfillmentResponse(
+    private fun convertToFulfillmentResponse(
         executionResult: ExecutionResult<OutputT>,
     ): FulfillmentResponse {
         val fulfillmentResponseBuilder =
             FulfillmentResponse.newBuilder().setStartDictation(executionResult.startDictation)
-        executionResult.output?.let { it ->
+        executionResult.output?.let {
             fulfillmentResponseBuilder.setExecutionOutput(
                 actionSpec.convertOutputToProto(it),
             )
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/DisambigEntityConverter.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/DisambigEntityConverter.java
deleted file mode 100644
index 9a95a32..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/DisambigEntityConverter.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.impl.converters;
-
-import androidx.annotation.NonNull;
-import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException;
-import androidx.appactions.interaction.proto.Entity;
-
-/**
- * Converter from {@code ValueTypeT} to the app-driven disambig entity i.e. {@code Entity} proto.
- * The ValueTypeT instance is usually a value object provided by the app.
- *
- * @param <ValueTypeT>
- */
-@FunctionalInterface
-public interface DisambigEntityConverter<ValueTypeT> {
-    @NonNull
-    Entity convert(ValueTypeT type) throws StructConversionException;
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/EntityConverter.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/EntityConverter.java
new file mode 100644
index 0000000..eb9c1c4
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/EntityConverter.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.impl.converters;
+
+import androidx.annotation.NonNull;
+import androidx.appactions.interaction.proto.Entity;
+
+/**
+ * Converter from any Type to the Entity proto. This converter is usually used in the direction from
+ * app to Assistant. Examples where the converter is needed is the developer
+ * setting possible values in Properties or returning "disambiguation entities" from an inventory
+ * listener.
+ *
+ * @param <T> The T instance is usually a value object provided by the app, e.g. a Timer
+ * object from the built-in-types library.
+ */
+@FunctionalInterface
+public interface EntityConverter<T> {
+
+    /** Converter to an Entity proto. */
+    @NonNull
+    Entity convert(@NonNull T type);
+}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/PropertyConverter.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/PropertyConverter.java
index 9bc407b..65b9116 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/PropertyConverter.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/PropertyConverter.java
@@ -19,79 +19,30 @@
 import static androidx.appactions.interaction.capabilities.core.impl.utils.ImmutableCollectors.toImmutableList;
 
 import androidx.annotation.NonNull;
-import androidx.appactions.interaction.capabilities.core.properties.EntityProperty;
-import androidx.appactions.interaction.capabilities.core.properties.EnumProperty;
-import androidx.appactions.interaction.capabilities.core.properties.IntegerProperty;
 import androidx.appactions.interaction.capabilities.core.properties.ParamProperty;
-import androidx.appactions.interaction.capabilities.core.properties.SimpleProperty;
-import androidx.appactions.interaction.capabilities.core.properties.StringOrEnumProperty;
-import androidx.appactions.interaction.capabilities.core.properties.StringProperty;
-import androidx.appactions.interaction.capabilities.core.properties.StringProperty.PossibleValue;
+import androidx.appactions.interaction.capabilities.core.properties.StringValue;
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty;
 import androidx.appactions.interaction.proto.AppActionsContext.IntentParameter;
 import androidx.appactions.interaction.proto.Entity;
 
 import java.util.List;
-import java.util.function.Function;
 
 /** Contains utility functions that convert properties to IntentParameter proto. */
 public final class PropertyConverter {
 
     private PropertyConverter() {}
 
-    /** Create IntentParameter proto from a StringProperty. */
+    /** Create IntentParameter proto from a TypeProperty. */
     @NonNull
-    public static IntentParameter getIntentParameter(
-            @NonNull String paramName, @NonNull StringProperty property) {
+    public static <T> IntentParameter getIntentParameter(
+            @NonNull String paramName, @NonNull TypeProperty<T> property,
+            @NonNull EntityConverter<T> entityConverter) {
         IntentParameter.Builder builder = newIntentParameterBuilder(paramName, property);
-        extractPossibleValues(property, PropertyConverter::possibleValueToProto).stream()
+        extractPossibleValues(property, entityConverter).stream()
                 .forEach(builder::addPossibleEntities);
         return builder.build();
     }
 
-    /** Create IntentParameter proto from a StringOrEnumProperty. */
-    @NonNull
-    public static <EnumT extends Enum<EnumT>> IntentParameter getIntentParameter(
-            @NonNull String paramName, @NonNull StringOrEnumProperty<EnumT> property) {
-        IntentParameter.Builder builder = newIntentParameterBuilder(paramName, property);
-        extractPossibleValues(property, PropertyConverter::possibleValueToProto).stream()
-                .forEach(builder::addPossibleEntities);
-        return builder.build();
-    }
-
-    /** Create IntentParameter proto from a EntityProperty. */
-    @NonNull
-    public static IntentParameter getIntentParameter(
-            @NonNull String paramName, @NonNull EntityProperty property) {
-        IntentParameter.Builder builder = newIntentParameterBuilder(paramName, property);
-        extractPossibleValues(property, PropertyConverter::entityToProto).stream()
-                .forEach(builder::addPossibleEntities);
-        return builder.build();
-    }
-
-    /** Create IntentParameter proto from a EnumProperty. */
-    @NonNull
-    public static <EnumT extends Enum<EnumT>> IntentParameter getIntentParameter(
-            @NonNull String paramName, @NonNull EnumProperty<EnumT> property) {
-        IntentParameter.Builder builder = newIntentParameterBuilder(paramName, property);
-        extractPossibleValues(property, PropertyConverter::enumToProto).stream()
-                .forEach(builder::addPossibleEntities);
-        return builder.build();
-    }
-
-    /** Create IntentParameter proto from a IntegerProperty. */
-    @NonNull
-    public static IntentParameter getIntentParameter(
-            @NonNull String paramName, @NonNull IntegerProperty property) {
-        return newIntentParameterBuilder(paramName, property).build();
-    }
-
-    /** Create IntentParameter proto from a SimpleProperty. */
-    @NonNull
-    public static IntentParameter getIntentParameter(
-            @NonNull String paramName, @NonNull SimpleProperty property) {
-        return newIntentParameterBuilder(paramName, property).build();
-    }
-
     /** Create IntentParameter.Builder from a generic ParamProperty, fills in the common fields. */
     private static IntentParameter.Builder newIntentParameterBuilder(
             String paramName, ParamProperty<?> paramProperty) {
@@ -103,8 +54,10 @@
     }
 
     private static <T> List<Entity> extractPossibleValues(
-            ParamProperty<T> property, Function<T, Entity> function) {
-        return property.getPossibleValues().stream().map(function).collect(toImmutableList());
+            ParamProperty<T> property, EntityConverter<T> function) {
+        return property.getPossibleValues().stream()
+                .map(function::convert)
+                .collect(toImmutableList());
     }
 
     /** Converts a properties/Entity to a appactions Entity proto. */
@@ -122,38 +75,14 @@
     }
 
     /**
-     * Converts a capabilities library StringProperty.PossibleValue to a appactions Entity proto .
+     * Converts a capabilities library [PossibleStringValue] to a appactions Entity proto .
      */
     @NonNull
-    public static Entity possibleValueToProto(@NonNull PossibleValue possibleValue) {
-        return androidx.appactions.interaction.proto.Entity.newBuilder()
+    public static Entity stringValueToProto(@NonNull StringValue possibleValue) {
+        return Entity.newBuilder()
                 .setIdentifier(possibleValue.getName())
                 .setName(possibleValue.getName())
                 .addAllAlternateNames(possibleValue.getAlternateNames())
                 .build();
     }
-
-    /**
-     * Converts a capabilities library StringOrEnumProperty.PossibleValue to a appactions Entity
-     * proto.
-     */
-    @NonNull
-    public static <EnumT extends Enum<EnumT>> Entity possibleValueToProto(
-            @NonNull StringOrEnumProperty.PossibleValue<EnumT> possibleValue) {
-
-        switch (possibleValue.getKind()) {
-            case STRING_VALUE:
-                return possibleValueToProto(possibleValue.getStringValue());
-            case ENUM_VALUE:
-                return enumToProto(possibleValue.getEnumValue());
-        }
-        throw new IllegalStateException("unreachable");
-    }
-
-    @NonNull
-    public static <EnumT extends Enum<EnumT>> Entity enumToProto(EnumT enumValue) {
-        return androidx.appactions.interaction.proto.Entity.newBuilder()
-                .setIdentifier(enumValue.toString())
-                .build();
-    }
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConverters.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConverters.java
index 272395f..8e872f3 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConverters.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeConverters.java
@@ -17,6 +17,7 @@
 package androidx.appactions.interaction.capabilities.core.impl.converters;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.appactions.interaction.capabilities.core.ExecutionResult;
 import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException;
 import androidx.appactions.interaction.capabilities.core.values.Alarm;
@@ -52,7 +53,6 @@
 import java.time.format.DateTimeParseException;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Optional;
 
 /** Converters for capability argument values. Convert from internal proto types to public types. */
 public final class TypeConverters {
@@ -94,13 +94,14 @@
                             ParcelDelivery::getTrackingNumber,
                             ParcelDelivery.Builder::setTrackingNumber)
                     .bindStringField(
-                            "trackingUrl", ParcelDelivery::getTrackingUrl,
+                            "trackingUrl",
+                            ParcelDelivery::getTrackingUrl,
                             ParcelDelivery.Builder::setTrackingUrl)
                     .build();
     public static final TypeSpec<Order> ORDER_TYPE_SPEC =
             TypeSpecBuilder.newBuilderForThing("Order", Order::newBuilder)
-                    .bindZonedDateTimeField("orderDate", Order::getOrderDate,
-                            Order.Builder::setOrderDate)
+                    .bindZonedDateTimeField(
+                            "orderDate", Order::getOrderDate, Order.Builder::setOrderDate)
                     .bindSpecField(
                             "orderDelivery",
                             Order::getOrderDelivery,
@@ -117,24 +118,34 @@
                             Order.Builder::setOrderStatus,
                             Order.OrderStatus.class)
                     .bindSpecField(
-                            "seller", Order::getSeller, Order.Builder::setSeller,
+                            "seller",
+                            Order::getSeller,
+                            Order.Builder::setSeller,
                             ORGANIZATION_TYPE_SPEC)
                     .build();
     public static final TypeSpec<Person> PERSON_TYPE_SPEC =
             TypeSpecBuilder.newBuilderForThing("Person", Person::newBuilder)
                     .bindStringField("email", Person::getEmail, Person.Builder::setEmail)
-                    .bindStringField("telephone", Person::getTelephone,
-                            Person.Builder::setTelephone)
+                    .bindStringField(
+                            "telephone", Person::getTelephone, Person.Builder::setTelephone)
                     .bindStringField("name", Person::getName, Person.Builder::setName)
                     .build();
     public static final TypeSpec<Alarm> ALARM_TYPE_SPEC =
             TypeSpecBuilder.newBuilderForThing("Alarm", Alarm::newBuilder).build();
     public static final TypeSpec<Timer> TIMER_TYPE_SPEC =
             TypeSpecBuilder.newBuilderForThing("Timer", Timer::newBuilder).build();
+    public static final TypeSpec<Attendee> ATTENDEE_TYPE_SPEC =
+            new UnionTypeSpec.Builder<Attendee>()
+                    .bindMemberType(
+                            (attendee) -> attendee.asPerson().orElse(null),
+                            (person) -> new Attendee(person),
+                            PERSON_TYPE_SPEC)
+                    .build();
     public static final TypeSpec<CalendarEvent> CALENDAR_EVENT_TYPE_SPEC =
             TypeSpecBuilder.newBuilderForThing("CalendarEvent", CalendarEvent::newBuilder)
                     .bindZonedDateTimeField(
-                            "startDate", CalendarEvent::getStartDate,
+                            "startDate",
+                            CalendarEvent::getStartDate,
                             CalendarEvent.Builder::setStartDate)
                     .bindZonedDateTimeField(
                             "endDate", CalendarEvent::getEndDate, CalendarEvent.Builder::setEndDate)
@@ -142,16 +153,31 @@
                             "attendee",
                             CalendarEvent::getAttendeeList,
                             CalendarEvent.Builder::addAllAttendee,
-                            new AttendeeTypeSpec())
+                            ATTENDEE_TYPE_SPEC)
                     .build();
     public static final TypeSpec<SafetyCheck> SAFETY_CHECK_TYPE_SPEC =
             TypeSpecBuilder.newBuilderForThing("SafetyCheck", SafetyCheck::newBuilder)
-                    .bindDurationField("duration", SafetyCheck::getDuration,
-                            SafetyCheck.Builder::setDuration)
+                    .bindDurationField(
+                            "duration", SafetyCheck::getDuration, SafetyCheck.Builder::setDuration)
                     .bindZonedDateTimeField(
-                            "checkinTime", SafetyCheck::getCheckinTime,
+                            "checkinTime",
+                            SafetyCheck::getCheckinTime,
                             SafetyCheck.Builder::setCheckinTime)
                     .build();
+    public static final TypeSpec<Recipient> RECIPIENT_TYPE_SPEC =
+            new UnionTypeSpec.Builder<Recipient>()
+                    .bindMemberType(
+                            (recipient) -> recipient.asPerson().orElse(null),
+                            (person) -> new Recipient(person),
+                            PERSON_TYPE_SPEC)
+                    .build();
+    public static final TypeSpec<Participant> PARTICIPANT_TYPE_SPEC =
+            new UnionTypeSpec.Builder<Participant>()
+                    .bindMemberType(
+                            (participant) -> participant.asPerson().orElse(null),
+                            (person) -> new Participant(person),
+                            PERSON_TYPE_SPEC)
+                    .build();
     private static final String FIELD_NAME_CALL_FORMAT = "callFormat";
     private static final String FIELD_NAME_PARTICIPANT = "participant";
     private static final String FIELD_NAME_TYPE_CALL = "Call";
@@ -160,7 +186,22 @@
     private static final String FIELD_NAME_RECIPIENT = "recipient";
     private static final String FIELD_NAME_TEXT = "text";
 
-    private TypeConverters() {
+    private TypeConverters() {}
+
+    /**
+     * @param paramValue
+     * @return
+     */
+    @NonNull
+    public static Call.CallFormat toCallFormat(@NonNull ParamValue paramValue)
+            throws StructConversionException {
+        String identifier = paramValue.getIdentifier();
+        if (identifier.equals(Call.CallFormat.AUDIO.toString())) {
+            return Call.CallFormat.AUDIO;
+        } else if (identifier.equals(Call.CallFormat.VIDEO.toString())) {
+            return Call.CallFormat.VIDEO;
+        }
+        throw new StructConversionException(String.format("Unknown enum format '%s'.", identifier));
     }
 
     /**
@@ -185,7 +226,8 @@
         return (int) paramValue.getNumberValue();
     }
 
-    /** Converts a ParamValue to a Boolean object.
+    /**
+     * Converts a ParamValue to a Boolean object.
      *
      * @param paramValue
      * @return
@@ -214,8 +256,8 @@
             try {
                 return LocalDate.parse(paramValue.getStringValue());
             } catch (DateTimeParseException e) {
-                throw new StructConversionException("Failed to parse ISO 8601 string to LocalDate",
-                        e);
+                throw new StructConversionException(
+                        "Failed to parse ISO 8601 string to LocalDate", e);
             }
         }
         throw new StructConversionException(
@@ -234,8 +276,8 @@
             try {
                 return LocalTime.parse(paramValue.getStringValue());
             } catch (DateTimeParseException e) {
-                throw new StructConversionException("Failed to parse ISO 8601 string to LocalTime",
-                        e);
+                throw new StructConversionException(
+                        "Failed to parse ISO 8601 string to LocalTime", e);
             }
         }
         throw new StructConversionException(
@@ -297,8 +339,8 @@
      */
     @NonNull
     public static Entity toEntity(@NonNull ItemList itemList) {
-        Entity.Builder builder = Entity.newBuilder().setValue(
-                ITEM_LIST_TYPE_SPEC.toStruct(itemList));
+        Entity.Builder builder =
+                Entity.newBuilder().setValue(ITEM_LIST_TYPE_SPEC.toStruct(itemList));
         itemList.getId().ifPresent(builder::setIdentifier);
         return builder.build();
     }
@@ -311,8 +353,8 @@
      */
     @NonNull
     public static Entity toEntity(@NonNull ListItem listItem) {
-        Entity.Builder builder = Entity.newBuilder().setValue(
-                LIST_ITEM_TYPE_SPEC.toStruct(listItem));
+        Entity.Builder builder =
+                Entity.newBuilder().setValue(LIST_ITEM_TYPE_SPEC.toStruct(listItem));
         listItem.getId().ifPresent(builder::setIdentifier);
         return builder.build();
     }
@@ -344,6 +386,88 @@
     }
 
     /**
+     * Converts a Timer object to an Entity proto message.
+     *
+     * @param timer
+     * @return
+     */
+    @NonNull
+    public static Entity toEntity(@NonNull Timer timer) {
+        Entity.Builder builder = Entity.newBuilder().setValue(TIMER_TYPE_SPEC.toStruct(timer));
+        timer.getId().ifPresent(builder::setIdentifier);
+        return builder.build();
+    }
+
+    /**
+     * @param zonedDateTime
+     * @return
+     */
+    @NonNull
+    public static Entity toEntity(@NonNull ZonedDateTime zonedDateTime) {
+        // TODO(b/274838299): Do not set "name" field after protos are checked in.
+        return Entity.newBuilder().setName(zonedDateTime.toOffsetDateTime().toString()).build();
+    }
+
+    /**
+     * @param localTime
+     * @return
+     */
+    @NonNull
+    public static Entity toEntity(@NonNull LocalTime localTime) {
+        // TODO(b/274838299): Do not set "name" field after protos are checked in.
+        return Entity.newBuilder().setName(localTime.toString()).build();
+    }
+
+    /**
+     * @param duration
+     * @return
+     */
+    @NonNull
+    public static Entity toEntity(@NonNull Duration duration) {
+        // TODO(b/274838299): Do not set "name" field after protos are checked in.
+        return Entity.newBuilder().setName(duration.toString()).build();
+    }
+
+    /**
+     * @param callFormat
+     * @return
+     */
+    @NonNull
+    public static Entity toEntity(@NonNull Call.CallFormat callFormat) {
+        return Entity.newBuilder().setIdentifier(callFormat.toString()).build();
+    }
+
+    /**
+     * @param participant
+     * @return
+     */
+    @NonNull
+    public static Entity toEntity(@NonNull Participant participant) {
+        Entity.Builder builder =
+                Entity.newBuilder().setValue(PARTICIPANT_TYPE_SPEC.toStruct(participant));
+        @Nullable String identifier = PARTICIPANT_TYPE_SPEC.getIdentifier(participant);
+        if (identifier != null) {
+            builder.setIdentifier(identifier);
+        }
+        return builder.build();
+    }
+
+    /**
+     * @param recipient
+     * @return
+     */
+    @NonNull
+    public static Entity toEntity(@NonNull Recipient recipient) {
+        Entity.Builder builder =
+                Entity.newBuilder().setValue(RECIPIENT_TYPE_SPEC.toStruct(recipient));
+        @Nullable String identifier = RECIPIENT_TYPE_SPEC.getIdentifier(recipient);
+        if (identifier != null) {
+            builder.setIdentifier(identifier);
+        }
+        return builder.build();
+    }
+
+    /**
      * Converts a ParamValue to a single ItemList object.
      *
      * @param paramValue
@@ -465,8 +589,8 @@
             @NonNull TypeSpec<T> nestedTypeSpec) {
         return TypeSpecBuilder.<SearchAction<T>, SearchAction.Builder<T>>newBuilder(
                         "SearchAction", SearchAction::newBuilder)
-                .bindStringField("query", SearchAction<T>::getQuery,
-                        SearchAction.Builder<T>::setQuery)
+                .bindStringField(
+                        "query", SearchAction<T>::getQuery, SearchAction.Builder<T>::setQuery)
                 .bindSpecField(
                         "object",
                         SearchAction<T>::getObject,
@@ -565,20 +689,24 @@
     /** Converts a Participant to a ParamValue. */
     @NonNull
     public static ParamValue toParamValue(@NonNull Participant value) {
-        ParticipantTypeSpec typeSpec = new ParticipantTypeSpec();
-        ParamValue.Builder builder = ParamValue.newBuilder().setStructValue(
-                typeSpec.toStruct(value));
-        typeSpec.getId(value).ifPresent(builder::setIdentifier);
+        ParamValue.Builder builder =
+                ParamValue.newBuilder().setStructValue(PARTICIPANT_TYPE_SPEC.toStruct(value));
+        @Nullable String identifier = PARTICIPANT_TYPE_SPEC.getIdentifier(value);
+        if (identifier != null) {
+            builder.setIdentifier(identifier);
+        }
         return builder.build();
     }
 
     /** Converts a Recipient to a ParamValue. */
     @NonNull
     public static ParamValue toParamValue(@NonNull Recipient value) {
-        RecipientTypeSpec typeSpec = new RecipientTypeSpec();
-        ParamValue.Builder builder = ParamValue.newBuilder().setStructValue(
-                typeSpec.toStruct(value));
-        typeSpec.getId(value).ifPresent(builder::setIdentifier);
+        ParamValue.Builder builder =
+                ParamValue.newBuilder().setStructValue(RECIPIENT_TYPE_SPEC.toStruct(value));
+        @Nullable String identifier = RECIPIENT_TYPE_SPEC.getIdentifier(value);
+        if (identifier != null) {
+            builder.setIdentifier(identifier);
+        }
         return builder.build();
     }
 
@@ -587,13 +715,14 @@
     public static ParamValue toParamValue(@NonNull Call value) {
         ParamValue.Builder builder = ParamValue.newBuilder();
         Map<String, Value> fieldsMap = new HashMap<>();
-        fieldsMap.put(FIELD_NAME_TYPE,
-                Value.newBuilder().setStringValue(FIELD_NAME_TYPE_CALL).build());
+        fieldsMap.put(
+                FIELD_NAME_TYPE, Value.newBuilder().setStringValue(FIELD_NAME_TYPE_CALL).build());
         if (value.getCallFormat().isPresent()) {
             fieldsMap.put(
                     FIELD_NAME_CALL_FORMAT,
-                    Value.newBuilder().setStringValue(
-                            value.getCallFormat().get().toString()).build());
+                    Value.newBuilder()
+                            .setStringValue(value.getCallFormat().get().toString())
+                            .build());
         }
         ListValue.Builder participantListBuilder = ListValue.newBuilder();
         for (Participant participant : value.getParticipantList()) {
@@ -654,112 +783,4 @@
         }
         return fieldsMap.get(FIELD_NAME_TYPE).getStringValue();
     }
-
-    /** {@link TypeSpec} for {@link Participant}. */
-    public static class ParticipantTypeSpec implements TypeSpec<Participant> {
-        @Override
-        @NonNull
-        public Struct toStruct(@NonNull Participant object) {
-            if (object.asPerson().isPresent()) {
-                return PERSON_TYPE_SPEC.toStruct(object.asPerson().get());
-            }
-            return Struct.getDefaultInstance();
-        }
-
-        @Override
-        @NonNull
-        public Participant fromStruct(@NonNull Struct struct) throws StructConversionException {
-            if (FIELD_NAME_TYPE_PERSON.equals(getStructType(struct))) {
-                return new Participant(PERSON_TYPE_SPEC.fromStruct(struct));
-            }
-            throw new StructConversionException(
-                    String.format(
-                            "Unexpected type, expected type is %s while actual type is %s",
-                            FIELD_NAME_TYPE_PERSON, getStructType(struct)));
-        }
-
-        /**
-         * Retrieves identifier from the object within union value.
-         *
-         * @param object
-         * @return
-         */
-        @NonNull
-        public Optional<String> getId(@NonNull Participant object) {
-            return object.asPerson().isPresent() ? object.asPerson().get().getId()
-                    : Optional.empty();
-        }
-    }
-
-    /** {@link TypeSpec} for {@link Recipient}. */
-    public static class RecipientTypeSpec implements TypeSpec<Recipient> {
-        @NonNull
-        @Override
-        public Struct toStruct(@NonNull Recipient object) {
-            if (object.asPerson().isPresent()) {
-                return PERSON_TYPE_SPEC.toStruct(object.asPerson().get());
-            }
-            return Struct.getDefaultInstance();
-        }
-
-        @Override
-        @NonNull
-        public Recipient fromStruct(@NonNull Struct struct) throws StructConversionException {
-            if (FIELD_NAME_TYPE_PERSON.equals(getStructType(struct))) {
-                return new Recipient(PERSON_TYPE_SPEC.fromStruct(struct));
-            }
-            throw new StructConversionException(
-                    String.format(
-                            "Unexpected type, expected type is %s while actual type is %s",
-                            FIELD_NAME_TYPE_PERSON, getStructType(struct)));
-        }
-
-        /**
-         * Retrieves identifier from the object within union value.
-         *
-         * @param object
-         * @return
-         */
-        @NonNull
-        public Optional<String> getId(@NonNull Recipient object) {
-            return object.asPerson().isPresent() ? object.asPerson().get().getId()
-                    : Optional.empty();
-        }
-    }
-
-    /** {@link TypeSpec} for {@link Attendee}. */
-    public static class AttendeeTypeSpec implements TypeSpec<Attendee> {
-        @Override
-        @NonNull
-        public Struct toStruct(@NonNull Attendee object) {
-            if (object.asPerson().isPresent()) {
-                return PERSON_TYPE_SPEC.toStruct(object.asPerson().get());
-            }
-            return Struct.getDefaultInstance();
-        }
-
-        @NonNull
-        @Override
-        public Attendee fromStruct(@NonNull Struct struct) throws StructConversionException {
-            if (FIELD_NAME_TYPE_PERSON.equals(getStructType(struct))) {
-                return new Attendee(TypeConverters.PERSON_TYPE_SPEC.fromStruct(struct));
-            }
-            throw new StructConversionException(
-                    String.format(
-                            "Unexpected type, expected type is %s while actual type is %s",
-                            FIELD_NAME_TYPE_PERSON, getStructType(struct)));
-        }
-
-        /**
-         * Retrieves identifier from the object within union value.
-         *
-         * @param object
-         * @return
-         */
-        @NonNull
-        public Optional<String> getId(@NonNull Attendee object) {
-            return object.asPerson().isPresent() ? object.asPerson().get().getId()
-                    : Optional.empty();
-        }
-    }
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpec.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpec.java
deleted file mode 100644
index 11181a6..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpec.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.impl.converters;
-
-import androidx.annotation.NonNull;
-import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException;
-import androidx.appactions.interaction.protobuf.Struct;
-
-/**
- * TypeSpec is used to convert between java objects in capabilities/values and Struct proto.
- *
- * @param <T>
- */
-public interface TypeSpec<T> {
-
-    /** Converts a java object into a Struct proto. */
-    @NonNull
-    Struct toStruct(@NonNull T object);
-
-    /**
-     * Converts a Struct into java object.
-     *
-     * @throws StructConversionException if the Struct is malformed.
-     */
-    @NonNull
-    T fromStruct(@NonNull Struct struct) throws StructConversionException;
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpec.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpec.kt
new file mode 100644
index 0000000..7e07afe
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpec.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.impl.converters
+
+import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException
+import androidx.appactions.interaction.protobuf.Struct
+
+/**
+ * TypeSpec is used to convert between native objects in capabilities/values and Struct proto.
+ */
+interface TypeSpec<T> {
+    /* Given the object, returns its identifier, which can be null. */
+    fun getIdentifier(obj: T): String?
+
+    /** Converts a object into a Struct proto. */
+    fun toStruct(obj: T): Struct
+
+    /**
+     * Converts a Struct into object.
+     *
+     * @throws StructConversionException if the Struct is malformed.
+     */
+    @Throws(StructConversionException::class)
+    fun fromStruct(struct: Struct): T
+}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecBuilder.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecBuilder.java
index 46aa2d1..8cd3ede 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecBuilder.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecBuilder.java
@@ -42,6 +42,7 @@
     private final List<FieldBinding<T, BuilderT>> mBindings = new ArrayList<>();
     private final Supplier<BuilderT> mBuilderSupplier;
     private CheckedInterfaces.Consumer<Struct> mStructValidator;
+    private Function<T, Optional<String>> mIdentifierGetter = (unused) -> Optional.empty();
 
     private TypeSpecBuilder(Supplier<BuilderT> builderSupplier) {
         this.mBuilderSupplier = builderSupplier;
@@ -66,29 +67,30 @@
      * StructConversionException.
      *
      * @param struct the Struct to get values from.
-     * @param key    the String key of the field to retrieve.
+     * @param key the String key of the field to retrieve.
      */
     private static Value getFieldFromStruct(Struct struct, String key)
             throws StructConversionException {
         try {
             return struct.getFieldsOrThrow(key);
         } catch (IllegalArgumentException e) {
-            throw new StructConversionException(String.format("%s does not exist in Struct", key),
-                    e);
+            throw new StructConversionException(
+                    String.format("%s does not exist in Struct", key), e);
         }
     }
 
     static <T, BuilderT extends BuilderOf<T>> TypeSpecBuilder<T, BuilderT> newBuilder(
             String typeName, Supplier<BuilderT> builderSupplier) {
         return new TypeSpecBuilder<>(builderSupplier)
-                .bindStringField("@type", (unused) -> Optional.of(typeName), (builder, val) -> {
-                })
+                .bindStringField("@type", (unused) -> Optional.of(typeName), (builder, val) -> {})
                 .setStructValidator(
                         struct -> {
-                            if (!getFieldFromStruct(struct, "@type").getStringValue().equals(
-                                    typeName)) {
+                            if (!getFieldFromStruct(struct, "@type")
+                                    .getStringValue()
+                                    .equals(typeName)) {
                                 throw new StructConversionException(
-                                        String.format("Struct @type field must be equal to %s.",
+                                        String.format(
+                                                "Struct @type field must be equal to %s.",
                                                 typeName));
                             }
                         });
@@ -103,6 +105,7 @@
             TypeSpecBuilder<T, BuilderT> newBuilderForThing(
                     String typeName, Supplier<BuilderT> builderSupplier) {
         return newBuilder(typeName, builderSupplier)
+                .bindIdentifier(T::getId)
                 .bindStringField("identifier", T::getId, BuilderT::setId)
                 .bindStringField("name", T::getName, BuilderT::setName);
     }
@@ -113,6 +116,11 @@
         return this;
     }
 
+    TypeSpecBuilder<T, BuilderT> bindIdentifier(Function<T, Optional<String>> identifierGetter) {
+        this.mIdentifierGetter = identifierGetter;
+        return this;
+    }
+
     private TypeSpecBuilder<T, BuilderT> bindFieldInternal(
             String name,
             Function<T, Optional<Value>> valueGetter,
@@ -166,8 +174,7 @@
                 name,
                 (object) -> stringGetter.apply(object).map(TypeSpecBuilder::getStringValue),
                 (builder, value) ->
-                        value
-                                .map(Value::getStringValue)
+                        value.map(Value::getStringValue)
                                 .ifPresent(
                                         stringValue -> stringSetter.accept(builder, stringValue)));
     }
@@ -184,8 +191,10 @@
         return bindFieldInternal(
                 name,
                 (object) ->
-                        valueGetter.apply(object).map(Enum::toString).map(
-                                TypeSpecBuilder::getStringValue),
+                        valueGetter
+                                .apply(object)
+                                .map(Enum::toString)
+                                .map(TypeSpecBuilder::getStringValue),
                 (builder, value) -> {
                     if (value.isPresent()) {
                         String stringValue = value.get().getStringValue();
@@ -215,13 +224,15 @@
         return bindFieldInternal(
                 name,
                 (object) ->
-                        valueGetter.apply(object).map(Duration::toString).map(
-                                TypeSpecBuilder::getStringValue),
+                        valueGetter
+                                .apply(object)
+                                .map(Duration::toString)
+                                .map(TypeSpecBuilder::getStringValue),
                 (builder, value) -> {
                     if (value.isPresent()) {
                         try {
-                            valueSetter.accept(builder,
-                                    Duration.parse(value.get().getStringValue()));
+                            valueSetter.accept(
+                                    builder, Duration.parse(value.get().getStringValue()));
                         } catch (DateTimeParseException e) {
                             throw new StructConversionException(
                                     "Failed to parse ISO 8601 string to Duration", e);
@@ -249,8 +260,8 @@
                 (builder, value) -> {
                     if (value.isPresent()) {
                         try {
-                            valueSetter.accept(builder,
-                                    ZonedDateTime.parse(value.get().getStringValue()));
+                            valueSetter.accept(
+                                    builder, ZonedDateTime.parse(value.get().getStringValue()));
                         } catch (DateTimeParseException e) {
                             throw new StructConversionException(
                                     "Failed to parse ISO 8601 string to ZonedDateTime", e);
@@ -270,9 +281,7 @@
                 (object) ->
                         valueGetter
                                 .apply(object)
-                                .map(
-                                        Function
-                                                .identity()) // Static analyzer incorrectly
+                                .map(Function.identity()) // Static analyzer incorrectly
                                 // throws error stating that the
                                 // input to toStruct is nullable. This is a workaround to avoid
                                 // the error from the analyzer.
@@ -296,12 +305,16 @@
                 valueGetter,
                 valueSetter,
                 (element) ->
-                        Optional.ofNullable(element).map(
-                                value -> getStructValue(spec.toStruct(value))),
+                        Optional.ofNullable(element)
+                                .map(value -> getStructValue(spec.toStruct(value))),
                 (value) -> spec.fromStruct(value.getStructValue()));
     }
 
     TypeSpec<T> build() {
-        return new TypeSpecImpl<>(mBindings, mBuilderSupplier, Optional.ofNullable(mStructValidator));
+        return new TypeSpecImpl<>(
+                mIdentifierGetter,
+                mBindings,
+                mBuilderSupplier,
+                Optional.ofNullable(mStructValidator));
     }
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecImpl.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecImpl.java
index 25ac2fc..c49c3b1 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecImpl.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecImpl.java
@@ -17,6 +17,7 @@
 package androidx.appactions.interaction.capabilities.core.impl.converters;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
 import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException;
 import androidx.appactions.interaction.protobuf.Struct;
@@ -26,10 +27,13 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.function.Function;
 import java.util.function.Supplier;
 
 /** TypeSpecImpl is used to convert between java objects in capabilities/values and Struct proto. */
 final class TypeSpecImpl<T, BuilderT extends BuilderOf<T>> implements TypeSpec<T> {
+    /* The function to retrieve the identifier. */
+    final Function<T, Optional<String>> mIdentifierGetter;
 
     /** The list of FieldBinding objects. */
     final List<FieldBinding<T, BuilderT>> mBindings;
@@ -41,23 +45,31 @@
     final Supplier<BuilderT> mBuilderSupplier;
 
     TypeSpecImpl(
+            Function<T, Optional<String>> identifierGetter,
             List<FieldBinding<T, BuilderT>> bindings,
             Supplier<BuilderT> builderSupplier,
             Optional<CheckedInterfaces.Consumer<Struct>> structValidator) {
+        this.mIdentifierGetter = identifierGetter;
         this.mBindings = Collections.unmodifiableList(bindings);
         this.mBuilderSupplier = builderSupplier;
         this.mStructValidator = structValidator;
     }
 
+    @Nullable
+    @Override
+    public String getIdentifier(T obj) {
+        return mIdentifierGetter.apply(obj).orElse(null);
+    }
+
     /** Converts a java object into a Struct proto using List of FieldBinding. */
     @NonNull
     @Override
-    public Struct toStruct(@NonNull T object) {
+    public Struct toStruct(@NonNull T obj) {
         Struct.Builder builder = Struct.newBuilder();
         for (FieldBinding<T, BuilderT> binding : mBindings) {
             binding
                     .valueGetter()
-                    .apply(object)
+                    .apply(obj)
                     .ifPresent(value -> builder.putFields(binding.name(), value));
         }
         return builder.build();
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/UnionTypeSpec.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/UnionTypeSpec.kt
new file mode 100644
index 0000000..b249bee
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/converters/UnionTypeSpec.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.impl.converters
+
+import androidx.annotation.RestrictTo
+import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException
+import androidx.appactions.interaction.protobuf.Struct
+
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class UnionTypeSpec<T : Any> internal constructor(
+    private val bindings: List<MemberBinding<T, *>>,
+) : TypeSpec<T> {
+    internal class MemberBinding<T, M>(
+        val memberGetter: (T) -> M?,
+        val ctor: (M) -> T,
+        val typeSpec: TypeSpec<M>,
+    ) {
+        @Throws(StructConversionException::class)
+        fun tryDeserialize(struct: Struct): T {
+            return ctor(typeSpec.fromStruct(struct))
+        }
+
+        fun serialize(obj: T): Struct {
+            return typeSpec.toStruct(memberGetter(obj)!!)
+        }
+
+        fun getIdentifier(obj: T): String? {
+            return typeSpec.getIdentifier(memberGetter(obj)!!)
+        }
+
+        fun isMemberSet(obj: T): Boolean {
+            return memberGetter(obj) != null
+        }
+    }
+
+    private fun getApplicableBinding(obj: T): MemberBinding<T, *> {
+        var applicableBindings = bindings.filter { it.isMemberSet(obj) }
+        return when (applicableBindings.size) {
+            0 -> throw IllegalStateException("$obj is invalid, all union members are null.")
+            1 -> applicableBindings[0]
+            else -> throw IllegalStateException(
+                "$obj is invalid, multiple union members are non-null."
+            )
+        }
+    }
+
+    override fun getIdentifier(obj: T): String? {
+        return getApplicableBinding(obj).getIdentifier(obj)
+    }
+
+    override fun toStruct(obj: T): Struct {
+        return getApplicableBinding(obj).serialize(obj)
+    }
+
+    @Throws(StructConversionException::class)
+    override fun fromStruct(struct: Struct): T {
+        for (binding in bindings) {
+            try {
+                return binding.tryDeserialize(struct)
+            } catch (e: StructConversionException) {
+                continue
+            }
+        }
+        throw StructConversionException("all member TypeSpecs failed to deserialize input Struct.")
+    }
+
+    class Builder<T : Any> {
+        private val bindings = mutableListOf<MemberBinding<T, *>>()
+
+        fun <M> bindMemberType(
+            memberGetter: (T) -> M?,
+            ctor: (M) -> T,
+            typeSpec: TypeSpec<M>,
+        ) = apply {
+            bindings.add(
+                MemberBinding(
+                    memberGetter,
+                    ctor,
+                    typeSpec,
+                ),
+            )
+        }
+
+        fun build() = UnionTypeSpec<T>(bindings.toList())
+    }
+}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/package-info.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/package-info.java
new file mode 100644
index 0000000..ea6e4cc
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/** @hide */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+package androidx.appactions.interaction.capabilities.core.impl;
+
+import androidx.annotation.RestrictTo;
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecBuilder.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecBuilder.java
index 209d7ff..93a8cf9 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecBuilder.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecBuilder.java
@@ -20,25 +20,17 @@
 
 import androidx.annotation.NonNull;
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
+import androidx.appactions.interaction.capabilities.core.impl.converters.EntityConverter;
 import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter;
 import androidx.appactions.interaction.capabilities.core.impl.converters.PropertyConverter;
 import androidx.appactions.interaction.capabilities.core.impl.converters.SlotTypeConverter;
-import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters;
 import androidx.appactions.interaction.capabilities.core.impl.spec.ParamBinding.ArgumentSetter;
-import androidx.appactions.interaction.capabilities.core.properties.EntityProperty;
-import androidx.appactions.interaction.capabilities.core.properties.EnumProperty;
-import androidx.appactions.interaction.capabilities.core.properties.IntegerProperty;
-import androidx.appactions.interaction.capabilities.core.properties.SimpleProperty;
-import androidx.appactions.interaction.capabilities.core.properties.StringOrEnumProperty;
-import androidx.appactions.interaction.capabilities.core.properties.StringProperty;
-import androidx.appactions.interaction.capabilities.core.values.EntityValue;
-import androidx.appactions.interaction.capabilities.core.values.StringOrEnumValue;
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty;
 import androidx.appactions.interaction.proto.AppActionsContext.IntentParameter;
 import androidx.appactions.interaction.proto.ParamValue;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -114,156 +106,124 @@
      * @return the builder itself.
      */
     @NonNull
-    public ActionSpecBuilder<PropertyT, ArgumentT, ArgumentBuilderT, OutputT> bindParameter(
-            @NonNull String paramName,
-            @NonNull Function<? super PropertyT, Optional<IntentParameter>> paramGetter,
-            @NonNull ArgumentSetter<ArgumentBuilderT> argumentSetter) {
+    private ActionSpecBuilder<PropertyT, ArgumentT, ArgumentBuilderT, OutputT>
+            bindParameterInternal(
+                    @NonNull String paramName,
+                    @NonNull Function<? super PropertyT, Optional<IntentParameter>> paramGetter,
+                    @NonNull ArgumentSetter<ArgumentBuilderT> argumentSetter) {
         mParamBindingList.add(ParamBinding.create(paramName, paramGetter, argumentSetter));
         return this;
     }
 
     /**
-     * Binds the parameter name, getter, and setter for a {@link EntityProperty}.
+     * Binds the parameter name, getter, and setter for a {@link TypeProperty}.
      *
      * <p>This parameter is required for any capability built from the generated {@link ActionSpec}.
      *
      * @param paramName the name of this action' parameter.
-     * @param propertyGetter a getter of the EntityProperty from the property, which must be able to
-     *     fetch a non-null {@code EntityProperty} from {@code PropertyT}.
+     * @param propertyGetter a getter of the TypeProperty from the property, which must be able to
+     *     fetch a non-null {@code TypeProperty} from {@code PropertyT}.
      * @param paramConsumer a setter to set the string value in the argument builder.
+     * @param paramValueConverter converter FROM assistant ParamValue proto
+     * @param entityConverter converter TO assistant Entity proto
      * @return the builder itself.
      */
     @NonNull
-    public ActionSpecBuilder<PropertyT, ArgumentT, ArgumentBuilderT, OutputT>
-            bindRequiredEntityParameter(
+    public <T, PossibleValueT>
+            ActionSpecBuilder<PropertyT, ArgumentT, ArgumentBuilderT, OutputT> bindParameter(
                     @NonNull String paramName,
-                    @NonNull Function<? super PropertyT, EntityProperty> propertyGetter,
-                    @NonNull BiConsumer<? super ArgumentBuilderT, EntityValue> paramConsumer) {
-        return bindEntityParameter(
+                    @NonNull
+                            Function<? super PropertyT, TypeProperty<PossibleValueT>>
+                                    propertyGetter,
+                    @NonNull BiConsumer<? super ArgumentBuilderT, T> paramConsumer,
+                    @NonNull ParamValueConverter<T> paramValueConverter,
+                    @NonNull EntityConverter<PossibleValueT> entityConverter) {
+        return bindOptionalParameter(
                 paramName,
-                property ->
-                        Optional.of(
-                                PropertyConverter.getIntentParameter(
-                                        paramName, propertyGetter.apply(property))),
-                paramConsumer);
+                property -> Optional.of(propertyGetter.apply(property)),
+                paramConsumer,
+                paramValueConverter,
+                entityConverter);
     }
 
     /**
-     * Binds the parameter name, getter, and setter for a {@link EntityProperty}.
+     * Binds the parameter name, getter, and setter for a {@link TypeProperty}.
      *
      * <p>This parameter is optional for any capability built from the generated {@link ActionSpec}.
      * If the Property Optional is not set, this parameter will not exist in the parameter
      * definition of the capability.
      *
      * @param paramName the name of this action' parameter.
-     * @param optionalPropertyGetter an optional getter of the EntityProperty from the property,
-     *     which may be able to fetch a non-null {@code EntityProperty} from {@code PropertyT}, or
-     *     get {@link Optional#empty}.
+     * @param optionalPropertyGetter an optional getter of the TypeProperty from the property, which
+     *     may be able to fetch a non-null {@code TypeProperty} from {@code PropertyT}, or get
+     *     {@link Optional#empty}.
      * @param paramConsumer a setter to set the string value in the argument builder.
+     * @param paramValueConverter converter FROM assistant ParamValue proto
+     * @param entityConverter converter TO assistant Entity proto
      * @return the builder itself.
      */
     @NonNull
-    public ActionSpecBuilder<PropertyT, ArgumentT, ArgumentBuilderT, OutputT>
-            bindOptionalEntityParameter(
-                    @NonNull String paramName,
-                    @NonNull
-                            Function<? super PropertyT, Optional<EntityProperty>>
-                                    optionalPropertyGetter,
-                    @NonNull BiConsumer<? super ArgumentBuilderT, EntityValue> paramConsumer) {
-        return bindEntityParameter(
+    public <T, PossibleValueT>
+            ActionSpecBuilder<PropertyT, ArgumentT, ArgumentBuilderT, OutputT>
+                    bindOptionalParameter(
+                            @NonNull String paramName,
+                            @NonNull
+                                    Function<
+                                                    ? super PropertyT,
+                                                    Optional<TypeProperty<PossibleValueT>>>
+                                            optionalPropertyGetter,
+                            @NonNull BiConsumer<? super ArgumentBuilderT, T> paramConsumer,
+                            @NonNull ParamValueConverter<T> paramValueConverter,
+                            @NonNull EntityConverter<PossibleValueT> entityConverter) {
+        return bindParameterInternal(
                 paramName,
                 property ->
                         optionalPropertyGetter
                                 .apply(property)
-                                .map(p -> PropertyConverter.getIntentParameter(paramName, p)),
-                paramConsumer);
-    }
-
-    /**
-     * This is similar to {@link ActionSpectBuilder#bindOptionalEntityParameter} but for setting a
-     * list of entities instead.
-     *
-     * <p>This parameter is optional for any capability built from the generated {@link ActionSpec}.
-     * If the Property Optional is not set, this parameter will not exist in the parameter
-     * definition of the capability.
-     */
-    @NonNull
-    public ActionSpecBuilder<PropertyT, ArgumentT, ArgumentBuilderT, OutputT>
-            bindRepeatedEntityParameter(
-                    @NonNull String paramName,
-                    @NonNull
-                            Function<? super PropertyT, Optional<EntityProperty>>
-                                    optionalPropertyGetter,
-                    @NonNull
-                            BiConsumer<? super ArgumentBuilderT, List<EntityValue>> paramConsumer) {
-        return bindParameter(
-                paramName,
-                property ->
-                        optionalPropertyGetter
-                                .apply(property)
-                                .map(p -> PropertyConverter.getIntentParameter(paramName, p)),
-                (argBuilder, paramList) ->
-                        paramConsumer.accept(
-                                argBuilder,
-                                SlotTypeConverter.ofRepeated(TypeConverters::toEntityValue)
-                                        .convert(paramList)));
-    }
-
-    private ActionSpecBuilder<PropertyT, ArgumentT, ArgumentBuilderT, OutputT> bindEntityParameter(
-            String paramName,
-            Function<? super PropertyT, Optional<IntentParameter>> propertyGetter,
-            BiConsumer<? super ArgumentBuilderT, EntityValue> paramConsumer) {
-        return bindParameter(
-                paramName,
-                propertyGetter,
+                                .map(
+                                        p ->
+                                                PropertyConverter.getIntentParameter(
+                                                        paramName, p, entityConverter)),
                 (argBuilder, paramList) -> {
                     if (!paramList.isEmpty()) {
                         paramConsumer.accept(
                                 argBuilder,
-                                SlotTypeConverter.ofSingular(TypeConverters::toEntityValue)
+                                SlotTypeConverter.ofSingular(paramValueConverter)
                                         .convert(paramList));
                     }
                 });
     }
 
+    /**
+     * This is similar to {@link ActionSpecBuilder#bindOptionalParameter} but for setting a list of
+     * entities instead.
+     *
+     * <p>This parameter is optional for any capability built from the generated {@link ActionSpec}.
+     * If the Property Optional is not set, this parameter will not exist in the parameter
+     * definition of the capability.
+     */
     @NonNull
-    public <T>
-            ActionSpecBuilder<PropertyT, ArgumentT, ArgumentBuilderT, OutputT> bindStructParameter(
-                    @NonNull String paramName,
-                    @NonNull
-                            Function<? super PropertyT, Optional<SimpleProperty>>
-                                    optionalPropertyGetter,
-                    @NonNull BiConsumer<? super ArgumentBuilderT, T> paramConsumer,
-                    @NonNull ParamValueConverter<T> paramValueConverter) {
-        return bindParameter(
-                paramName,
-                property ->
-                        optionalPropertyGetter
-                                .apply(property)
-                                .map(p -> PropertyConverter.getIntentParameter(paramName, p)),
-                (argBuilder, paramList) ->
-                        paramConsumer.accept(
-                                argBuilder,
-                                SlotTypeConverter.ofSingular(paramValueConverter)
-                                        .convert(paramList)));
-    }
-
-    @NonNull
-    public <T>
+    public <T, PossibleValueT>
             ActionSpecBuilder<PropertyT, ArgumentT, ArgumentBuilderT, OutputT>
-                    bindRepeatedStructParameter(
+                    bindRepeatedParameter(
                             @NonNull String paramName,
                             @NonNull
-                                    Function<? super PropertyT, Optional<SimpleProperty>>
+                                    Function<
+                                                    ? super PropertyT,
+                                                    Optional<TypeProperty<PossibleValueT>>>
                                             optionalPropertyGetter,
                             @NonNull BiConsumer<? super ArgumentBuilderT, List<T>> paramConsumer,
-                            @NonNull ParamValueConverter<T> paramValueConverter) {
-        return bindParameter(
+                            @NonNull ParamValueConverter<T> paramValueConverter,
+                            @NonNull EntityConverter<PossibleValueT> entityConverter) {
+        return bindParameterInternal(
                 paramName,
                 property ->
                         optionalPropertyGetter
                                 .apply(property)
-                                .map(p -> PropertyConverter.getIntentParameter(paramName, p)),
+                                .map(
+                                        p ->
+                                                PropertyConverter.getIntentParameter(
+                                                        paramName, p, entityConverter)),
                 (argBuilder, paramList) ->
                         paramConsumer.accept(
                                 argBuilder,
@@ -272,288 +232,6 @@
     }
 
     /**
-     * Binds an optional string parameter.
-     *
-     * @param paramName the BII slot name of this parameter.
-     * @param propertyGetter a function that returns a {@code Optional<StringProperty>} given a
-     *     {@code PropertyT} instance
-     * @param paramConsumer a function that accepts a String into the argument builder.
-     */
-    @NonNull
-    public ActionSpecBuilder<PropertyT, ArgumentT, ArgumentBuilderT, OutputT>
-            bindOptionalStringParameter(
-                    @NonNull String paramName,
-                    @NonNull Function<? super PropertyT, Optional<StringProperty>> propertyGetter,
-                    @NonNull BiConsumer<? super ArgumentBuilderT, String> paramConsumer) {
-        return bindParameter(
-                paramName,
-                property ->
-                        propertyGetter
-                                .apply(property)
-                                .map(
-                                        stringProperty ->
-                                                PropertyConverter.getIntentParameter(
-                                                        paramName, stringProperty)),
-                (argBuilder, paramList) -> {
-                    if (!paramList.isEmpty()) {
-                        paramConsumer.accept(
-                                argBuilder,
-                                SlotTypeConverter.ofSingular(TypeConverters::toStringValue)
-                                        .convert(paramList));
-                    }
-                });
-    }
-
-    /**
-     * Binds an required string parameter.
-     *
-     * @param paramName the BII slot name of this parameter.
-     * @param propertyGetter a function that returns a {@code StringProperty} given a {@code
-     *     PropertyT} instance
-     * @param paramConsumer a function that accepts a String into the argument builder.
-     */
-    @NonNull
-    public ActionSpecBuilder<PropertyT, ArgumentT, ArgumentBuilderT, OutputT>
-            bindRequiredStringParameter(
-                    @NonNull String paramName,
-                    @NonNull Function<? super PropertyT, StringProperty> propertyGetter,
-                    @NonNull BiConsumer<? super ArgumentBuilderT, String> paramConsumer) {
-        return bindOptionalStringParameter(
-                paramName, property -> Optional.of(propertyGetter.apply(property)), paramConsumer);
-    }
-
-    /**
-     * Binds an repeated string parameter.
-     *
-     * @param paramName the BII slot name of this parameter.
-     * @param propertyGetter a function that returns a {@code Optional<StringProperty>} given a
-     *     {@code PropertyT} instance
-     * @param paramConsumer a function that accepts a {@code List<String>} into the argument
-     *     builder.
-     */
-    @NonNull
-    public ActionSpecBuilder<PropertyT, ArgumentT, ArgumentBuilderT, OutputT>
-            bindRepeatedStringParameter(
-                    @NonNull String paramName,
-                    @NonNull Function<? super PropertyT, Optional<StringProperty>> propertyGetter,
-                    @NonNull BiConsumer<? super ArgumentBuilderT, List<String>> paramConsumer) {
-        return bindParameter(
-                paramName,
-                property ->
-                        propertyGetter
-                                .apply(property)
-                                .map(
-                                        stringProperty ->
-                                                PropertyConverter.getIntentParameter(
-                                                        paramName, stringProperty)),
-                (argBuilder, paramList) -> {
-                    if (!paramList.isEmpty()) {
-                        paramConsumer.accept(
-                                argBuilder,
-                                SlotTypeConverter.ofRepeated(TypeConverters::toStringValue)
-                                        .convert(paramList));
-                    }
-                });
-    }
-
-    /**
-     * Binds the parameter name, getter, and setter for a {@link EnumProperty}.
-     *
-     * <p>This parameter is optional for any capability built from the generated {@link ActionSpec}.
-     * If the Property Optional is not set, this parameter will not exist in the parameter
-     * definition of the capability.
-     *
-     * @param paramName the name of this action parameter.
-     * @param enumType
-     * @param optionalPropertyGetter an optional getter of the EntityProperty from the property,
-     *     which may be able to fetch a non-null {@code EnumProperty} from {@code PropertyT}, or get
-     *     {@link Optional#empty}.
-     * @param paramConsumer a setter to set the enum value in the argument builder.
-     * @return the builder itself.
-     * @param <EnumT>
-     */
-    @NonNull
-    public <EnumT extends Enum<EnumT>>
-            ActionSpecBuilder<PropertyT, ArgumentT, ArgumentBuilderT, OutputT>
-                    bindOptionalEnumParameter(
-                            @NonNull String paramName,
-                            @NonNull Class<EnumT> enumType,
-                            @NonNull
-                                    Function<? super PropertyT, Optional<EnumProperty<EnumT>>>
-                                            optionalPropertyGetter,
-                            @NonNull BiConsumer<? super ArgumentBuilderT, EnumT> paramConsumer) {
-        return bindEnumParameter(
-                paramName,
-                enumType,
-                property ->
-                        optionalPropertyGetter
-                                .apply(property)
-                                .map(e -> PropertyConverter.getIntentParameter(paramName, e)),
-                paramConsumer);
-    }
-
-    private <EnumT extends Enum<EnumT>>
-            ActionSpecBuilder<PropertyT, ArgumentT, ArgumentBuilderT, OutputT> bindEnumParameter(
-                    String paramName,
-                    Class<EnumT> enumType,
-                    Function<? super PropertyT, Optional<IntentParameter>> enumParamGetter,
-                    BiConsumer<? super ArgumentBuilderT, EnumT> paramConsumer) {
-        return bindParameter(
-                paramName,
-                enumParamGetter,
-                (argBuilder, paramList) -> {
-                    if (!paramList.isEmpty()) {
-                        Optional<EnumT> enumValue =
-                                EnumSet.allOf(enumType).stream()
-                                        .filter(
-                                                element ->
-                                                        element.toString()
-                                                                .equals(
-                                                                        paramList
-                                                                                .get(0)
-                                                                                .getIdentifier()))
-                                        .findFirst();
-                        if (enumValue.isPresent()) {
-                            paramConsumer.accept(argBuilder, enumValue.get());
-                        }
-                    }
-                });
-    }
-
-    @NonNull
-    public <EnumT extends Enum<EnumT>>
-            ActionSpecBuilder<PropertyT, ArgumentT, ArgumentBuilderT, OutputT>
-                    bindRequiredStringOrEnumParameter(
-                            @NonNull String paramName,
-                            @NonNull Class<EnumT> enumType,
-                            @NonNull
-                                    Function<? super PropertyT, StringOrEnumProperty<EnumT>>
-                                            propertyGetter,
-                            @NonNull
-                                    BiConsumer<? super ArgumentBuilderT, StringOrEnumValue<EnumT>>
-                                            paramConsumer) {
-        return bindStringOrEnumParameter(
-                paramName,
-                enumType,
-                property ->
-                        Optional.of(
-                                PropertyConverter.getIntentParameter(
-                                        paramName, propertyGetter.apply(property))),
-                paramConsumer);
-    }
-
-    private <EnumT extends Enum<EnumT>>
-            ActionSpecBuilder<PropertyT, ArgumentT, ArgumentBuilderT, OutputT>
-                    bindStringOrEnumParameter(
-                            String paramName,
-                            Class<EnumT> enumType,
-                            Function<? super PropertyT, Optional<IntentParameter>> propertyGetter,
-                            BiConsumer<? super ArgumentBuilderT, StringOrEnumValue<EnumT>>
-                                    paramConsumer) {
-        return bindParameter(
-                paramName,
-                propertyGetter,
-                (argBuilder, paramList) -> {
-                    if (!paramList.isEmpty()) {
-                        ParamValue param = paramList.get(0);
-                        if (param.hasIdentifier()) {
-                            Optional<EnumT> enumValue =
-                                    EnumSet.allOf(enumType).stream()
-                                            .filter(
-                                                    element ->
-                                                            element.toString()
-                                                                    .equals(param.getIdentifier()))
-                                            .findFirst();
-                            if (enumValue.isPresent()) {
-                                paramConsumer.accept(
-                                        argBuilder, StringOrEnumValue.ofEnumValue(enumValue.get()));
-                                return;
-                            }
-                        }
-                        paramConsumer.accept(
-                                argBuilder,
-                                StringOrEnumValue.ofStringValue(
-                                        SlotTypeConverter.ofSingular(TypeConverters::toStringValue)
-                                                .convert(paramList)));
-                    }
-                });
-    }
-
-    /**
-     * Binds the integer parameter name and setter for a {@link IntegerProperty}.
-     *
-     * <p>This parameter is optional for any capability built from the generated {@link ActionSpec}.
-     * If the Property Optional is not set, this parameter will not exist in the parameter
-     * definition of the capability.
-     *
-     * @param paramName the name of this action' parameter.
-     * @param optionalPropertyGetter
-     * @param paramConsumer a setter to set the int value in the argument builder.
-     * @return the builder itself.
-     */
-    @NonNull
-    public ActionSpecBuilder<PropertyT, ArgumentT, ArgumentBuilderT, OutputT>
-            bindOptionalIntegerParameter(
-                    @NonNull String paramName,
-                    @NonNull
-                            Function<? super PropertyT, Optional<IntegerProperty>>
-                                    optionalPropertyGetter,
-                    @NonNull BiConsumer<? super ArgumentBuilderT, Integer> paramConsumer) {
-        return bindParameter(
-                paramName,
-                property ->
-                        optionalPropertyGetter
-                                .apply(property)
-                                .map(e -> PropertyConverter.getIntentParameter(paramName, e)),
-                (argBuilder, paramList) -> {
-                    if (!paramList.isEmpty()) {
-                        paramConsumer.accept(
-                                argBuilder,
-                                SlotTypeConverter.ofSingular(TypeConverters::toIntegerValue)
-                                        .convert(paramList));
-                    }
-                });
-    }
-
-    /**
-     * Binds a Boolean parameter.
-     *
-     * <p>This parameter is optional for any capability built from the generated {@link ActionSpec}.
-     * If the Property Optional is not set, this parameter will not exist in the parameter
-     * definition of the capability.
-     *
-     * @param paramName the name of this action' parameter.
-     * @param paramConsumer a setter to set the boolean value in the argument builder.
-     * @param optionalPropertyGetter an optional getter of the EntityProperty from the property,
-     *     which may be able to fetch a non-null {@code SimpleProperty} from {@code PropertyT}, or
-     *     get {@link Optional#empty}.
-     * @return the builder itself.
-     */
-    @NonNull
-    public ActionSpecBuilder<PropertyT, ArgumentT, ArgumentBuilderT, OutputT>
-            bindOptionalBooleanParameter(
-                    @NonNull String paramName,
-                    @NonNull
-                            Function<? super PropertyT, Optional<SimpleProperty>>
-                                    optionalPropertyGetter,
-                    @NonNull BiConsumer<? super ArgumentBuilderT, Boolean> paramConsumer) {
-        return bindParameter(
-                paramName,
-                property ->
-                        optionalPropertyGetter
-                                .apply(property)
-                                .map(e -> PropertyConverter.getIntentParameter(paramName, e)),
-                (argBuilder, paramList) -> {
-                    if (!paramList.isEmpty()) {
-                        paramConsumer.accept(
-                                argBuilder,
-                                SlotTypeConverter.ofSingular(TypeConverters::toBooleanValue)
-                                        .convert(paramList));
-                    }
-                });
-    }
-
-    /**
      * Binds an optional output.
      *
      * @param name the BII output slot name of this parameter.
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecImpl.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecImpl.java
index 5fcc17e..906e2b6 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecImpl.java
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecImpl.java
@@ -90,6 +90,7 @@
         return argumentBuilder.build();
     }
 
+    @NonNull
     @Override
     public StructuredOutput convertOutputToProto(OutputT output) {
         StructuredOutput.Builder outputBuilder = StructuredOutput.newBuilder();
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/Entity.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/Entity.kt
index 4b04e11..4db410c 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/Entity.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/Entity.kt
@@ -17,7 +17,7 @@
 package androidx.appactions.interaction.capabilities.core.properties
 
 /**
- * Entities are used when defining ActionCapability for defining possible values for ParamProperty.
+ * Entities are used defining possible values for [ParamProperty].
  */
 class Entity internal constructor(
     val id: String?,
@@ -55,9 +55,9 @@
         /** Builds and returns an Entity. */
         fun build() = Entity(
             id,
-            requireNotNull(name, {
+            requireNotNull(name) {
                 "setName must be called before build"
-            }),
+            },
             alternateNames,
         )
     }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/EntityProperty.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/EntityProperty.kt
deleted file mode 100644
index c44932e..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/EntityProperty.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.properties
-
-/** The property which describes a entity parameter for {@code ActionCapability}. */
-class EntityProperty internal constructor(
-    override val possibleValues: List<Entity>,
-    override val isRequired: Boolean,
-    override val isValueMatchRequired: Boolean,
-    override val isProhibited: Boolean,
-) : ParamProperty<Entity> {
-    /** Builder for {@link EntityProperty}. */
-    public class Builder {
-
-        private val possibleEntities = mutableListOf<Entity>()
-        private var isRequired = false
-        private var isValueMatchRequired = false
-        private var isProhibited = false
-
-        /**
-         * Adds one or more possible entities for this entity parameter.
-         *
-         * @param entities the possible entities.
-         */
-        fun addPossibleEntities(vararg entities: Entity) = apply {
-            this.possibleEntities.addAll(entities)
-        }
-
-        /** Sets whether or not this property requires a value for fulfillment. */
-        fun setRequired(isRequired: Boolean) = apply {
-            this.isRequired = isRequired
-        }
-
-        /**
-         * Sets whether or not this property requires that the value for this property must match
-         * one of
-         * the Entity in the defined possible entities.
-         */
-        fun setValueMatchRequired(isValueMatchRequired: Boolean) = apply {
-            this.isValueMatchRequired = isValueMatchRequired
-        }
-
-        /**
-         * Sets whether this property is prohibited in the response.
-         *
-         * @param isProhibited Whether this property is prohibited in the response.
-         */
-        fun setProhibited(isProhibited: Boolean) = apply {
-            this.isProhibited = isProhibited
-        }
-
-        /** Builds the property for this entity parameter. */
-        fun build() = EntityProperty(
-            this.possibleEntities.toList(),
-            this.isRequired,
-            this.isValueMatchRequired,
-            this.isProhibited,
-        )
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/EnumProperty.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/EnumProperty.kt
deleted file mode 100644
index 7fa5dc1..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/EnumProperty.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.properties
-
-/**
- * The property which describes an Enum parameter for {@code ActionCapability}.
- */
-class EnumProperty<EnumT : Enum<EnumT>> internal constructor(
-    override val possibleValues: List<EnumT>,
-    override val isRequired: Boolean,
-    override val isValueMatchRequired: Boolean,
-    val enumType: Class<EnumT>,
-) : ParamProperty<EnumT> {
-    override val isProhibited = false
-
-    /**
-     * Builder for {@link EnumProperty}.
-     */
-    class Builder<EnumT : Enum<EnumT>>(
-        private val enumType: Class<EnumT>,
-    ) {
-        private val possibleValues = mutableListOf<EnumT>()
-        private var isValueMatchRequired = false
-        private var isRequired = false
-
-        /**
-         * Adds all app supported entity for this enum parameter. If any supported enum value is
-         * added
-         * then the entity matched is reuqired.
-         *
-         * @param supportedEnumValues supported enum values.
-         */
-        fun addSupportedEnumValues(vararg supportedEnumValues: EnumT) = apply {
-            this.possibleValues.addAll(supportedEnumValues)
-            this.isValueMatchRequired = true
-        }
-        fun setRequired(isRequired: Boolean) = apply {
-            this.isRequired = isRequired
-        }
-
-        /** Builds the property for this Enum parameter. */
-        fun build() = EnumProperty<EnumT>(
-            possibleValues.toList(),
-            isRequired,
-            isValueMatchRequired,
-            enumType,
-        )
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/IntegerProperty.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/IntegerProperty.kt
deleted file mode 100644
index f885706..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/IntegerProperty.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.properties
-
-/** The property which describes an integer parameter for {@code ActionCapability}. */
-class IntegerProperty internal constructor(override val isRequired: Boolean) : ParamProperty<Int> {
-    override val possibleValues = listOf<Int>()
-    override val isProhibited = false
-    override val isValueMatchRequired = false
-
-    /** Builder for {@link IntegerProperty}. */
-    class Builder {
-        private var isRequired: Boolean = false
-
-        /** Sets whether this property is required for fulfillment. */
-        fun setRequired(isRequired: Boolean) = apply {
-            this.isRequired = isRequired
-        }
-
-        /** Builds the property for this integer parameter. */
-        fun build() =
-            IntegerProperty(
-                isRequired,
-            ) }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/ParamProperty.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/ParamProperty.kt
index dc58a94..5a9a99ab 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/ParamProperty.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/ParamProperty.kt
@@ -24,7 +24,7 @@
  */
 sealed interface ParamProperty<V> {
 
-    /** The list of added possible values for this parameter. */
+    /** The current list of possible values for this parameter, can change over time. */
     val possibleValues: List<V>
 
     /** Indicates that a value for this property is required to be present for fulfillment. */
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/SimpleProperty.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/SimpleProperty.kt
deleted file mode 100644
index 02032e5..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/SimpleProperty.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.properties
-
-/**
- * A simple property which describes a parameter for {@code ActionCapability}. This property has
- * simple configurations available and is not tied to a specific type.
- */
-class SimpleProperty internal constructor(
-    override val isRequired: Boolean,
-) : ParamProperty<Void> {
-
-    override val possibleValues = emptyList<Void>()
-    override val isValueMatchRequired = false
-    override val isProhibited = false
-
-    /** Builder for {@link SimpleProperty}. */
-    class Builder {
-
-        private var isRequired = false
-
-        /** Sets whether or not this property requires a value for fulfillment. */
-        fun setRequired(isRequired: Boolean) = apply {
-            this.isRequired = isRequired
-        }
-
-        /** Builds the property for this string parameter. */
-        fun build() = SimpleProperty(isRequired)
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/StringOrEnumProperty.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/StringOrEnumProperty.kt
deleted file mode 100644
index e9e06d3..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/StringOrEnumProperty.kt
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.properties
-
-/**
- * The property which describes a parameter with String or Enum entity for {@code ActionCapability}.
- */
-class StringOrEnumProperty<EnumT : Enum<EnumT>> internal constructor(
-    override val possibleValues: List<StringOrEnumProperty.PossibleValue<EnumT>>,
-    override val isRequired: Boolean,
-    override val isValueMatchRequired: Boolean,
-    val enumType: Class<EnumT>,
-) : ParamProperty<StringOrEnumProperty.PossibleValue<EnumT>> {
-
-    override val isProhibited = false
-
-    /**
-     * Represents a single possible value in StringOrEnumProperty.
-     *
-     * @param kind The Kind of PossibleValue.
-     * @param stringValue the StringProperty.PossibleValue, corresponds to Kind.ENUM_VALUE.
-     * @param enumValue
-     */
-    class PossibleValue<EnumT : Enum<EnumT>> internal constructor(
-        val kind: Kind,
-        val stringValue: StringProperty.PossibleValue?,
-        val enumValue: EnumT?,
-    ) {
-        /**  */
-        enum class Kind {
-            STRING_VALUE,
-            ENUM_VALUE,
-        }
-
-        companion object {
-            /** Create a new StringOrEnumProperty.PossibleValue for Kind.STRING_VALUE. */
-            @JvmStatic
-            fun <EnumT : Enum<EnumT>> of(
-                name: String,
-                vararg alternateNames: String,
-            ) = PossibleValue<EnumT>(
-                Kind.STRING_VALUE,
-                StringProperty.PossibleValue.of(name, *alternateNames),
-                null,
-            )
-
-            /** Create a new StringOrEnumProperty.PossibleValue for Kind.ENUM_VALUE. */
-            @JvmStatic
-            fun <EnumT : Enum<EnumT>> of(enumValue: EnumT) = PossibleValue<EnumT>(
-                Kind.ENUM_VALUE,
-                null,
-                enumValue,
-            )
-        }
-    }
-
-    /**
-     * Builder for {@link StringOrEnumProperty}.
-     */
-    class Builder<EnumT : Enum<EnumT>> (
-        private val enumType: Class<EnumT>,
-    ) {
-        private val possibleValues = mutableListOf<PossibleValue<EnumT>>()
-        private var isRequired = false
-        private var isValueMatchRequired = false
-
-        /**
-         * Adds a possible string value for this property.
-         *
-         * @param name           the possible string value.
-         * @param alternateNames the alternative names for this value.
-         */
-
-        fun addPossibleValue(
-            name: String,
-            vararg alternateNames: String,
-        ) = apply {
-            possibleValues.add(PossibleValue.of(name, *alternateNames))
-            this.isValueMatchRequired = true
-        }
-
-        /**
-         * Adds possible Enum values for this parameter.
-         *
-         * @param enumValues possible enum entity values.
-         */
-        fun addPossibleValues(vararg enumValues: EnumT) = apply {
-            enumValues.forEach {
-                possibleValues.add(PossibleValue.of(it))
-            }
-            this.isValueMatchRequired = true
-        }
-
-        /** Sets whether or not this property requires a value for fulfillment. */
-        fun setRequired(isRequired: Boolean) = apply {
-            this.isRequired = isRequired
-        }
-
-        /**
-         * Sets whether matching a possible value is required for this parameter. Note that this
-         * value
-         * can be overrided by assistant.
-         *
-         * @param isValueMatchRequired whether value match is required
-         */
-        fun setValueMatchRequired(isValueMatchRequired: Boolean) = apply {
-            this.isValueMatchRequired = isValueMatchRequired
-        }
-
-        /** Builds the property for this Entity or Enum parameter. */
-        fun build() =
-            StringOrEnumProperty(
-                possibleValues.toList(),
-                isRequired,
-                isValueMatchRequired,
-                enumType,
-            )
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/StringProperty.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/StringProperty.kt
deleted file mode 100644
index 5d211e9..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/StringProperty.kt
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.properties
-
-/** The property which describes a string parameter for {@code ActionCapability}. */
-class StringProperty internal constructor(
-    override val possibleValues: List<StringProperty.PossibleValue>,
-    override val isRequired: Boolean,
-    override val isValueMatchRequired: Boolean,
-    override val isProhibited: Boolean,
-) : ParamProperty<StringProperty.PossibleValue> {
-    /** Represents a single possible value for StringProperty. */
-    class PossibleValue internal constructor(
-        val name: String,
-        val alternateNames: List<String>,
-    ) {
-        companion object {
-            @JvmStatic
-            fun of(name: String, vararg alternateNames: String) = PossibleValue(
-                name,
-                alternateNames.toList(),
-            )
-        }
-    }
-
-    /** Builder for {@link StringProperty}. */
-    class Builder {
-        private val possibleValues = mutableListOf<PossibleValue>()
-        private var isRequired = false
-        private var isValueMatchRequired = false
-        private var isProhibited = false
-
-        /**
-         * Adds a possible string value for this property.
-         *
-         * @param name           the possible string value.
-         * @param alternateNames the alternate names for this value.
-         */
-        fun addPossibleValue(name: String, vararg alternateNames: String) = apply {
-            possibleValues.add(PossibleValue.of(name, *alternateNames))
-        }
-
-        /** Sets whether or not this property requires a value for fulfillment. */
-        fun setRequired(isRequired: Boolean) = apply {
-            this.isRequired = isRequired
-        }
-
-        /**
-         * Sets whether or not this property requires that the value for this property must match
-         * one of
-         * the string values in the defined possible values.
-         */
-        fun setValueMatchRequired(isValueMatchRequired: Boolean) = apply {
-            this.isValueMatchRequired = isValueMatchRequired
-        }
-
-        /**
-         * Sets whether the String property is prohibited in the response.
-         *
-         * @param isProhibited Whether this property is prohibited in the response.
-         */
-        fun setProhibited(isProhibited: Boolean) = apply {
-            this.isProhibited = isProhibited
-        }
-
-        /** Builds the property for this string parameter. */
-        fun build() =
-            StringProperty(
-                possibleValues.toList(),
-                isRequired,
-                isValueMatchRequired,
-                isProhibited,
-            )
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/StringValue.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/StringValue.kt
new file mode 100644
index 0000000..fbe5bef
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/StringValue.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.appactions.interaction.capabilities.core.properties
+
+/**
+ * One of the possible possible values for [ParamProperty].
+ */
+class StringValue internal constructor(
+    val name: String,
+    val alternateNames: List<String>,
+) {
+    companion object {
+        @JvmStatic
+        fun of(name: String, vararg alternateNames: String) = StringValue(
+            name,
+            alternateNames.toList(),
+        )
+    }
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/TypeProperty.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/TypeProperty.kt
new file mode 100644
index 0000000..917e135
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/TypeProperty.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.appactions.interaction.capabilities.core.properties
+
+/** The property which describes a complex type. */
+class TypeProperty<T> internal constructor(
+    private val possibleValueSupplier: () -> List<T>,
+    override val isRequired: Boolean,
+    override val isValueMatchRequired: Boolean,
+    override val isProhibited: Boolean,
+) : ParamProperty<T> {
+    override val possibleValues: List<T>
+        get() = possibleValueSupplier()
+
+    /** Builder for {@link TypeProperty}. */
+    class Builder<T> {
+        private var possibleValueSupplier: () -> List<T> = { emptyList<T>() }
+        private var isRequired = false
+        private var isValueMatchRequired = false
+        private var isProhibited = false
+
+        /**
+         * Sets one or more possible values for this parameter.
+         *
+         * @param values the possible values.
+         */
+        fun setPossibleValues(vararg values: T) = apply {
+            this.possibleValueSupplier = { values.asList() }
+        }
+
+        /**
+         * Sets a supplier of possible values for this parameter.
+         *
+         * @param supplier the supplier of possible values.
+         */
+        fun setPossibleValueSupplier(supplier: () -> List<T>) = apply {
+            this.possibleValueSupplier = supplier
+        }
+
+        /** Sets whether or not this property requires a value for fulfillment. */
+        fun setRequired(isRequired: Boolean) = apply {
+            this.isRequired = isRequired
+        }
+
+        /**
+         * Sets whether or not this property requires that the value for this property must match
+         * one of
+         * the Entity in the defined possible entities.
+         */
+        fun setValueMatchRequired(isValueMatchRequired: Boolean) = apply {
+            this.isValueMatchRequired = isValueMatchRequired
+        }
+
+        /**
+         * Sets whether this property is prohibited in the response.
+         *
+         * @param isProhibited Whether this property is prohibited in the response.
+         */
+        fun setProhibited(isProhibited: Boolean) = apply {
+            this.isProhibited = isProhibited
+        }
+
+        /** Builds the property for this entity parameter. */
+        fun build() = TypeProperty(
+            this.possibleValueSupplier,
+            this.isRequired,
+            this.isValueMatchRequired,
+            this.isProhibited,
+        )
+    }
+}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/package-info.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/package-info.java
new file mode 100644
index 0000000..2229543
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/properties/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/** @hide */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+package androidx.appactions.interaction.capabilities.core.properties;
+
+import androidx.annotation.RestrictTo;
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/AppEntityListListener.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/AppEntityListListener.kt
new file mode 100644
index 0000000..5093c08
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/AppEntityListListener.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.appactions.interaction.capabilities.core.task
+
+import androidx.annotation.RestrictTo
+import androidx.appactions.interaction.capabilities.core.values.SearchAction
+import androidx.concurrent.futures.await
+import com.google.common.util.concurrent.ListenableFuture
+
+/**
+ * Similar to ValueListener, but also need to handle grounding of ungrounded values.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+interface AppEntityListListener<T> : ValueListener<List<T>> {
+    /**
+     * Given a search criteria, looks up the inventory during runtime, renders the search result
+     * within the app's own UI and then returns it to the Assistant so that the task can be kept in
+     * sync with the app UI.
+     *
+     * @return a structured search result represented by [EntitySearchResult] for the given
+     *   [searchAction]
+     */
+    suspend fun lookupAndRender(searchAction: SearchAction<T>): EntitySearchResult<T> =
+        lookupAndRenderAsync(searchAction).await()
+
+    /**
+     * Given a search criteria, looks up the inventory during runtime, renders the search result
+     * within the app's own UI and then returns it to the Assistant so that the task can be kept in
+     * sync with the app UI.
+     *
+     * @return a [ListenableFuture] of the structured search result represented by
+     *   [EntitySearchResult] for the given [searchAction]
+     */
+    fun lookupAndRenderAsync(
+        searchAction: SearchAction<T>
+    ): ListenableFuture<EntitySearchResult<T>> {
+        throw NotImplementedError()
+    }
+}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/AppEntityListResolver.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/AppEntityListResolver.java
deleted file mode 100644
index d1388ad..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/AppEntityListResolver.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.task;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.appactions.interaction.capabilities.core.values.SearchAction;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.List;
-
-/**
- * Similar to ValueListener, but also need to handle grounding of ungrounded values.
- *
- * @param <T>
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public interface AppEntityListResolver<T> extends ValueListener<List<T>> {
-    /**
-     * Given a search criteria, looks up the inventory during runtime, renders the search result
-     * within the app's own UI and then returns it to the Assistant so that the task can be kept in
-     * sync with the app UI.
-     */
-    @NonNull
-    ListenableFuture<EntitySearchResult<T>> lookupAndRender(@NonNull SearchAction<T> searchAction);
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/AppEntityListener.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/AppEntityListener.kt
new file mode 100644
index 0000000..ef0c47b
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/AppEntityListener.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.appactions.interaction.capabilities.core.task
+
+import androidx.annotation.RestrictTo
+import androidx.appactions.interaction.capabilities.core.values.SearchAction
+import androidx.concurrent.futures.await
+import com.google.common.util.concurrent.ListenableFuture
+
+/**
+ * Similar to ValueListener, but also need to handle grounding of ungrounded values.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+interface AppEntityListener<T> : ValueListener<T> {
+    /**
+     * Given a search criteria, looks up the inventory during runtime, renders the search result
+     * within the app's own UI and then returns it to the Assistant so that the task can be kept in
+     * sync with the app UI.
+     *
+     * @return a structured search result represented by [EntitySearchResult] for the given
+     *   [searchAction]
+     */
+    suspend fun lookupAndRender(searchAction: SearchAction<T>): EntitySearchResult<T> =
+        lookupAndRenderAsync(searchAction).await()
+
+    /**
+     * Given a search criteria, looks up the inventory during runtime, renders the search result
+     * within the app's own UI and then returns it to the Assistant so that the task can be kept in
+     * sync with the app UI.
+     *
+     * @return a [ListenableFuture] of the structured search result represented by
+     *   [EntitySearchResult] for the given [searchAction]
+     */
+    fun lookupAndRenderAsync(
+        searchAction: SearchAction<T>
+    ): ListenableFuture<EntitySearchResult<T>> {
+        throw NotImplementedError()
+    }
+}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/AppEntityResolver.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/AppEntityResolver.java
deleted file mode 100644
index cda8917c..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/AppEntityResolver.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.task;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.appactions.interaction.capabilities.core.values.SearchAction;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-/**
- * Similar to ValueListener, but also need to handle grounding of ungrounded values.
- *
- * @param <T>
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public interface AppEntityResolver<T> extends ValueListener<T> {
-    /**
-     * Given a search criteria, looks up the inventory during runtime, renders the search result
-     * within the app's own UI and then returns it to the Assistant so that the task can be kept in
-     * sync with the app UI.
-     */
-    @NonNull
-    ListenableFuture<EntitySearchResult<T>> lookupAndRender(@NonNull SearchAction<T> searchAction);
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/EntitySearchResult.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/EntitySearchResult.kt
index 7216699..f23055b 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/EntitySearchResult.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/EntitySearchResult.kt
@@ -17,6 +17,7 @@
 package androidx.appactions.interaction.capabilities.core.task
 
 import java.util.Objects
+
 /**
  * Represents results from searching for ungrounded value(s).
  *
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/InventoryListListener.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/InventoryListListener.kt
new file mode 100644
index 0000000..9450a1e
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/InventoryListListener.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.appactions.interaction.capabilities.core.task
+
+import androidx.annotation.RestrictTo
+import androidx.concurrent.futures.await
+import com.google.common.util.concurrent.ListenableFuture
+
+/**
+ * Repeated version of [InventoryListener].
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+interface InventoryListListener<T> : ValueListener<List<T>> {
+    /**
+     * Renders the provided entities in the app UI for disambiguation.
+     *
+     * The app should not modify the entity contents or their orders during the rendering.
+     * Otherwise, the Assistant task will be out of sync with the app UI.
+     */
+    suspend fun renderChoices(entityIDs: List<String>) {
+        renderChoicesAsync(entityIDs).await()
+    }
+    /**
+     * Renders the provided entities in the app UI for disambiguation.
+     *
+     * The app should not modify the entity contents or their orders during the rendering.
+     * Otherwise, the Assistant task will be out of sync with the app UI.
+     *
+     * @return a [ListenableFuture] of nothing, which only indicates when the rendering finishes.
+     */
+    fun renderChoicesAsync(entityIDs: List<String>): ListenableFuture<Unit> {
+        throw NotImplementedError()
+    }
+}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/InventoryListResolver.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/InventoryListResolver.java
deleted file mode 100644
index 7b346381..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/InventoryListResolver.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.task;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.List;
-
-/**
- * Repeated version of {@code InventoryResolver}.
- *
- * @param <T>
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public interface InventoryListResolver<T> extends ValueListener<List<T>> {
-    /**
-     * Renders the provided entities in the app UI for dismabiguation.
-     *
-     * <p>The app should not modify the entity contents or their orders during the rendering.
-     * Otherwise, the Assistant task will be out of sync with the app UI.
-     */
-    @NonNull
-    ListenableFuture<Void> renderChoices(@NonNull List<String> entityIDs);
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/InventoryListener.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/InventoryListener.kt
new file mode 100644
index 0000000..75d2764
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/InventoryListener.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.appactions.interaction.capabilities.core.task
+
+import androidx.annotation.RestrictTo
+import androidx.concurrent.futures.await
+import com.google.common.util.concurrent.ListenableFuture
+
+/**
+ * Similar to ValueListener, but also need to handle entity rendering.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+interface InventoryListener<T> : ValueListener<T> {
+    /**
+     * Renders the provided entities in the app UI for disambiguation.
+     *
+     * The app should not modify the entity contents or their orders during the rendering.
+     * Otherwise, the Assistant task will be out of sync with the app UI.
+     */
+    suspend fun renderChoices(entityIDs: List<String>) {
+        renderChoicesAsync(entityIDs).await()
+    }
+    /**
+     * Renders the provided entities in the app UI for disambiguation.
+     *
+     * The app should not modify the entity contents or their orders during the rendering.
+     * Otherwise, the Assistant task will be out of sync with the app UI.
+     *
+     * @return a [ListenableFuture] of nothing, which only indicates when the rendering finishes.
+     */
+    fun renderChoicesAsync(entityIDs: List<String>): ListenableFuture<Void> {
+        throw NotImplementedError()
+    }
+}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/InventoryResolver.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/InventoryResolver.java
deleted file mode 100644
index 8d149d0..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/InventoryResolver.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.task;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.List;
-
-/**
- * Similar to ValueListener, but also need to handle entity rendering.
- *
- * @param <T>
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public interface InventoryResolver<T> extends ValueListener<T> {
-    /**
-     * Renders the provided entities in the app UI for dismabiguation.
-     *
-     * <p>The app should not modify the entity contents or their orders during the rendering.
-     * Otherwise, the Assistant task will be out of sync with the app UI.
-     */
-    @NonNull
-    ListenableFuture<Void> renderChoices(@NonNull List<String> entityIDs);
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/ValueListener.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/ValueListener.kt
index d2a7ea3..a85ce32 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/ValueListener.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/ValueListener.kt
@@ -16,7 +16,8 @@
 
 package androidx.appactions.interaction.capabilities.core.task
 
-import androidx.appactions.interaction.capabilities.core.impl.concurrent.convertToListenableFuture
+import androidx.appactions.interaction.capabilities.core.impl.concurrent.Futures
+import androidx.concurrent.futures.await
 import com.google.common.util.concurrent.ListenableFuture
 
 /** Provides a mechanism for the app to listen to argument updates from Assistant. */
@@ -32,11 +33,9 @@
      * <li>2. If the given values are valid, update app UI state if applicable.
      * </ul>
      *
-     * <p>Returns the ValidationResult.
+     * @return the [ValidationResult].
      */
-    suspend fun onReceived(value: T): ValidationResult {
-        return ValidationResult.newAccepted()
-    }
+    suspend fun onReceived(value: T): ValidationResult = onReceivedAsync(value).await()
 
     /**
      * Invoked when Assistant reports that an argument value has changed. This method should be
@@ -49,8 +48,8 @@
      * <li>2. If the given values are valid, update app UI state if applicable.
      * </ul>
      *
-     * <p>Returns a ListenableFuture containing the ValidationResult.
+     * @return a [ListenableFuture] containing the [ValidationResult].
      */
     fun onReceivedAsync(value: T): ListenableFuture<ValidationResult> =
-        convertToListenableFuture("ValueListener#onReceived") { onReceived(value) }
+        Futures.immediateFuture(ValidationResult.newAccepted())
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/AssistantUpdateRequest.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/AssistantUpdateRequest.kt
index 4799845..b4af6e8 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/AssistantUpdateRequest.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/AssistantUpdateRequest.kt
@@ -15,7 +15,6 @@
  */
 package androidx.appactions.interaction.capabilities.core.task.impl
 
-import androidx.annotation.RestrictTo
 import androidx.appactions.interaction.capabilities.core.impl.ArgumentsWrapper
 import androidx.appactions.interaction.capabilities.core.impl.CallbackInternal
 
@@ -24,10 +23,8 @@
  *
  * @param argumentsWrapper The fulfillment request data.
  * @param callbackInternal The callback to report results from handling this request.
- * @hide
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-data class AssistantUpdateRequest(
+internal data class AssistantUpdateRequest(
     val argumentsWrapper: ArgumentsWrapper,
     val callbackInternal: CallbackInternal,
 )
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/GenericResolverInternal.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/GenericResolverInternal.java
deleted file mode 100644
index 7954fa2..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/GenericResolverInternal.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.task.impl;
-
-import androidx.annotation.NonNull;
-import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter;
-import androidx.appactions.interaction.capabilities.core.impl.converters.SlotTypeConverter;
-import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException;
-import androidx.appactions.interaction.capabilities.core.task.AppEntityListResolver;
-import androidx.appactions.interaction.capabilities.core.task.AppEntityResolver;
-import androidx.appactions.interaction.capabilities.core.task.EntitySearchResult;
-import androidx.appactions.interaction.capabilities.core.task.InventoryListResolver;
-import androidx.appactions.interaction.capabilities.core.task.InventoryResolver;
-import androidx.appactions.interaction.capabilities.core.task.ValidationResult;
-import androidx.appactions.interaction.capabilities.core.task.ValueListener;
-import androidx.appactions.interaction.capabilities.core.task.impl.exceptions.InvalidResolverException;
-import androidx.appactions.interaction.capabilities.core.values.SearchAction;
-import androidx.appactions.interaction.proto.ParamValue;
-
-import com.google.auto.value.AutoOneOf;
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.List;
-
-/**
- * A wrapper around all types of slot resolvers (value listeners + disambig resolvers).
- *
- * <p>This allows one type of resolver to be bound for each slot, and abstracts the details of the
- * individual resolvers. It is also the place where repeated fields are handled.
- *
- * @param <ValueTypeT>
- */
-@AutoOneOf(GenericResolverInternal.Kind.class)
-public abstract class GenericResolverInternal<ValueTypeT> {
-    @NonNull
-    public static <ValueTypeT> GenericResolverInternal<ValueTypeT> fromValueListener(
-            @NonNull ValueListener<ValueTypeT> valueListener) {
-        return AutoOneOf_GenericResolverInternal.value(valueListener);
-    }
-
-    @NonNull
-    public static <ValueTypeT> GenericResolverInternal<ValueTypeT> fromValueListListener(
-            @NonNull ValueListener<List<ValueTypeT>> valueListListener) {
-        return AutoOneOf_GenericResolverInternal.valueList(valueListListener);
-    }
-
-    @NonNull
-    public static <ValueTypeT> GenericResolverInternal<ValueTypeT> fromAppEntityResolver(
-            @NonNull AppEntityResolver<ValueTypeT> appEntityResolver) {
-        return AutoOneOf_GenericResolverInternal.appEntityResolver(appEntityResolver);
-    }
-
-    @NonNull
-    public static <ValueTypeT> GenericResolverInternal<ValueTypeT> fromAppEntityListResolver(
-            @NonNull AppEntityListResolver<ValueTypeT> appEntityListResolver) {
-        return AutoOneOf_GenericResolverInternal.appEntityListResolver(appEntityListResolver);
-    }
-
-    @NonNull
-    public static <ValueTypeT> GenericResolverInternal<ValueTypeT> fromInventoryResolver(
-            @NonNull InventoryResolver<ValueTypeT> inventoryResolver) {
-        return AutoOneOf_GenericResolverInternal.inventoryResolver(inventoryResolver);
-    }
-
-    @NonNull
-    public static <ValueTypeT> GenericResolverInternal<ValueTypeT> fromInventoryListResolver(
-            @NonNull InventoryListResolver<ValueTypeT> inventoryListResolverListener) {
-        return AutoOneOf_GenericResolverInternal.inventoryListResolver(
-                inventoryListResolverListener);
-    }
-
-    /** Returns the Kind of this resolver */
-    @NonNull
-    public abstract Kind getKind();
-
-    abstract ValueListener<ValueTypeT> value();
-
-    abstract ValueListener<List<ValueTypeT>> valueList();
-
-    abstract AppEntityResolver<ValueTypeT> appEntityResolver();
-
-    abstract AppEntityListResolver<ValueTypeT> appEntityListResolver();
-
-    abstract InventoryResolver<ValueTypeT> inventoryResolver();
-
-    abstract InventoryListResolver<ValueTypeT> inventoryListResolver();
-
-    /** Wrapper which should invoke the `lookupAndRender` provided by the developer. */
-    @NonNull
-    public ListenableFuture<EntitySearchResult<ValueTypeT>> invokeLookup(
-            @NonNull SearchAction<ValueTypeT> searchAction) throws InvalidResolverException {
-        switch (getKind()) {
-            case APP_ENTITY_RESOLVER:
-                return appEntityResolver().lookupAndRender(searchAction);
-            case APP_ENTITY_LIST_RESOLVER:
-                return appEntityListResolver().lookupAndRender(searchAction);
-            default:
-                throw new InvalidResolverException(
-                        String.format(
-                                "invokeLookup is not supported on this resolver of type %s",
-                                getKind().name()));
-        }
-    }
-
-    /**
-     * Wrapper which should invoke the EntityRender#renderEntities method when the Assistant is
-     * prompting for disambiguation.
-     */
-    @NonNull
-    public ListenableFuture<Void> invokeEntityRender(@NonNull List<String> entityIds)
-            throws InvalidResolverException {
-        switch (getKind()) {
-            case INVENTORY_RESOLVER:
-                return inventoryResolver().renderChoices(entityIds);
-            case INVENTORY_LIST_RESOLVER:
-                return inventoryListResolver().renderChoices(entityIds);
-            default:
-                throw new InvalidResolverException(
-                        String.format(
-                                "invokeEntityRender is not supported on this resolver of type %s",
-                                getKind().name()));
-        }
-    }
-
-    /**
-     * Notifies the app that a new value for this argument has been set by Assistant. This method
-     * should only be called with completely grounded values.
-     */
-    @NonNull
-    public ListenableFuture<ValidationResult> notifyValueChange(
-            @NonNull List<ParamValue> paramValues,
-            @NonNull ParamValueConverter<ValueTypeT> converter)
-            throws StructConversionException {
-        SlotTypeConverter<ValueTypeT> singularConverter = SlotTypeConverter.ofSingular(converter);
-        SlotTypeConverter<List<ValueTypeT>> repeatedConverter =
-                SlotTypeConverter.ofRepeated(converter);
-
-        switch (getKind()) {
-            case VALUE:
-                return value().onReceivedAsync(singularConverter.convert(paramValues));
-            case VALUE_LIST:
-                return valueList().onReceivedAsync(repeatedConverter.convert(paramValues));
-            case APP_ENTITY_RESOLVER:
-                return appEntityResolver().onReceivedAsync(singularConverter.convert(paramValues));
-            case APP_ENTITY_LIST_RESOLVER:
-                return appEntityListResolver()
-                        .onReceivedAsync(repeatedConverter.convert(paramValues));
-            case INVENTORY_RESOLVER:
-                return inventoryResolver().onReceivedAsync(singularConverter.convert(paramValues));
-            case INVENTORY_LIST_RESOLVER:
-                return inventoryListResolver()
-                        .onReceivedAsync(repeatedConverter.convert(paramValues));
-        }
-        throw new IllegalStateException("unreachable");
-    }
-
-    /** The kind of resolver. */
-    public enum Kind {
-        VALUE,
-        VALUE_LIST,
-        APP_ENTITY_RESOLVER,
-        APP_ENTITY_LIST_RESOLVER,
-        INVENTORY_RESOLVER,
-        INVENTORY_LIST_RESOLVER
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/GenericResolverInternal.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/GenericResolverInternal.kt
new file mode 100644
index 0000000..eb30e5b
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/GenericResolverInternal.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.appactions.interaction.capabilities.core.task.impl
+
+import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter
+import androidx.appactions.interaction.capabilities.core.impl.converters.SlotTypeConverter
+import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException
+import androidx.appactions.interaction.capabilities.core.task.AppEntityListListener
+import androidx.appactions.interaction.capabilities.core.task.AppEntityListener
+import androidx.appactions.interaction.capabilities.core.task.EntitySearchResult
+import androidx.appactions.interaction.capabilities.core.task.InventoryListListener
+import androidx.appactions.interaction.capabilities.core.task.InventoryListener
+import androidx.appactions.interaction.capabilities.core.task.ValidationResult
+import androidx.appactions.interaction.capabilities.core.task.ValueListener
+import androidx.appactions.interaction.capabilities.core.task.impl.exceptions.InvalidResolverException
+import androidx.appactions.interaction.capabilities.core.values.SearchAction
+import androidx.appactions.interaction.proto.ParamValue
+
+/**
+ * A wrapper around all types of slot resolvers (value listeners + disambig resolvers).
+ *
+ * This allows one type of resolver to be bound for each slot, and abstracts the details of the
+ * individual resolvers. It is also the place where repeated fields are handled.
+ *
+ * </ValueTypeT>
+ */
+internal class GenericResolverInternal<ValueTypeT>
+private constructor(
+    val value: ValueListener<ValueTypeT>? = null,
+    val valueList: ValueListener<List<ValueTypeT>>? = null,
+    val appEntity: AppEntityListener<ValueTypeT>? = null,
+    val appEntityList: AppEntityListListener<ValueTypeT>? = null,
+    val inventory: InventoryListener<ValueTypeT>? = null,
+    val inventoryList: InventoryListListener<ValueTypeT>? = null,
+) {
+
+    /** Wrapper which should invoke the `lookupAndRender` provided by the developer. */
+    @Throws(InvalidResolverException::class)
+    suspend fun invokeLookup(
+        searchAction: SearchAction<ValueTypeT>
+    ): EntitySearchResult<ValueTypeT> {
+        return if (appEntity != null) {
+            appEntity.lookupAndRender(searchAction)
+        } else if (appEntityList != null) {
+            appEntityList.lookupAndRender(searchAction)
+        } else {
+            throw InvalidResolverException("invokeLookup is not supported on this resolver")
+        }
+    }
+
+    /**
+     * Wrapper which should invoke the EntityRender#renderEntities method when the Assistant is
+     * prompting for disambiguation.
+     */
+    @Throws(InvalidResolverException::class)
+    suspend fun invokeEntityRender(entityIds: List<String>) {
+        if (inventory != null) {
+            inventory.renderChoices(entityIds)
+        } else if (inventoryList != null) inventoryList.renderChoices(entityIds)
+        else {
+            throw InvalidResolverException("invokeEntityRender is not supported on this resolver")
+        }
+    }
+
+    /**
+     * Notifies the app that a new value for this argument has been set by Assistant. This method
+     * should only be called with completely grounded values.
+     */
+    @Throws(StructConversionException::class)
+    suspend fun notifyValueChange(
+        paramValues: List<ParamValue>,
+        converter: ParamValueConverter<ValueTypeT>
+    ): ValidationResult {
+        val singularConverter = SlotTypeConverter.ofSingular(converter)
+        val repeatedConverter = SlotTypeConverter.ofRepeated(converter)
+        return when {
+            value != null -> value.onReceived(singularConverter.convert(paramValues))
+            valueList != null -> valueList.onReceived(repeatedConverter.convert(paramValues))
+            appEntity != null ->
+                appEntity.onReceived(singularConverter.convert(paramValues))
+            appEntityList != null ->
+                appEntityList.onReceived(repeatedConverter.convert(paramValues))
+            inventory != null ->
+                inventory.onReceived(singularConverter.convert(paramValues))
+            inventoryList != null ->
+                inventoryList.onReceived(repeatedConverter.convert(paramValues))
+            else -> throw IllegalStateException("unreachable")
+        }
+    }
+
+    companion object {
+        fun <ValueTypeT> fromValueListener(valueListener: ValueListener<ValueTypeT>) =
+            GenericResolverInternal(value = valueListener)
+
+        fun <ValueTypeT> fromValueListListener(valueListListener: ValueListener<List<ValueTypeT>>) =
+            GenericResolverInternal(valueList = valueListListener)
+
+        fun <ValueTypeT> fromAppEntityListener(appEntity: AppEntityListener<ValueTypeT>) =
+            GenericResolverInternal(appEntity = appEntity)
+
+        fun <ValueTypeT> fromAppEntityListListener(
+            appEntityList: AppEntityListListener<ValueTypeT>
+        ) = GenericResolverInternal(appEntityList = appEntityList)
+
+        fun <ValueTypeT> fromInventoryListener(inventory: InventoryListener<ValueTypeT>) =
+            GenericResolverInternal(inventory = inventory)
+
+        fun <ValueTypeT> fromInventoryListListener(
+            inventoryList: InventoryListListener<ValueTypeT>
+        ) = GenericResolverInternal(inventoryList = inventoryList)
+    }
+}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/OnReadyToConfirmListenerInternal.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/OnReadyToConfirmListenerInternal.java
deleted file mode 100644
index e4a9047..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/OnReadyToConfirmListenerInternal.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.task.impl;
-
-import androidx.annotation.NonNull;
-import androidx.appactions.interaction.capabilities.core.ConfirmationOutput;
-import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException;
-import androidx.appactions.interaction.capabilities.core.task.impl.exceptions.MissingRequiredArgException;
-import androidx.appactions.interaction.proto.ParamValue;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * Generic onReadyToConfirm listener for a task capability. This is the entry point to specific
- * onReadyToCOnfirm listeners. For example, Search/Update sub-BIIs factories may invoke specific
- * onReadyToConfirm listeners for that BII.
- *
- * @param <ConfirmationT>
- */
-public interface OnReadyToConfirmListenerInternal<ConfirmationT> {
-
-    /** onReadyToConfirm callback for a task capability. */
-    @NonNull
-    ListenableFuture<ConfirmationOutput<ConfirmationT>> onReadyToConfirm(
-            @NonNull Map<String, List<ParamValue>> args)
-            throws StructConversionException, MissingRequiredArgException;
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/OnReadyToConfirmListenerInternal.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/OnReadyToConfirmListenerInternal.kt
new file mode 100644
index 0000000..9183b96
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/OnReadyToConfirmListenerInternal.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.appactions.interaction.capabilities.core.task.impl
+
+import androidx.appactions.interaction.capabilities.core.ConfirmationOutput
+import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException
+import androidx.appactions.interaction.capabilities.core.task.impl.exceptions.MissingRequiredArgException
+import androidx.appactions.interaction.proto.ParamValue
+
+/**
+ * Generic onReadyToConfirm listener for a task capability. This is the entry point to specific
+ * onReadyToConfirm listeners. For example, Search/Update sub-BIIs factories may invoke specific
+ * onReadyToConfirm listeners for that BII.
+ */
+interface OnReadyToConfirmListenerInternal<ConfirmationT> {
+    /** onReadyToConfirm callback for a task capability. */
+    @Throws(StructConversionException::class, MissingRequiredArgException::class)
+    suspend fun onReadyToConfirm(
+        args: Map<String, List<ParamValue>>
+    ): ConfirmationOutput<ConfirmationT>
+}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/SlotProcessingResult.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/SlotProcessingResult.kt
index 93f8e5d..67cca58 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/SlotProcessingResult.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/SlotProcessingResult.kt
@@ -15,7 +15,6 @@
  */
 package androidx.appactions.interaction.capabilities.core.task.impl
 
-import androidx.annotation.RestrictTo
 import androidx.appactions.interaction.proto.CurrentValue
 
 /**
@@ -25,8 +24,8 @@
  * * listener#onReceived returned ACCEPTED for all grounded values (which could be empty list)
  *
  * @param processedValues Processed CurrentValue objects.
- * @hide
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-data class SlotProcessingResult
-constructor(val isSuccessful: Boolean, val processedValues: List<CurrentValue>)
+internal data class SlotProcessingResult(
+    val isSuccessful: Boolean,
+    val processedValues: List<CurrentValue>,
+)
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskHandler.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskHandler.kt
index 7cb544d..2df1d53 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskHandler.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskHandler.kt
@@ -16,165 +16,158 @@
 
 package androidx.appactions.interaction.capabilities.core.task.impl
 
-import androidx.appactions.interaction.capabilities.core.impl.converters.DisambigEntityConverter
+import androidx.annotation.RestrictTo
+import androidx.appactions.interaction.capabilities.core.impl.converters.EntityConverter
 import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter
 import androidx.appactions.interaction.capabilities.core.impl.converters.SearchActionConverter
-import androidx.appactions.interaction.capabilities.core.task.AppEntityListResolver
-import androidx.appactions.interaction.capabilities.core.task.AppEntityResolver
-import androidx.appactions.interaction.capabilities.core.task.InventoryListResolver
-import androidx.appactions.interaction.capabilities.core.task.InventoryResolver
+import androidx.appactions.interaction.capabilities.core.task.AppEntityListListener
+import androidx.appactions.interaction.capabilities.core.task.AppEntityListener
+import androidx.appactions.interaction.capabilities.core.task.InventoryListListener
+import androidx.appactions.interaction.capabilities.core.task.InventoryListener
 import androidx.appactions.interaction.capabilities.core.task.ValueListener
 import androidx.appactions.interaction.proto.ParamValue
-import java.util.function.Function
 
-import androidx.annotation.RestrictTo
-
-/**
- * Container of multi-turn Task related function references.
- *
- */
+/** Container of multi-turn Task related function references. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-data class TaskHandler<ConfirmationT> (
-    val taskParamMap: Map<String, TaskParamBinding<*>>,
-    val confirmationType: ConfirmationType,
-    val confirmationDataBindings: Map<String, Function<ConfirmationT, List<ParamValue>>>,
-    val onReadyToConfirmListener: OnReadyToConfirmListenerInternal<ConfirmationT>?,
+data class TaskHandler<ConfirmationT>
+internal constructor(
+    internal val taskParamMap: Map<String, TaskParamBinding<*>>,
+    internal val confirmationType: ConfirmationType,
+    internal val confirmationDataBindings: Map<String, (ConfirmationT) -> List<ParamValue>>,
+    internal val onReadyToConfirmListener: OnReadyToConfirmListenerInternal<ConfirmationT>?,
 ) {
     class Builder<ConfirmationT>(
-        val confirmationType: ConfirmationType = ConfirmationType.NOT_SUPPORTED,
+        private val confirmationType: ConfirmationType = ConfirmationType.NOT_SUPPORTED,
     ) {
-        val mutableTaskParamMap = mutableMapOf<String, TaskParamBinding<*>>()
-        val confirmationDataBindings: MutableMap<
-            String, Function<ConfirmationT, List<ParamValue>>,> = mutableMapOf()
-        var onReadyToConfirmListener: OnReadyToConfirmListenerInternal<ConfirmationT>? = null
+        private val mutableTaskParamMap = mutableMapOf<String, TaskParamBinding<*>>()
+        private val confirmationDataBindings =
+            mutableMapOf<String, (ConfirmationT) -> List<ParamValue>>()
+        private var onReadyToConfirmListener: OnReadyToConfirmListenerInternal<ConfirmationT>? =
+            null
 
         fun <ValueTypeT> registerInventoryTaskParam(
             paramName: String,
-            listener: InventoryResolver<ValueTypeT>,
+            listener: InventoryListener<ValueTypeT>,
             converter: ParamValueConverter<ValueTypeT>,
-        ): Builder<ConfirmationT> {
-            mutableTaskParamMap[paramName] = TaskParamBinding(
-                paramName,
-                GROUND_IF_NO_IDENTIFIER,
-                GenericResolverInternal.fromInventoryResolver(listener),
-                converter,
-                null,
-                null,
-            )
-            return this
+        ): Builder<ConfirmationT> = apply {
+            mutableTaskParamMap[paramName] =
+                TaskParamBinding(
+                    paramName,
+                    GROUND_IF_NO_IDENTIFIER,
+                    GenericResolverInternal.fromInventoryListener(listener),
+                    converter,
+                    null,
+                    null,
+                )
         }
 
         fun <ValueTypeT> registerInventoryListTaskParam(
             paramName: String,
-            listener: InventoryListResolver<ValueTypeT>,
+            listener: InventoryListListener<ValueTypeT>,
             converter: ParamValueConverter<ValueTypeT>,
-        ): Builder<ConfirmationT> {
-            mutableTaskParamMap[paramName] = TaskParamBinding(
-                paramName,
-                GROUND_IF_NO_IDENTIFIER,
-                GenericResolverInternal.fromInventoryListResolver(listener),
-                converter,
-                null,
-                null,
-            )
-            return this
+        ): Builder<ConfirmationT> = apply {
+            mutableTaskParamMap[paramName] =
+                TaskParamBinding(
+                    paramName,
+                    GROUND_IF_NO_IDENTIFIER,
+                    GenericResolverInternal.fromInventoryListListener(listener),
+                    converter,
+                    null,
+                    null,
+                )
         }
 
         fun <ValueTypeT> registerAppEntityTaskParam(
             paramName: String,
-            listener: AppEntityResolver<ValueTypeT>,
+            listener: AppEntityListener<ValueTypeT>,
             converter: ParamValueConverter<ValueTypeT>,
-            entityConverter: DisambigEntityConverter<ValueTypeT>,
+            entityConverter: EntityConverter<ValueTypeT>,
             searchActionConverter: SearchActionConverter<ValueTypeT>,
-        ): Builder<ConfirmationT> {
-            mutableTaskParamMap[paramName] = TaskParamBinding(
-                paramName,
-                GROUND_IF_NO_IDENTIFIER,
-                GenericResolverInternal.fromAppEntityResolver(listener),
-                converter,
-                entityConverter,
-                searchActionConverter,
-            )
-            return this
+        ): Builder<ConfirmationT> = apply {
+            mutableTaskParamMap[paramName] =
+                TaskParamBinding(
+                    paramName,
+                    GROUND_IF_NO_IDENTIFIER,
+                    GenericResolverInternal.fromAppEntityListener(listener),
+                    converter,
+                    entityConverter,
+                    searchActionConverter,
+                )
         }
 
         fun <ValueTypeT> registerAppEntityListTaskParam(
             paramName: String,
-            listener: AppEntityListResolver<ValueTypeT>,
+            listener: AppEntityListListener<ValueTypeT>,
             converter: ParamValueConverter<ValueTypeT>,
-            entityConverter: DisambigEntityConverter<ValueTypeT>,
+            entityConverter: EntityConverter<ValueTypeT>,
             searchActionConverter: SearchActionConverter<ValueTypeT>,
-        ): Builder<ConfirmationT> {
-            mutableTaskParamMap[paramName] = TaskParamBinding(
-                paramName,
-                GROUND_IF_NO_IDENTIFIER,
-                GenericResolverInternal.fromAppEntityListResolver(listener),
-                converter,
-                entityConverter,
-                searchActionConverter,
-            )
-            return this
+        ): Builder<ConfirmationT> = apply {
+            mutableTaskParamMap[paramName] =
+                TaskParamBinding(
+                    paramName,
+                    GROUND_IF_NO_IDENTIFIER,
+                    GenericResolverInternal.fromAppEntityListListener(listener),
+                    converter,
+                    entityConverter,
+                    searchActionConverter,
+                )
         }
 
         fun <ValueTypeT> registerValueTaskParam(
             paramName: String,
             listener: ValueListener<ValueTypeT>,
             converter: ParamValueConverter<ValueTypeT>,
-        ): Builder<ConfirmationT> {
-            mutableTaskParamMap[paramName] = TaskParamBinding(
-                paramName,
-                GROUND_NEVER,
-                GenericResolverInternal.fromValueListener(listener),
-                converter,
-                null,
-                null,
-            )
-            return this
+        ): Builder<ConfirmationT> = apply {
+            mutableTaskParamMap[paramName] =
+                TaskParamBinding(
+                    paramName,
+                    GROUND_NEVER,
+                    GenericResolverInternal.fromValueListener(listener),
+                    converter,
+                    null,
+                    null,
+                )
         }
 
         fun <ValueTypeT> registerValueListTaskParam(
             paramName: String,
             listener: ValueListener<List<ValueTypeT>>,
             converter: ParamValueConverter<ValueTypeT>,
-        ): Builder<ConfirmationT> {
-            mutableTaskParamMap[paramName] = TaskParamBinding(
-                paramName,
-                GROUND_NEVER,
-                GenericResolverInternal.fromValueListListener(listener),
-                converter,
-                null,
-                null,
-            )
-            return this
+        ): Builder<ConfirmationT> = apply {
+            mutableTaskParamMap[paramName] =
+                TaskParamBinding(
+                    paramName,
+                    GROUND_NEVER,
+                    GenericResolverInternal.fromValueListListener(listener),
+                    converter,
+                    null,
+                    null,
+                )
         }
 
         /**
          * Registers an optional, non-repeated confirmation data.
          *
-         * @param paramName          the BIC confirmation data slot name of this parameter.
-         * @param confirmationGetter a getter of the confirmation data from the {@code ConfirmationT}
-         *                           instance.
-         * @param converter          a converter from confirmation data to a ParamValue.
+         * @param paramName the BIC confirmation data slot name of this parameter.
+         * @param confirmationGetter a getter of the confirmation data from the {@code
+         *   ConfirmationT} instance.
+         * @param converter a converter from confirmation data to a ParamValue.
          */
         fun <T> registerConfirmationOutput(
             paramName: String,
             confirmationGetter: (ConfirmationT) -> T?,
             converter: (T) -> ParamValue,
-        ): Builder<ConfirmationT> {
-            confirmationDataBindings.put(
-                paramName,
-            ) { output: ConfirmationT ->
+        ): Builder<ConfirmationT> = apply {
+            confirmationDataBindings[paramName] = { output: ConfirmationT ->
                 listOfNotNull(confirmationGetter(output)).map(converter)
             }
-            return this
         }
 
         /** Sets the onReadyToConfirmListener for this capability. */
         fun setOnReadyToConfirmListenerInternal(
             onReadyToConfirmListener: OnReadyToConfirmListenerInternal<ConfirmationT>,
-        ): Builder<ConfirmationT> {
+        ): Builder<ConfirmationT> = apply {
             this.onReadyToConfirmListener = onReadyToConfirmListener
-            return this
         }
 
         fun build(): TaskHandler<ConfirmationT> {
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskOrchestrator.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskOrchestrator.kt
index c234681..527c33e 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskOrchestrator.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskOrchestrator.kt
@@ -22,8 +22,8 @@
 import androidx.appactions.interaction.capabilities.core.InitArg
 import androidx.appactions.interaction.capabilities.core.impl.ActionCapabilitySession
 import androidx.appactions.interaction.capabilities.core.impl.ArgumentsWrapper
-import androidx.appactions.interaction.capabilities.core.impl.CallbackInternal
 import androidx.appactions.interaction.capabilities.core.impl.ErrorStatusInternal
+import androidx.appactions.interaction.capabilities.core.impl.FulfillmentResult
 import androidx.appactions.interaction.capabilities.core.impl.TouchEventCallback
 import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
@@ -39,8 +39,9 @@
 import androidx.appactions.interaction.proto.FulfillmentResponse
 import androidx.appactions.interaction.proto.ParamValue
 import androidx.appactions.interaction.proto.TouchEventMetadata
-import androidx.concurrent.futures.await
-import java.util.Collections
+import java.util.concurrent.locks.ReentrantReadWriteLock
+import kotlin.concurrent.read
+import kotlin.concurrent.write
 import kotlin.jvm.Throws
 
 /**
@@ -56,11 +57,17 @@
     private val actionSpec: ActionSpec<*, ArgumentT, OutputT>,
     private val appAction: AppActionsContext.AppAction,
     private val taskHandler: TaskHandler<ConfirmationT>,
-    private val externalSession: BaseSession<ArgumentT, OutputT>
+    private val externalSession: BaseSession<ArgumentT, OutputT>,
 ) {
+    /**
+     * A [reader-writer lock](https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock) to protect
+     * the synchronizing operation on [currentValuesMap]
+     */
+    private val valuesMapLock = ReentrantReadWriteLock()
+
     /** Map of argument name to the [CurrentValue] which wraps the argument name and status . */
-    private val currentValuesMap: MutableMap<String, List<CurrentValue>> =
-        Collections.synchronizedMap(HashMap())
+    @GuardedBy("valuesMapLock")
+    private val currentValuesMap = mutableMapOf<String, List<CurrentValue>>()
 
     /**
      * The callback that should be invoked when manual input processing finishes. This sends the
@@ -82,7 +89,8 @@
 
     private val inProgressLock = Any()
 
-    @GuardedBy("inProgressLock") private var inProgress = false
+    @GuardedBy("inProgressLock")
+    private var inProgress = false
 
     /** Returns whether or not a request is currently being processed */
     internal fun isIdle(): Boolean = synchronized(inProgressLock) { !inProgress }
@@ -91,14 +99,17 @@
         get() =
             AppActionsContext.AppDialogState.newBuilder()
                 .addAllParams(
-                    appAction.paramsList.map { intentParam ->
-                        val dialogParameterBuilder =
-                            AppActionsContext.DialogParameter.newBuilder().setName(intentParam.name)
-                        currentValuesMap[intentParam.name]?.let {
-                            dialogParameterBuilder.addAllCurrentValue(it)
+                    valuesMapLock.read {
+                        appAction.paramsList.map { intentParam ->
+                            val dialogParameterBuilder =
+                                AppActionsContext.DialogParameter.newBuilder()
+                                    .setName(intentParam.name)
+                            currentValuesMap[intentParam.name]?.let {
+                                dialogParameterBuilder.addAllCurrentValue(it)
+                            }
+                            dialogParameterBuilder.build()
                         }
-                        dialogParameterBuilder.build()
-                    }
+                    },
                 )
                 .setFulfillmentIdentifier(appAction.identifier)
                 .build()
@@ -118,7 +129,7 @@
             if (inProgress) {
                 throw IllegalStateException(
                     "processUpdateRequest should never be called when the task orchestrator" +
-                        " isn't idle."
+                        " isn't idle.",
                 )
             }
             inProgress = true
@@ -139,49 +150,58 @@
     /** Processes an assistant update request. */
     @Suppress("DEPRECATION")
     private suspend fun processAssistantUpdateRequest(
-        assistantUpdateRequest: AssistantUpdateRequest
+        assistantUpdateRequest: AssistantUpdateRequest,
     ) {
         val argumentsWrapper = assistantUpdateRequest.argumentsWrapper
         val callback = assistantUpdateRequest.callbackInternal
+        val fulfillmentResult: FulfillmentResult
         if (argumentsWrapper.requestMetadata == null) {
-            callback.onError(ErrorStatusInternal.INVALID_REQUEST_TYPE)
-            return
-        }
-        when (argumentsWrapper.requestMetadata.requestType()) {
-            FulfillmentRequest.Fulfillment.Type.UNRECOGNIZED,
-            FulfillmentRequest.Fulfillment.Type.UNKNOWN_TYPE ->
-                callback.onError(ErrorStatusInternal.INVALID_REQUEST_TYPE)
-            FulfillmentRequest.Fulfillment.Type.SYNC -> handleSync(argumentsWrapper, callback)
-            FulfillmentRequest.Fulfillment.Type.CONFIRM -> handleConfirm(callback)
-            FulfillmentRequest.Fulfillment.Type.CANCEL,
-            FulfillmentRequest.Fulfillment.Type.TERMINATE -> {
-                terminate()
-                callback.onSuccess(FulfillmentResponse.getDefaultInstance())
+            fulfillmentResult = FulfillmentResult(ErrorStatusInternal.INVALID_REQUEST_TYPE)
+        } else {
+            fulfillmentResult = when (argumentsWrapper.requestMetadata.requestType()) {
+                FulfillmentRequest.Fulfillment.Type.UNRECOGNIZED,
+                FulfillmentRequest.Fulfillment.Type.UNKNOWN_TYPE,
+                ->
+                    FulfillmentResult(ErrorStatusInternal.INVALID_REQUEST_TYPE)
+
+                FulfillmentRequest.Fulfillment.Type.SYNC -> handleSync(argumentsWrapper)
+                FulfillmentRequest.Fulfillment.Type.CONFIRM -> handleConfirm()
+                FulfillmentRequest.Fulfillment.Type.CANCEL,
+                FulfillmentRequest.Fulfillment.Type.TERMINATE,
+                -> {
+                    terminate()
+                    FulfillmentResult(FulfillmentResponse.getDefaultInstance())
+                }
             }
         }
+        fulfillmentResult.applyToCallback(callback)
     }
 
     private suspend fun processTouchEventUpdateRequest(
-        touchEventUpdateRequest: TouchEventUpdateRequest
+        touchEventUpdateRequest: TouchEventUpdateRequest,
     ) {
         val paramValuesMap = touchEventUpdateRequest.paramValuesMap
         if (
             mTouchEventCallback == null ||
-                paramValuesMap.isEmpty() ||
-                status !== ActionCapabilitySession.Status.IN_PROGRESS
+            paramValuesMap.isEmpty() ||
+            status !== ActionCapabilitySession.Status.IN_PROGRESS
         ) {
             return
         }
-        for ((argName, value) in paramValuesMap) {
-            currentValuesMap[argName] =
-                value.map { TaskCapabilityUtils.toCurrentValue(it, CurrentValue.Status.ACCEPTED) }
+        valuesMapLock.write {
+            for ((argName, value) in paramValuesMap) {
+                currentValuesMap[argName] =
+                    value.map {
+                        TaskCapabilityUtils.toCurrentValue(it, CurrentValue.Status.ACCEPTED)
+                    }
+            }
         }
 
         try {
             if (!anyParamsOfStatus(CurrentValue.Status.DISAMBIG)) {
                 val fulfillmentValuesMap =
                     TaskCapabilityUtils.paramValuesMapToFulfillmentValuesMap(
-                        currentPendingArguments
+                        getCurrentPendingArguments(),
                     )
                 processFulfillmentValues(fulfillmentValuesMap)
             }
@@ -190,13 +210,13 @@
             if (mTouchEventCallback != null) {
                 mTouchEventCallback!!.onSuccess(
                     fulfillmentResponse,
-                    TouchEventMetadata.getDefaultInstance()
+                    TouchEventMetadata.getDefaultInstance(),
                 )
             } else {
                 LoggerInternal.log(
                     CapabilityLogger.LogLevel.ERROR,
                     LOG_TAG,
-                    "Manual input null callback"
+                    "Manual input null callback",
                 )
             }
         } catch (t: Throwable) {
@@ -207,7 +227,7 @@
                 LoggerInternal.log(
                     CapabilityLogger.LogLevel.ERROR,
                     LOG_TAG,
-                    "Manual input null callback"
+                    "Manual input null callback",
                 )
             }
         }
@@ -226,16 +246,18 @@
      */
     @Throws(StructConversionException::class, MissingRequiredArgException::class)
     private suspend fun maybeConfirmOrFinish(): FulfillmentResponse {
-        val finalArguments = currentAcceptedArguments
+        val finalArguments = getCurrentAcceptedArguments()
         if (
             anyParamsOfStatus(CurrentValue.Status.REJECTED) ||
-                !TaskCapabilityUtils.isSlotFillingComplete(finalArguments, appAction.paramsList)
+            !TaskCapabilityUtils.isSlotFillingComplete(finalArguments, appAction.paramsList)
         ) {
             return FulfillmentResponse.getDefaultInstance()
         }
-        return if (taskHandler.onReadyToConfirmListener != null)
+        return if (taskHandler.onReadyToConfirmListener != null) {
             getFulfillmentResponseForConfirmation(finalArguments)
-        else getFulfillmentResponseForExecution(finalArguments)
+        } else {
+            getFulfillmentResponseForExecution(finalArguments)
+        }
     }
 
     private fun maybeInitializeTask() {
@@ -251,17 +273,17 @@
      * Control-flow logic for a single task turn. Note, a task may start and finish in the same
      * turn, so the logic should include onEnter, arg validation, and onExit.
      */
-    private suspend fun handleSync(argumentsWrapper: ArgumentsWrapper, callback: CallbackInternal) {
+    private suspend fun handleSync(argumentsWrapper: ArgumentsWrapper): FulfillmentResult {
         maybeInitializeTask()
         clearMissingArgs(argumentsWrapper)
-        try {
+        return try {
             processFulfillmentValues(argumentsWrapper.paramValues)
             val fulfillmentResponse = maybeConfirmOrFinish()
             LoggerInternal.log(CapabilityLogger.LogLevel.INFO, LOG_TAG, "Task sync success")
-            callback.onSuccess(fulfillmentResponse)
+            FulfillmentResult(fulfillmentResponse)
         } catch (t: Throwable) {
             LoggerInternal.log(CapabilityLogger.LogLevel.ERROR, LOG_TAG, "Task sync fail", t)
-            callback.onError(ErrorStatusInternal.SYNC_REQUEST_FAILURE)
+            FulfillmentResult(ErrorStatusInternal.SYNC_REQUEST_FAILURE)
         }
     }
 
@@ -269,24 +291,26 @@
      * Control-flow logic for a single task turn in which the user has confirmed in the previous
      * turn.
      */
-    private suspend fun handleConfirm(callback: CallbackInternal) {
-        val finalArguments = currentAcceptedArguments
-        try {
+    private suspend fun handleConfirm(): FulfillmentResult {
+        val finalArguments = getCurrentAcceptedArguments()
+        return try {
             val fulfillmentResponse = getFulfillmentResponseForExecution(finalArguments)
             LoggerInternal.log(CapabilityLogger.LogLevel.INFO, LOG_TAG, "Task confirm success")
-            callback.onSuccess(fulfillmentResponse)
+            FulfillmentResult(fulfillmentResponse)
         } catch (t: Throwable) {
             LoggerInternal.log(CapabilityLogger.LogLevel.ERROR, LOG_TAG, "Task confirm fail")
-            callback.onError(ErrorStatusInternal.CONFIRMATION_REQUEST_FAILURE)
+            FulfillmentResult(ErrorStatusInternal.CONFIRMATION_REQUEST_FAILURE)
         }
     }
 
     private fun clearMissingArgs(assistantArgs: ArgumentsWrapper) {
-        val argsCleared =
-            currentValuesMap.keys.filter { !assistantArgs.paramValues.containsKey(it) }
-        for (arg in argsCleared) {
-            currentValuesMap.remove(arg)
-            // TODO(b/234170829): notify listener#onReceived of the cleared arguments
+        valuesMapLock.write {
+            val argsCleared =
+                currentValuesMap.keys.filter { !assistantArgs.paramValues.containsKey(it) }
+            for (arg in argsCleared) {
+                currentValuesMap.remove(arg)
+                // TODO(b/234170829): notify listener#onReceived of the cleared arguments
+            }
         }
     }
 
@@ -303,7 +327,7 @@
         InvalidResolverException::class,
     )
     private suspend fun processFulfillmentValues(
-        fulfillmentValuesMap: Map<String, List<FulfillmentRequest.Fulfillment.FulfillmentValue>>
+        fulfillmentValuesMap: Map<String, List<FulfillmentRequest.Fulfillment.FulfillmentValue>>,
     ) {
         var currentResult = SlotProcessingResult(true, emptyList())
         for ((name, fulfillmentValues) in fulfillmentValuesMap) {
@@ -321,9 +345,10 @@
     private suspend fun maybeProcessSlotAndUpdateCurrentValues(
         previousResult: SlotProcessingResult,
         slotKey: String,
-        newSlotValues: List<FulfillmentRequest.Fulfillment.FulfillmentValue>
+        newSlotValues: List<FulfillmentRequest.Fulfillment.FulfillmentValue>,
     ): SlotProcessingResult {
-        val currentSlotValues = currentValuesMap.getOrDefault(slotKey, emptyList())
+        val currentSlotValues =
+            valuesMapLock.read { currentValuesMap.getOrDefault(slotKey, emptyList()) }
         val modifiedSlotValues =
             TaskCapabilityUtils.getMaybeModifiedSlotValues(currentSlotValues, newSlotValues)
         if (TaskCapabilityUtils.canSkipSlotProcessing(currentSlotValues, modifiedSlotValues)) {
@@ -332,10 +357,10 @@
         val pendingArgs =
             TaskCapabilityUtils.fulfillmentValuesToCurrentValues(
                 modifiedSlotValues,
-                CurrentValue.Status.PENDING
+                CurrentValue.Status.PENDING,
             )
         val currentResult = processSlot(slotKey, previousResult, pendingArgs)
-        currentValuesMap[slotKey] = currentResult.processedValues
+        valuesMapLock.write { currentValuesMap[slotKey] = currentResult.processedValues }
         return currentResult
     }
 
@@ -354,10 +379,13 @@
     private suspend fun processSlot(
         name: String,
         previousResult: SlotProcessingResult,
-        pendingArgs: List<CurrentValue>
+        pendingArgs: List<CurrentValue>,
     ): SlotProcessingResult {
-        return if (!previousResult.isSuccessful) SlotProcessingResult(false, pendingArgs)
-        else TaskSlotProcessor.processSlot(name, pendingArgs, taskHandler.taskParamMap)
+        return if (!previousResult.isSuccessful) {
+            SlotProcessingResult(false, pendingArgs)
+        } else {
+            TaskSlotProcessor.processSlot(name, pendingArgs, taskHandler.taskParamMap)
+        }
     }
 
     /**
@@ -365,36 +393,42 @@
      *
      * A slot is considered accepted if all CurrentValues in the slot has ACCEPTED status.
      */
-    private val currentAcceptedArguments: Map<String, List<ParamValue>>
-        get() =
-            currentValuesMap
-                .filterValues { currentValues ->
+    private fun getCurrentAcceptedArguments(): Map<String, List<ParamValue>> =
+        valuesMapLock
+            .read {
+                currentValuesMap.filterValues { currentValues ->
                     currentValues.all { it.status == CurrentValue.Status.ACCEPTED }
                 }
-                .mapValues { currentValue -> currentValue.value.map { it.value } }
+            }
+            .mapValues { currentValue -> currentValue.value.map { it.value } }
 
     /**
      * Retrieve all ParamValue from pending slots in currentValuesMap.
      *
      * A slot is considered pending if any CurrentValues in the slot has PENDING status.
      */
-    private val currentPendingArguments: Map<String, List<ParamValue>>
-        get() =
-            currentValuesMap
-                .filterValues { currentValues ->
+    private fun getCurrentPendingArguments(): Map<String, List<ParamValue>> =
+        valuesMapLock
+            .read {
+                currentValuesMap.filterValues { currentValues ->
                     currentValues.any { it.status == CurrentValue.Status.PENDING }
                 }
-                .mapValues { currentValues -> currentValues.value.map { it.value } }
+            }
+            .mapValues { currentValues -> currentValues.value.map { it.value } }
 
     /** Returns true if any CurrentValue in currentValuesMap has the given Status. */
     private fun anyParamsOfStatus(status: CurrentValue.Status) =
-        currentValuesMap.values.any { currentValues -> currentValues.any { it.status == status } }
+        valuesMapLock.read {
+            currentValuesMap.values.any { currentValues ->
+                currentValues.any { it.status == status }
+            }
+        }
 
     @Throws(StructConversionException::class, MissingRequiredArgException::class)
     private suspend fun getFulfillmentResponseForConfirmation(
-        finalArguments: Map<String, List<ParamValue>>
+        finalArguments: Map<String, List<ParamValue>>,
     ): FulfillmentResponse {
-        val result = taskHandler.onReadyToConfirmListener!!.onReadyToConfirm(finalArguments).await()
+        val result = taskHandler.onReadyToConfirmListener!!.onReadyToConfirm(finalArguments)
         val fulfillmentResponse = FulfillmentResponse.newBuilder()
         convertToConfirmationOutput(result)?.let { fulfillmentResponse.confirmationData = it }
         return fulfillmentResponse.build()
@@ -402,14 +436,12 @@
 
     @Throws(StructConversionException::class)
     private suspend fun getFulfillmentResponseForExecution(
-        finalArguments: Map<String, List<ParamValue>>
+        finalArguments: Map<String, List<ParamValue>>,
     ): FulfillmentResponse {
-        val result = externalSession.onFinishAsync(actionSpec.buildArgument(finalArguments)).await()
+        val result = externalSession.onFinish(actionSpec.buildArgument(finalArguments))
         status = ActionCapabilitySession.Status.COMPLETED
         val fulfillmentResponse = FulfillmentResponse.newBuilder()
-        convertToExecutionOutput(result)?.let { value: FulfillmentResponse.StructuredOutput? ->
-            fulfillmentResponse.executionOutput = value
-        }
+        convertToExecutionOutput(result)?.let { fulfillmentResponse.executionOutput = it }
         return fulfillmentResponse.build()
     }
 
@@ -418,7 +450,7 @@
      * proto.
      */
     private fun convertToExecutionOutput(
-        executionResult: ExecutionResult<OutputT>
+        executionResult: ExecutionResult<OutputT>,
     ): FulfillmentResponse.StructuredOutput? =
         executionResult.output?.let { actionSpec.convertOutputToProto(it) }
 
@@ -427,19 +459,19 @@
      * proto.
      */
     private fun convertToConfirmationOutput(
-        confirmationOutput: ConfirmationOutput<ConfirmationT>
+        confirmationOutput: ConfirmationOutput<ConfirmationT>,
     ): FulfillmentResponse.StructuredOutput? {
         val confirmation = confirmationOutput.confirmation ?: return null
-        val confirmationOutputBuilder = FulfillmentResponse.StructuredOutput.newBuilder()
-        for ((key, value) in taskHandler.confirmationDataBindings) {
-            confirmationOutputBuilder.addOutputValues(
-                FulfillmentResponse.StructuredOutput.OutputValue.newBuilder()
-                    .setName(key)
-                    .addAllValues(value.apply(confirmation))
-                    .build()
+        return FulfillmentResponse.StructuredOutput.newBuilder()
+            .addAllOutputValues(
+                taskHandler.confirmationDataBindings.entries.map {
+                    FulfillmentResponse.StructuredOutput.OutputValue.newBuilder()
+                        .setName(it.key)
+                        .addAllValues(it.value.invoke(confirmation))
+                        .build()
+                },
             )
-        }
-        return confirmationOutputBuilder.build()
+            .build()
     }
 
     companion object {
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskParamBinding.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskParamBinding.kt
index e24f9fa..d4e68f0 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskParamBinding.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskParamBinding.kt
@@ -15,8 +15,7 @@
  */
 package androidx.appactions.interaction.capabilities.core.task.impl
 
-import androidx.annotation.RestrictTo
-import androidx.appactions.interaction.capabilities.core.impl.converters.DisambigEntityConverter
+import androidx.appactions.interaction.capabilities.core.impl.converters.EntityConverter
 import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter
 import androidx.appactions.interaction.capabilities.core.impl.converters.SearchActionConverter
 import androidx.appactions.interaction.proto.ParamValue
@@ -24,14 +23,14 @@
 /**
  * A binding between a parameter and its Property converter / Argument setter.
  *
- *
-</ValueTypeT> */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-data class TaskParamBinding<ValueTypeT> internal constructor(
+ * </ValueTypeT>
+ */
+internal data class TaskParamBinding<ValueTypeT>
+constructor(
     val name: String,
     val groundingPredicate: (ParamValue) -> Boolean,
     val resolver: GenericResolverInternal<ValueTypeT>,
     val converter: ParamValueConverter<ValueTypeT>,
-    val entityConverter: DisambigEntityConverter<ValueTypeT>?,
+    val entityConverter: EntityConverter<ValueTypeT>?,
     val searchActionConverter: SearchActionConverter<ValueTypeT>?,
-)
\ No newline at end of file
+)
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskSlotProcessor.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskSlotProcessor.kt
index fc41c53..696db9d 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskSlotProcessor.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskSlotProcessor.kt
@@ -15,7 +15,7 @@
  */
 package androidx.appactions.interaction.capabilities.core.task.impl
 
-import androidx.appactions.interaction.capabilities.core.impl.converters.DisambigEntityConverter
+import androidx.appactions.interaction.capabilities.core.impl.converters.EntityConverter
 import androidx.appactions.interaction.capabilities.core.impl.exceptions.StructConversionException
 import androidx.appactions.interaction.capabilities.core.task.EntitySearchResult
 import androidx.appactions.interaction.capabilities.core.task.ValidationResult
@@ -25,7 +25,6 @@
 import androidx.appactions.interaction.proto.CurrentValue
 import androidx.appactions.interaction.proto.DisambiguationData
 import androidx.appactions.interaction.proto.ParamValue
-import androidx.concurrent.futures.await
 import kotlin.IllegalStateException
 import kotlin.String
 import kotlin.Throws
@@ -166,7 +165,7 @@
         updatedValue: List<ParamValue>,
         binding: TaskParamBinding<T>
     ): ValidationResult {
-        return binding.resolver.notifyValueChange(updatedValue, binding.converter).await()
+        return binding.resolver.notifyValueChange(updatedValue, binding.converter)
     }
 
     @Throws(InvalidResolverException::class)
@@ -175,7 +174,7 @@
         binding: TaskParamBinding<*>
     ) {
         val entityIds = disambiguationData.entitiesList.map { it.identifier }
-        binding.resolver.invokeEntityRender(entityIds).await()
+        binding.resolver.invokeEntityRender(entityIds)
     }
 
     /**
@@ -237,12 +236,8 @@
         val entityConverter = binding.entityConverter
         val searchActionConverter = binding.searchActionConverter
         val searchAction = searchActionConverter.toSearchAction(ungroundedParamValue)
-        val entitySearchResult = binding.resolver.invokeLookup(searchAction).await()
-        return processEntitySearchResult<T>(
-            entitySearchResult,
-            entityConverter,
-            ungroundedParamValue
-        )
+        val entitySearchResult = binding.resolver.invokeLookup(searchAction)
+        return processEntitySearchResult(entitySearchResult, entityConverter, ungroundedParamValue)
     }
 
     /**
@@ -254,7 +249,7 @@
     @Throws(StructConversionException::class)
     private fun <T> processEntitySearchResult(
         entitySearchResult: EntitySearchResult<T>,
-        entityConverter: DisambigEntityConverter<T>,
+        entityConverter: EntityConverter<T>,
         ungroundedValue: ParamValue
     ): AppGroundingResult {
         return when (entitySearchResult.possibleValues.size) {
@@ -273,7 +268,7 @@
             }
             else -> {
                 val disambigEntities =
-                    entitySearchResult.possibleValues.map { entityConverter.convert(it) }
+                    entitySearchResult.possibleValues.map { entityConverter.convert(it!!) }
                 AppGroundingResult.ofFailure(
                     TaskCapabilityUtils.getCurrentValueForDisambiguation(
                         ungroundedValue,
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TouchEventUpdateRequest.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TouchEventUpdateRequest.kt
index d7b9a5c..46731ea 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TouchEventUpdateRequest.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/TouchEventUpdateRequest.kt
@@ -13,19 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package androidx.appactions.interaction.capabilities.core.task.impl
 
-import androidx.annotation.RestrictTo
 import androidx.appactions.interaction.proto.ParamValue
 
-/**
- * Represents a fulfillment request coming from user tap.
- *
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-data class TouchEventUpdateRequest(val paramValuesMap: Map<String, List<ParamValue>>) {
+/** Represents a fulfillment request coming from user tap. */
+internal data class TouchEventUpdateRequest(val paramValuesMap: Map<String, List<ParamValue>>) {
     /**
      * merge two TouchEventUpdateRequest instances. Map entries in newRequest will take priority in
      * case of conflict.
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/UpdateRequest.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/UpdateRequest.kt
index 60c3816..e189b7b 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/UpdateRequest.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/impl/UpdateRequest.kt
@@ -15,15 +15,8 @@
  */
 package androidx.appactions.interaction.capabilities.core.task.impl
 
-import androidx.annotation.RestrictTo
-
-/**
- * Contains either an AssistantUpdateRequest or a TouchEventUpdateRequest
- *
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-class UpdateRequest
+/** Contains either an [AssistantUpdateRequest] or a [TouchEventUpdateRequest] */
+internal class UpdateRequest
 private constructor(
     val assistantRequest: AssistantUpdateRequest?,
     val touchEventRequest: TouchEventUpdateRequest?,
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/package-info.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/package-info.java
new file mode 100644
index 0000000..838a123
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/task/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/** @hide */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+package androidx.appactions.interaction.capabilities.core.task;
+
+import androidx.annotation.RestrictTo;
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/values/StringOrEnumValue.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/values/StringOrEnumValue.java
deleted file mode 100644
index 2c41ecb..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/values/StringOrEnumValue.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.values;
-
-import androidx.annotation.NonNull;
-
-import com.google.auto.value.AutoOneOf;
-
-/**
- * Represents a string or enum argument value for {@code ActionCapability}.
- *
- * @param <EnumT>
- */
-@AutoOneOf(StringOrEnumValue.Kind.class)
-public abstract class StringOrEnumValue<EnumT extends Enum<EnumT>> {
-    /** Creates a StringOrEnumValue instance with the given String. */
-    @NonNull
-    public static <EnumT extends Enum<EnumT>> StringOrEnumValue<EnumT> ofStringValue(
-            @NonNull String s) {
-        return AutoOneOf_StringOrEnumValue.stringValue(s);
-    }
-
-    /** Creates a StringOrEnumValue instance with the given Enum value. */
-    @NonNull
-    public static <EnumT extends Enum<EnumT>> StringOrEnumValue<EnumT> ofEnumValue(
-            @NonNull EnumT enumValue) {
-        return AutoOneOf_StringOrEnumValue.enumValue(enumValue);
-    }
-
-    /** The Kind of this StringOrEnumValue. */
-    @NonNull
-    public abstract Kind getKind();
-
-    /** The String value of this StringOrEnumValue, for Kind.STRING_VALUE. */
-    @NonNull
-    public abstract String stringValue();
-
-    /** The Enum value of this StringOrEnumValue, for Kind.ENUM_VALUE. */
-    @NonNull
-    public abstract EnumT enumValue();
-
-    /** Possible argument type. */
-    public enum Kind {
-        STRING_VALUE,
-        ENUM_VALUE,
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/values/package-info.java b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/values/package-info.java
new file mode 100644
index 0000000..dcdff02
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/core/values/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/** @hide */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+package androidx.appactions.interaction.capabilities.core.values;
+
+import androidx.annotation.RestrictTo;
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/entity/EntityProviderTest.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/entity/EntityProviderTest.kt
new file mode 100644
index 0000000..97a1293
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/entity/EntityProviderTest.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.entity
+
+import androidx.appactions.interaction.capabilities.core.values.Order
+import androidx.appactions.interaction.proto.Entity
+import androidx.appactions.interaction.proto.GroundingRequest
+import androidx.appactions.interaction.proto.GroundingResponse
+import androidx.appactions.interaction.proto.ParamValue
+import androidx.appactions.interaction.protobuf.ByteString
+import androidx.appactions.interaction.protobuf.Struct
+import androidx.appactions.interaction.protobuf.Value
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+private val VALID_GROUNDING_REQUEST = GroundingRequest.newBuilder()
+    .setRequest(
+        GroundingRequest.Request.newBuilder()
+            .setSearchAction(
+                ParamValue.newBuilder()
+                    .setStructValue(
+                        Struct.newBuilder()
+                            .putFields(
+                                "@type",
+                                Value.newBuilder().setStringValue("SearchAction").build()
+                            )
+                            .putFields(
+                                "object",
+                                Value.newBuilder()
+                                    .setStructValue(
+                                        Struct.newBuilder()
+                                            .putFields(
+                                                "@type",
+                                                Value.newBuilder()
+                                                    .setStringValue("Order")
+                                                    .build()
+                                            )
+                                    )
+                                    .build()
+                            )
+                    )
+            )
+    )
+    .build()
+
+@RunWith(JUnit4::class)
+class EntityProviderTest {
+    private fun createExternalResponse(
+        candidateList: List<EntityLookupCandidate<Order>>,
+        status: Int
+    ): EntityLookupResponse<Order> {
+        return EntityLookupResponse.Builder<Order>()
+            .setCandidateList(candidateList)
+            .setStatus(status)
+            .setNextPageToken(ByteString.EMPTY)
+            .build()
+    }
+
+    private fun createInternalResponse(
+        candidateList: List<GroundingResponse.Candidate>,
+        status: GroundingResponse.Status
+    ): GroundingResponse? {
+        return GroundingResponse.newBuilder().setResponse(
+            GroundingResponse.Response.newBuilder().addAllCandidates(candidateList)
+                .setStatus(status).build()
+        ).build()
+    }
+
+    private fun createExternalCandidate(id: String, name: String): EntityLookupCandidate<Order> {
+        val candidateBuilder: EntityLookupCandidate.Builder<Order> =
+            EntityLookupCandidate.Builder()
+        candidateBuilder.setCandidate(Order.newBuilder().setName(name).setId(id).build())
+        return candidateBuilder.build()
+    }
+
+    private fun createInternalCandidate(id: String, name: String): GroundingResponse.Candidate {
+        return GroundingResponse.Candidate.newBuilder()
+            .setGroundedEntity(
+                Entity.newBuilder()
+                    .setValue(
+                        Struct.newBuilder()
+                            .putFields("@type", Value.newBuilder().setStringValue("Order").build())
+                            .putFields("identifier", Value.newBuilder().setStringValue(id).build())
+                            .putFields("name", Value.newBuilder().setStringValue(name).build())
+                    )
+            )
+            .build()
+    }
+
+    @Test
+    fun invalidEntity_returnError() {
+        val entityProvider = OrderProvider(
+            "id", createExternalResponse(
+                listOf(), EntityLookupResponse.SUCCESS
+            )
+        )
+
+        val response: GroundingResponse? =
+            entityProvider.lookupInternal(GroundingRequest.getDefaultInstance())
+
+        assertThat(response)
+            .isEqualTo(
+                createInternalResponse(
+                    listOf(), GroundingResponse.Status.INVALID_ENTITY_ARGUMENT
+                )
+            )
+    }
+
+    @Test
+    fun errorInExternalResponse_returnError() {
+        val entityProvider = OrderProvider(
+            "id", createExternalResponse(
+                listOf(), EntityLookupResponse.CANCELED
+            )
+        )
+
+        val response: GroundingResponse? = entityProvider.lookupInternal(VALID_GROUNDING_REQUEST)
+
+        assertThat(response)
+            .isEqualTo(createInternalResponse(listOf(), GroundingResponse.Status.CANCELED))
+    }
+
+    @Test
+    fun success() {
+        val candidateBuilder: EntityLookupCandidate.Builder<Order> =
+            EntityLookupCandidate.Builder()
+        candidateBuilder.setCandidate(Order.newBuilder().setName("testing-order").build())
+        val entityProvider = OrderProvider(
+            "id", createExternalResponse(
+                listOf(
+                    createExternalCandidate("id-1", "name-1"),
+                    createExternalCandidate("id-2", "name-2")
+                ),
+                EntityLookupResponse.SUCCESS
+            )
+        )
+
+        val response: GroundingResponse? = entityProvider.lookupInternal(VALID_GROUNDING_REQUEST)
+
+        assertThat(response)
+            .isEqualTo(
+                createInternalResponse(
+                    listOf(
+                        createInternalCandidate("id-1", "name-1"),
+                        createInternalCandidate("id-2", "name-2")
+                    ),
+                    GroundingResponse.Status.SUCCESS
+                )
+            )
+    }
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/entity/OrderProvider.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/entity/OrderProvider.kt
new file mode 100644
index 0000000..749872e
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/entity/OrderProvider.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.entity
+
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
+import androidx.appactions.interaction.capabilities.core.values.Order
+
+/**  Internal testing object for entity provider */
+class OrderProvider internal constructor(
+    private var id: String,
+    private var response: EntityLookupResponse<Order>
+) : EntityProvider<Order>(TypeConverters.ORDER_TYPE_SPEC) {
+    override fun getId(): String = id
+    override fun lookup(request: EntityLookupRequest<Order>): EntityLookupResponse<Order> = response
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityTest.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityTest.kt
index aeabac6..6d6ddf2 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityTest.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/SingleTurnCapabilityTest.kt
@@ -17,20 +17,21 @@
 package androidx.appactions.interaction.capabilities.core.impl
 
 import android.util.SizeF
-import androidx.appactions.interaction.capabilities.core.ActionCapability
 import androidx.appactions.interaction.capabilities.core.ActionExecutor
 import androidx.appactions.interaction.capabilities.core.ActionExecutorAsync
 import androidx.appactions.interaction.capabilities.core.ActionExecutorAsync.Companion.toActionExecutorAsync
 import androidx.appactions.interaction.capabilities.core.ExecutionResult
 import androidx.appactions.interaction.capabilities.core.HostProperties
 import androidx.appactions.interaction.capabilities.core.impl.concurrent.Futures
+import androidx.appactions.interaction.capabilities.core.impl.converters.PropertyConverter
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.EntityProperty
-import androidx.appactions.interaction.capabilities.core.properties.StringProperty
+import androidx.appactions.interaction.capabilities.core.properties.Entity
+import androidx.appactions.interaction.capabilities.core.properties.StringValue
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty
 import androidx.appactions.interaction.capabilities.core.testing.ArgumentUtils
-import androidx.appactions.interaction.capabilities.core.testing.buildCallbackInternalWithChannel
+import androidx.appactions.interaction.capabilities.core.testing.FakeCallbackInternal
 import androidx.appactions.interaction.capabilities.core.testing.TestingUtils.CB_TIMEOUT
 import androidx.appactions.interaction.capabilities.core.testing.spec.Argument
 import androidx.appactions.interaction.capabilities.core.testing.spec.Output
@@ -40,89 +41,126 @@
 import androidx.appactions.interaction.proto.FulfillmentResponse.StructuredOutput.OutputValue
 import androidx.appactions.interaction.proto.ParamValue
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.withTimeout
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
 @RunWith(JUnit4::class)
 class SingleTurnCapabilityTest {
-    val hostProperties = HostProperties.Builder().setMaxHostSizeDp(SizeF(300f, 500f)).build()
+    private val hostProperties =
+        HostProperties.Builder().setMaxHostSizeDp(SizeF(300f, 500f)).build()
 
     @Test
     fun oneShotCapability_successWithOutput() {
-        val actionExecutor = object : ActionExecutor<Argument, Output> {
-            override suspend fun execute(argument: Argument): ExecutionResult<Output> =
-                ExecutionResult.Builder<Output>().setOutput(
-                    Output.builder().setOptionalStringField("stringOutput")
+        val actionExecutor =
+            ActionExecutor<Argument, Output> {
+                ExecutionResult.Builder<Output>()
+                    .setOutput(
+                        Output.builder().setOptionalStringField("stringOutput").build(),
+                    )
+                    .build()
+            }
+        val capability =
+            SingleTurnCapabilityImpl(
+                id = "capabilityId",
+                actionSpec = ACTION_SPEC,
+                property =
+                    Property.newBuilder()
+                        .setRequiredEntityField(
+                            TypeProperty.Builder<Entity>().build(),
+                        )
+                        .setOptionalStringField(
+                            TypeProperty.Builder<StringValue>().setProhibited(true).build(),
+                        )
                         .build(),
-                ).build()
-        }
-        val capability: ActionCapability =
-            SingleTurnCapabilityImpl<Property, Argument, Output>(
-                "capabilityId",
-                ACTION_SPEC,
-                Property.newBuilder().setRequiredEntityField(
-                    EntityProperty.Builder().build(),
-                ).setOptionalStringField(
-                    StringProperty.Builder().setProhibited(true).build(),
-                ).build(),
-                actionExecutor.toActionExecutorAsync(),
+                actionExecutorAsync = actionExecutor.toActionExecutorAsync(),
             )
-        val expectedFulfillmentResponse: FulfillmentResponse =
-            FulfillmentResponse.newBuilder().setExecutionOutput(
-                StructuredOutput.newBuilder()
-                    .addOutputValues(
-                        OutputValue.newBuilder()
-                            .setName("optionalStringOutput")
-                            .addValues(
-                                ParamValue.newBuilder()
-                                    .setStringValue("stringOutput")
+
+        val capabilitySession = capability.createSession(hostProperties)
+        val callbackInternal = FakeCallbackInternal(CB_TIMEOUT)
+        capabilitySession.execute(
+            ArgumentUtils.buildArgs(
+                mapOf(
+                    "optionalString" to
+                        ParamValue.newBuilder().setIdentifier("string argument value").build(),
+                ),
+            ),
+            callbackInternal,
+        )
+
+        val response = callbackInternal.receiveResponse()
+        assertThat(response.fulfillmentResponse).isNotNull()
+        assertThat(response.fulfillmentResponse)
+            .isEqualTo(
+                FulfillmentResponse.newBuilder()
+                    .setExecutionOutput(
+                        StructuredOutput.newBuilder()
+                            .addOutputValues(
+                                OutputValue.newBuilder()
+                                    .setName("optionalStringOutput")
+                                    .addValues(
+                                        ParamValue.newBuilder()
+                                            .setStringValue("stringOutput")
+                                            .build(),
+                                    )
                                     .build(),
                             )
                             .build(),
                     )
-                    .build(),
-            ).build()
+                    .build()
+            )
+    }
+    @Test
+    fun oneShotCapability_failure() {
+        val actionExecutor = ActionExecutor<Argument, Output> { throw IllegalStateException("") }
+        val capability =
+            SingleTurnCapabilityImpl(
+                id = "capabilityId",
+                actionSpec = ACTION_SPEC,
+                property =
+                    Property.newBuilder()
+                        .setRequiredEntityField(
+                            TypeProperty.Builder<Entity>().build(),
+                        )
+                        .setOptionalStringField(
+                            TypeProperty.Builder<StringValue>().setProhibited(true).build(),
+                        )
+                        .build(),
+                actionExecutorAsync = actionExecutor.toActionExecutorAsync(),
+            )
 
         val capabilitySession = capability.createSession(hostProperties)
-        val responseChannel = Channel<FulfillmentResponse>(1)
+        val callbackInternal = FakeCallbackInternal(CB_TIMEOUT)
         capabilitySession.execute(
             ArgumentUtils.buildArgs(
                 mapOf(
-                    "optionalString" to ParamValue.newBuilder().setIdentifier(
-                        "string argument value",
-                    ).build(),
+                    "optionalString" to
+                        ParamValue.newBuilder().setIdentifier("string argument value").build(),
                 ),
             ),
-            buildCallbackInternalWithChannel(responseChannel, CB_TIMEOUT),
+            callbackInternal,
         )
 
-        runBlocking {
-            withTimeout(CB_TIMEOUT) {
-                assertThat(
-                    responseChannel.receive(),
-                ).isEqualTo(expectedFulfillmentResponse)
-            }
-        }
+        val response = callbackInternal.receiveResponse()
+        assertThat(response.errorStatus).isNotNull()
+        assertThat(response.errorStatus).isEqualTo(ErrorStatusInternal.CANCELLED)
     }
 
     @Test
     fun oneShotSession_uiHandle_withActionExecutor() {
         val actionExecutor =
-            ActionExecutor<Argument, Output> {
-                ExecutionResult.getDefaultInstance()
-            }
-        val capability: ActionCapability =
-            SingleTurnCapabilityImpl<Property, Argument, Output>(
-                "capabilityId",
-                ACTION_SPEC,
-                Property.newBuilder().setRequiredEntityField(
-                    EntityProperty.Builder().build(),
-                ).build(),
-                actionExecutor.toActionExecutorAsync(),
+            ActionExecutor<Argument, Output> { ExecutionResult.getDefaultInstance() }
+        val capability =
+            SingleTurnCapabilityImpl(
+                id = "capabilityId",
+                actionSpec = ACTION_SPEC,
+                property =
+                    Property.newBuilder()
+                        .setRequiredEntityField(
+                            TypeProperty.Builder<Entity>().build(),
+                        )
+                        .build(),
+                actionExecutorAsync = actionExecutor.toActionExecutorAsync(),
             )
         val session = capability.createSession(hostProperties)
         assertThat(session.uiHandle).isSameInstanceAs(actionExecutor)
@@ -130,17 +168,21 @@
 
     @Test
     fun oneShotSession_uiHandle_withActionExecutorAsync() {
-        val actionExecutorAsync = ActionExecutorAsync<Argument, Output> {
-            Futures.immediateFuture(ExecutionResult.getDefaultInstance())
-        }
-        val capability: ActionCapability =
-            SingleTurnCapabilityImpl<Property, Argument, Output>(
-                "capabilityId",
-                ACTION_SPEC,
-                Property.newBuilder().setRequiredEntityField(
-                    EntityProperty.Builder().build(),
-                ).build(),
-                actionExecutorAsync,
+        val actionExecutorAsync =
+            ActionExecutorAsync<Argument, Output> {
+                Futures.immediateFuture(ExecutionResult.getDefaultInstance())
+            }
+        val capability =
+            SingleTurnCapabilityImpl(
+                id = "capabilityId",
+                actionSpec = ACTION_SPEC,
+                property =
+                    Property.newBuilder()
+                        .setRequiredEntityField(
+                            TypeProperty.Builder<Entity>().build(),
+                        )
+                        .build(),
+                actionExecutorAsync = actionExecutorAsync,
             )
         val session = capability.createSession(hostProperties)
         assertThat(session.uiHandle).isSameInstanceAs(actionExecutorAsync)
@@ -149,20 +191,23 @@
     companion object {
         val ACTION_SPEC: ActionSpec<Property, Argument, Output> =
             ActionSpecBuilder.ofCapabilityNamed(
-                "actions.intent.TEST",
-            )
+                    "actions.intent.TEST",
+                )
                 .setDescriptor(Property::class.java)
                 .setArgument(Argument::class.java, Argument::newBuilder)
                 .setOutput(Output::class.java)
-                .bindOptionalStringParameter(
+                .bindOptionalParameter(
                     "optionalString",
                     Property::optionalStringField,
                     Argument.Builder::setOptionalStringField,
+                    TypeConverters::toStringValue,
+                    PropertyConverter::stringValueToProto
                 )
                 .bindOptionalOutput(
                     "optionalStringOutput",
                     Output::optionalStringField,
                     TypeConverters::toParamValue,
-                ).build()
+                )
+                .build()
     }
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/concurrent/ListenableFutureHelperTest.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/concurrent/ListenableFutureHelperTest.kt
index 310078e..df2f2ac 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/concurrent/ListenableFutureHelperTest.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/concurrent/ListenableFutureHelperTest.kt
@@ -22,6 +22,7 @@
 import kotlinx.coroutines.channels.SendChannel
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withTimeout
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
@@ -49,6 +50,7 @@
         assertThat(stringFuture.get()).isEqualTo("hello")
     }
 
+    @Ignore // b/272659961
     @Test
     fun suspendToListenableFuture_cancellationTest() {
         val stringChannel = Channel<String>(1)
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecImplTest.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecImplTest.java
index 6a09135..648cb5e 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecImplTest.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/converters/TypeSpecImplTest.java
@@ -38,6 +38,20 @@
 public final class TypeSpecImplTest {
 
     @Test
+    public void bindIdentifier_success() {
+        TypeSpec<TestEntity> entityTypeSpec =
+                TypeSpecBuilder.newBuilder("TestEntity", TestEntity::newBuilder)
+                        .bindIdentifier(TestEntity::getId)
+                        .build();
+        assertThat(entityTypeSpec.getIdentifier(
+                TestEntity.newBuilder().setId("identifier1").build()
+        )).isEqualTo("identifier1");
+        assertThat(entityTypeSpec.getIdentifier(
+                TestEntity.newBuilder().build()
+        )).isNull();
+    }
+
+    @Test
     public void bindEnumField_convertsSuccessfully() throws Exception {
         TypeSpec<TestEntity> entityTypeSpec =
                 TypeSpecBuilder.newBuilder("TestEntity", TestEntity::newBuilder)
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/converters/UnionTypeSpecTest.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/converters/UnionTypeSpecTest.kt
new file mode 100644
index 0000000..cad1967
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/converters/UnionTypeSpecTest.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.impl.converters
+
+import androidx.appactions.interaction.capabilities.core.values.Alarm
+import androidx.appactions.interaction.capabilities.core.values.Timer
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+/* Union type for testing */
+class AlarmOrTimer private constructor(
+  val asAlarm: Alarm? = null,
+  val asTimer: Timer? = null,
+) {
+  constructor(alarm: Alarm) : this(asAlarm = alarm)
+  constructor(timer: Timer) : this(asTimer = timer)
+}
+
+private val ALARM_OR_TIMER_TYPE_SPEC = UnionTypeSpec.Builder<AlarmOrTimer>()
+  .bindMemberType(
+    memberGetter = AlarmOrTimer::asAlarm,
+    ctor = { AlarmOrTimer(it) },
+    typeSpec = TypeConverters.ALARM_TYPE_SPEC,
+  )
+  .bindMemberType(
+    memberGetter = AlarmOrTimer::asTimer,
+    ctor = { AlarmOrTimer(it) },
+    typeSpec = TypeConverters.TIMER_TYPE_SPEC,
+  ).build()
+
+@RunWith(JUnit4::class)
+class UnionTypeSpecTest {
+  @Test
+  fun unionType_identifier() {
+    val alarmUnion = AlarmOrTimer(Alarm.newBuilder().setId("alarmId").build())
+    val timerUnion = AlarmOrTimer(Timer.newBuilder().setId("timerId").build())
+    val timerUnionWithoutId = AlarmOrTimer(Timer.newBuilder().build())
+    assertThat(ALARM_OR_TIMER_TYPE_SPEC.getIdentifier(alarmUnion)).isEqualTo("alarmId")
+    assertThat(ALARM_OR_TIMER_TYPE_SPEC.getIdentifier(timerUnion)).isEqualTo("timerId")
+    assertThat(ALARM_OR_TIMER_TYPE_SPEC.getIdentifier(timerUnionWithoutId)).isNull()
+  }
+}
\ No newline at end of file
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecTest.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecTest.java
index f012e395..13f5ff0 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecTest.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/spec/ActionSpecTest.java
@@ -20,11 +20,13 @@
 
 import androidx.annotation.NonNull;
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
+import androidx.appactions.interaction.capabilities.core.impl.converters.EntityConverter;
+import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter;
+import androidx.appactions.interaction.capabilities.core.impl.converters.PropertyConverter;
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters;
 import androidx.appactions.interaction.capabilities.core.properties.Entity;
-import androidx.appactions.interaction.capabilities.core.properties.EntityProperty;
-import androidx.appactions.interaction.capabilities.core.properties.EnumProperty;
-import androidx.appactions.interaction.capabilities.core.properties.StringProperty;
+import androidx.appactions.interaction.capabilities.core.properties.StringValue;
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty;
 import androidx.appactions.interaction.capabilities.core.testing.spec.Output;
 import androidx.appactions.interaction.capabilities.core.values.EntityValue;
 import androidx.appactions.interaction.proto.AppActionsContext.AppAction;
@@ -50,35 +52,42 @@
                     .setDescriptor(Property.class)
                     .setArgument(Argument.class, Argument::newBuilder)
                     .setOutput(Output.class)
-                    .bindRequiredEntityParameter(
+                    .bindParameter(
                             "requiredEntity",
                             Property::requiredEntityField,
-                            Argument.Builder::setRequiredEntityField)
-                    .bindOptionalEntityParameter(
+                            Argument.Builder::setRequiredEntityField,
+                            TypeConverters::toEntityValue,
+                            PropertyConverter::entityToProto)
+                    .bindOptionalParameter(
                             "optionalEntity",
                             Property::optionalEntityField,
-                            Argument.Builder::setOptionalEntityField)
-                    .bindOptionalEnumParameter(
-                            "optionalEnum",
-                            TestEnum.class,
-                            Property::optionalEnumField,
-                            Argument.Builder::setOptionalEnumField)
-                    .bindRepeatedEntityParameter(
+                            Argument.Builder::setOptionalEntityField,
+                            TypeConverters::toEntityValue,
+                            PropertyConverter::entityToProto)
+                    .bindRepeatedParameter(
                             "repeatedEntity",
                             Property::repeatedEntityField,
-                            Argument.Builder::setRepeatedEntityField)
-                    .bindRequiredStringParameter(
+                            Argument.Builder::setRepeatedEntityField,
+                            TypeConverters::toEntityValue,
+                            PropertyConverter::entityToProto)
+                    .bindParameter(
                             "requiredString",
                             Property::requiredStringField,
-                            Argument.Builder::setRequiredStringField)
-                    .bindOptionalStringParameter(
+                            Argument.Builder::setRequiredStringField,
+                            TypeConverters::toStringValue,
+                            PropertyConverter::stringValueToProto)
+                    .bindOptionalParameter(
                             "optionalString",
                             Property::optionalStringField,
-                            Argument.Builder::setOptionalStringField)
-                    .bindRepeatedStringParameter(
+                            Argument.Builder::setOptionalStringField,
+                            TypeConverters::toStringValue,
+                            PropertyConverter::stringValueToProto)
+                    .bindRepeatedParameter(
                             "repeatedString",
                             Property::repeatedStringField,
-                            Argument.Builder::setRepeatedStringField)
+                            Argument.Builder::setRepeatedStringField,
+                            TypeConverters::toStringValue,
+                            PropertyConverter::stringValueToProto)
                     .bindOptionalOutput(
                             "optionalStringOutput",
                             Output::optionalStringField,
@@ -88,13 +97,99 @@
                             Output::repeatedStringField,
                             TypeConverters::toParamValue)
                     .build();
+    private static final ParamValueConverter<String> STRING_PARAM_VALUE_CONVERTER =
+            (paramValue) -> "test";
+    private static final EntityConverter<String> STRING_ENTITY_CONVERTER =
+            (theString) ->
+                    androidx.appactions.interaction.proto.Entity.newBuilder()
+                            .setIdentifier(theString)
+                            .setName(theString)
+                            .build();
+
+    private static final ActionSpec<GenericEntityProperty, GenericEntityArgument, Output>
+            GENERIC_TYPES_ACTION_SPEC =
+                    ActionSpecBuilder.ofCapabilityNamed("actions.intent.TEST")
+                            .setDescriptor(GenericEntityProperty.class)
+                            .setArgument(
+                                    GenericEntityArgument.class, GenericEntityArgument::newBuilder)
+                            .setOutput(Output.class)
+                            .bindParameter(
+                                    "requiredEntity",
+                                    GenericEntityProperty::singularField,
+                                    GenericEntityArgument.Builder::setSingularField,
+                                    STRING_PARAM_VALUE_CONVERTER,
+                                    STRING_ENTITY_CONVERTER)
+                            .bindOptionalParameter("optionalEntity",
+                                    GenericEntityProperty::optionalField,
+                                    GenericEntityArgument.Builder::setOptionalField,
+                                    STRING_PARAM_VALUE_CONVERTER,
+                                    STRING_ENTITY_CONVERTER)
+                            .bindRepeatedParameter("repeatedEntities",
+                                    GenericEntityProperty::repeatedField,
+                                    GenericEntityArgument.Builder::setRepeatedField,
+                                    STRING_PARAM_VALUE_CONVERTER,
+                                    STRING_ENTITY_CONVERTER)
+                            .build();
+
+    @Test
+    public void getAppAction_genericParameters() {
+        GenericEntityProperty property =
+                GenericEntityProperty.create(
+                        new TypeProperty.Builder<String>()
+                                .setRequired(true)
+                                .setPossibleValues("one")
+                                .build(),
+                        Optional.of(
+                                new TypeProperty.Builder<String>()
+                                        .setRequired(true)
+                                        .setPossibleValues("two")
+                                        .build()),
+                        Optional.of(
+                                new TypeProperty.Builder<String>()
+                                        .setRequired(true)
+                                        .setPossibleValues("three")
+                                        .build()));
+
+        assertThat(GENERIC_TYPES_ACTION_SPEC.convertPropertyToProto(property))
+                .isEqualTo(
+                        AppAction.newBuilder()
+                                .setName("actions.intent.TEST")
+                                .addParams(
+                                        IntentParameter.newBuilder()
+                                                .setName("requiredEntity")
+                                                .setIsRequired(true)
+                                                .addPossibleEntities(
+                                                        androidx.appactions.interaction.proto.Entity
+                                                                .newBuilder()
+                                                                .setIdentifier("one")
+                                                                .setName("one")))
+                                .addParams(
+                                        IntentParameter.newBuilder()
+                                                .setName("optionalEntity")
+                                                .setIsRequired(true)
+                                                .addPossibleEntities(
+                                                        androidx.appactions.interaction.proto.Entity
+                                                                .newBuilder()
+                                                                .setIdentifier("two")
+                                                                .setName("two")))
+                                .addParams(
+                                        IntentParameter.newBuilder()
+                                                .setName("repeatedEntities")
+                                                .setIsRequired(true)
+                                                .addPossibleEntities(
+                                                        androidx.appactions.interaction.proto.Entity
+                                                                .newBuilder()
+                                                                .setIdentifier("three")
+                                                                .setName("three")))
+                                .build());
+    }
 
     @Test
     public void getAppAction_onlyRequiredProperty() {
         Property property =
                 Property.create(
-                        new EntityProperty.Builder()
-                                .addPossibleEntities(
+                        new TypeProperty.Builder<Entity>()
+                                .setPossibleValues(
                                         new Entity.Builder()
                                                 .setId("contact_2")
                                                 .setName("Donald")
@@ -102,7 +197,7 @@
                                                 .build())
                                 .setValueMatchRequired(true)
                                 .build(),
-                        new StringProperty.Builder().build());
+                        new TypeProperty.Builder<StringValue>().build());
 
         assertThat(ACTION_SPEC.convertPropertyToProto(property))
                 .isEqualTo(
@@ -128,8 +223,8 @@
     public void getAppAction_allProperties() {
         Property property =
                 Property.create(
-                        new EntityProperty.Builder()
-                                .addPossibleEntities(
+                        new TypeProperty.Builder<Entity>()
+                                .setPossibleValues(
                                         new Entity.Builder()
                                                 .setId("contact_2")
                                                 .setName("Donald")
@@ -137,8 +232,8 @@
                                                 .build())
                                 .build(),
                         Optional.of(
-                                new EntityProperty.Builder()
-                                        .addPossibleEntities(
+                                new TypeProperty.Builder<Entity>()
+                                        .setPossibleValues(
                                                 new Entity.Builder()
                                                         .setId("entity1")
                                                         .setName("optional possible entity")
@@ -146,13 +241,13 @@
                                         .setRequired(true)
                                         .build()),
                         Optional.of(
-                                new EnumProperty.Builder<TestEnum>(TestEnum.class)
-                                        .addSupportedEnumValues(TestEnum.VALUE_1)
+                                new TypeProperty.Builder<TestEnum>()
+                                        .setPossibleValues(TestEnum.VALUE_1)
                                         .setRequired(true)
                                         .build()),
                         Optional.of(
-                                new EntityProperty.Builder()
-                                        .addPossibleEntities(
+                                new TypeProperty.Builder<Entity>()
+                                        .setPossibleValues(
                                                 new Entity.Builder()
                                                         .setId("entity1")
                                                         .setName("repeated entity1")
@@ -163,14 +258,17 @@
                                                         .build())
                                         .setRequired(true)
                                         .build()),
-                        new StringProperty.Builder().build(),
+                        new TypeProperty.Builder<StringValue>().build(),
                         Optional.of(
-                                new StringProperty.Builder()
-                                        .addPossibleValue("value1")
+                                new TypeProperty.Builder<StringValue>()
+                                        .setPossibleValues(StringValue.of("value1"))
                                         .setValueMatchRequired(true)
                                         .setRequired(true)
                                         .build()),
-                        Optional.of(new StringProperty.Builder().setProhibited(true).build()));
+                        Optional.of(
+                                new TypeProperty.Builder<StringValue>()
+                                        .setProhibited(true)
+                                        .build()));
 
         assertThat(ACTION_SPEC.convertPropertyToProto(property))
                 .isEqualTo(
@@ -201,17 +299,6 @@
                                                 .setEntityMatchRequired(false))
                                 .addParams(
                                         IntentParameter.newBuilder()
-                                                .setName("optionalEnum")
-                                                .addPossibleEntities(
-                                                        androidx.appactions.interaction.proto.Entity
-                                                                .newBuilder()
-                                                                .setIdentifier(
-                                                                        TestEnum.VALUE_1.toString())
-                                                                .build())
-                                                .setIsRequired(true)
-                                                .setEntityMatchRequired(true))
-                                .addParams(
-                                        IntentParameter.newBuilder()
                                                 .setName("repeatedEntity")
                                                 .addPossibleEntities(
                                                         androidx.appactions.interaction.proto.Entity
@@ -247,7 +334,6 @@
     }
 
     @Test
-    @SuppressWarnings("JdkImmutableCollections")
     public void convertOutputToProto_string() {
         Output output =
                 Output.builder()
@@ -286,7 +372,6 @@
     }
 
     @Test
-    @SuppressWarnings("JdkImmutableCollections")
     public void convertOutputToProto_emptyOutput() {
         Output output = Output.builder().setRepeatedStringField(List.of("test3", "test4")).build();
         // No optionalStringOutput since it is not in the output above.
@@ -328,8 +413,6 @@
 
         abstract EntityValue optionalEntityField();
 
-        abstract TestEnum optionalEnumField();
-
         abstract List<EntityValue> repeatedEntityField();
 
         abstract String requiredStringField();
@@ -345,8 +428,6 @@
 
             abstract Builder setOptionalEntityField(EntityValue value);
 
-            abstract Builder setOptionalEnumField(TestEnum value);
-
             abstract Builder setRepeatedEntityField(List<EntityValue> repeated);
 
             abstract Builder setRequiredStringField(String value);
@@ -365,13 +446,13 @@
     abstract static class Property {
 
         static Property create(
-                EntityProperty requiredEntityField,
-                Optional<EntityProperty> optionalEntityField,
-                Optional<EnumProperty<TestEnum>> optionalEnumField,
-                Optional<EntityProperty> repeatedEntityField,
-                StringProperty requiredStringField,
-                Optional<StringProperty> optionalStringField,
-                Optional<StringProperty> repeatedStringField) {
+                TypeProperty<Entity> requiredEntityField,
+                Optional<TypeProperty<Entity>> optionalEntityField,
+                Optional<TypeProperty<TestEnum>> optionalEnumField,
+                Optional<TypeProperty<Entity>> repeatedEntityField,
+                TypeProperty<StringValue> requiredStringField,
+                Optional<TypeProperty<StringValue>> optionalStringField,
+                Optional<TypeProperty<StringValue>> repeatedStringField) {
             return new AutoValue_ActionSpecTest_Property(
                     requiredEntityField,
                     optionalEntityField,
@@ -383,7 +464,8 @@
         }
 
         static Property create(
-                EntityProperty requiredEntityField, StringProperty requiredStringField) {
+                TypeProperty<Entity> requiredEntityField,
+                TypeProperty<StringValue> requiredStringField) {
             return create(
                     requiredEntityField,
                     Optional.empty(),
@@ -394,18 +476,64 @@
                     Optional.empty());
         }
 
-        abstract EntityProperty requiredEntityField();
+        abstract TypeProperty<Entity> requiredEntityField();
 
-        abstract Optional<EntityProperty> optionalEntityField();
+        abstract Optional<TypeProperty<Entity>> optionalEntityField();
 
-        abstract Optional<EnumProperty<TestEnum>> optionalEnumField();
+        abstract Optional<TypeProperty<TestEnum>> optionalEnumField();
 
-        abstract Optional<EntityProperty> repeatedEntityField();
+        abstract Optional<TypeProperty<Entity>> repeatedEntityField();
 
-        abstract StringProperty requiredStringField();
+        abstract TypeProperty<StringValue> requiredStringField();
 
-        abstract Optional<StringProperty> optionalStringField();
+        abstract Optional<TypeProperty<StringValue>> optionalStringField();
 
-        abstract Optional<StringProperty> repeatedStringField();
+        abstract Optional<TypeProperty<StringValue>> repeatedStringField();
+    }
+
+    @AutoValue
+    abstract static class GenericEntityArgument {
+
+        static Builder newBuilder() {
+            return new AutoValue_ActionSpecTest_GenericEntityArgument.Builder();
+        }
+
+        abstract String singularField();
+
+        abstract String optionalField();
+
+        abstract List<String> repeatedField();
+
+        @AutoValue.Builder
+        abstract static class Builder implements BuilderOf<GenericEntityArgument> {
+
+            abstract Builder setSingularField(String value);
+
+            abstract Builder setOptionalField(String value);
+
+            abstract Builder setRepeatedField(List<String> value);
+
+            @NonNull
+            @Override
+            public abstract GenericEntityArgument build();
+        }
+    }
+
+    @AutoValue
+    abstract static class GenericEntityProperty {
+
+        static GenericEntityProperty create(
+                TypeProperty<String> singularField,
+                Optional<TypeProperty<String>> optionalField,
+                Optional<TypeProperty<String>> repeatedField) {
+            return new AutoValue_ActionSpecTest_GenericEntityProperty(
+                    singularField, optionalField, repeatedField);
+        }
+
+        abstract TypeProperty<String> singularField();
+
+        abstract Optional<TypeProperty<String>> optionalField();
+
+        abstract Optional<TypeProperty<String>> repeatedField();
     }
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/properties/TypePropertyTest.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/properties/TypePropertyTest.kt
new file mode 100644
index 0000000..c6d5eda
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/properties/TypePropertyTest.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.properties
+
+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 TypePropertyTest {
+    @Test
+    fun dynamicInventory_test() {
+        val mutablePossibleValues = mutableListOf<String>("a", "b")
+        val testProperty = TypeProperty.Builder<String>()
+            .setPossibleValueSupplier { mutablePossibleValues.toList() }
+            .build()
+
+        assertThat(testProperty.possibleValues).containsExactly("a", "b")
+
+        mutablePossibleValues.add("c")
+
+        assertThat(testProperty.possibleValues).containsExactly("a", "b", "c")
+    }
+}
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityImplTest.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityImplTest.kt
index 62b6478..d37e39c 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityImplTest.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityImplTest.kt
@@ -25,16 +25,14 @@
 import androidx.appactions.interaction.capabilities.core.impl.ActionCapabilitySession
 import androidx.appactions.interaction.capabilities.core.impl.ErrorStatusInternal
 import androidx.appactions.interaction.capabilities.core.impl.concurrent.Futures
-import androidx.appactions.interaction.capabilities.core.impl.converters.DisambigEntityConverter
+import androidx.appactions.interaction.capabilities.core.impl.converters.PropertyConverter
 import androidx.appactions.interaction.capabilities.core.impl.converters.SearchActionConverter
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.EntityProperty
-import androidx.appactions.interaction.capabilities.core.properties.EnumProperty
-import androidx.appactions.interaction.capabilities.core.properties.SimpleProperty
-import androidx.appactions.interaction.capabilities.core.properties.StringProperty
-import androidx.appactions.interaction.capabilities.core.task.AppEntityResolver
+import androidx.appactions.interaction.capabilities.core.properties.StringValue
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty
+import androidx.appactions.interaction.capabilities.core.task.AppEntityListener
 import androidx.appactions.interaction.capabilities.core.task.EntitySearchResult
 import androidx.appactions.interaction.capabilities.core.task.ValidationResult
 import androidx.appactions.interaction.capabilities.core.task.ValueListener
@@ -75,30 +73,35 @@
 import androidx.concurrent.futures.CallbackToFutureAdapter.Completer
 import com.google.common.truth.Truth.assertThat
 import com.google.common.util.concurrent.ListenableFuture
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import java.util.concurrent.TimeUnit.MILLISECONDS
 import java.util.concurrent.atomic.AtomicInteger
 import java.util.concurrent.atomic.AtomicReference
 import java.util.function.Supplier
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
 
 @RunWith(JUnit4::class)
 class TaskCapabilityImplTest {
-    val capability: ActionCapability = createCapability<EmptyTaskUpdater>(
-        SINGLE_REQUIRED_FIELD_PROPERTY,
-        sessionFactory = SessionFactory {
-            object : Session {
-                override fun onFinishAsync(argument: Argument) =
-                    Futures.immediateFuture(ExecutionResult.getDefaultInstance<Output>())
-            }
-        },
-        sessionBridge = SessionBridge { TaskHandler.Builder<Confirmation>().build() },
-        sessionUpdaterSupplier = ::EmptyTaskUpdater,
-    )
-    val hostProperties: HostProperties = HostProperties.Builder().setMaxHostSizeDp(
-        SizeF(300f, 500f),
-    ).build()
+    val capability: ActionCapability =
+        createCapability<EmptyTaskUpdater>(
+            SINGLE_REQUIRED_FIELD_PROPERTY,
+            sessionFactory =
+                SessionFactory {
+                    object : Session {
+                        override fun onFinishAsync(argument: Argument) =
+                            Futures.immediateFuture(ExecutionResult.getDefaultInstance<Output>())
+                    }
+                },
+            sessionBridge = SessionBridge { TaskHandler.Builder<Confirmation>().build() },
+            sessionUpdaterSupplier = ::EmptyTaskUpdater,
+        )
+    val hostProperties: HostProperties =
+        HostProperties.Builder()
+            .setMaxHostSizeDp(
+                SizeF(300f, 500f),
+            )
+            .build()
 
     @Test
     fun getAppAction_smokeTest() {
@@ -108,9 +111,7 @@
                     .setName("actions.intent.TEST")
                     .setIdentifier("id")
                     .addParams(
-                        IntentParameter.newBuilder()
-                            .setName("required")
-                            .setIsRequired(true),
+                        IntentParameter.newBuilder().setName("required").setIsRequired(true),
                     )
                     .setTaskInfo(
                         TaskInfo.newBuilder().setSupportsPartialFulfillment(true),
@@ -122,12 +123,13 @@
     @Test
     fun actionCapabilitySession_getUiHandle() {
         val externalSession = object : Session {}
-        val capability = createCapability(
-            SINGLE_REQUIRED_FIELD_PROPERTY,
-            SessionFactory { externalSession },
-            SessionBridge { TaskHandler.Builder<Confirmation>().build() },
-            ::EmptyTaskUpdater,
-        )
+        val capability =
+            createCapability(
+                SINGLE_REQUIRED_FIELD_PROPERTY,
+                SessionFactory { externalSession },
+                SessionBridge { TaskHandler.Builder<Confirmation>().build() },
+                ::EmptyTaskUpdater,
+            )
         val session = capability.createSession(hostProperties)
         assertThat(session.uiHandle).isSameInstanceAs(externalSession)
     }
@@ -136,22 +138,24 @@
     @kotlin.Throws(Exception::class)
     fun onInitInvoked_invokedOnce() {
         val onSuccessInvocationCount = AtomicInteger(0)
-        val capability: ActionCapability = createCapability(
-            SINGLE_REQUIRED_FIELD_PROPERTY,
-            sessionFactory = SessionFactory {
-                object : Session {
-                    override fun onInit(initArg: InitArg) {
-                        onSuccessInvocationCount.incrementAndGet()
-                    }
-                    override fun onFinishAsync(argument: Argument) =
-                        Futures.immediateFuture(
-                            ExecutionResult.getDefaultInstance<Output>(),
-                        )
-                }
-            },
-            sessionBridge = SessionBridge { TaskHandler.Builder<Confirmation>().build() },
-            sessionUpdaterSupplier = ::EmptyTaskUpdater,
-        )
+        val capability: ActionCapability =
+            createCapability(
+                SINGLE_REQUIRED_FIELD_PROPERTY,
+                sessionFactory =
+                    SessionFactory {
+                        object : Session {
+                            override fun onInit(initArg: InitArg) {
+                                onSuccessInvocationCount.incrementAndGet()
+                            }
+                            override fun onFinishAsync(argument: Argument) =
+                                Futures.immediateFuture(
+                                    ExecutionResult.getDefaultInstance<Output>(),
+                                )
+                        }
+                    },
+                sessionBridge = SessionBridge { TaskHandler.Builder<Confirmation>().build() },
+                sessionUpdaterSupplier = ::EmptyTaskUpdater,
+            )
         val session = capability.createSession(hostProperties)
 
         // TURN 1.
@@ -181,8 +185,7 @@
         fun setRequiredEntityValue(entityValue: EntityValue) {
             super.updateParamValues(
                 mapOf(
-                    "required" to
-                        listOf(TypeConverters.toParamValue(entityValue)),
+                    "required" to listOf(TypeConverters.toParamValue(entityValue)),
                 ),
             )
         }
@@ -193,16 +196,18 @@
             AtomicReference<Completer<ValidationResult>>()
 
         fun setValidationResult(t: ValidationResult) {
-            val completer: Completer<ValidationResult> = mCompleterRef.getAndSet(null)
-                ?: throw IllegalStateException("no onReceived is waiting")
+            val completer: Completer<ValidationResult> =
+                mCompleterRef.getAndSet(null)
+                    ?: throw IllegalStateException("no onReceived is waiting")
             completer.set(t)
         }
 
         override fun onReceivedAsync(value: T): ListenableFuture<ValidationResult> {
             return CallbackToFutureAdapter.getFuture { newCompleter ->
-                val oldCompleter: Completer<ValidationResult>? = mCompleterRef.getAndSet(
-                    newCompleter,
-                )
+                val oldCompleter: Completer<ValidationResult>? =
+                    mCompleterRef.getAndSet(
+                        newCompleter,
+                    )
                 if (oldCompleter != null) {
                     oldCompleter.setCancelled()
                 }
@@ -214,19 +219,21 @@
     @Test
     @kotlin.Throws(Exception::class)
     fun fulfillmentType_unknown_errorReported() {
-        val capability: ActionCapability = createCapability(
-            SINGLE_REQUIRED_FIELD_PROPERTY,
-            sessionFactory = SessionFactory {
-                object : Session {
-                    override fun onFinishAsync(argument: Argument) =
-                        Futures.immediateFuture(
-                            ExecutionResult.getDefaultInstance<Output>(),
-                        )
-                }
-            },
-            sessionBridge = SessionBridge { TaskHandler.Builder<Confirmation>().build() },
-            sessionUpdaterSupplier = ::RequiredTaskUpdater,
-        )
+        val capability: ActionCapability =
+            createCapability(
+                SINGLE_REQUIRED_FIELD_PROPERTY,
+                sessionFactory =
+                    SessionFactory {
+                        object : Session {
+                            override fun onFinishAsync(argument: Argument) =
+                                Futures.immediateFuture(
+                                    ExecutionResult.getDefaultInstance<Output>(),
+                                )
+                        }
+                    },
+                sessionBridge = SessionBridge { TaskHandler.Builder<Confirmation>().build() },
+                sessionUpdaterSupplier = ::RequiredTaskUpdater,
+            )
         val session = capability.createSession(hostProperties)
 
         assertThat(capability.getAppAction())
@@ -235,9 +242,7 @@
                     .setName("actions.intent.TEST")
                     .setIdentifier("id")
                     .addParams(
-                        IntentParameter.newBuilder()
-                            .setName("required")
-                            .setIsRequired(true),
+                        IntentParameter.newBuilder().setName("required").setIsRequired(true),
                     )
                     .setTaskInfo(
                         TaskInfo.newBuilder().setSupportsPartialFulfillment(true),
@@ -256,35 +261,53 @@
     fun slotFilling_getStatus_smokeTest() {
         val property: CapabilityTwoEntityValues.Property =
             CapabilityTwoEntityValues.Property.newBuilder()
-                .setSlotA(EntityProperty.Builder().setRequired(true).build())
-                .setSlotB(EntityProperty.Builder().setRequired(true).build())
+                .setSlotA(
+                    TypeProperty.Builder<
+                            androidx.appactions.interaction.capabilities.core.properties.Entity
+                        >()
+                        .setRequired(true)
+                        .build()
+                )
+                .setSlotB(
+                    TypeProperty.Builder<
+                            androidx.appactions.interaction.capabilities.core.properties.Entity
+                        >()
+                        .setRequired(true)
+                        .build()
+                )
                 .build()
-        val sessionFactory = SessionFactory<CapabilityTwoEntityValues.Session> {
-            object : CapabilityTwoEntityValues.Session {
-                override suspend fun onFinish(
-                    argument: CapabilityTwoEntityValues.Argument,
-                ): ExecutionResult<Void> = ExecutionResult.getDefaultInstance()
+        val sessionFactory =
+            SessionFactory<CapabilityTwoEntityValues.Session> {
+                object : CapabilityTwoEntityValues.Session {
+                    override suspend fun onFinish(
+                        argument: CapabilityTwoEntityValues.Argument,
+                    ): ExecutionResult<Void> = ExecutionResult.getDefaultInstance()
+                }
             }
-        }
-        val sessionBridge = SessionBridge<CapabilityTwoEntityValues.Session, Void> {
-            TaskHandler.Builder<Void>().registerValueTaskParam(
-                "slotA",
-                AUTO_ACCEPT_ENTITY_VALUE,
-                TypeConverters::toEntityValue,
-            ).registerValueTaskParam(
-                "slotB",
-                AUTO_ACCEPT_ENTITY_VALUE,
-                TypeConverters::toEntityValue,
-            ).build()
-        }
-        val capability: ActionCapability = TaskCapabilityImpl(
-            "fakeId",
-            CapabilityTwoEntityValues.ACTION_SPEC,
-            property,
-            sessionFactory,
-            sessionBridge,
-            ::EmptyTaskUpdater,
-        )
+        val sessionBridge =
+            SessionBridge<CapabilityTwoEntityValues.Session, Void> {
+                TaskHandler.Builder<Void>()
+                    .registerValueTaskParam(
+                        "slotA",
+                        AUTO_ACCEPT_ENTITY_VALUE,
+                        TypeConverters::toEntityValue,
+                    )
+                    .registerValueTaskParam(
+                        "slotB",
+                        AUTO_ACCEPT_ENTITY_VALUE,
+                        TypeConverters::toEntityValue,
+                    )
+                    .build()
+            }
+        val capability: ActionCapability =
+            TaskCapabilityImpl(
+                "fakeId",
+                CapabilityTwoEntityValues.ACTION_SPEC,
+                property,
+                sessionFactory,
+                sessionBridge,
+                ::EmptyTaskUpdater,
+            )
 
         val session = capability.createSession(hostProperties)
         assertThat(session.status).isEqualTo(ActionCapabilitySession.Status.UNINITIATED)
@@ -333,38 +356,56 @@
         val onFinishInvocationCount = AtomicInteger(0)
         val property: CapabilityTwoEntityValues.Property =
             CapabilityTwoEntityValues.Property.newBuilder()
-                .setSlotA(EntityProperty.Builder().setRequired(true).build())
-                .setSlotB(EntityProperty.Builder().setRequired(false).build())
+                .setSlotA(
+                    TypeProperty.Builder<
+                            androidx.appactions.interaction.capabilities.core.properties.Entity
+                        >()
+                        .setRequired(true)
+                        .build()
+                )
+                .setSlotB(
+                    TypeProperty.Builder<
+                            androidx.appactions.interaction.capabilities.core.properties.Entity
+                        >()
+                        .setRequired(false)
+                        .build()
+                )
                 .build()
-        val sessionFactory = SessionFactory<CapabilityTwoEntityValues.Session> {
-            object : CapabilityTwoEntityValues.Session {
-                override suspend fun onFinish(
-                    argument: CapabilityTwoEntityValues.Argument,
-                ): ExecutionResult<Void> {
-                    onFinishInvocationCount.incrementAndGet()
-                    return ExecutionResult.getDefaultInstance()
+        val sessionFactory =
+            SessionFactory<CapabilityTwoEntityValues.Session> {
+                object : CapabilityTwoEntityValues.Session {
+                    override suspend fun onFinish(
+                        argument: CapabilityTwoEntityValues.Argument,
+                    ): ExecutionResult<Void> {
+                        onFinishInvocationCount.incrementAndGet()
+                        return ExecutionResult.getDefaultInstance()
+                    }
                 }
             }
-        }
-        val sessionBridge = SessionBridge<CapabilityTwoEntityValues.Session, Void> {
-            TaskHandler.Builder<Void>().registerValueTaskParam(
-                "slotA",
-                AUTO_ACCEPT_ENTITY_VALUE,
-                TypeConverters::toEntityValue,
-            ).registerValueTaskParam(
-                "slotB",
-                AUTO_REJECT_ENTITY_VALUE,
-                TypeConverters::toEntityValue,
-            ).build()
-        }
-        val capability: ActionCapability = TaskCapabilityImpl(
-            "fakeId",
-            CapabilityTwoEntityValues.ACTION_SPEC,
-            property,
-            sessionFactory,
-            sessionBridge,
-            ::EmptyTaskUpdater,
-        )
+        val sessionBridge =
+            SessionBridge<CapabilityTwoEntityValues.Session, Void> {
+                TaskHandler.Builder<Void>()
+                    .registerValueTaskParam(
+                        "slotA",
+                        AUTO_ACCEPT_ENTITY_VALUE,
+                        TypeConverters::toEntityValue,
+                    )
+                    .registerValueTaskParam(
+                        "slotB",
+                        AUTO_REJECT_ENTITY_VALUE,
+                        TypeConverters::toEntityValue,
+                    )
+                    .build()
+            }
+        val capability: ActionCapability =
+            TaskCapabilityImpl(
+                "fakeId",
+                CapabilityTwoEntityValues.ACTION_SPEC,
+                property,
+                sessionFactory,
+                sessionBridge,
+                ::EmptyTaskUpdater,
+            )
         val session = capability.createSession(hostProperties)
 
         // TURN 1.
@@ -385,9 +426,7 @@
             .containsExactly(
                 CurrentValue.newBuilder()
                     .setValue(
-                        ParamValue.newBuilder()
-                            .setIdentifier("foo")
-                            .setStringValue("foo"),
+                        ParamValue.newBuilder().setIdentifier("foo").setStringValue("foo"),
                     )
                     .setStatus(CurrentValue.Status.ACCEPTED)
                     .build(),
@@ -396,9 +435,7 @@
             .containsExactly(
                 CurrentValue.newBuilder()
                     .setValue(
-                        ParamValue.newBuilder()
-                            .setIdentifier("bar")
-                            .setStringValue("bar"),
+                        ParamValue.newBuilder().setIdentifier("bar").setStringValue("bar"),
                     )
                     .setStatus(CurrentValue.Status.REJECTED)
                     .build(),
@@ -408,23 +445,29 @@
     @Test
     @kotlin.Throws(Exception::class)
     fun slotFilling_assistantRemovedParam_clearInSdkState() {
-        val property: Property = Property.newBuilder()
-            .setRequiredEntityField(
-                EntityProperty.Builder().setRequired(true).build(),
+        val property: Property =
+            Property.newBuilder()
+                .setRequiredEntityField(
+                    TypeProperty.Builder<
+                            androidx.appactions.interaction.capabilities.core.properties.Entity
+                        >()
+                        .setRequired(true)
+                        .build(),
+                )
+                .setEnumField(
+                    TypeProperty.Builder<TestEnum>()
+                        .setPossibleValues(TestEnum.VALUE_1, TestEnum.VALUE_2)
+                        .setRequired(true)
+                        .build(),
+                )
+                .build()
+        val capability: ActionCapability =
+            createCapability(
+                property,
+                sessionFactory = SessionFactory { Session.DEFAULT },
+                sessionBridge = SessionBridge { TaskHandler.Builder<Confirmation>().build() },
+                sessionUpdaterSupplier = ::EmptyTaskUpdater,
             )
-            .setEnumField(
-                EnumProperty.Builder(TestEnum::class.java)
-                    .addSupportedEnumValues(TestEnum.VALUE_1, TestEnum.VALUE_2)
-                    .setRequired(true)
-                    .build(),
-            )
-            .build()
-        val capability: ActionCapability = createCapability(
-            property,
-            sessionFactory = SessionFactory { Session.DEFAULT },
-            sessionBridge = SessionBridge { TaskHandler.Builder<Confirmation>().build() },
-            sessionUpdaterSupplier = ::EmptyTaskUpdater,
-        )
         val session = capability.createSession(hostProperties)
 
         // TURN 1.
@@ -442,9 +485,7 @@
             .containsExactly(
                 CurrentValue.newBuilder()
                     .setValue(
-                        ParamValue.newBuilder()
-                            .setIdentifier("foo")
-                            .setStringValue("foo"),
+                        ParamValue.newBuilder().setIdentifier("foo").setStringValue("foo"),
                     )
                     .setStatus(CurrentValue.Status.ACCEPTED)
                     .build(),
@@ -472,52 +513,52 @@
     @kotlin.Throws(Exception::class)
     @Suppress("DEPRECATION") // TODO(b/269638788) migrate session state to AppDialogState message
     fun disambig_singleParam_disambigEntitiesInContext() {
-        val capability: ActionCapability = createCapability(
-            SINGLE_REQUIRED_FIELD_PROPERTY,
-            sessionFactory = {
-                object : Session {
-                    override suspend fun onFinish(argument: Argument) =
-                        ExecutionResult.getDefaultInstance<Output>()
-                    override fun getRequiredEntityListener() =
-                        object : AppEntityResolver<EntityValue> {
-                            override fun lookupAndRender(
-                                searchAction: SearchAction<EntityValue>,
-                            ): ListenableFuture<EntitySearchResult<EntityValue>> {
-                                val result =
-                                    EntitySearchResult.Builder<EntityValue>()
-                                return Futures.immediateFuture(
-                                    result.addPossibleValue(EntityValue.ofId("valid1"))
-                                        .addPossibleValue(EntityValue.ofId("valid2"))
-                                        .build(),
-                                )
-                            }
+        val capability: ActionCapability =
+            createCapability(
+                SINGLE_REQUIRED_FIELD_PROPERTY,
+                sessionFactory = {
+                    object : Session {
+                        override suspend fun onFinish(argument: Argument) =
+                            ExecutionResult.getDefaultInstance<Output>()
+                        override fun getRequiredEntityListener() =
+                            object : AppEntityListener<EntityValue> {
+                                override fun lookupAndRenderAsync(
+                                    searchAction: SearchAction<EntityValue>,
+                                ): ListenableFuture<EntitySearchResult<EntityValue>> {
+                                    val result = EntitySearchResult.Builder<EntityValue>()
+                                    return Futures.immediateFuture(
+                                        result
+                                            .addPossibleValue(EntityValue.ofId("valid1"))
+                                            .addPossibleValue(EntityValue.ofId("valid2"))
+                                            .build(),
+                                    )
+                                }
 
-                            override fun onReceivedAsync(
-                                value: EntityValue,
-                            ): ListenableFuture<ValidationResult> {
-                                return Futures.immediateFuture(ValidationResult.newAccepted())
+                                override fun onReceivedAsync(
+                                    value: EntityValue,
+                                ): ListenableFuture<ValidationResult> {
+                                    return Futures.immediateFuture(ValidationResult.newAccepted())
+                                }
                             }
+                    }
+                },
+                sessionBridge =
+                    SessionBridge<Session, Confirmation> { session ->
+                        val builder = TaskHandler.Builder<Confirmation>()
+                        session.getRequiredEntityListener()?.let {
+                            listener: AppEntityListener<EntityValue> ->
+                            builder.registerAppEntityTaskParam(
+                                "required",
+                                listener,
+                                TypeConverters::toEntityValue,
+                                TypeConverters::toEntity,
+                                getTrivialSearchActionConverter(),
+                            )
                         }
-                }
-            },
-            sessionBridge = SessionBridge<Session, Confirmation> {
-                    session ->
-                val builder = TaskHandler.Builder<Confirmation>()
-                session.getRequiredEntityListener()?.let {
-                        listener: AppEntityResolver<EntityValue> ->
-                    builder.registerAppEntityTaskParam(
-                        "required",
-                        listener,
-                        TypeConverters::toEntityValue,
-                        TypeConverters::toEntity,
-                        getTrivialSearchActionConverter(),
-
-                    )
-                }
-                builder.build()
-            },
-            sessionUpdaterSupplier = ::EmptyTaskUpdater,
-        )
+                        builder.build()
+                    },
+                sessionUpdaterSupplier = ::EmptyTaskUpdater,
+            )
         val session = capability.createSession(hostProperties)
 
         // TURN 1.
@@ -537,19 +578,15 @@
                             .addCurrentValue(
                                 CurrentValue.newBuilder()
                                     .setValue(
-                                        buildSearchActionParamValue(
-                                            "invalid"
-                                        ),
+                                        buildSearchActionParamValue("invalid"),
                                     )
                                     .setStatus(
                                         CurrentValue.Status.DISAMBIG,
                                     )
                                     .setDisambiguationData(
-                                        DisambiguationData
-                                            .newBuilder()
+                                        DisambiguationData.newBuilder()
                                             .addEntities(
-                                                Entity
-                                                    .newBuilder()
+                                                Entity.newBuilder()
                                                     .setIdentifier(
                                                         "valid1",
                                                     )
@@ -558,8 +595,7 @@
                                                     )
                                             )
                                             .addEntities(
-                                                Entity
-                                                    .newBuilder()
+                                                Entity.newBuilder()
                                                     .setIdentifier(
                                                         "valid2",
                                                     )
@@ -569,7 +605,8 @@
                                             ),
                                     ),
                             ),
-                    ).build()
+                    )
+                    .build()
             )
 
         // TURN 2.
@@ -578,10 +615,7 @@
             buildRequestArgs(
                 SYNC,
                 "required",
-                ParamValue.newBuilder()
-                    .setIdentifier("valid2")
-                    .setStringValue("valid2")
-                    .build(),
+                ParamValue.newBuilder().setIdentifier("valid2").setStringValue("valid2").build(),
             ),
             buildActionCallback(turn2SuccessInvoked),
         )
@@ -621,68 +655,73 @@
     @kotlin.Throws(Exception::class)
     @Suppress("DEPRECATION") // TODO(b/269638788) migrate session state to AppDialogState message
     fun identifierOnly_refillsStruct() {
-        val property: CapabilityStructFill.Property = CapabilityStructFill.Property.newBuilder()
-            .setListItem(SimpleProperty.Builder().setRequired(true).build())
-            .setAnyString(StringProperty.Builder().setRequired(true).build())
-            .build()
+        val property: CapabilityStructFill.Property =
+            CapabilityStructFill.Property.newBuilder()
+                .setListItem(TypeProperty.Builder<ListItem>().setRequired(true).build())
+                .setAnyString(TypeProperty.Builder<StringValue>().setRequired(true).build())
+                .build()
         val item1: ListItem = ListItem.newBuilder().setName("red apple").setId("item1").build()
         val item2: ListItem = ListItem.newBuilder().setName("green apple").setId("item2").build()
         val onReceivedCb: SettableFutureWrapper<ListItem> = SettableFutureWrapper()
         val onFinishListItemCb: SettableFutureWrapper<ListItem> = SettableFutureWrapper()
         val onFinishStringCb: SettableFutureWrapper<String> = SettableFutureWrapper()
 
-        val sessionFactory = SessionFactory<CapabilityStructFill.Session> {
-            object : CapabilityStructFill.Session {
-                override suspend fun onFinish(
-                    argument: CapabilityStructFill.Argument,
-                ): ExecutionResult<Void> {
-                    val listItem: ListItem = argument.listItem().orElse(null)
-                    val string: String = argument.anyString().orElse(null)
-                    onFinishListItemCb.set(listItem)
-                    onFinishStringCb.set(string)
-                    return ExecutionResult.getDefaultInstance<Void>()
-                }
-
-                override fun getListItemListener() = object : AppEntityResolver<ListItem> {
-                    override fun onReceivedAsync(
-                        value: ListItem,
-                    ): ListenableFuture<ValidationResult> {
-                        onReceivedCb.set(value)
-                        return Futures.immediateFuture(ValidationResult.newAccepted())
+        val sessionFactory =
+            SessionFactory<CapabilityStructFill.Session> {
+                object : CapabilityStructFill.Session {
+                    override suspend fun onFinish(
+                        argument: CapabilityStructFill.Argument,
+                    ): ExecutionResult<Void> {
+                        val listItem: ListItem = argument.listItem().orElse(null)
+                        val string: String = argument.anyString().orElse(null)
+                        onFinishListItemCb.set(listItem)
+                        onFinishStringCb.set(string)
+                        return ExecutionResult.getDefaultInstance<Void>()
                     }
 
-                    override fun lookupAndRender(
-                        searchAction: SearchAction<ListItem>,
-                    ): ListenableFuture<EntitySearchResult<ListItem>> = Futures.immediateFuture(
-                        EntitySearchResult.Builder<ListItem>()
-                            .addPossibleValue(item1)
-                            .addPossibleValue(item2)
-                            .build(),
-                    )
+                    override fun getListItemListener() =
+                        object : AppEntityListener<ListItem> {
+                            override fun onReceivedAsync(
+                                value: ListItem,
+                            ): ListenableFuture<ValidationResult> {
+                                onReceivedCb.set(value)
+                                return Futures.immediateFuture(ValidationResult.newAccepted())
+                            }
+
+                            override fun lookupAndRenderAsync(
+                                searchAction: SearchAction<ListItem>,
+                            ): ListenableFuture<EntitySearchResult<ListItem>> =
+                                Futures.immediateFuture(
+                                    EntitySearchResult.Builder<ListItem>()
+                                        .addPossibleValue(item1)
+                                        .addPossibleValue(item2)
+                                        .build(),
+                                )
+                        }
                 }
             }
-        }
-        val sessionBridge = SessionBridge<CapabilityStructFill.Session, Void> {
-                session ->
-            TaskHandler.Builder<Void>()
-                .registerAppEntityTaskParam(
-                    "listItem",
-                    session.getListItemListener(),
-                    TypeConverters::toListItem,
-                    TypeConverters::toEntity,
-                    getTrivialSearchActionConverter(),
-                )
-                .build()
-        }
+        val sessionBridge =
+            SessionBridge<CapabilityStructFill.Session, Void> { session ->
+                TaskHandler.Builder<Void>()
+                    .registerAppEntityTaskParam(
+                        "listItem",
+                        session.getListItemListener(),
+                        TypeConverters::toListItem,
+                        TypeConverters::toEntity,
+                        getTrivialSearchActionConverter(),
+                    )
+                    .build()
+            }
 
-        val capability: ActionCapability = TaskCapabilityImpl(
-            "selectListItem",
-            CapabilityStructFill.ACTION_SPEC,
-            property,
-            sessionFactory,
-            sessionBridge,
-            ::EmptyTaskUpdater,
-        )
+        val capability: ActionCapability =
+            TaskCapabilityImpl(
+                "selectListItem",
+                CapabilityStructFill.ACTION_SPEC,
+                property,
+                sessionFactory,
+                sessionBridge,
+                ::EmptyTaskUpdater,
+            )
         val session = capability.createSession(hostProperties)
 
         // first sync request
@@ -708,23 +747,18 @@
                                             "apple",
                                         ),
                                     )
-                                    .setStatus(
-                                        CurrentValue.Status.DISAMBIG
-                                    )
+                                    .setStatus(CurrentValue.Status.DISAMBIG)
                                     .setDisambiguationData(
-                                        DisambiguationData
-                                            .newBuilder()
+                                        DisambiguationData.newBuilder()
                                             .addEntities(
-                                                TypeConverters
-                                                    .toEntity(
-                                                        item1,
-                                                    ),
+                                                TypeConverters.toEntity(
+                                                    item1,
+                                                ),
                                             )
                                             .addEntities(
-                                                TypeConverters
-                                                    .toEntity(
-                                                        item2,
-                                                    ),
+                                                TypeConverters.toEntity(
+                                                    item2,
+                                                ),
                                             )
                                             .build(),
                                     )
@@ -732,11 +766,7 @@
                             )
                             .build(),
                     )
-                    .addParams(
-                        DialogParameter.newBuilder()
-                            .setName("string")
-                            .build()
-                    )
+                    .addParams(DialogParameter.newBuilder().setName("string").build())
                     .build()
             )
 
@@ -767,66 +797,61 @@
             buildActionCallback(thirdTurnSuccess),
         )
         assertThat(thirdTurnSuccess.getFuture().get(CB_TIMEOUT, MILLISECONDS)).isTrue()
-        assertThat(onFinishListItemCb.getFuture().get(CB_TIMEOUT, MILLISECONDS)).isEqualTo(
-            item2,
-        )
+        assertThat(onFinishListItemCb.getFuture().get(CB_TIMEOUT, MILLISECONDS))
+            .isEqualTo(
+                item2,
+            )
         assertThat(
-            onFinishStringCb.getFuture()
-                .get(CB_TIMEOUT, MILLISECONDS),
-        ).isEqualTo("unused")
+                onFinishStringCb.getFuture().get(CB_TIMEOUT, MILLISECONDS),
+            )
+            .isEqualTo("unused")
     }
 
     @Test
     @kotlin.Throws(Exception::class)
     fun executionResult_resultReturned() {
-        val sessionFactory = SessionFactory<Session> {
-            object : Session {
-                override suspend fun onFinish(argument: Argument) =
-                    ExecutionResult.Builder<Output>()
-                        .setOutput(
-                            Output.builder()
-                                .setOptionalStringField("bar")
-                                .setRepeatedStringField(
-                                    listOf("bar1", "bar2"),
-                                )
-                                .build(),
-                        )
-                        .build()
+        val sessionFactory =
+            SessionFactory<Session> {
+                object : Session {
+                    override suspend fun onFinish(argument: Argument) =
+                        ExecutionResult.Builder<Output>()
+                            .setOutput(
+                                Output.builder()
+                                    .setOptionalStringField("bar")
+                                    .setRepeatedStringField(
+                                        listOf("bar1", "bar2"),
+                                    )
+                                    .build(),
+                            )
+                            .build()
+                }
             }
-        }
-        val capability = CapabilityBuilder()
-            .setId("fakeId")
-            .setSessionFactory(sessionFactory)
-            .build()
+        val capability =
+            CapabilityBuilder().setId("fakeId").setSessionFactory(sessionFactory).build()
         val session = capability.createSession(hostProperties)
         val onSuccessInvoked: SettableFutureWrapper<FulfillmentResponse> = SettableFutureWrapper()
-        val expectedOutput: StructuredOutput = StructuredOutput.newBuilder()
-            .addOutputValues(
-                OutputValue.newBuilder()
-                    .setName("optionalStringOutput")
-                    .addValues(
-                        ParamValue.newBuilder()
-                            .setStringValue("bar")
-                            .build(),
-                    )
-                    .build(),
-            )
-            .addOutputValues(
-                OutputValue.newBuilder()
-                    .setName("repeatedStringOutput")
-                    .addValues(
-                        ParamValue.newBuilder()
-                            .setStringValue("bar1")
-                            .build(),
-                    )
-                    .addValues(
-                        ParamValue.newBuilder()
-                            .setStringValue("bar2")
-                            .build(),
-                    )
-                    .build(),
-            )
-            .build()
+        val expectedOutput: StructuredOutput =
+            StructuredOutput.newBuilder()
+                .addOutputValues(
+                    OutputValue.newBuilder()
+                        .setName("optionalStringOutput")
+                        .addValues(
+                            ParamValue.newBuilder().setStringValue("bar").build(),
+                        )
+                        .build(),
+                )
+                .addOutputValues(
+                    OutputValue.newBuilder()
+                        .setName("repeatedStringOutput")
+                        .addValues(
+                            ParamValue.newBuilder().setStringValue("bar1").build(),
+                        )
+                        .addValues(
+                            ParamValue.newBuilder().setStringValue("bar2").build(),
+                        )
+                        .build(),
+                )
+                .build()
         session.execute(
             buildRequestArgs(
                 SYNC, /* args...= */
@@ -836,26 +861,28 @@
             buildActionCallbackWithFulfillmentResponse(onSuccessInvoked),
         )
         assertThat(
-            onSuccessInvoked
-                .getFuture()
-                .get(CB_TIMEOUT, MILLISECONDS)
-                .getExecutionOutput()
-                .getOutputValuesList(),
-        )
+                onSuccessInvoked
+                    .getFuture()
+                    .get(CB_TIMEOUT, MILLISECONDS)
+                    .getExecutionOutput()
+                    .getOutputValuesList(),
+            )
             .containsExactlyElementsIn(expectedOutput.getOutputValuesList())
     }
 
     /**
-     * an implementation of CapabilityBuilderBase using Argument. Output, etc. defined under testing/spec
+     * an implementation of CapabilityBuilderBase using Argument. Output, etc. defined under
+     * testing/spec
      */
-    class CapabilityBuilder : CapabilityBuilderBase<
-        CapabilityBuilder,
-        Property,
-        Argument,
-        Output,
-        Confirmation,
-        RequiredTaskUpdater,
-        Session,
+    class CapabilityBuilder :
+        CapabilityBuilderBase<
+            CapabilityBuilder,
+            Property,
+            Argument,
+            Output,
+            Confirmation,
+            RequiredTaskUpdater,
+            Session,
         >(ACTION_SPEC) {
 
         init {
@@ -874,14 +901,10 @@
     }
 
     companion object {
-        private val DISAMBIG_ENTITY_CONVERTER: DisambigEntityConverter<EntityValue> =
-            DisambigEntityConverter {
-                TypeConverters.toEntity(it)
-            }
 
-        private val AUTO_ACCEPT_ENTITY_VALUE: AppEntityResolver<EntityValue> =
-            object : AppEntityResolver<EntityValue> {
-                override fun lookupAndRender(
+        private val AUTO_ACCEPT_ENTITY_VALUE: AppEntityListener<EntityValue> =
+            object : AppEntityListener<EntityValue> {
+                override fun lookupAndRenderAsync(
                     searchAction: SearchAction<EntityValue>,
                 ): ListenableFuture<EntitySearchResult<EntityValue>> {
                     val result: EntitySearchResult.Builder<EntityValue> =
@@ -897,9 +920,9 @@
                     return Futures.immediateFuture(ValidationResult.newAccepted())
                 }
             }
-        private val AUTO_REJECT_ENTITY_VALUE: AppEntityResolver<EntityValue> =
-            object : AppEntityResolver<EntityValue> {
-                override fun lookupAndRender(
+        private val AUTO_REJECT_ENTITY_VALUE: AppEntityListener<EntityValue> =
+            object : AppEntityListener<EntityValue> {
+                override fun lookupAndRenderAsync(
                     searchAction: SearchAction<EntityValue>,
                 ): ListenableFuture<EntitySearchResult<EntityValue>> {
                     val result: EntitySearchResult.Builder<EntityValue> =
@@ -923,32 +946,40 @@
         private const val CAPABILITY_NAME = "actions.intent.TEST"
         private val ACTION_SPEC: ActionSpec<Property, Argument, Output> =
             ActionSpecBuilder.ofCapabilityNamed(
-                CAPABILITY_NAME,
-            )
+                    CAPABILITY_NAME,
+                )
                 .setDescriptor(Property::class.java)
                 .setArgument(Argument::class.java, Argument::newBuilder)
                 .setOutput(Output::class.java)
-                .bindRequiredEntityParameter(
+                .bindParameter(
                     "required",
                     Property::requiredEntityField,
                     Argument.Builder::setRequiredEntityField,
+                    TypeConverters::toEntityValue,
+                    PropertyConverter::entityToProto
                 )
-                .bindOptionalStringParameter(
+                .bindOptionalParameter(
                     "optional",
                     Property::optionalStringField,
                     Argument.Builder::setOptionalStringField,
+                    TypeConverters::toStringValue,
+                    PropertyConverter::stringValueToProto
                 )
-                .bindOptionalEnumParameter(
+                .bindOptionalParameter(
                     "optionalEnum",
-                    TestEnum::class.java,
                     Property::enumField,
                     Argument.Builder::setEnumField,
+                    { TestEnum.VALUE_1 },
+                    { Entity.newBuilder().setIdentifier(it.toString()).build() }
                 )
-                .bindRepeatedStringParameter(
+                .bindRepeatedParameter(
                     "repeated",
                     Property::repeatedStringField,
                     Argument.Builder::setRepeatedStringField,
-                ).bindOptionalOutput(
+                    TypeConverters::toStringValue,
+                    PropertyConverter::stringValueToProto
+                )
+                .bindOptionalOutput(
                     "optionalStringOutput",
                     Output::optionalStringField,
                     TypeConverters::toParamValue,
@@ -960,9 +991,16 @@
                 )
                 .build()
 
-        private val SINGLE_REQUIRED_FIELD_PROPERTY: Property = Property.newBuilder()
-            .setRequiredEntityField(EntityProperty.Builder().setRequired(true).build())
-            .build()
+        private val SINGLE_REQUIRED_FIELD_PROPERTY: Property =
+            Property.newBuilder()
+                .setRequiredEntityField(
+                    TypeProperty.Builder<
+                            androidx.appactions.interaction.capabilities.core.properties.Entity
+                        >()
+                        .setRequired(true)
+                        .build()
+                )
+                .build()
 
         private fun groundingPredicate(paramValue: ParamValue): Boolean {
             return !paramValue.hasIdentifier()
@@ -972,7 +1010,9 @@
             argName: String,
             appDialogState: AppDialogState
         ): List<CurrentValue> {
-            return appDialogState.getParamsList().stream()
+            return appDialogState
+                .getParamsList()
+                .stream()
                 .filter { dialogParam -> dialogParam.getName().equals(argName) }
                 .findFirst()
                 .orElse(DialogParameter.getDefaultInstance())
@@ -980,20 +1020,22 @@
         }
 
         /**
-         * Create a capability instance templated with Property, Argument, Output,
-         *Confirmation etc., defined under ../../testing/spec
+         * Create a capability instance templated with Property, Argument, Output, Confirmation
+         * etc., defined under ../../testing/spec
          */
         private fun <SessionUpdaterT : AbstractTaskUpdater> createCapability(
             property: Property,
             sessionFactory: SessionFactory<Session>,
             sessionBridge: SessionBridge<Session, Confirmation>,
             sessionUpdaterSupplier: Supplier<SessionUpdaterT>,
-        ): TaskCapabilityImpl<Property,
+        ): TaskCapabilityImpl<
+            Property,
             Argument,
             Output,
             Session,
             Confirmation,
-            SessionUpdaterT,> {
+            SessionUpdaterT,
+        > {
             return TaskCapabilityImpl(
                 "id",
                 ACTION_SPEC,
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityUtilsTest.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityUtilsTest.kt
index 54f25db..eca0b7a 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityUtilsTest.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityUtilsTest.kt
@@ -16,7 +16,8 @@
 package androidx.appactions.interaction.capabilities.core.task.impl
 
 import androidx.appactions.interaction.capabilities.core.impl.converters.PropertyConverter
-import androidx.appactions.interaction.capabilities.core.properties.StringProperty
+import androidx.appactions.interaction.capabilities.core.properties.StringValue
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty
 import androidx.appactions.interaction.proto.AppActionsContext
 import androidx.appactions.interaction.proto.CurrentValue
 import androidx.appactions.interaction.proto.FulfillmentRequest
@@ -36,7 +37,8 @@
         intentParameters.add(
             PropertyConverter.getIntentParameter(
                 "required",
-                StringProperty.Builder().setRequired(true).build()
+                TypeProperty.Builder<StringValue>().setRequired(true).build(),
+                PropertyConverter::stringValueToProto
             )
         )
         assertThat(TaskCapabilityUtils.isSlotFillingComplete(args, intentParameters)).isTrue()
@@ -48,7 +50,8 @@
         intentParameters.add(
             PropertyConverter.getIntentParameter(
                 "required",
-                StringProperty.Builder().setRequired(true).build()
+                TypeProperty.Builder<StringValue>().setRequired(true).build(),
+                PropertyConverter::stringValueToProto
             )
         )
         assertThat(TaskCapabilityUtils.isSlotFillingComplete(emptyMap(), intentParameters))
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskSlotProcessorTest.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskSlotProcessorTest.kt
index 38a6cde..83e3f69 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskSlotProcessorTest.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskSlotProcessorTest.kt
@@ -17,10 +17,10 @@
 
 import androidx.appactions.interaction.capabilities.core.impl.concurrent.Futures
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
-import androidx.appactions.interaction.capabilities.core.task.AppEntityResolver
+import androidx.appactions.interaction.capabilities.core.task.AppEntityListener
 import androidx.appactions.interaction.capabilities.core.task.EntitySearchResult
 import androidx.appactions.interaction.capabilities.core.task.EntitySearchResult.Companion.empty
-import androidx.appactions.interaction.capabilities.core.task.InventoryResolver
+import androidx.appactions.interaction.capabilities.core.task.InventoryListener
 import androidx.appactions.interaction.capabilities.core.task.ValidationResult
 import androidx.appactions.interaction.capabilities.core.task.ValidationResult.Companion.newAccepted
 import androidx.appactions.interaction.capabilities.core.task.ValidationResult.Companion.newRejected
@@ -48,14 +48,14 @@
         valueConsumer: (T) -> Unit,
         renderConsumer: (List<String>) -> Unit,
     ): GenericResolverInternal<T> {
-        return GenericResolverInternal.fromInventoryResolver(
-            object : InventoryResolver<T> {
+        return GenericResolverInternal.fromInventoryListener(
+            object : InventoryListener<T> {
                 override fun onReceivedAsync(value: T): ListenableFuture<ValidationResult> {
                     valueConsumer.invoke(value)
                     return Futures.immediateFuture(validationResult)
                 }
 
-                override fun renderChoices(entityIDs: List<String>): ListenableFuture<Void> {
+                override fun renderChoicesAsync(entityIDs: List<String>): ListenableFuture<Void> {
                     renderConsumer.invoke(entityIDs)
                     return Futures.immediateVoidFuture()
                 }
@@ -97,20 +97,20 @@
         )
     }
 
-    private fun <T> createAppEntityResolver(
+    private fun <T> createAppEntityListener(
         validationResult: ValidationResult,
         valueConsumer: (T) -> Unit,
         appSearchResult: EntitySearchResult<T>,
         appSearchConsumer: (SearchAction<T>) -> Unit,
     ): GenericResolverInternal<T> {
-        return GenericResolverInternal.fromAppEntityResolver(
-            object : AppEntityResolver<T> {
+        return GenericResolverInternal.fromAppEntityListener(
+            object : AppEntityListener<T> {
                 override fun onReceivedAsync(value: T): ListenableFuture<ValidationResult> {
                     valueConsumer.invoke(value)
                     return Futures.immediateFuture(validationResult)
                 }
 
-                override fun lookupAndRender(
+                override fun lookupAndRenderAsync(
                     searchAction: SearchAction<T>
                 ): ListenableFuture<EntitySearchResult<T>> {
                     appSearchConsumer.invoke(searchAction)
@@ -336,7 +336,7 @@
         val appSearchCb = SettableFutureWrapper<SearchAction<String>>()
         val entitySearchResult = empty<String>()
         val resolver =
-            createAppEntityResolver(
+            createAppEntityListener(
                 newAccepted(),
                 { result: String -> onReceivedCb.set(result) },
                 entitySearchResult
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/FakeCallbackInternal.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/FakeCallbackInternal.kt
new file mode 100644
index 0000000..346b554
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/FakeCallbackInternal.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.core.testing
+
+import androidx.appactions.interaction.capabilities.core.impl.CallbackInternal
+import androidx.appactions.interaction.capabilities.core.impl.FulfillmentResult
+import androidx.appactions.interaction.capabilities.core.impl.ErrorStatusInternal
+import androidx.appactions.interaction.proto.FulfillmentResponse
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withTimeout
+
+/**
+ * A fake CallbackInternal instance being used for testing to receive the [FulfillmentResult]
+ * containing either [FulfillmentResponse] or [ErrorStatusInternal]
+ */
+class FakeCallbackInternal constructor(private val timeoutMs: Long) : CallbackInternal {
+
+    private val completer = CompletableDeferred<FulfillmentResult>()
+
+    override fun onSuccess(fulfillmentResponse: FulfillmentResponse) {
+        completer.complete(FulfillmentResult(fulfillmentResponse))
+    }
+
+    override fun onError(errorStatus: ErrorStatusInternal) {
+        completer.complete(FulfillmentResult(errorStatus))
+    }
+
+    fun receiveResponse(): FulfillmentResult = runBlocking {
+        withTimeout(timeoutMs) { completer.await() }
+    }
+}
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/TestingUtils.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/TestingUtils.kt
deleted file mode 100644
index 49e2769..0000000
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/TestingUtils.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appactions.interaction.capabilities.core.testing
-
-import androidx.appactions.interaction.capabilities.core.impl.CallbackInternal
-import androidx.appactions.interaction.capabilities.core.impl.ErrorStatusInternal
-import androidx.appactions.interaction.proto.FulfillmentResponse
-import kotlinx.coroutines.channels.SendChannel
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.withTimeout
-
-/**
- * Returns a CallbackInternal instance which will forward the FulfillmentResponse it receives
- * to a SendChannel and closes the channel afterwards.
- */
-fun buildCallbackInternalWithChannel(
-    responseChannel: SendChannel<FulfillmentResponse>,
-    sendTimeoutMs: Long,
-): CallbackInternal = object : CallbackInternal {
-    override fun onSuccess(
-        fulfillmentResponse: FulfillmentResponse,
-    ) {
-        runBlocking {
-            withTimeout(sendTimeoutMs) {
-                responseChannel.send(fulfillmentResponse)
-            }
-        }
-        responseChannel.close()
-    }
-    override fun onError(errorStatus: ErrorStatusInternal) {
-        responseChannel.close()
-    }
-}
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityStructFill.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityStructFill.java
index 26dab57..a24fc6c 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityStructFill.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityStructFill.java
@@ -19,12 +19,13 @@
 import androidx.annotation.NonNull;
 import androidx.appactions.interaction.capabilities.core.BaseSession;
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
+import androidx.appactions.interaction.capabilities.core.impl.converters.PropertyConverter;
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters;
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec;
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder;
-import androidx.appactions.interaction.capabilities.core.properties.SimpleProperty;
-import androidx.appactions.interaction.capabilities.core.properties.StringProperty;
-import androidx.appactions.interaction.capabilities.core.task.AppEntityResolver;
+import androidx.appactions.interaction.capabilities.core.properties.StringValue;
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty;
+import androidx.appactions.interaction.capabilities.core.task.AppEntityListener;
 import androidx.appactions.interaction.capabilities.core.values.ListItem;
 
 import com.google.auto.value.AutoValue;
@@ -39,13 +40,18 @@
             ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
                     .setDescriptor(Property.class)
                     .setArgument(Argument.class, Argument::newBuilder)
-                    .bindStructParameter(
+                    .bindOptionalParameter(
                             "listItem",
                             Property::listItem,
                             Argument.Builder::setListItem,
-                            TypeConverters::toListItem)
-                    .bindOptionalStringParameter(
-                            "string", Property::anyString, Argument.Builder::setAnyString)
+                            TypeConverters::toListItem,
+                            TypeConverters::toEntity)
+                    .bindOptionalParameter(
+                            "string",
+                            Property::anyString,
+                            Argument.Builder::setAnyString,
+                            TypeConverters::toStringValue,
+                            PropertyConverter::stringValueToProto)
                     .build();
 
     private CapabilityStructFill() {}
@@ -83,19 +89,19 @@
             return new AutoValue_CapabilityStructFill_Property.Builder();
         }
 
-        public abstract Optional<SimpleProperty> listItem();
+        public abstract Optional<TypeProperty<ListItem>> listItem();
 
-        public abstract Optional<StringProperty> anyString();
+        public abstract Optional<TypeProperty<StringValue>> anyString();
 
         /** Builder for {@link Property} */
         @AutoValue.Builder
         public abstract static class Builder {
 
             @NonNull
-            public abstract Builder setListItem(@NonNull SimpleProperty value);
+            public abstract Builder setListItem(@NonNull TypeProperty<ListItem> value);
 
             @NonNull
-            public abstract Builder setAnyString(@NonNull StringProperty value);
+            public abstract Builder setAnyString(@NonNull TypeProperty<StringValue> value);
 
             @NonNull
             public abstract Property build();
@@ -104,6 +110,6 @@
 
     public interface Session extends BaseSession<Argument, Void> {
         @NonNull
-        AppEntityResolver<ListItem> getListItemListener();
+        AppEntityListener<ListItem> getListItemListener();
     }
 }
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoEntityValues.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoEntityValues.java
index bab4659..f1e0d43 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoEntityValues.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoEntityValues.java
@@ -19,9 +19,12 @@
 import androidx.annotation.NonNull;
 import androidx.appactions.interaction.capabilities.core.BaseSession;
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
+import androidx.appactions.interaction.capabilities.core.impl.converters.PropertyConverter;
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters;
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec;
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder;
-import androidx.appactions.interaction.capabilities.core.properties.EntityProperty;
+import androidx.appactions.interaction.capabilities.core.properties.Entity;
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty;
 import androidx.appactions.interaction.capabilities.core.values.EntityValue;
 
 import com.google.auto.value.AutoValue;
@@ -35,10 +38,18 @@
             ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
                     .setDescriptor(Property.class)
                     .setArgument(Argument.class, Argument::newBuilder)
-                    .bindOptionalEntityParameter(
-                            "slotA", Property::slotA, Argument.Builder::setSlotA)
-                    .bindOptionalEntityParameter(
-                            "slotB", Property::slotB, Argument.Builder::setSlotB)
+                    .bindOptionalParameter(
+                            "slotA",
+                            Property::slotA,
+                            Argument.Builder::setSlotA,
+                            TypeConverters::toEntityValue,
+                            PropertyConverter::entityToProto)
+                    .bindOptionalParameter(
+                            "slotB",
+                            Property::slotB,
+                            Argument.Builder::setSlotB,
+                            TypeConverters::toEntityValue,
+                            PropertyConverter::entityToProto)
                     .build();
 
     private CapabilityTwoEntityValues() {}
@@ -58,11 +69,10 @@
         @AutoValue.Builder
         public abstract static class Builder implements BuilderOf<Argument> {
 
-            public abstract Builder setSlotA(@NonNull EntityValue value);
+            public abstract Builder setSlotA(EntityValue value);
 
-            public abstract Builder setSlotB(@NonNull EntityValue value);
+            public abstract Builder setSlotB(EntityValue value);
 
-            @NonNull
             @Override
             public abstract Argument build();
         }
@@ -76,19 +86,19 @@
             return new AutoValue_CapabilityTwoEntityValues_Property.Builder();
         }
 
-        public abstract Optional<EntityProperty> slotA();
+        public abstract Optional<TypeProperty<Entity>> slotA();
 
-        public abstract Optional<EntityProperty> slotB();
+        public abstract Optional<TypeProperty<Entity>> slotB();
 
         /** Builder for {@link Property} */
         @AutoValue.Builder
         public abstract static class Builder {
 
             @NonNull
-            public abstract Builder setSlotA(@NonNull EntityProperty value);
+            public abstract Builder setSlotA(@NonNull TypeProperty<Entity> value);
 
             @NonNull
-            public abstract Builder setSlotB(@NonNull EntityProperty value);
+            public abstract Builder setSlotB(@NonNull TypeProperty<Entity> value);
 
             @NonNull
             public abstract Property build();
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoStrings.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoStrings.java
index f504c72..21b737f 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoStrings.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/CapabilityTwoStrings.java
@@ -18,9 +18,12 @@
 
 import androidx.annotation.NonNull;
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
+import androidx.appactions.interaction.capabilities.core.impl.converters.PropertyConverter;
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters;
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpec;
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder;
-import androidx.appactions.interaction.capabilities.core.properties.StringProperty;
+import androidx.appactions.interaction.capabilities.core.properties.StringValue;
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty;
 
 import com.google.auto.value.AutoValue;
 
@@ -33,10 +36,18 @@
             ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
                     .setDescriptor(Property.class)
                     .setArgument(Argument.class, Argument::newBuilder)
-                    .bindOptionalStringParameter(
-                            "stringSlotA", Property::stringSlotA, Argument.Builder::setStringSlotA)
-                    .bindOptionalStringParameter(
-                            "stringSlotB", Property::stringSlotB, Argument.Builder::setStringSlotB)
+                    .bindOptionalParameter(
+                            "stringSlotA",
+                            Property::stringSlotA,
+                            Argument.Builder::setStringSlotA,
+                            TypeConverters::toStringValue,
+                            PropertyConverter::stringValueToProto)
+                    .bindOptionalParameter(
+                            "stringSlotB",
+                            Property::stringSlotB,
+                            Argument.Builder::setStringSlotB,
+                            TypeConverters::toStringValue,
+                            PropertyConverter::stringValueToProto)
                     .build();
 
     private CapabilityTwoStrings() {
@@ -75,19 +86,19 @@
             return new AutoValue_CapabilityTwoStrings_Property.Builder();
         }
 
-        public abstract Optional<StringProperty> stringSlotA();
+        public abstract Optional<TypeProperty<StringValue>> stringSlotA();
 
-        public abstract Optional<StringProperty> stringSlotB();
+        public abstract Optional<TypeProperty<StringValue>> stringSlotB();
 
         /** Builder for {@link Property} */
         @AutoValue.Builder
         public abstract static class Builder {
 
             @NonNull
-            public abstract Builder setStringSlotA(@NonNull StringProperty value);
+            public abstract Builder setStringSlotA(@NonNull TypeProperty<StringValue> value);
 
             @NonNull
-            public abstract Builder setStringSlotB(@NonNull StringProperty value);
+            public abstract Builder setStringSlotB(@NonNull TypeProperty<StringValue> value);
 
             @NonNull
             public abstract Property build();
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Property.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Property.java
index 78d3e97..030b5c3 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Property.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Property.java
@@ -18,9 +18,9 @@
 
 import androidx.annotation.NonNull;
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf;
-import androidx.appactions.interaction.capabilities.core.properties.EntityProperty;
-import androidx.appactions.interaction.capabilities.core.properties.EnumProperty;
-import androidx.appactions.interaction.capabilities.core.properties.StringProperty;
+import androidx.appactions.interaction.capabilities.core.properties.Entity;
+import androidx.appactions.interaction.capabilities.core.properties.StringValue;
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty;
 
 import com.google.auto.value.AutoValue;
 
@@ -34,25 +34,25 @@
         return new AutoValue_Property.Builder();
     }
 
-    public abstract EntityProperty requiredEntityField();
+    public abstract TypeProperty<Entity> requiredEntityField();
 
-    public abstract Optional<StringProperty> optionalStringField();
+    public abstract Optional<TypeProperty<StringValue>> optionalStringField();
 
-    public abstract Optional<EnumProperty<TestEnum>> enumField();
+    public abstract Optional<TypeProperty<TestEnum>> enumField();
 
-    public abstract Optional<StringProperty> repeatedStringField();
+    public abstract Optional<TypeProperty<StringValue>> repeatedStringField();
 
     /** Builder for the testing Property. */
     @AutoValue.Builder
     public abstract static class Builder implements BuilderOf<Property> {
 
-        public abstract Builder setRequiredEntityField(EntityProperty property);
+        public abstract Builder setRequiredEntityField(TypeProperty<Entity> property);
 
-        public abstract Builder setOptionalStringField(StringProperty property);
+        public abstract Builder setOptionalStringField(TypeProperty<StringValue> property);
 
-        public abstract Builder setEnumField(EnumProperty<TestEnum> property);
+        public abstract Builder setEnumField(TypeProperty<TestEnum> property);
 
-        public abstract Builder setRepeatedStringField(StringProperty property);
+        public abstract Builder setRepeatedStringField(TypeProperty<StringValue> property);
 
         @NonNull
         @Override
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Session.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Session.kt
index 2a42788..8acbac8 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Session.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/Session.kt
@@ -19,12 +19,12 @@
 import androidx.appactions.interaction.capabilities.core.BaseSession
 import androidx.appactions.interaction.capabilities.core.ExecutionResult
 import androidx.appactions.interaction.capabilities.core.impl.concurrent.Futures
-import androidx.appactions.interaction.capabilities.core.task.AppEntityResolver
+import androidx.appactions.interaction.capabilities.core.task.AppEntityListener
 import androidx.appactions.interaction.capabilities.core.values.EntityValue
 
 interface Session : BaseSession<Argument, Output> {
 
-    fun getRequiredEntityListener(): AppEntityResolver<EntityValue>? = null
+    fun getRequiredEntityListener(): AppEntityListener<EntityValue>? = null
 
     companion object {
         @JvmStatic
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEntity.java b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEntity.java
index ee3db39..a8d103c 100644
--- a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEntity.java
+++ b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/testing/spec/TestEntity.java
@@ -33,6 +33,8 @@
         return new AutoValue_TestEntity.Builder();
     }
 
+    public abstract Optional<String> getId();
+
     public abstract Optional<String> getName();
 
     public abstract Optional<Duration> getDuration();
@@ -63,6 +65,8 @@
     @AutoValue.Builder
     public abstract static class Builder implements BuilderOf<TestEntity> {
 
+        public abstract Builder setId(String id);
+
         public abstract Builder setName(String name);
 
         public abstract Builder setDuration(Duration duration);
diff --git a/appactions/interaction/interaction-capabilities-fitness/build.gradle b/appactions/interaction/interaction-capabilities-fitness/build.gradle
index 94d8298..4e36a6c 100644
--- a/appactions/interaction/interaction-capabilities-fitness/build.gradle
+++ b/appactions/interaction/interaction-capabilities-fitness/build.gradle
@@ -24,11 +24,16 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    // Add dependencies here
+    implementation("androidx.annotation:annotation:1.1.0")
+    implementation(project(":appactions:interaction:interaction-capabilities-core"))
 }
 
 android {
     namespace "androidx.appactions.interaction.capabilities.fitness"
+    defaultConfig {
+        // TODO(b/266649259): lower minSdk version once Optional is removed.
+        minSdkVersion 33
+    }
 }
 
 androidx {
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/androidx-appactions-interaction-interaction-capabilities-fitness-documentation.md b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/androidx-appactions-interaction-interaction-capabilities-fitness-documentation.md
deleted file mode 100644
index da534a3..0000000
--- a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/androidx-appactions-interaction-interaction-capabilities-fitness-documentation.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# Module root
-
-androidx.appactions.interaction interaction-capablities-fitness
-
-# Package androidx.appactions.interaction.capabilities.fitness
-
-Insert package level documentation here
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetExerciseObservation.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetExerciseObservation.kt
new file mode 100644
index 0000000..1f61dfe
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetExerciseObservation.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.fitness.fitness
+
+import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
+import androidx.appactions.interaction.capabilities.core.ActionCapability
+import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.CapabilityFactory
+import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
+import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty
+import androidx.appactions.interaction.capabilities.core.task.impl.AbstractTaskUpdater
+import java.time.LocalTime
+import java.util.Optional
+
+/** GetExerciseObservation.kt in interaction-capabilities-fitness */
+private const val CAPABILITY_NAME = "actions.intent.START_EXERCISE"
+
+// TODO(b/273602015): Update to use Name property from builtintype library.
+private val ACTION_SPEC =
+    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+        .setDescriptor(GetExerciseObservation.Property::class.java)
+        .setArgument(
+            GetExerciseObservation.Argument::class.java,
+            GetExerciseObservation.Argument::Builder
+        )
+        .setOutput(GetExerciseObservation.Output::class.java)
+        .bindOptionalParameter(
+            "healthObservation.startTime",
+            { property -> Optional.ofNullable(property.startTime) },
+            GetExerciseObservation.Argument.Builder::setStartTime,
+            TypeConverters::toLocalTime,
+            TypeConverters::toEntity
+        )
+        .bindOptionalParameter(
+            "healthObservation.endTime",
+            { property -> Optional.ofNullable(property.endTime) },
+            GetExerciseObservation.Argument.Builder::setEndTime,
+            TypeConverters::toLocalTime,
+            TypeConverters::toEntity
+        )
+        .build()
+
+@CapabilityFactory(name = CAPABILITY_NAME)
+class GetExerciseObservation private constructor() {
+    class CapabilityBuilder :
+        CapabilityBuilderBase<
+            CapabilityBuilder, Property, Argument, Output, Confirmation, TaskUpdater, Session
+            >(ACTION_SPEC) {
+        private var propertyBuilder: Property.Builder = Property.Builder()
+        fun setStartTimeProperty(startTime: TypeProperty<LocalTime>): CapabilityBuilder = apply {
+            propertyBuilder.setEndTime(startTime)
+        }
+
+        fun setEndTimeProperty(endTime: TypeProperty<LocalTime>): CapabilityBuilder = apply {
+            propertyBuilder.setEndTime(endTime)
+        }
+
+        override fun build(): ActionCapability {
+            // TODO(b/268369632): Clean this up after Property is removed
+            super.setProperty(propertyBuilder.build())
+            return super.build()
+        }
+    }
+
+    // TODO(b/268369632): Remove Property from public capability APIs.
+    class Property internal constructor(
+        val startTime: TypeProperty<LocalTime>?,
+        val endTime: TypeProperty<LocalTime>?
+    ) {
+        override fun toString(): String {
+            return "Property(startTime=$startTime, endTime=$endTime)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass !== other?.javaClass) return false
+
+            other as Property
+
+            if (startTime != other.startTime) return false
+            if (endTime != other.endTime) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            var result = startTime.hashCode()
+            result += 31 * endTime.hashCode()
+            return result
+        }
+
+        class Builder {
+            private var startTime: TypeProperty<LocalTime>? = null
+            private var endTime: TypeProperty<LocalTime>? = null
+
+            fun setStartTime(startTime: TypeProperty<LocalTime>): Builder =
+                apply { this.startTime = startTime }
+
+            fun setEndTime(endTime: TypeProperty<LocalTime>): Builder =
+                apply { this.endTime = endTime }
+
+            fun build(): Property = Property(startTime, endTime)
+        }
+    }
+
+    class Argument internal constructor(
+        val startTime: LocalTime?,
+        val endTime: LocalTime?
+    ) {
+        override fun toString(): String {
+            return "Argument(startTime=$startTime, endTime=$endTime)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass !== other?.javaClass) return false
+
+            other as Argument
+
+            if (startTime != other.startTime) return false
+            if (endTime != other.endTime) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            var result = startTime.hashCode()
+            result += 31 * endTime.hashCode()
+            return result
+        }
+
+        class Builder : BuilderOf<Argument> {
+            private var startTime: LocalTime? = null
+            private var endTime: LocalTime? = null
+
+            fun setStartTime(startTime: LocalTime): Builder =
+                apply { this.startTime = startTime }
+
+            fun setEndTime(endTime: LocalTime): Builder =
+                apply { this.endTime = endTime }
+
+            override fun build(): Argument = Argument(startTime, endTime)
+        }
+    }
+
+    class Output internal constructor()
+
+    class Confirmation internal constructor()
+
+    class TaskUpdater internal constructor() : AbstractTaskUpdater()
+
+    sealed interface Session : BaseSession<Argument, Output>
+}
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetHealthObservation.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetHealthObservation.kt
new file mode 100644
index 0000000..ac98ea1
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/GetHealthObservation.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.fitness.fitness
+
+import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
+import androidx.appactions.interaction.capabilities.core.ActionCapability
+import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.CapabilityFactory
+import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
+import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty
+import androidx.appactions.interaction.capabilities.core.task.impl.AbstractTaskUpdater
+import java.time.LocalTime
+import java.util.Optional
+
+/** GetHealthObservation.kt in interaction-capabilities-fitness */
+private const val CAPABILITY_NAME = "actions.intent.START_EXERCISE"
+
+// TODO(b/273602015): Update to use Name property from builtintype library.
+private val ACTION_SPEC =
+    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+        .setDescriptor(GetHealthObservation.Property::class.java)
+        .setArgument(
+            GetHealthObservation.Argument::class.java,
+            GetHealthObservation.Argument::Builder
+        )
+        .setOutput(GetHealthObservation.Output::class.java)
+        .bindOptionalParameter(
+            "exerciseObservation.startTime",
+            { property -> Optional.ofNullable(property.startTime) },
+            GetHealthObservation.Argument.Builder::setStartTime,
+            TypeConverters::toLocalTime,
+            TypeConverters::toEntity
+        )
+        .bindOptionalParameter(
+            "exerciseObservation.endTime",
+            { property -> Optional.ofNullable(property.endTime) },
+            GetHealthObservation.Argument.Builder::setEndTime,
+            TypeConverters::toLocalTime,
+            TypeConverters::toEntity
+        )
+        .build()
+
+@CapabilityFactory(name = CAPABILITY_NAME)
+class GetHealthObservation private constructor() {
+    class CapabilityBuilder :
+        CapabilityBuilderBase<
+            CapabilityBuilder, Property, Argument, Output, Confirmation, TaskUpdater, Session
+            >(ACTION_SPEC) {
+        private var propertyBuilder: Property.Builder = Property.Builder()
+        fun setStartTimeProperty(startTime: TypeProperty<LocalTime>): CapabilityBuilder = apply {
+            propertyBuilder.setEndTime(startTime)
+        }
+
+        fun setEndTimeProperty(endTime: TypeProperty<LocalTime>): CapabilityBuilder = apply {
+            propertyBuilder.setEndTime(endTime)
+        }
+
+        override fun build(): ActionCapability {
+            // TODO(b/268369632): Clean this up after Property is removed
+            super.setProperty(propertyBuilder.build())
+            return super.build()
+        }
+    }
+
+    // TODO(b/268369632): Remove Property from public capability APIs.
+    class Property internal constructor(
+        val startTime: TypeProperty<LocalTime>?,
+        val endTime: TypeProperty<LocalTime>?
+    ) {
+        override fun toString(): String {
+            return "Property(startTime=$startTime, endTime=$endTime)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass !== other?.javaClass) return false
+
+            other as Property
+
+            if (startTime != other.startTime) return false
+            if (endTime != other.endTime) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            var result = startTime.hashCode()
+            result += 31 * endTime.hashCode()
+            return result
+        }
+
+        class Builder {
+            private var startTime: TypeProperty<LocalTime>? = null
+            private var endTime: TypeProperty<LocalTime>? = null
+
+            fun setStartTime(startTime: TypeProperty<LocalTime>): Builder =
+                apply { this.startTime = startTime }
+
+            fun setEndTime(endTime: TypeProperty<LocalTime>): Builder =
+                apply { this.endTime = endTime }
+
+            fun build(): Property = Property(startTime, endTime)
+        }
+    }
+
+    class Argument internal constructor(
+        val startTime: LocalTime?,
+        val endTime: LocalTime?
+    ) {
+        override fun toString(): String {
+            return "Argument(startTime=$startTime, endTime=$endTime)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass !== other?.javaClass) return false
+
+            other as Argument
+
+            if (startTime != other.startTime) return false
+            if (endTime != other.endTime) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            var result = startTime.hashCode()
+            result += 31 * endTime.hashCode()
+            return result
+        }
+
+        class Builder : BuilderOf<Argument> {
+            private var startTime: LocalTime? = null
+            private var endTime: LocalTime? = null
+
+            fun setStartTime(startTime: LocalTime): Builder =
+                apply { this.startTime = startTime }
+
+            fun setEndTime(endTime: LocalTime): Builder =
+                apply { this.endTime = endTime }
+
+            override fun build(): Argument = Argument(startTime, endTime)
+        }
+    }
+
+    class Output internal constructor()
+
+    class Confirmation internal constructor()
+
+    class TaskUpdater internal constructor() : AbstractTaskUpdater()
+
+    sealed interface Session : BaseSession<Argument, Output>
+}
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/PauseExercise.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/PauseExercise.kt
new file mode 100644
index 0000000..aed3db4
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/PauseExercise.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.fitness.fitness
+
+import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
+import androidx.appactions.interaction.capabilities.core.ActionCapability
+import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.CapabilityFactory
+import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
+import androidx.appactions.interaction.capabilities.core.impl.converters.PropertyConverter
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
+import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
+import androidx.appactions.interaction.capabilities.core.properties.StringValue
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty
+import androidx.appactions.interaction.capabilities.core.task.impl.AbstractTaskUpdater
+import java.util.Optional
+
+/** PauseExercise.kt in interaction-capabilities-fitness */
+private const val CAPABILITY_NAME = "actions.intent.PAUSE_EXERCISE"
+
+// TODO(b/273602015): Update to use Name property from builtintype library.
+private val ACTION_SPEC =
+    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+        .setDescriptor(PauseExercise.Property::class.java)
+        .setArgument(PauseExercise.Argument::class.java, PauseExercise.Argument::Builder)
+        .setOutput(PauseExercise.Output::class.java)
+        .bindOptionalParameter(
+            "exercise.name",
+            { property -> Optional.ofNullable(property.name) },
+            PauseExercise.Argument.Builder::setName,
+            TypeConverters::toStringValue,
+            PropertyConverter::stringValueToProto
+        )
+        .build()
+
+@CapabilityFactory(name = CAPABILITY_NAME)
+class PauseExercise private constructor() {
+    class CapabilityBuilder :
+        CapabilityBuilderBase<
+            CapabilityBuilder, Property, Argument, Output, Confirmation, TaskUpdater, Session
+            >(ACTION_SPEC) {
+        private var propertyBuilder: Property.Builder = Property.Builder()
+        fun setNameProperty(name: TypeProperty<StringValue>): CapabilityBuilder =
+            apply {
+                propertyBuilder.setName(name)
+            }
+
+        override fun build(): ActionCapability {
+            // TODO(b/268369632): Clean this up after Property is removed
+            super.setProperty(propertyBuilder.build())
+            return super.build()
+        }
+    }
+
+    // TODO(b/268369632): Remove Property from public capability APIs.
+    class Property internal constructor(
+        val name: TypeProperty<StringValue>?,
+    ) {
+        override fun toString(): String {
+            return "Property(name=$name)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass !== other?.javaClass) return false
+
+            other as Property
+
+            if (name != other.name) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            return name.hashCode()
+        }
+
+        class Builder {
+            private var name: TypeProperty<StringValue>? = null
+
+            fun setName(name: TypeProperty<StringValue>): Builder =
+                apply { this.name = name }
+
+            fun build(): Property = Property(name)
+        }
+    }
+
+    class Argument internal constructor(
+        val name: String?
+    ) {
+        override fun toString(): String {
+            return "Argument(name=$name)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass !== other?.javaClass) return false
+
+            other as Argument
+
+            if (name != other.name) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            return name.hashCode()
+        }
+
+        class Builder : BuilderOf<Argument> {
+            private var name: String? = null
+
+            fun setName(name: String): Builder =
+                apply { this.name = name }
+
+            override fun build(): Argument = Argument(name)
+        }
+    }
+
+    class Output internal constructor()
+
+    class Confirmation internal constructor()
+
+    class TaskUpdater internal constructor() : AbstractTaskUpdater()
+
+    sealed interface Session : BaseSession<Argument, Output>
+}
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/ResumeExercise.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/ResumeExercise.kt
new file mode 100644
index 0000000..629896e
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/ResumeExercise.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.fitness.fitness
+
+import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
+import androidx.appactions.interaction.capabilities.core.ActionCapability
+import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.CapabilityFactory
+import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
+import androidx.appactions.interaction.capabilities.core.impl.converters.PropertyConverter
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
+import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
+import androidx.appactions.interaction.capabilities.core.properties.StringValue
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty
+import androidx.appactions.interaction.capabilities.core.task.impl.AbstractTaskUpdater
+import java.util.Optional
+
+/** ResumeExercise.kt in interaction-capabilities-fitness */
+private const val CAPABILITY_NAME = "actions.intent.RESUME_EXERCISE"
+
+// TODO(b/273602015): Update to use Name property from builtintype library.
+private val ACTION_SPEC =
+    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+        .setDescriptor(ResumeExercise.Property::class.java)
+        .setArgument(ResumeExercise.Argument::class.java, ResumeExercise.Argument::Builder)
+        .setOutput(ResumeExercise.Output::class.java)
+        .bindOptionalParameter(
+            "exercise.name",
+            { property -> Optional.ofNullable(property.name) },
+            ResumeExercise.Argument.Builder::setName,
+            TypeConverters::toStringValue,
+            PropertyConverter::stringValueToProto
+        )
+        .build()
+
+@CapabilityFactory(name = CAPABILITY_NAME)
+class ResumeExercise private constructor() {
+    class CapabilityBuilder :
+        CapabilityBuilderBase<
+            CapabilityBuilder, Property, Argument, Output, Confirmation, TaskUpdater, Session
+            >(ACTION_SPEC) {
+        private var propertyBuilder: Property.Builder = Property.Builder()
+        fun setNameProperty(name: TypeProperty<StringValue>): CapabilityBuilder =
+            apply {
+                propertyBuilder.setName(name)
+            }
+
+        override fun build(): ActionCapability {
+            // TODO(b/268369632): Clean this up after Property is removed
+            super.setProperty(propertyBuilder.build())
+            return super.build()
+        }
+    }
+
+    // TODO(b/268369632): Remove Property from public capability APIs.
+    class Property internal constructor(
+        val name: TypeProperty<StringValue>?,
+    ) {
+        override fun toString(): String {
+            return "Property(name=$name)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass !== other?.javaClass) return false
+
+            other as Property
+
+            if (name != other.name) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            return name.hashCode()
+        }
+
+        class Builder {
+            private var name: TypeProperty<StringValue>? = null
+
+            fun setName(name: TypeProperty<StringValue>): Builder =
+                apply { this.name = name }
+
+            fun build(): Property = Property(name)
+        }
+    }
+
+    class Argument internal constructor(
+        val name: String?
+    ) {
+        override fun toString(): String {
+            return "Argument(name=$name)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass !== other?.javaClass) return false
+
+            other as Argument
+
+            if (name != other.name) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            return name.hashCode()
+        }
+
+        class Builder : BuilderOf<Argument> {
+            private var name: String? = null
+
+            fun setName(name: String): Builder =
+                apply { this.name = name }
+
+            override fun build(): Argument = Argument(name)
+        }
+    }
+
+    class Output internal constructor()
+
+    class Confirmation internal constructor()
+
+    class TaskUpdater internal constructor() : AbstractTaskUpdater()
+
+    sealed interface Session : BaseSession<Argument, Output>
+}
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StartExercise.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StartExercise.kt
new file mode 100644
index 0000000..0ecf95b
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StartExercise.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.fitness.fitness
+
+import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
+import androidx.appactions.interaction.capabilities.core.ActionCapability
+import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.CapabilityFactory
+import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
+import androidx.appactions.interaction.capabilities.core.impl.converters.PropertyConverter
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
+import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
+import androidx.appactions.interaction.capabilities.core.properties.StringValue
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty
+import androidx.appactions.interaction.capabilities.core.task.impl.AbstractTaskUpdater
+import java.time.Duration
+import java.util.Optional
+
+/** StartExercise.kt in interaction-capabilities-fitness */
+private const val CAPABILITY_NAME = "actions.intent.START_EXERCISE"
+
+// TODO(b/273602015): Update to use Name property from builtintype library.
+private val ACTION_SPEC =
+    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+        .setDescriptor(StartExercise.Property::class.java)
+        .setArgument(StartExercise.Argument::class.java, StartExercise.Argument::Builder)
+        .setOutput(StartExercise.Output::class.java)
+        .bindOptionalParameter(
+            "exercise.duration",
+            { property -> Optional.ofNullable(property.duration) },
+            StartExercise.Argument.Builder::setDuration,
+            TypeConverters::toDuration,
+            TypeConverters::toEntity
+        )
+        .bindOptionalParameter(
+            "exercise.name",
+            { property -> Optional.ofNullable(property.name) },
+            StartExercise.Argument.Builder::setName,
+            TypeConverters::toStringValue,
+            PropertyConverter::stringValueToProto
+        )
+        .build()
+
+@CapabilityFactory(name = CAPABILITY_NAME)
+class StartExercise private constructor() {
+    class CapabilityBuilder :
+        CapabilityBuilderBase<
+            CapabilityBuilder, Property, Argument, Output, Confirmation, TaskUpdater, Session
+            >(ACTION_SPEC) {
+        fun setDurationProperty(duration: TypeProperty<Duration>): CapabilityBuilder =
+            apply {
+                Property.Builder().setDuration(duration).build()
+            }
+
+        fun setNameProperty(name: TypeProperty<StringValue>): CapabilityBuilder =
+            apply {
+                Property.Builder().setName(name).build()
+            }
+
+        override fun build(): ActionCapability {
+            // TODO(b/268369632): No-op remove empty property builder after Property od removed
+            super.setProperty(Property.Builder().build())
+            return super.build()
+        }
+    }
+
+    // TODO(b/268369632): Remove Property from public capability APIs.
+    class Property internal constructor(
+        val duration: TypeProperty<Duration>?,
+        val name: TypeProperty<StringValue>?
+    ) {
+        override fun toString(): String {
+            return "Property(duration=$duration, name=$name)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass !== other?.javaClass) return false
+
+            other as Property
+
+            if (duration != other.duration) return false
+            if (name != other.name) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            var result = duration.hashCode()
+            result += 31 * name.hashCode()
+            return result
+        }
+
+        class Builder {
+            private var duration: TypeProperty<Duration>? = null
+            private var name: TypeProperty<StringValue>? = null
+
+            fun setDuration(duration: TypeProperty<Duration>): Builder =
+                apply { this.duration = duration }
+
+            fun setName(name: TypeProperty<StringValue>): Builder =
+                apply { this.name = name }
+
+            fun build(): Property = Property(duration, name)
+        }
+    }
+
+    class Argument internal constructor(
+        val duration: Duration?,
+        val name: String?
+    ) {
+        override fun toString(): String {
+            return "Argument(duration=$duration, name=$name)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass !== other?.javaClass) return false
+
+            other as Argument
+
+            if (duration != other.duration) return false
+            if (name != other.name) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            var result = duration.hashCode()
+            result += 31 * name.hashCode()
+            return result
+        }
+
+        class Builder : BuilderOf<Argument> {
+            private var duration: Duration? = null
+            private var name: String? = null
+
+            fun setDuration(duration: Duration): Builder =
+                apply { this.duration = duration }
+
+            fun setName(name: String): Builder =
+                apply { this.name = name }
+
+            override fun build(): Argument = Argument(duration, name)
+        }
+    }
+
+    class Output internal constructor()
+
+    class Confirmation internal constructor()
+
+    class TaskUpdater internal constructor() : AbstractTaskUpdater()
+
+    sealed interface Session : BaseSession<Argument, Output>
+}
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StopExercise.kt b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StopExercise.kt
new file mode 100644
index 0000000..bc9de14
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/StopExercise.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.fitness.fitness
+
+import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
+import androidx.appactions.interaction.capabilities.core.ActionCapability
+import androidx.appactions.interaction.capabilities.core.BaseSession
+import androidx.appactions.interaction.capabilities.core.CapabilityFactory
+import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
+import androidx.appactions.interaction.capabilities.core.impl.converters.PropertyConverter
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
+import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty
+import androidx.appactions.interaction.capabilities.core.properties.StringValue
+import androidx.appactions.interaction.capabilities.core.task.impl.AbstractTaskUpdater
+import java.util.Optional
+
+/** StopExercise.kt in interaction-capabilities-fitness */
+private const val CAPABILITY_NAME = "actions.intent.PAUSE_EXERCISE"
+
+// TODO(b/273602015): Update to use Name property from builtintype library.
+private val ACTION_SPEC =
+    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+        .setDescriptor(StopExercise.Property::class.java)
+        .setArgument(StopExercise.Argument::class.java, StopExercise.Argument::Builder)
+        .setOutput(StopExercise.Output::class.java)
+        .bindOptionalParameter(
+            "exercise.name",
+            { property -> Optional.ofNullable(property.name) },
+            StopExercise.Argument.Builder::setName,
+            TypeConverters::toStringValue,
+            PropertyConverter::stringValueToProto
+        )
+        .build()
+
+@CapabilityFactory(name = CAPABILITY_NAME)
+class StopExercise private constructor() {
+    class CapabilityBuilder :
+        CapabilityBuilderBase<
+            CapabilityBuilder, Property, Argument, Output, Confirmation, TaskUpdater, Session
+            >(ACTION_SPEC) {
+        private var propertyBuilder: Property.Builder = Property.Builder()
+        fun setNameProperty(name: TypeProperty<StringValue>): CapabilityBuilder =
+            apply {
+                propertyBuilder.setName(name)
+            }
+
+        override fun build(): ActionCapability {
+            // TODO(b/268369632): Clean this up after Property is removed
+            super.setProperty(propertyBuilder.build())
+            return super.build()
+        }
+    }
+
+    // TODO(b/268369632): Remove Property from public capability APIs.
+    class Property internal constructor(
+        val name: TypeProperty<StringValue>?,
+    ) {
+        override fun toString(): String {
+            return "Property(name=$name)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass !== other?.javaClass) return false
+
+            other as Property
+
+            if (name != other.name) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            return name.hashCode()
+        }
+
+        class Builder {
+            private var name: TypeProperty<StringValue>? = null
+
+            fun setName(name: TypeProperty<StringValue>): Builder =
+                apply { this.name = name }
+
+            fun build(): Property = Property(name)
+        }
+    }
+
+    class Argument internal constructor(
+        val name: String?
+    ) {
+        override fun toString(): String {
+            return "Argument(name=$name)"
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (javaClass !== other?.javaClass) return false
+
+            other as Argument
+
+            if (name != other.name) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            return name.hashCode()
+        }
+
+        class Builder : BuilderOf<Argument> {
+            private var name: String? = null
+
+            fun setName(name: String): Builder =
+                apply { this.name = name }
+
+            override fun build(): Argument = Argument(name)
+        }
+    }
+
+    class Output internal constructor()
+
+    class Confirmation internal constructor()
+
+    class TaskUpdater internal constructor() : AbstractTaskUpdater()
+
+    sealed interface Session : BaseSession<Argument, Output>
+}
diff --git a/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/package-info.java b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/package-info.java
new file mode 100644
index 0000000..8b8e318
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-fitness/src/main/java/androidx/appactions/interaction/capabilities/fitness/fitness/package-info.java
@@ -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.
+ */
+
+/** @hide */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+package androidx.appactions.interaction.capabilities.fitness.fitness;
+
+import androidx.annotation.RestrictTo;
+
diff --git a/appactions/interaction/interaction-capabilities-productivity/build.gradle b/appactions/interaction/interaction-capabilities-productivity/build.gradle
index 21b8526..aa110aa 100644
--- a/appactions/interaction/interaction-capabilities-productivity/build.gradle
+++ b/appactions/interaction/interaction-capabilities-productivity/build.gradle
@@ -24,7 +24,6 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.10")
     implementation("androidx.annotation:annotation:1.1.0")
     implementation(project(":appactions:interaction:interaction-capabilities-core"))
 }
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/PauseTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/PauseTimer.kt
index b196e5f..c79ab5d 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/PauseTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/PauseTimer.kt
@@ -9,7 +9,7 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+b * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
@@ -22,11 +22,10 @@
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.SimpleProperty
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty
 import androidx.appactions.interaction.capabilities.core.task.impl.AbstractTaskUpdater
 import androidx.appactions.interaction.capabilities.core.values.GenericErrorStatus
 import androidx.appactions.interaction.capabilities.core.values.SuccessStatus
-import androidx.appactions.interaction.capabilities.core.values.Timer
 import androidx.appactions.interaction.proto.ParamValue
 import androidx.appactions.interaction.protobuf.Struct
 import androidx.appactions.interaction.protobuf.Value
@@ -35,27 +34,38 @@
 /** PauseTimer.kt in interaction-capabilities-productivity */
 private const val CAPABILITY_NAME = "actions.intent.PAUSE_TIMER"
 
-private val ACTION_SPEC = ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-    .setDescriptor(PauseTimer.Property::class.java)
-    .setArgument(PauseTimer.Argument::class.java, PauseTimer.Argument::Builder)
-    .setOutput(PauseTimer.Output::class.java).bindRepeatedStructParameter(
-        "timer",
-        { property -> Optional.ofNullable(property.timerList) },
-        PauseTimer.Argument.Builder::setTimerList,
-        TypeConverters::toTimer
-    ).bindOptionalOutput(
-        "executionStatus",
-        { output -> Optional.ofNullable(output.executionStatus) },
-        PauseTimer.ExecutionStatus::toParamValue
-    ).build()
+private val ACTION_SPEC =
+    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+        .setDescriptor(PauseTimer.Property::class.java)
+        .setArgument(PauseTimer.Argument::class.java, PauseTimer.Argument::Builder)
+        .setOutput(PauseTimer.Output::class.java)
+        .bindRepeatedParameter(
+            "timer",
+            { property -> Optional.ofNullable(property.timerList) },
+            PauseTimer.Argument.Builder::setTimerList,
+            TimerValue.FROM_PARAM_VALUE,
+            TimerValue.TO_ENTITY_VALUE
+        )
+        .bindOptionalOutput(
+            "executionStatus",
+            { output -> Optional.ofNullable(output.executionStatus) },
+            PauseTimer.ExecutionStatus::toParamValue,
+        )
+        .build()
 
 // TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
 class PauseTimer private constructor() {
 
     class CapabilityBuilder :
         CapabilityBuilderBase<
-            CapabilityBuilder, Property, Argument, Output, Confirmation, TaskUpdater, Session
-            >(ACTION_SPEC) {
+            CapabilityBuilder,
+            Property,
+            Argument,
+            Output,
+            Confirmation,
+            TaskUpdater,
+            Session,
+        >(ACTION_SPEC) {
         override fun build(): ActionCapability {
             super.setProperty(Property.Builder().build())
             return super.build()
@@ -63,8 +73,9 @@
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property internal constructor(
-        val timerList: SimpleProperty?
+    class Property
+    internal constructor(
+        val timerList: TypeProperty<TimerValue>?,
     ) {
         override fun toString(): String {
             return "Property(timerList=$timerList}"
@@ -86,17 +97,19 @@
         }
 
         class Builder {
-            private var timerList: SimpleProperty? = null
+            private var timerList: TypeProperty<TimerValue>? = null
 
-            fun setTimerList(timerList: SimpleProperty): Builder =
-                apply { this.timerList = timerList }
+            fun setTimerList(timerList: TypeProperty<TimerValue>): Builder = apply {
+                this.timerList = timerList
+            }
 
             fun build(): Property = Property(timerList)
         }
     }
 
-    class Argument internal constructor(
-        val timerList: List<Timer>?
+    class Argument
+    internal constructor(
+        val timerList: List<TimerValue>?,
     ) {
         override fun toString(): String {
             return "Argument(timerList=$timerList)"
@@ -118,9 +131,11 @@
         }
 
         class Builder : BuilderOf<Argument> {
-            private var timerList: List<Timer>? = null
+            private var timerList: List<TimerValue>? = null
 
-            fun setTimerList(timerList: List<Timer>): Builder = apply { this.timerList = timerList }
+            fun setTimerList(timerList: List<TimerValue>): Builder = apply {
+                this.timerList = timerList
+            }
 
             override fun build(): Argument = Argument(timerList)
         }
@@ -149,8 +164,9 @@
         class Builder {
             private var executionStatus: ExecutionStatus? = null
 
-            fun setExecutionStatus(executionStatus: ExecutionStatus): Builder =
-                apply { this.executionStatus = executionStatus }
+            fun setExecutionStatus(executionStatus: ExecutionStatus): Builder = apply {
+                this.executionStatus = executionStatus
+            }
 
             fun build(): Output = Output(executionStatus)
         }
@@ -177,9 +193,11 @@
                 status = genericErrorStatus.toString()
             }
             val value: Value = Value.newBuilder().setStringValue(status).build()
-            return ParamValue.newBuilder().setStructValue(
-                Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build(),
-            ).build()
+            return ParamValue.newBuilder()
+                .setStructValue(
+                    Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build(),
+                )
+                .build()
         }
     }
 
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResetTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResetTimer.kt
index d62d99a..2392e27 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResetTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResetTimer.kt
@@ -22,11 +22,10 @@
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.SimpleProperty
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty
 import androidx.appactions.interaction.capabilities.core.task.impl.AbstractTaskUpdater
 import androidx.appactions.interaction.capabilities.core.values.GenericErrorStatus
 import androidx.appactions.interaction.capabilities.core.values.SuccessStatus
-import androidx.appactions.interaction.capabilities.core.values.Timer
 import androidx.appactions.interaction.proto.ParamValue
 import androidx.appactions.interaction.protobuf.Struct
 import androidx.appactions.interaction.protobuf.Value
@@ -35,19 +34,24 @@
 /** ResetTimer.kt in interaction-capabilities-productivity */
 private const val CAPABILITY_NAME = "actions.intent.RESET_TIMER"
 
-private val ACTION_SPEC = ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-    .setDescriptor(ResetTimer.Property::class.java)
-    .setArgument(ResetTimer.Argument::class.java, ResetTimer.Argument::Builder)
-    .setOutput(ResetTimer.Output::class.java).bindRepeatedStructParameter(
-        "timer",
-        { property -> Optional.ofNullable(property.timerList) },
-        ResetTimer.Argument.Builder::setTimerList,
-        TypeConverters::toTimer
-    ).bindOptionalOutput(
-        "executionStatus",
-        { output -> Optional.ofNullable(output.executionStatus) },
-        ResetTimer.ExecutionStatus::toParamValue
-    ).build()
+private val ACTION_SPEC =
+    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+        .setDescriptor(ResetTimer.Property::class.java)
+        .setArgument(ResetTimer.Argument::class.java, ResetTimer.Argument::Builder)
+        .setOutput(ResetTimer.Output::class.java)
+        .bindRepeatedParameter(
+            "timer",
+            { property -> Optional.ofNullable(property.timerList) },
+            ResetTimer.Argument.Builder::setTimerList,
+            TimerValue.FROM_PARAM_VALUE,
+            TimerValue.TO_ENTITY_VALUE
+        )
+        .bindOptionalOutput(
+            "executionStatus",
+            { output -> Optional.ofNullable(output.executionStatus) },
+            ResetTimer.ExecutionStatus::toParamValue
+        )
+        .build()
 
 // TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
 class ResetTimer private constructor() {
@@ -55,7 +59,7 @@
     class CapabilityBuilder :
         CapabilityBuilderBase<
             CapabilityBuilder, Property, Argument, Output, Confirmation, TaskUpdater, Session
-            >(ACTION_SPEC) {
+        >(ACTION_SPEC) {
         override fun build(): ActionCapability {
             super.setProperty(Property.Builder().build())
             return super.build()
@@ -63,9 +67,7 @@
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property internal constructor(
-        val timerList: SimpleProperty?
-    ) {
+    class Property internal constructor(val timerList: TypeProperty<TimerValue>?) {
         override fun toString(): String {
             return "Property(timerList=$timerList}"
         }
@@ -86,18 +88,17 @@
         }
 
         class Builder {
-            private var timerList: SimpleProperty? = null
+            private var timerList: TypeProperty<TimerValue>? = null
 
-            fun setTimerList(timerList: SimpleProperty): Builder =
-                apply { this.timerList = timerList }
+            fun setTimerList(timerList: TypeProperty<TimerValue>): Builder = apply {
+                this.timerList = timerList
+            }
 
             fun build(): Property = Property(timerList)
         }
     }
 
-    class Argument internal constructor(
-        val timerList: List<Timer>?
-    ) {
+    class Argument internal constructor(val timerList: List<TimerValue>?) {
         override fun toString(): String {
             return "Argument(timerList=$timerList)"
         }
@@ -118,9 +119,11 @@
         }
 
         class Builder : BuilderOf<Argument> {
-            private var timerList: List<Timer>? = null
+            private var timerList: List<TimerValue>? = null
 
-            fun setTimerList(timerList: List<Timer>): Builder = apply { this.timerList = timerList }
+            fun setTimerList(
+                timerList: List<TimerValue>,
+            ): Builder = apply { this.timerList = timerList }
 
             override fun build(): Argument = Argument(timerList)
         }
@@ -149,8 +152,9 @@
         class Builder {
             private var executionStatus: ExecutionStatus? = null
 
-            fun setExecutionStatus(executionStatus: ExecutionStatus): Builder =
-                apply { this.executionStatus = executionStatus }
+            fun setExecutionStatus(executionStatus: ExecutionStatus): Builder = apply {
+                this.executionStatus = executionStatus
+            }
 
             fun build(): Output = Output(executionStatus)
         }
@@ -177,9 +181,11 @@
                 status = genericErrorStatus.toString()
             }
             val value: Value = Value.newBuilder().setStringValue(status).build()
-            return ParamValue.newBuilder().setStructValue(
-                Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build(),
-            ).build()
+            return ParamValue.newBuilder()
+                .setStructValue(
+                    Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build(),
+                )
+                .build()
         }
     }
 
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResumeTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResumeTimer.kt
index 367ecce..b06f25f 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResumeTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/ResumeTimer.kt
@@ -22,11 +22,10 @@
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.SimpleProperty
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty
 import androidx.appactions.interaction.capabilities.core.task.impl.AbstractTaskUpdater
 import androidx.appactions.interaction.capabilities.core.values.GenericErrorStatus
 import androidx.appactions.interaction.capabilities.core.values.SuccessStatus
-import androidx.appactions.interaction.capabilities.core.values.Timer
 import androidx.appactions.interaction.proto.ParamValue
 import androidx.appactions.interaction.protobuf.Struct
 import androidx.appactions.interaction.protobuf.Value
@@ -35,19 +34,24 @@
 /** ResumeTimer.kt in interaction-capabilities-productivity */
 private const val CAPABILITY_NAME = "actions.intent.RESUME_TIMER"
 
-private val ACTION_SPEC = ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-    .setDescriptor(ResumeTimer.Property::class.java)
-    .setArgument(ResumeTimer.Argument::class.java, ResumeTimer.Argument::Builder)
-    .setOutput(ResumeTimer.Output::class.java).bindRepeatedStructParameter(
-        "timer",
-        { property -> Optional.ofNullable(property.timerList) },
-        ResumeTimer.Argument.Builder::setTimerList,
-        TypeConverters::toTimer
-    ).bindOptionalOutput(
-        "executionStatus",
-        { output -> Optional.ofNullable(output.executionStatus) },
-        ResumeTimer.ExecutionStatus::toParamValue
-    ).build()
+private val ACTION_SPEC =
+    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+        .setDescriptor(ResumeTimer.Property::class.java)
+        .setArgument(ResumeTimer.Argument::class.java, ResumeTimer.Argument::Builder)
+        .setOutput(ResumeTimer.Output::class.java)
+        .bindRepeatedParameter(
+            "timer",
+            { property -> Optional.ofNullable(property.timerList) },
+            ResumeTimer.Argument.Builder::setTimerList,
+            TimerValue.FROM_PARAM_VALUE,
+            TimerValue.TO_ENTITY_VALUE
+        )
+        .bindOptionalOutput(
+            "executionStatus",
+            { output -> Optional.ofNullable(output.executionStatus) },
+            ResumeTimer.ExecutionStatus::toParamValue
+        )
+        .build()
 
 // TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
 class ResumeTimer private constructor() {
@@ -55,7 +59,7 @@
     class CapabilityBuilder :
         CapabilityBuilderBase<
             CapabilityBuilder, Property, Argument, Output, Confirmation, TaskUpdater, Session
-            >(ACTION_SPEC) {
+        >(ACTION_SPEC) {
         override fun build(): ActionCapability {
             super.setProperty(Property.Builder().build())
             return super.build()
@@ -63,9 +67,7 @@
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property internal constructor(
-        val timerList: SimpleProperty?
-    ) {
+    class Property internal constructor(val timerList: TypeProperty<TimerValue>?) {
         override fun toString(): String {
             return "Property(timerList=$timerList}"
         }
@@ -86,18 +88,17 @@
         }
 
         class Builder {
-            private var timerList: SimpleProperty? = null
+            private var timerList: TypeProperty<TimerValue>? = null
 
-            fun setTimerList(timerList: SimpleProperty): Builder =
-                apply { this.timerList = timerList }
+            fun setTimerList(timerList: TypeProperty<TimerValue>): Builder = apply {
+                this.timerList = timerList
+            }
 
             fun build(): Property = Property(timerList)
         }
     }
 
-    class Argument internal constructor(
-        val timerList: List<Timer>?
-    ) {
+    class Argument internal constructor(val timerList: List<TimerValue>?) {
         override fun toString(): String {
             return "Argument(timerList=$timerList)"
         }
@@ -118,9 +119,11 @@
         }
 
         class Builder : BuilderOf<Argument> {
-            private var timerList: List<Timer>? = null
+            private var timerList: List<TimerValue>? = null
 
-            fun setTimerList(timerList: List<Timer>): Builder = apply { this.timerList = timerList }
+            fun setTimerList(
+                timerList: List<TimerValue>,
+            ): Builder = apply { this.timerList = timerList }
 
             override fun build(): Argument = Argument(timerList)
         }
@@ -149,8 +152,9 @@
         class Builder {
             private var executionStatus: ExecutionStatus? = null
 
-            fun setExecutionStatus(executionStatus: ExecutionStatus): Builder =
-                apply { this.executionStatus = executionStatus }
+            fun setExecutionStatus(executionStatus: ExecutionStatus): Builder = apply {
+                this.executionStatus = executionStatus
+            }
 
             fun build(): Output = Output(executionStatus)
         }
@@ -177,9 +181,11 @@
                 status = genericErrorStatus.toString()
             }
             val value: Value = Value.newBuilder().setStringValue(status).build()
-            return ParamValue.newBuilder().setStructValue(
-                Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build(),
-            ).build()
+            return ParamValue.newBuilder()
+                .setStructValue(
+                    Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build(),
+                )
+                .build()
         }
     }
 
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StartTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StartTimer.kt
index 74bec9e..8a561ee 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StartTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StartTimer.kt
@@ -21,10 +21,11 @@
 import androidx.appactions.interaction.capabilities.core.CapabilityBuilderBase
 import androidx.appactions.interaction.capabilities.core.HostProperties
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
+import androidx.appactions.interaction.capabilities.core.impl.converters.PropertyConverter
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.SimpleProperty
-import androidx.appactions.interaction.capabilities.core.properties.StringProperty
+import androidx.appactions.interaction.capabilities.core.properties.StringValue
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty
 import androidx.appactions.interaction.capabilities.core.task.ValueListener
 import androidx.appactions.interaction.capabilities.core.task.impl.AbstractTaskUpdater
 import androidx.appactions.interaction.capabilities.core.values.GenericErrorStatus
@@ -38,32 +39,38 @@
 /** StartTimer.kt in interaction-capabilities-productivity */
 private const val CAPABILITY_NAME = "actions.intent.START_TIMER"
 
-private val ACTION_SPEC = ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-    .setDescriptor(StartTimer.Property::class.java)
-    .setArgument(StartTimer.Argument::class.java, StartTimer.Argument::Builder)
-    .setOutput(StartTimer.Output::class.java)
-    .bindOptionalStringParameter(
-        "timer.identifier",
-        { property -> Optional.ofNullable(property.identifier) },
-        StartTimer.Argument.Builder::setIdentifier
-    )
-    .bindOptionalStringParameter(
-        "timer.name",
-        { property -> Optional.ofNullable(property.name) },
-        StartTimer.Argument.Builder::setName
-    )
-    .bindStructParameter(
-        "timer.duration",
-        { property -> Optional.ofNullable(property.duration) },
-        StartTimer.Argument.Builder::setDuration,
-        TypeConverters::toDuration
-    )
-    .bindOptionalOutput(
-        "executionStatus",
-        { output -> Optional.ofNullable(output.executionStatus) },
-        StartTimer.ExecutionStatus::toParamValue
-    )
-    .build()
+private val ACTION_SPEC =
+    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+        .setDescriptor(StartTimer.Property::class.java)
+        .setArgument(StartTimer.Argument::class.java, StartTimer.Argument::Builder)
+        .setOutput(StartTimer.Output::class.java)
+        .bindOptionalParameter(
+            "timer.identifier",
+            { property -> Optional.ofNullable(property.identifier) },
+            StartTimer.Argument.Builder::setIdentifier,
+            TypeConverters::toStringValue,
+            PropertyConverter::stringValueToProto
+        )
+        .bindOptionalParameter(
+            "timer.name",
+            { property -> Optional.ofNullable(property.name) },
+            StartTimer.Argument.Builder::setName,
+            TypeConverters::toStringValue,
+            PropertyConverter::stringValueToProto
+        )
+        .bindOptionalParameter(
+            "timer.duration",
+            { property -> Optional.ofNullable(property.duration) },
+            StartTimer.Argument.Builder::setDuration,
+            TypeConverters::toDuration,
+            TypeConverters::toEntity
+        )
+        .bindOptionalOutput(
+            "executionStatus",
+            { output -> Optional.ofNullable(output.executionStatus) },
+            StartTimer.ExecutionStatus::toParamValue
+        )
+        .build()
 
 // TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
 class StartTimer private constructor() {
@@ -71,7 +78,7 @@
     class CapabilityBuilder :
         CapabilityBuilderBase<
             CapabilityBuilder, Property, Argument, Output, Confirmation, TaskUpdater, Session
-            >(ACTION_SPEC) {
+        >(ACTION_SPEC) {
 
         fun setSessionFactory(): CapabilityBuilder {
             return this
@@ -95,10 +102,11 @@
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property internal constructor(
-        val identifier: StringProperty?,
-        val name: StringProperty?,
-        val duration: SimpleProperty?
+    class Property
+    internal constructor(
+        val identifier: TypeProperty<StringValue>?,
+        val name: TypeProperty<StringValue>?,
+        val duration: TypeProperty<Duration>?
     ) {
         override fun toString(): String {
             return "Property(identifier=$identifier,name=$name,duration=$duration}"
@@ -125,28 +133,26 @@
         }
 
         class Builder {
-            private var identifier: StringProperty? = null
-            private var name: StringProperty? = null
-            private var duration: SimpleProperty? = null
+            private var identifier: TypeProperty<StringValue>? = null
+            private var name: TypeProperty<StringValue>? = null
+            private var duration: TypeProperty<Duration>? = null
 
-            fun setIdentifier(identifier: StringProperty): Builder =
-                apply { this.identifier = identifier }
+            fun setIdentifier(identifier: TypeProperty<StringValue>): Builder = apply {
+                this.identifier = identifier
+            }
 
-            fun setName(name: StringProperty): Builder =
-                apply { this.name = name }
+            fun setName(name: TypeProperty<StringValue>): Builder = apply { this.name = name }
 
-            fun setDuration(duration: SimpleProperty): Builder =
-                apply { this.duration = duration }
+            fun setDuration(duration: TypeProperty<Duration>): Builder = apply {
+                this.duration = duration
+            }
 
             fun build(): Property = Property(identifier, name, duration)
         }
     }
 
-    class Argument internal constructor(
-        val identifier: String?,
-        val name: String?,
-        val duration: Duration?
-    ) {
+    class Argument
+    internal constructor(val identifier: String?, val name: String?, val duration: Duration?) {
         override fun toString(): String {
             return "Argument(identifier=$identifier,name=$name,duration=$duration)"
         }
@@ -176,14 +182,11 @@
             private var name: String? = null
             private var duration: Duration? = null
 
-            fun setIdentifier(identifier: String): Builder =
-                apply { this.identifier = identifier }
+            fun setIdentifier(identifier: String): Builder = apply { this.identifier = identifier }
 
-            fun setName(name: String): Builder =
-                apply { this.name = name }
+            fun setName(name: String): Builder = apply { this.name = name }
 
-            fun setDuration(duration: Duration): Builder =
-                apply { this.duration = duration }
+            fun setDuration(duration: Duration): Builder = apply { this.duration = duration }
 
             override fun build(): Argument = Argument(identifier, name, duration)
         }
@@ -212,8 +215,9 @@
         class Builder {
             private var executionStatus: ExecutionStatus? = null
 
-            fun setExecutionStatus(executionStatus: ExecutionStatus): Builder =
-                apply { this.executionStatus = executionStatus }
+            fun setExecutionStatus(executionStatus: ExecutionStatus): Builder = apply {
+                this.executionStatus = executionStatus
+            }
 
             fun build(): Output = Output(executionStatus)
         }
@@ -240,13 +244,15 @@
                 status = genericErrorStatus.toString()
             }
             val value: Value = Value.newBuilder().setStringValue(status).build()
-            return ParamValue.newBuilder().setStructValue(
-                Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build(),
-            ).build()
+            return ParamValue.newBuilder()
+                .setStructValue(
+                    Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build(),
+                )
+                .build()
         }
     }
 
     class Confirmation internal constructor()
 
     class TaskUpdater internal constructor() : AbstractTaskUpdater()
-}
\ No newline at end of file
+}
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StopTimer.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StopTimer.kt
index 24e9407..eb62e6d 100644
--- a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StopTimer.kt
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/StopTimer.kt
@@ -22,11 +22,10 @@
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.SimpleProperty
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty
 import androidx.appactions.interaction.capabilities.core.task.impl.AbstractTaskUpdater
 import androidx.appactions.interaction.capabilities.core.values.GenericErrorStatus
 import androidx.appactions.interaction.capabilities.core.values.SuccessStatus
-import androidx.appactions.interaction.capabilities.core.values.Timer
 import androidx.appactions.interaction.proto.ParamValue
 import androidx.appactions.interaction.protobuf.Struct
 import androidx.appactions.interaction.protobuf.Value
@@ -35,19 +34,24 @@
 /** StopTimer.kt in interaction-capabilities-productivity */
 private const val CAPABILITY_NAME = "actions.intent.STOP_TIMER"
 
-private val ACTION_SPEC = ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
-    .setDescriptor(StopTimer.Property::class.java)
-    .setArgument(StopTimer.Argument::class.java, StopTimer.Argument::Builder)
-    .setOutput(StopTimer.Output::class.java).bindRepeatedStructParameter(
-        "timer",
-        { property -> Optional.ofNullable(property.timerList) },
-        StopTimer.Argument.Builder::setTimerList,
-        TypeConverters::toTimer
-    ).bindOptionalOutput(
-        "executionStatus",
-        { output -> Optional.ofNullable(output.executionStatus) },
-        StopTimer.ExecutionStatus::toParamValue
-    ).build()
+private val ACTION_SPEC =
+    ActionSpecBuilder.ofCapabilityNamed(CAPABILITY_NAME)
+        .setDescriptor(StopTimer.Property::class.java)
+        .setArgument(StopTimer.Argument::class.java, StopTimer.Argument::Builder)
+        .setOutput(StopTimer.Output::class.java)
+        .bindRepeatedParameter(
+            "timer",
+            { property -> Optional.ofNullable(property.timerList) },
+            StopTimer.Argument.Builder::setTimerList,
+            TimerValue.FROM_PARAM_VALUE,
+            TimerValue.TO_ENTITY_VALUE
+        )
+        .bindOptionalOutput(
+            "executionStatus",
+            { output -> Optional.ofNullable(output.executionStatus) },
+            StopTimer.ExecutionStatus::toParamValue
+        )
+        .build()
 
 // TODO(b/267806701): Add capability factory annotation once the testing library is fully migrated.
 class StopTimer private constructor() {
@@ -55,7 +59,7 @@
     class CapabilityBuilder :
         CapabilityBuilderBase<
             CapabilityBuilder, Property, Argument, Output, Confirmation, TaskUpdater, Session
-            >(ACTION_SPEC) {
+        >(ACTION_SPEC) {
         override fun build(): ActionCapability {
             super.setProperty(Property.Builder().build())
             return super.build()
@@ -63,9 +67,7 @@
     }
 
     // TODO(b/268369632): Remove Property from public capability APIs.
-    class Property internal constructor(
-        val timerList: SimpleProperty?
-    ) {
+    class Property internal constructor(val timerList: TypeProperty<TimerValue>?) {
         override fun toString(): String {
             return "Property(timerList=$timerList}"
         }
@@ -86,18 +88,17 @@
         }
 
         class Builder {
-            private var timerList: SimpleProperty? = null
+            private var timerList: TypeProperty<TimerValue>? = null
 
-            fun setTimerList(timerList: SimpleProperty): Builder =
-                apply { this.timerList = timerList }
+            fun setTimerList(timerList: TypeProperty<TimerValue>): Builder = apply {
+                this.timerList = timerList
+            }
 
             fun build(): Property = Property(timerList)
         }
     }
 
-    class Argument internal constructor(
-        val timerList: List<Timer>?
-    ) {
+    class Argument internal constructor(val timerList: List<TimerValue>?) {
         override fun toString(): String {
             return "Argument(timerList=$timerList)"
         }
@@ -118,9 +119,11 @@
         }
 
         class Builder : BuilderOf<Argument> {
-            private var timerList: List<Timer>? = null
+            private var timerList: List<TimerValue>? = null
 
-            fun setTimerList(timerList: List<Timer>): Builder = apply { this.timerList = timerList }
+            fun setTimerList(
+                timerList: List<TimerValue>,
+            ): Builder = apply { this.timerList = timerList }
 
             override fun build(): Argument = Argument(timerList)
         }
@@ -149,8 +152,9 @@
         class Builder {
             private var executionStatus: ExecutionStatus? = null
 
-            fun setExecutionStatus(executionStatus: ExecutionStatus): Builder =
-                apply { this.executionStatus = executionStatus }
+            fun setExecutionStatus(executionStatus: ExecutionStatus): Builder = apply {
+                this.executionStatus = executionStatus
+            }
 
             fun build(): Output = Output(executionStatus)
         }
@@ -177,9 +181,11 @@
                 status = genericErrorStatus.toString()
             }
             val value: Value = Value.newBuilder().setStringValue(status).build()
-            return ParamValue.newBuilder().setStructValue(
-                Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build(),
-            ).build()
+            return ParamValue.newBuilder()
+                .setStructValue(
+                    Struct.newBuilder().putFields(TypeConverters.FIELD_NAME_TYPE, value).build(),
+                )
+                .build()
         }
     }
 
diff --git a/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/TimerValue.kt b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/TimerValue.kt
new file mode 100644
index 0000000..44cbb94
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-productivity/src/main/java/androidx/appactions/interaction/capabilities/productivity/TimerValue.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appactions.interaction.capabilities.productivity
+
+import androidx.appactions.interaction.capabilities.core.impl.converters.EntityConverter
+import androidx.appactions.interaction.capabilities.core.impl.converters.ParamValueConverter
+import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
+import androidx.appactions.interaction.capabilities.core.impl.converters.UnionTypeSpec
+import androidx.appactions.interaction.capabilities.core.values.SearchAction
+import androidx.appactions.interaction.capabilities.core.values.Timer
+import androidx.appactions.interaction.proto.Entity
+import java.util.Objects
+
+class TimerValue
+private constructor(
+    val asTimer: Timer?,
+    val asTimerFilter: SearchAction<Timer>?,
+) {
+    constructor(timer: Timer) : this(timer, null)
+
+    // TODO(b/268071906) add TimerFilter type to SearchAction
+    constructor(timerFilter: SearchAction<Timer>) : this(null, timerFilter)
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as TimerValue
+
+        return asTimer == other.asTimer && asTimerFilter == other.asTimerFilter
+    }
+
+    override fun hashCode(): Int {
+        return Objects.hash(asTimer, asTimerFilter)
+    }
+
+    companion object {
+        private val TYPE_SPEC =
+            UnionTypeSpec.Builder<TimerValue>()
+                .bindMemberType(
+                    memberGetter = TimerValue::asTimer,
+                    ctor = { TimerValue(it) },
+                    typeSpec = TypeConverters.TIMER_TYPE_SPEC,
+                )
+                .bindMemberType(
+                    memberGetter = TimerValue::asTimerFilter,
+                    ctor = { TimerValue(it) },
+                    typeSpec =
+                        TypeConverters.createSearchActionTypeSpec(
+                            TypeConverters.TIMER_TYPE_SPEC,
+                        ),
+                )
+                .build()
+
+        internal val FROM_PARAM_VALUE = ParamValueConverter {
+            TYPE_SPEC.fromStruct(it.structValue)
+        }
+
+        internal val TO_ENTITY_VALUE =
+            EntityConverter<TimerValue> { it ->
+                val builder = Entity.newBuilder().setValue(TYPE_SPEC.toStruct(it))
+                it.asTimer?.id?.ifPresent { builder.identifier = it }
+                builder.build()
+            }
+    }
+}
diff --git a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartSafetyCheck.kt b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartSafetyCheck.kt
index accef44..c126d96 100644
--- a/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartSafetyCheck.kt
+++ b/appactions/interaction/interaction-capabilities-safety/src/main/java/androidx/appactions/interaction/capabilities/safety/StartSafetyCheck.kt
@@ -22,7 +22,7 @@
 import androidx.appactions.interaction.capabilities.core.impl.BuilderOf
 import androidx.appactions.interaction.capabilities.core.impl.converters.TypeConverters
 import androidx.appactions.interaction.capabilities.core.impl.spec.ActionSpecBuilder
-import androidx.appactions.interaction.capabilities.core.properties.SimpleProperty
+import androidx.appactions.interaction.capabilities.core.properties.TypeProperty
 import androidx.appactions.interaction.capabilities.core.task.impl.AbstractTaskUpdater
 import androidx.appactions.interaction.capabilities.core.values.GenericErrorStatus
 import androidx.appactions.interaction.capabilities.core.values.SafetyCheck
@@ -47,17 +47,19 @@
         .setDescriptor(StartSafetyCheck.Property::class.java)
         .setArgument(StartSafetyCheck.Argument::class.java, StartSafetyCheck.Argument::Builder)
         .setOutput(StartSafetyCheck.Output::class.java)
-        .bindStructParameter(
+        .bindOptionalParameter(
             "safetyCheck.duration",
             { property -> Optional.ofNullable(property.duration) },
             StartSafetyCheck.Argument.Builder::setDuration,
-            TypeConverters::toDuration
+            TypeConverters::toDuration,
+            TypeConverters::toEntity
         )
-        .bindStructParameter(
+        .bindOptionalParameter(
             "safetyCheck.checkInTime",
             { property -> Optional.ofNullable(property.checkInTime) },
             StartSafetyCheck.Argument.Builder::setCheckInTime,
-            TypeConverters::toZonedDateTime
+            TypeConverters::toZonedDateTime,
+            TypeConverters::toEntity
         )
         .bindOptionalOutput(
             "safetyCheck",
@@ -87,8 +89,8 @@
 
     // TODO(b/268369632): Remove Property from public capability APIs.
     class Property internal constructor(
-        val duration: SimpleProperty?,
-        val checkInTime: SimpleProperty?
+        val duration: TypeProperty<Duration>?,
+        val checkInTime: TypeProperty<ZonedDateTime>?
     ) {
         override fun toString(): String {
             return "Property(duration=$duration, checkInTime=$checkInTime)"
@@ -113,14 +115,14 @@
         }
 
         class Builder {
-            private var duration: SimpleProperty? = null
+            private var duration: TypeProperty<Duration>? = null
 
-            private var checkInTime: SimpleProperty? = null
+            private var checkInTime: TypeProperty<ZonedDateTime>? = null
 
-            fun setDuration(duration: SimpleProperty): Builder =
+            fun setDuration(duration: TypeProperty<Duration>): Builder =
                 apply { this.duration = duration }
 
-            fun setCheckInTime(checkInTime: SimpleProperty): Builder =
+            fun setCheckInTime(checkInTime: TypeProperty<ZonedDateTime>): Builder =
                 apply { this.checkInTime = checkInTime }
 
             fun build(): Property = Property(duration, checkInTime)
diff --git a/ads/ads-identifier-common/api/current.txt b/appactions/interaction/interaction-capabilities-testing/api/current.txt
similarity index 100%
rename from ads/ads-identifier-common/api/current.txt
rename to appactions/interaction/interaction-capabilities-testing/api/current.txt
diff --git a/ads/ads-identifier-common/api/public_plus_experimental_current.txt b/appactions/interaction/interaction-capabilities-testing/api/public_plus_experimental_current.txt
similarity index 100%
rename from ads/ads-identifier-common/api/public_plus_experimental_current.txt
rename to appactions/interaction/interaction-capabilities-testing/api/public_plus_experimental_current.txt
diff --git a/ads/ads-identifier-common/api/res-current.txt b/appactions/interaction/interaction-capabilities-testing/api/res-current.txt
similarity index 100%
rename from ads/ads-identifier-common/api/res-current.txt
rename to appactions/interaction/interaction-capabilities-testing/api/res-current.txt
diff --git a/ads/ads-identifier-common/api/current.txt b/appactions/interaction/interaction-capabilities-testing/api/restricted_current.txt
similarity index 100%
copy from ads/ads-identifier-common/api/current.txt
copy to appactions/interaction/interaction-capabilities-testing/api/restricted_current.txt
diff --git a/appactions/interaction/interaction-capabilities-testing/build.gradle b/appactions/interaction/interaction-capabilities-testing/build.gradle
new file mode 100644
index 0000000..1a76e781
--- /dev/null
+++ b/appactions/interaction/interaction-capabilities-testing/build.gradle
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryType
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    api(libs.kotlinStdlib)
+    // Add dependencies here
+}
+
+android {
+    namespace "androidx.appactions.interaction.capabilities.testing"
+}
+
+androidx {
+    name = "androidx.appactions.interaction:interaction-capabilities-testing"
+    type = LibraryType.PUBLISHED_TEST_LIBRARY
+    inceptionYear = "2023"
+    description = "Test integrations with App Actions Interactions capabilities libraries."
+}
diff --git a/appactions/interaction/interaction-proto/src/main/proto/app_actions_data.proto b/appactions/interaction/interaction-proto/src/main/proto/app_actions_data.proto
index ff4aec8..f30d013 100644
--- a/appactions/interaction/interaction-proto/src/main/proto/app_actions_data.proto
+++ b/appactions/interaction/interaction-proto/src/main/proto/app_actions_data.proto
@@ -163,7 +163,7 @@
   message AppDialogState {
     // Each App capability can be associated with multiple dialogs. Use this
     // field to identify the dialog instance.
-    string dialog_identifier = 1;
+    string session_identifier = 1;
 
     // Identifier of the corresponding capability that this dialog state maps
     // to. This field maps to AppActionsContext.AppAction.identifier.
@@ -210,7 +210,7 @@
 }
 
 // Represents the data used to perform an intent.
-// Next ID: 8
+// Next ID: 7
 message FulfillmentRequest {
   message Fulfillment {
     // Name of the intent. For example: actions.intent.UPDATE_FORM_FIELD
@@ -220,24 +220,6 @@
     // |identifier| field with in `AppActionsContext.AppAction`.
     string identifier = 2;
 
-    message SessionInfo {
-      // Identifier of the dialog session that the user is currently interacting
-      // with. Assistant will generate this ID as UUID. This is different from
-      // the identifier field in FulfillmentRequest.Fulfillment in that identifier
-      // stays constant for each intent, even when there are multiple dialog
-      // instances of the intent, but the session_identifier is unique per dialog
-      // instance.
-      string session_identifier = 1;
-
-      // True if this fulfillment request is from a new dialog. Mainly used for
-      // debugging purpose.
-      bool is_new_session = 2;
-    }
-
-    // Information related to this dialog session. Used by the SDK to create a new
-    // session.
-    optional SessionInfo session_info = 7;
-
     message FulfillmentValue {
       // The resolved value for the parameter.
       ParamValue value = 1;
diff --git a/appactions/interaction/interaction-service-proto/src/main/proto/app_interaction_service.proto b/appactions/interaction/interaction-service-proto/src/main/proto/app_interaction_service.proto
index e67272e..e4cbeb2 100644
--- a/appactions/interaction/interaction-service-proto/src/main/proto/app_interaction_service.proto
+++ b/appactions/interaction/interaction-service-proto/src/main/proto/app_interaction_service.proto
@@ -30,11 +30,11 @@
 // user issues a query. This proto contains user's query (parsed into BII).
 // NEXT_ID: 3
 message Request {
-  // Session_id is used to identify the hosts. When there are multiple hosts
-  // connecting to the provider at the same time for different requests,
-  // provider can use the session_ids to distinguish sessions, thus
-  // providing different responses to different hosts.
-  int32 session_id = 1  [deprecated = true];
+  // This session id corresponds to the one in [AppDialogState]. Session id is used to identify the
+  // hosts. When there are multiple hosts connecting to the provider at the same time for different
+  // requests, provider can use the session_ids to distinguish sessions, thus providing different
+  // responses to different hosts.
+  string session_identifier = 1;
   // FulfillmentRequest contains the request data sent from Assistant, such as
   // new values of BII arguments.
   .androidx.appactions.interaction.proto.FulfillmentRequest fulfillment_request = 2;
@@ -91,8 +91,8 @@
 // NEXT_ID: 3
 message UiRequest {
   int32 session_id = 1 [deprecated = true];
-  // Indicates the UiResponse should contain only the responses for this
-  // particular session.
+  // This session id corresponds to the one in [AppDialogState]. Indicates the UiResponse should
+  // contain only the responses for this particular session.
   string session_identifier = 2;
 }
 
@@ -195,8 +195,8 @@
 // NEXT_ID: 11
 message CollectionRequest {
   int32 session_id = 1 [deprecated = true];
-  // Indicates the CollectionResponse should contain only the responses for this
-  // particular session.
+  // This session id corresponds to the one in [AppDialogState]. Indicates the CollectionResponse
+  // should contain only the responses for this particular session.
   string session_identifier = 10;
   int32 view_id = 2;
   oneof request_data {
diff --git a/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/AppInteractionServiceGrpcImpl.java b/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/AppInteractionServiceGrpcImpl.java
index bbc565b..238fbfb 100644
--- a/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/AppInteractionServiceGrpcImpl.java
+++ b/appactions/interaction/interaction-service/src/main/java/androidx/appactions/interaction/service/AppInteractionServiceGrpcImpl.java
@@ -221,9 +221,8 @@
                                     ERROR_NO_ACTION_CAPABILITY)));
             return;
         }
-        String sessionId = selectedFulfillment.getSessionInfo().getSessionIdentifier();
-        ActionCapabilitySession currentSession =
-                SessionManager.INSTANCE.getSession(sessionId);
+        String sessionId = request.getSessionIdentifier();
+        ActionCapabilitySession currentSession = SessionManager.INSTANCE.getSession(sessionId);
         if (currentSession == null) {
             responseObserver.onError(
                     new StatusRuntimeException(
@@ -245,8 +244,7 @@
                         Response.Builder responseBuilder =
                                 convertFulfillmentResponse(fulfillmentResponse, capability.get())
                                         .toBuilder();
-                        UiCache uiCache = UiSessions.INSTANCE.getUiCacheOrNull(
-                                sessionId);
+                        UiCache uiCache = UiSessions.INSTANCE.getUiCacheOrNull(sessionId);
                         if (uiCache != null && uiCache.hasUnreadUiResponse()) {
                             responseBuilder.setUiUpdate(UiUpdate.getDefaultInstance());
                             if (!uiCache.getCachedChangedViewIds().isEmpty()) {
diff --git a/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/AppInteractionServiceGrpcImplTest.kt b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/AppInteractionServiceGrpcImplTest.kt
index 8cfa1da..9b7e7e6 100644
--- a/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/AppInteractionServiceGrpcImplTest.kt
+++ b/appactions/interaction/interaction-service/src/test/java/androidx/appactions/interaction/service/AppInteractionServiceGrpcImplTest.kt
@@ -24,7 +24,6 @@
 import androidx.appactions.interaction.proto.AppActionsContext.AppDialogState
 import androidx.appactions.interaction.proto.FulfillmentRequest
 import androidx.appactions.interaction.proto.FulfillmentRequest.Fulfillment
-import androidx.appactions.interaction.proto.FulfillmentRequest.Fulfillment.SessionInfo
 import androidx.appactions.interaction.proto.FulfillmentResponse
 import androidx.appactions.interaction.proto.FulfillmentResponse.StructuredOutput
 import androidx.appactions.interaction.proto.FulfillmentResponse.StructuredOutput.OutputValue
@@ -66,8 +65,6 @@
 import java.util.Collections
 import kotlin.test.assertFailsWith
 import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
 import org.junit.Before
 import org.junit.Rule
@@ -76,7 +73,6 @@
 
 // TODO(b/271929200) Implement tests for the 2 UI related RPCs
 @RunWith(AndroidJUnit4::class)
-@kotlinx.coroutines.ExperimentalCoroutinesApi
 class AppInteractionServiceGrpcImplTest {
 
     @get:Rule val grpcCleanup = GrpcCleanupRule()
@@ -96,18 +92,14 @@
     private val testFulfillmentRequest =
         FulfillmentRequest.newBuilder()
             .addFulfillments(
-                Fulfillment.newBuilder()
-                    .setName(testBiiName)
-                    .setIdentifier(capabilityId)
-                    .setSessionInfo(SessionInfo.newBuilder().setSessionIdentifier(sessionId))
-                    .build()
+                Fulfillment.newBuilder().setName(testBiiName).setIdentifier(capabilityId).build()
             )
             .build()
     private val testFulfillmentResponse =
         FulfillmentResponse.newBuilder()
             .setExecutionOutput(
                 StructuredOutput.newBuilder()
-                    .addOutputValues(OutputValue.newBuilder().setName("bio_arg1"))
+                    .addOutputValues(OutputValue.newBuilder().setName("bio_arg1")),
             )
             .build()
     private var capability1 = mock<ActionCapability>()
@@ -126,36 +118,33 @@
         val server =
             createInProcessServer(
                 AppInteractionServiceGrpcImpl(FakeAppInteractionService(listOf(capability1))),
-                remoteViewsInterceptor
+                remoteViewsInterceptor,
             )
-        coroutineScope {
-            launch {
-                val channel = createInProcessChannel()
-                val stub = AppInteractionServiceGrpc.newStub(channel)
 
-                // Set up gRPC response capture
-                val startUpSessionCallback = CompletableDeferred<Unit>()
-                val startSessionResponseObserver = mock<StreamObserver<StartSessionResponse>>()
-                whenever(startSessionResponseObserver.onNext(any())) doAnswer
-                    {
-                        startUpSessionCallback.complete(Unit)
-                        Unit
-                    }
+        val channel = createInProcessChannel()
+        val stub = AppInteractionServiceGrpc.newStub(channel)
 
-                // Send startup request
-                val startSessionRequestObserver = stub.startUpSession(startSessionResponseObserver)
-                startSessionRequestObserver.onNext(defaultStartSessionRequest)
-
-                // Assert startup response
-                startUpSessionCallback.await()
-                val responseCaptor = argumentCaptor<StartSessionResponse>()
-                verify(startSessionResponseObserver).onNext(responseCaptor.capture())
-                val startSessionResponse = responseCaptor.firstValue
-                assertThat(startSessionResponse)
-                    .isEqualTo(StartSessionResponse.getDefaultInstance())
-                verify(startSessionResponseObserver, times(1)).onNext(any())
+        // Set up gRPC response capture
+        val startUpSessionCallback = CompletableDeferred<Unit>()
+        val startSessionResponseObserver = mock<StreamObserver<StartSessionResponse>>()
+        whenever(startSessionResponseObserver.onNext(any())) doAnswer
+            {
+                startUpSessionCallback.complete(Unit)
+                Unit
             }
-        }
+
+        // Send startup request
+        val startSessionRequestObserver = stub.startUpSession(startSessionResponseObserver)
+        startSessionRequestObserver.onNext(defaultStartSessionRequest)
+
+        // Assert startup response
+        startUpSessionCallback.await()
+        val responseCaptor = argumentCaptor<StartSessionResponse>()
+        verify(startSessionResponseObserver).onNext(responseCaptor.capture())
+        val startSessionResponse = responseCaptor.firstValue
+        assertThat(startSessionResponse).isEqualTo(StartSessionResponse.getDefaultInstance())
+        verify(startSessionResponseObserver, times(1)).onNext(any())
+
         server.shutdownNow()
     }
 
@@ -164,34 +153,32 @@
         val server =
             createInProcessServer(
                 AppInteractionServiceGrpcImpl(FakeAppInteractionService(listOf(capability1))),
-                remoteViewsInterceptor
+                remoteViewsInterceptor,
             )
-        coroutineScope {
-            launch {
-                val channel = createInProcessChannel()
-                val stub = AppInteractionServiceGrpc.newStub(channel)
-                val startSessionResponseObserver = mock<StreamObserver<StartSessionResponse>>()
 
-                // Send startup request
-                val invalidStartSessionRequest =
-                    StartSessionRequest.newBuilder()
-                        .setIntentName(testBiiName)
-                        .setIdentifier("UNKNOWN_FULFILLMENT_ID")
-                        .setSessionIdentifier(sessionId)
-                        .build()
-                val startSessionRequestObserver = stub.startUpSession(startSessionResponseObserver)
-                startSessionRequestObserver.onNext(invalidStartSessionRequest)
+        val channel = createInProcessChannel()
+        val stub = AppInteractionServiceGrpc.newStub(channel)
+        val startSessionResponseObserver = mock<StreamObserver<StartSessionResponse>>()
 
-                // Assert.
-                val exceptionCaptor = argumentCaptor<StatusRuntimeException>()
-                verify(startSessionResponseObserver).onError(exceptionCaptor.capture())
-                assertThat(Status.fromThrowable(exceptionCaptor.firstValue).code)
-                    .isEqualTo(Status.Code.FAILED_PRECONDITION)
-                assertThat(Status.fromThrowable(exceptionCaptor.firstValue).description)
-                    .isEqualTo(ERROR_NO_ACTION_CAPABILITY)
-                verify(capability1, never()).createSession(any())
-            }
-        }
+        // Send startup request
+        val invalidStartSessionRequest =
+            StartSessionRequest.newBuilder()
+                .setIntentName(testBiiName)
+                .setIdentifier("UNKNOWN_FULFILLMENT_ID")
+                .setSessionIdentifier(sessionId)
+                .build()
+        val startSessionRequestObserver = stub.startUpSession(startSessionResponseObserver)
+        startSessionRequestObserver.onNext(invalidStartSessionRequest)
+
+        // Assert.
+        val exceptionCaptor = argumentCaptor<StatusRuntimeException>()
+        verify(startSessionResponseObserver).onError(exceptionCaptor.capture())
+        assertThat(Status.fromThrowable(exceptionCaptor.firstValue).code)
+            .isEqualTo(Status.Code.FAILED_PRECONDITION)
+        assertThat(Status.fromThrowable(exceptionCaptor.firstValue).description)
+            .isEqualTo(ERROR_NO_ACTION_CAPABILITY)
+        verify(capability1, never()).createSession(any())
+
         server.shutdownNow()
     }
 
@@ -200,26 +187,25 @@
         val server =
             createInProcessServer(
                 AppInteractionServiceGrpcImpl(FakeAppInteractionService(listOf(capability1))),
-                remoteViewsInterceptor
+                remoteViewsInterceptor,
             )
-        coroutineScope {
-            launch {
-                val channel = createInProcessChannel()
-                val stub = AppInteractionServiceGrpc.newStub(channel)
-                val futureStub = AppInteractionServiceGrpc.newFutureStub(channel)
-                assertStartupSession(stub)
-                verify(capability1, times(1)).createSession(any())
+        val channel = createInProcessChannel()
+        val stub = AppInteractionServiceGrpc.newStub(channel)
+        val futureStub = AppInteractionServiceGrpc.newFutureStub(channel)
+        assertStartupSession(stub)
+        verify(capability1, times(1)).createSession(any())
 
-                // Send fulfillment request
-                val request =
-                    Request.newBuilder().setFulfillmentRequest(testFulfillmentRequest).build()
-                val responseFuture = futureStub.sendRequestFulfillment(request)
+        // Send fulfillment request
+        val request =
+            Request.newBuilder()
+                .setSessionIdentifier(sessionId)
+                .setFulfillmentRequest(testFulfillmentRequest)
+                .build()
+        val responseFuture = futureStub.sendRequestFulfillment(request)
 
-                val response = responseFuture.await()
-                assertThat(response).isNotNull()
-                assertThat(response.fulfillmentResponse).isEqualTo(testFulfillmentResponse)
-            }
-        }
+        val response = responseFuture.await()
+        assertThat(response).isNotNull()
+        assertThat(response.fulfillmentResponse).isEqualTo(testFulfillmentResponse)
         server.shutdownNow()
     }
 
@@ -228,31 +214,28 @@
         val server =
             createInProcessServer(
                 AppInteractionServiceGrpcImpl(FakeAppInteractionService(listOf(capability1))),
-                remoteViewsInterceptor
+                remoteViewsInterceptor,
             )
-        coroutineScope {
-            launch {
-                val channel = createInProcessChannel()
-                val stub = AppInteractionServiceGrpc.newStub(channel)
-                val futureStub = AppInteractionServiceGrpc.newFutureStub(channel)
-                assertStartupSession(stub)
-                verify(capability1, times(1)).createSession(any())
 
-                // Ensure a failed future is returned when missing fulfillment
-                val requestWithMissingFulfillment =
-                    Request.newBuilder()
-                        .setFulfillmentRequest(FulfillmentRequest.getDefaultInstance())
-                        .build()
-                val exception =
-                    assertFailsWith<StatusRuntimeException> {
-                        futureStub.sendRequestFulfillment(requestWithMissingFulfillment).await()
-                    }
-                assertThat(Status.fromThrowable(exception).code)
-                    .isEqualTo(Status.Code.FAILED_PRECONDITION)
-                assertThat(Status.fromThrowable(exception).description)
-                    .isEqualTo(ERROR_NO_FULFILLMENT_REQUEST)
+        val channel = createInProcessChannel()
+        val stub = AppInteractionServiceGrpc.newStub(channel)
+        val futureStub = AppInteractionServiceGrpc.newFutureStub(channel)
+        assertStartupSession(stub)
+        verify(capability1, times(1)).createSession(any())
+
+        // Ensure a failed future is returned when missing fulfillment
+        val requestWithMissingFulfillment =
+            Request.newBuilder()
+                .setFulfillmentRequest(FulfillmentRequest.getDefaultInstance())
+                .build()
+        val exception =
+            assertFailsWith<StatusRuntimeException> {
+                futureStub.sendRequestFulfillment(requestWithMissingFulfillment).await()
             }
-        }
+        assertThat(Status.fromThrowable(exception).code).isEqualTo(Status.Code.FAILED_PRECONDITION)
+        assertThat(Status.fromThrowable(exception).description)
+            .isEqualTo(ERROR_NO_FULFILLMENT_REQUEST)
+
         server.shutdownNow()
     }
 
@@ -261,41 +244,33 @@
         val server =
             createInProcessServer(
                 AppInteractionServiceGrpcImpl(FakeAppInteractionService(listOf(capability1))),
-                remoteViewsInterceptor
+                remoteViewsInterceptor,
             )
-        coroutineScope {
-            launch {
-                val channel = createInProcessChannel()
-                val stub = AppInteractionServiceGrpc.newStub(channel)
-                val futureStub = AppInteractionServiceGrpc.newFutureStub(channel)
-                assertStartupSession(stub)
-                verify(capability1, times(1)).createSession(any())
 
-                val requestWithUnknownFulfillmentId =
-                    Request.newBuilder()
-                        .setFulfillmentRequest(
-                            FulfillmentRequest.newBuilder()
-                                .addFulfillments(
-                                    Fulfillment.newBuilder()
-                                        .setIdentifier("UNKNOWN_FULFILLMENT_ID")
-                                        .setSessionInfo(
-                                            SessionInfo.newBuilder()
-                                                .setIsNewSession(true)
-                                                .setSessionIdentifier(sessionId)
-                                        )
-                                )
-                        )
-                        .build()
-                val exception =
-                    assertFailsWith<StatusRuntimeException> {
-                        futureStub.sendRequestFulfillment(requestWithUnknownFulfillmentId).await()
-                    }
-                assertThat(Status.fromThrowable(exception).code)
-                    .isEqualTo(Status.Code.FAILED_PRECONDITION)
-                assertThat(Status.fromThrowable(exception).description)
-                    .isEqualTo(ERROR_NO_ACTION_CAPABILITY)
+        val channel = createInProcessChannel()
+        val stub = AppInteractionServiceGrpc.newStub(channel)
+        val futureStub = AppInteractionServiceGrpc.newFutureStub(channel)
+        assertStartupSession(stub)
+        verify(capability1, times(1)).createSession(any())
+
+        val requestWithUnknownFulfillmentId =
+            Request.newBuilder()
+                .setSessionIdentifier(sessionId)
+                .setFulfillmentRequest(
+                    FulfillmentRequest.newBuilder()
+                        .addFulfillments(
+                            Fulfillment.newBuilder().setIdentifier("UNKNOWN_FULFILLMENT_ID")
+                        ),
+                )
+                .build()
+        val exception =
+            assertFailsWith<StatusRuntimeException> {
+                futureStub.sendRequestFulfillment(requestWithUnknownFulfillmentId).await()
             }
-        }
+        assertThat(Status.fromThrowable(exception).code).isEqualTo(Status.Code.FAILED_PRECONDITION)
+        assertThat(Status.fromThrowable(exception).description)
+            .isEqualTo(ERROR_NO_ACTION_CAPABILITY)
+
         server.shutdownNow()
     }
 
@@ -304,40 +279,30 @@
         val server =
             createInProcessServer(
                 AppInteractionServiceGrpcImpl(FakeAppInteractionService(listOf(capability1))),
-                remoteViewsInterceptor
+                remoteViewsInterceptor,
             )
-        coroutineScope {
-            launch {
-                val channel = createInProcessChannel()
-                val stub = AppInteractionServiceGrpc.newStub(channel)
-                val futureStub = AppInteractionServiceGrpc.newFutureStub(channel)
-                assertStartupSession(stub)
-                verify(capability1, times(1)).createSession(any())
 
-                val requestWithUnknownFulfillmentId =
-                    Request.newBuilder()
-                        .setFulfillmentRequest(
-                            FulfillmentRequest.newBuilder()
-                                .addFulfillments(
-                                    Fulfillment.newBuilder()
-                                        .setIdentifier(capabilityId)
-                                        .setSessionInfo(
-                                            SessionInfo.newBuilder()
-                                                .setIsNewSession(true)
-                                                .setSessionIdentifier("UNKNOWN_SESSION_ID")
-                                        )
-                                )
-                        )
-                        .build()
-                val exception =
-                    assertFailsWith<StatusRuntimeException> {
-                        futureStub.sendRequestFulfillment(requestWithUnknownFulfillmentId).await()
-                    }
-                assertThat(Status.fromThrowable(exception).code)
-                    .isEqualTo(Status.Code.FAILED_PRECONDITION)
-                assertThat(Status.fromThrowable(exception).description).isEqualTo(ERROR_NO_SESSION)
+        val channel = createInProcessChannel()
+        val stub = AppInteractionServiceGrpc.newStub(channel)
+        val futureStub = AppInteractionServiceGrpc.newFutureStub(channel)
+        assertStartupSession(stub)
+        verify(capability1, times(1)).createSession(any())
+
+        val requestWithUnknownFulfillmentId =
+            Request.newBuilder()
+                .setSessionIdentifier("UNKNOWN_SESSION_ID")
+                .setFulfillmentRequest(
+                    FulfillmentRequest.newBuilder()
+                        .addFulfillments(Fulfillment.newBuilder().setIdentifier(capabilityId)),
+                )
+                .build()
+        val exception =
+            assertFailsWith<StatusRuntimeException> {
+                futureStub.sendRequestFulfillment(requestWithUnknownFulfillmentId).await()
             }
-        }
+        assertThat(Status.fromThrowable(exception).code).isEqualTo(Status.Code.FAILED_PRECONDITION)
+        assertThat(Status.fromThrowable(exception).description).isEqualTo(ERROR_NO_SESSION)
+
         server.shutdownNow()
     }
 
@@ -346,34 +311,33 @@
         val server =
             createInProcessServer(
                 AppInteractionServiceGrpcImpl(FakeAppInteractionService(listOf(capability1))),
-                remoteViewsInterceptor
+                remoteViewsInterceptor,
             )
-        coroutineScope {
-            launch {
-                val channel = createInProcessChannel()
-                val stub = AppInteractionServiceGrpc.newStub(channel)
-                val futureStub = AppInteractionServiceGrpc.newFutureStub(channel)
 
-                // Verify capability session is created
-                val mockSession = createMockSession()
-                whenever(mockSession.status).thenReturn(ActionCapabilitySession.Status.COMPLETED)
-                whenever(capability1.createSession(any())).thenReturn(mockSession)
-                assertStartupSession(stub)
-                verify(capability1, times(1)).createSession(any())
+        val channel = createInProcessChannel()
+        val stub = AppInteractionServiceGrpc.newStub(channel)
+        val futureStub = AppInteractionServiceGrpc.newFutureStub(channel)
 
-                // Send request to completed session.
-                val requestToEndedSession =
-                    Request.newBuilder().setFulfillmentRequest(testFulfillmentRequest).build()
-                val exception =
-                    assertFailsWith<StatusRuntimeException> {
-                        futureStub.sendRequestFulfillment(requestToEndedSession).await()
-                    }
-                assertThat(Status.fromThrowable(exception).code)
-                    .isEqualTo(Status.Code.FAILED_PRECONDITION)
-                assertThat(Status.fromThrowable(exception).description)
-                    .isEqualTo(ERROR_SESSION_ENDED)
+        // Verify capability session is created
+        val mockSession = createMockSession()
+        whenever(mockSession.status).thenReturn(ActionCapabilitySession.Status.COMPLETED)
+        whenever(capability1.createSession(any())).thenReturn(mockSession)
+        assertStartupSession(stub)
+        verify(capability1, times(1)).createSession(any())
+
+        // Send request to completed session.
+        val requestToEndedSession =
+            Request.newBuilder()
+                .setSessionIdentifier(sessionId)
+                .setFulfillmentRequest(testFulfillmentRequest)
+                .build()
+        val exception =
+            assertFailsWith<StatusRuntimeException> {
+                futureStub.sendRequestFulfillment(requestToEndedSession).await()
             }
-        }
+        assertThat(Status.fromThrowable(exception).code).isEqualTo(Status.Code.FAILED_PRECONDITION)
+        assertThat(Status.fromThrowable(exception).description).isEqualTo(ERROR_SESSION_ENDED)
+
         server.shutdownNow()
     }
 
@@ -403,20 +367,20 @@
     @Throws(IOException::class)
     private fun createInProcessServer(
         service: BindableService,
-        vararg interceptors: ServerInterceptor
+        vararg interceptors: ServerInterceptor,
     ): Server {
         return grpcCleanup.register(
             InProcessServerBuilder.forName(testServerName)
                 .directExecutor()
                 .addService(ServerInterceptors.intercept(service, *interceptors))
                 .build()
-                .start()
+                .start(),
         )
     }
 
     private fun createInProcessChannel(): ManagedChannel {
         return grpcCleanup.register(
-            InProcessChannelBuilder.forName(testServerName).directExecutor().build()
+            InProcessChannelBuilder.forName(testServerName).directExecutor().build(),
         )
     }
 
diff --git a/appcompat/appcompat-resources/build.gradle b/appcompat/appcompat-resources/build.gradle
index bab4b22..cd96606 100644
--- a/appcompat/appcompat-resources/build.gradle
+++ b/appcompat/appcompat-resources/build.gradle
@@ -23,11 +23,6 @@
 }
 
 dependencies {
-    // Atomic group
-    constraints {
-        implementation(project(":appcompat:appcompat"))
-    }
-
     api("androidx.annotation:annotation:1.2.0")
     api("androidx.core:core:1.6.0")
     implementation("androidx.collection:collection:1.0.0")
diff --git a/appcompat/appcompat/build.gradle b/appcompat/appcompat/build.gradle
index b381126..86dddcc 100644
--- a/appcompat/appcompat/build.gradle
+++ b/appcompat/appcompat/build.gradle
@@ -8,11 +8,6 @@
 }
 
 dependencies {
-    // Atomic group
-    constraints {
-        implementation(project(":appcompat:appcompat-resources"))
-    }
-
     api("androidx.annotation:annotation:1.3.0")
     api("androidx.core:core:1.9.0")
 
@@ -20,17 +15,17 @@
     implementation("androidx.core:core-ktx:1.8.0")
     implementation(libs.kotlinStdlib)
 
-    implementation("androidx.emoji2:emoji2:1.2.0")
+    implementation("androidx.emoji2:emoji2:1.3.0")
     implementation("androidx.emoji2:emoji2-views-helper:1.2.0")
     implementation("androidx.collection:collection:1.0.0")
     api("androidx.cursoradapter:cursoradapter:1.0.0")
-    api("androidx.activity:activity:1.6.0")
+    api("androidx.activity:activity:1.7.0")
     api("androidx.fragment:fragment:1.5.4")
     api(project(":appcompat:appcompat-resources"))
     api("androidx.drawerlayout:drawerlayout:1.0.0")
     implementation(projectOrArtifact(":lifecycle:lifecycle-runtime"))
     implementation("androidx.lifecycle:lifecycle-viewmodel:2.5.1")
-    implementation("androidx.profileinstaller:profileinstaller:1.2.1")
+    implementation("androidx.profileinstaller:profileinstaller:1.3.0")
     implementation("androidx.resourceinspection:resourceinspection-annotation:1.0.1")
     api("androidx.savedstate:savedstate:1.2.1")
 
diff --git a/appcompat/appcompat/src/androidTest/AndroidManifest.xml b/appcompat/appcompat/src/androidTest/AndroidManifest.xml
index cf53763..fb17a4b 100644
--- a/appcompat/appcompat/src/androidTest/AndroidManifest.xml
+++ b/appcompat/appcompat/src/androidTest/AndroidManifest.xml
@@ -156,9 +156,9 @@
             android:theme="@style/Theme.AppCompat.Light"/>
 
         <activity
-            android:name="androidx.appcompat.widget.AppCompatEditTextImeFocusActivity"
-            android:label="@string/app_compat_edit_text_ime_focus_activity"
-            android:theme="@style/Theme.TextColors"/>
+            android:name="androidx.appcompat.widget.AppCompatImeFocusActivity"
+            android:label="@string/app_compat_ime_focus_activity"
+            android:theme="@style/Theme.AppCompat.Light"/>
 
         <activity
             android:name="androidx.appcompat.widget.AppCompatButtonAutoSizeActivity"
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/AppCompatDialogTest.kt b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/AppCompatDialogTest.kt
new file mode 100644
index 0000000..ceb8677
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/AppCompatDialogTest.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.appcompat.app
+
+import android.view.View
+import androidx.lifecycle.findViewTreeLifecycleOwner
+import androidx.lifecycle.Lifecycle
+import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.testutils.withActivity
+import androidx.testutils.withUse
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class AppCompatDialogTest {
+
+    @Test
+    fun testViewTreeLifecycleOwner() {
+        withUse(ActivityScenario.launch(AppCompatActivity::class.java)) {
+            lateinit var view: View
+            val dialog = withActivity {
+                view = View(this)
+                AppCompatDialog(this)
+            }
+            dialog.setContentView(view)
+
+            val lifecycle = dialog.lifecycle
+            assertThat(lifecycle.currentState).isEqualTo(Lifecycle.State.INITIALIZED)
+
+            onActivity {
+                dialog.show()
+            }
+            assertThat(lifecycle.currentState).isEqualTo(Lifecycle.State.RESUMED)
+
+            val viewOwner = dialog.window?.decorView?.findViewTreeLifecycleOwner()!!
+            assertThat(viewOwner).isEqualTo(dialog)
+
+            onActivity {
+                dialog.dismiss()
+            }
+            assertThat(lifecycle.currentState).isEqualTo(Lifecycle.State.DESTROYED)
+
+            assertWithMessage("A new Lifecycle object should be created after destruction")
+                .that(dialog.lifecycle)
+                .isNotSameInstanceAs(lifecycle)
+        }
+    }
+}
\ No newline at end of file
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatEditTextImeFocusActivity.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatEditTextImeFocusActivity.java
deleted file mode 100644
index 6783008..0000000
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatEditTextImeFocusActivity.java
+++ /dev/null
@@ -1,76 +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.appcompat.widget;
-
-import android.view.ViewGroup;
-import android.view.inputmethod.InputMethodManager;
-
-import androidx.annotation.RequiresApi;
-import androidx.appcompat.test.R;
-import androidx.appcompat.testutils.BaseTestActivity;
-
-public class AppCompatEditTextImeFocusActivity extends BaseTestActivity {
-
-    ViewGroup mLayout;
-    AppCompatEditText mEditText1;
-    AppCompatEditText mEditText2;
-    InputMethodManager mInputMethodManager;
-
-    @Override
-    protected int getContentViewLayoutResId() {
-        return R.layout.appcompat_edittext_ime_focus_activity;
-    }
-
-    @RequiresApi(23)
-    public void initActivity() {
-        mLayout = findViewById(R.id.ime_layout);
-        mEditText1 = new AppCompatEditText(this);
-        mEditText2 = new AppCompatEditText(this);
-        mInputMethodManager = getSystemService(InputMethodManager.class);
-    }
-
-    public void addFirstEditorAndRequestFocus() {
-        mEditText1.requestFocus();
-        mLayout.addView(mEditText1);
-    }
-
-    public void addSecondEditor() {
-        mLayout.addView(mEditText2);
-    }
-
-    @RequiresApi(19)
-    public boolean isSecondEditorLayout() {
-        return mEditText2.isLaidOut();
-    }
-
-    public void switchToSecondEditor() {
-        mEditText2.requestFocus();
-        mLayout.removeView(mEditText1);
-    }
-
-    public boolean isFirstEditorActive() {
-        return mInputMethodManager.isActive(mEditText1);
-    }
-
-    public boolean isSecondEditorActive() {
-        return mInputMethodManager.isActive(mEditText2);
-    }
-
-    public void removeSecondEditor() {
-        mLayout.removeView(mEditText2);
-    }
-}
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatEditTextImeFocusTest.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatEditTextImeFocusTest.java
deleted file mode 100644
index 284cca4..0000000
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatEditTextImeFocusTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.appcompat.widget;
-
-
-import static org.junit.Assert.assertTrue;
-
-import androidx.test.core.app.ActivityScenario;
-import androidx.test.ext.junit.rules.ActivityScenarioRule;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.LargeTest;
-import androidx.test.filters.SdkSuppress;
-import androidx.testutils.PollingCheck;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.TimeUnit;
-
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class AppCompatEditTextImeFocusTest {
-
-    private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
-    private static final int STRESS_TEST_COUNTS = 500;
-
-    @Rule
-    public final ActivityScenarioRule<AppCompatEditTextImeFocusActivity> mActivityScenarioRule =
-            new ActivityScenarioRule<>(AppCompatEditTextImeFocusActivity.class);
-
-    @Test
-    @SdkSuppress(minSdkVersion = 30)
-    public void detachServed_withDifferentNextServed_stressTest() {
-        for (int i = 0; i < STRESS_TEST_COUNTS; i++) {
-            detachServed_withDifferentNextServed();
-        }
-    }
-
-    @SdkSuppress(minSdkVersion = 30)
-    private void detachServed_withDifferentNextServed() {
-        final ActivityScenario<AppCompatEditTextImeFocusActivity> activityScenario =
-                mActivityScenarioRule.getScenario();
-
-        // Initialize activity components and add first editor into layout
-        activityScenario.onActivity(activity -> {
-            activity.initActivity();
-            activity.addFirstEditorAndRequestFocus();
-        });
-
-        // The first editor should be active for input method.
-        activityScenario.onActivity(
-                activity -> PollingCheck.waitFor(TIMEOUT, activity::isFirstEditorActive));
-
-        // Add second editor into layout and ensure it is laid out.
-        activityScenario.onActivity(AppCompatEditTextImeFocusActivity::addSecondEditor);
-        activityScenario.onActivity(
-                activity -> PollingCheck.waitFor(TIMEOUT, activity::isSecondEditorLayout));
-
-        // Second editor request focus and detach the served view(first editor).
-        // Verify second editor should be active.
-        activityScenario.onActivity(activity -> {
-            activity.switchToSecondEditor();
-            assertTrue(activity.isSecondEditorActive());
-            activity.removeSecondEditor();
-        });
-    }
-}
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatImeFocusActivity.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatImeFocusActivity.java
new file mode 100644
index 0000000..0ffec9e
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatImeFocusActivity.java
@@ -0,0 +1,98 @@
+/*
+ * 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.appcompat.widget;
+
+import android.text.InputType;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.TextView;
+
+import androidx.annotation.RequiresApi;
+import androidx.appcompat.test.R;
+import androidx.appcompat.testutils.BaseTestActivity;
+
+public class AppCompatImeFocusActivity extends BaseTestActivity {
+
+    public static final int TEST_COMPAT_EDIT_TEXT = 1;
+    public static final int TEST_COMPAT_TEXT_VIEW = 2;
+
+    ViewGroup mLayout;
+    TextView mEditText1;
+    TextView mEditText2;
+    InputMethodManager mInputMethodManager;
+
+    @Override
+    protected int getContentViewLayoutResId() {
+        return R.layout.appcompat_edittext_ime_focus_activity;
+    }
+
+    @RequiresApi(23)
+    public void initActivity(int targetWidget) {
+        mLayout = findViewById(R.id.ime_layout);
+        if (targetWidget == TEST_COMPAT_EDIT_TEXT) {
+            mEditText1 = new AppCompatEditText(this);
+            mEditText2 = new AppCompatEditText(this);
+        } else if (targetWidget == TEST_COMPAT_TEXT_VIEW) {
+            mEditText1 = initEditableCompatTextView();
+            mEditText2 = initEditableCompatTextView();
+        } else {
+            throw new RuntimeException("initActivity with an invalid target");
+        }
+        mInputMethodManager = getSystemService(InputMethodManager.class);
+    }
+
+    public void addFirstEditorAndRequestFocus() {
+        mEditText1.requestFocus();
+        mLayout.addView(mEditText1);
+    }
+
+    public void addSecondEditor() {
+        mLayout.addView(mEditText2);
+    }
+
+    @RequiresApi(19)
+    public boolean isSecondEditorLayout() {
+        return mEditText2.isLaidOut();
+    }
+
+    public void switchToSecondEditor() {
+        mEditText2.requestFocus();
+        mLayout.removeView(mEditText1);
+    }
+
+    public boolean isFirstEditorActive() {
+        return mInputMethodManager.isActive(mEditText1);
+    }
+
+    public boolean isSecondEditorActive() {
+        return mInputMethodManager.isActive(mEditText2);
+    }
+
+    public void removeSecondEditor() {
+        mLayout.removeView(mEditText2);
+    }
+
+    private AppCompatTextView initEditableCompatTextView() {
+        AppCompatTextView editText = new AppCompatTextView(this);
+        editText.setInputType(InputType.TYPE_CLASS_TEXT);
+        editText.setFocusable(true);
+        editText.setCursorVisible(true);
+        editText.setEnabled(true);
+        editText.setFocusableInTouchMode(true);
+        return editText;
+    }
+}
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatImeFocusTest.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatImeFocusTest.java
new file mode 100644
index 0000000..d89d748
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatImeFocusTest.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.appcompat.widget;
+
+
+import static androidx.appcompat.widget.AppCompatImeFocusActivity.TEST_COMPAT_EDIT_TEXT;
+import static androidx.appcompat.widget.AppCompatImeFocusActivity.TEST_COMPAT_TEXT_VIEW;
+
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SdkSuppress;
+import androidx.testutils.PollingCheck;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class AppCompatImeFocusTest {
+
+    private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+    private static final int STRESS_TEST_COUNTS = 500;
+
+    @Rule
+    public final ActivityScenarioRule<AppCompatImeFocusActivity> mActivityScenarioRule =
+            new ActivityScenarioRule<>(AppCompatImeFocusActivity.class);
+
+    @Test
+    @SdkSuppress(minSdkVersion = 30)
+    public void detachServedWithDifferentNextServed_appCompatEditText_stressTest() {
+        for (int i = 0; i < STRESS_TEST_COUNTS; i++) {
+            detachServed_withDifferentNextServed(TEST_COMPAT_EDIT_TEXT);
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 30)
+    public void detachServedWithDifferentNextServed_appCompatTextView_stressTest() {
+        for (int i = 0; i < STRESS_TEST_COUNTS; i++) {
+            detachServed_withDifferentNextServed(TEST_COMPAT_TEXT_VIEW);
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = 30)
+    private void detachServed_withDifferentNextServed(int targetWidget) {
+        final ActivityScenario<AppCompatImeFocusActivity> activityScenario =
+                mActivityScenarioRule.getScenario();
+
+        // Initialize activity components and add first editor into layout
+        activityScenario.onActivity(activity -> {
+            activity.initActivity(targetWidget);
+            activity.addFirstEditorAndRequestFocus();
+        });
+
+        // The first editor should be active for input method.
+        activityScenario.onActivity(
+                activity -> PollingCheck.waitFor(TIMEOUT, activity::isFirstEditorActive));
+
+        // Add second editor into layout and ensure it is laid out.
+        activityScenario.onActivity(AppCompatImeFocusActivity::addSecondEditor);
+        activityScenario.onActivity(
+                activity -> PollingCheck.waitFor(TIMEOUT, activity::isSecondEditorLayout));
+
+        // Second editor request focus and detach the served view(first editor).
+        // Verify second editor should be active.
+        activityScenario.onActivity(activity -> {
+            activity.switchToSecondEditor();
+            assertTrue(activity.isSecondEditorActive());
+            activity.removeSecondEditor();
+        });
+    }
+}
diff --git a/appcompat/appcompat/src/androidTest/res/values/donottranslate-strings.xml b/appcompat/appcompat/src/androidTest/res/values/donottranslate-strings.xml
index a73602f..151400a 100644
--- a/appcompat/appcompat/src/androidTest/res/values/donottranslate-strings.xml
+++ b/appcompat/appcompat/src/androidTest/res/values/donottranslate-strings.xml
@@ -64,10 +64,10 @@
     <string name="app_compat_text_view_auto_size_activity">AppCompat text view auto-size</string>
     <string name="app_compat_text_view_emoji_activity">AppCompat text view emoji</string>
     <string name="app_compat_edit_text_activity">AppCompat edit text</string>
-    <string name="app_compat_edit_text_ime_focus_activity">AppCompat edit text ime focus</string>
     <string name="app_compat_edit_text_receive_content_activity">
         AppCompat edit text receive content activity
     </string>
+    <string name="app_compat_ime_focus_activity">AppCompat ime focus</string>
     <string name="app_compat_button_auto_size_activity">AppCompat button auto-size</string>
     <string name="sample_text1">Sample text 1</string>
     <string name="sample_text2">Sample text 2</string>
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDialog.java b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDialog.java
index be0fab9..aa4aff9 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDialog.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDialog.java
@@ -26,6 +26,7 @@
 import android.view.ViewGroup;
 
 import androidx.activity.ComponentDialog;
+import androidx.activity.ViewTreeOnBackPressedDispatcherOwner;
 import androidx.annotation.IdRes;
 import androidx.annotation.LayoutRes;
 import androidx.annotation.NonNull;
@@ -34,6 +35,8 @@
 import androidx.appcompat.R;
 import androidx.appcompat.view.ActionMode;
 import androidx.core.view.KeyEventDispatcher;
+import androidx.lifecycle.ViewTreeLifecycleOwner;
+import androidx.savedstate.ViewTreeSavedStateRegistryOwner;
 
 /**
  * Base class for AppCompat themed {@link android.app.Dialog}s.
@@ -92,19 +95,30 @@
 
     @Override
     public void setContentView(@LayoutRes int layoutResID) {
+        initViewTreeOwners();
         getDelegate().setContentView(layoutResID);
     }
 
     @Override
     public void setContentView(@NonNull View view) {
+        initViewTreeOwners();
         getDelegate().setContentView(view);
     }
 
     @Override
     public void setContentView(@NonNull View view, ViewGroup.LayoutParams params) {
+        initViewTreeOwners();
         getDelegate().setContentView(view, params);
     }
 
+    private void initViewTreeOwners() {
+        // Set the view tree owners before setting the content view so that the inflation process
+        // and attach listeners will see them already present
+        ViewTreeLifecycleOwner.set(getWindow().getDecorView(), this);
+        ViewTreeSavedStateRegistryOwner.set(getWindow().getDecorView(), this);
+        ViewTreeOnBackPressedDispatcherOwner.set(getWindow().getDecorView(), this);
+    }
+
     @SuppressWarnings("TypeParameterUnusedInFormals")
     @Nullable
     @Override
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java
index a87414d..1e92e4a 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java
@@ -32,6 +32,7 @@
 import android.view.ActionMode;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
 import android.view.textclassifier.TextClassifier;
 import android.widget.TextView;
 
@@ -769,6 +770,21 @@
 
     }
 
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (Build.VERSION.SDK_INT >= 30 && Build.VERSION.SDK_INT < 33 && onCheckIsTextEditor()) {
+            final InputMethodManager imm = (InputMethodManager) getContext().getSystemService(
+                    Context.INPUT_METHOD_SERVICE);
+            // If the AppCompatTextView is editable, user can input text on it.
+            // Calling isActive() here implied a checkFocus() call to update the active served
+            // view for input method. This is a backport for mServedView was detached, but the
+            // next served view gets mistakenly cleared as well.
+            // https://android.googlesource.com/platform/frameworks/base/+/734613a500fb
+            imm.isActive(this);
+        }
+    }
+
     @UiThread
     @RequiresApi(api = 26)
     SuperCaller getSuperCaller() {
diff --git a/appsearch/appsearch-builtin-types/src/main/res/values/attrs.xml b/appsearch/appsearch-builtin-types/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..ad887f5
--- /dev/null
+++ b/appsearch/appsearch-builtin-types/src/main/res/values/attrs.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.
+  -->
+<resources>
+    <!-- Attributes that are read when parsing a <appsearch> tag. -->
+    <declare-styleable name="AppSearch">
+        <!-- Indicates the database of the AppSearch corpus. -->
+        <attr name="database" format="string" />
+        <!-- Indicates the namespace of the AppSearch corpus. -->
+        <attr name="namespace" format="string" />
+        <!-- Indicates the schema type of the AppSearch corpus. -->
+        <attr name="schemaType" format="string" />
+    </declare-styleable>
+
+    <!-- Attributes that are read when parsing a <capability> tag. -->
+    <declare-styleable name="Capability">
+        <!-- Indicates whether a parameter in a capability template can only contain a matched
+             AppSearch entity and not an arbitrary string. -->
+        <attr name="entityMatchRequired" format="boolean" />
+    </declare-styleable>
+</resources>
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 4fef3f4..63bba79 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
@@ -78,6 +78,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
@@ -938,6 +939,7 @@
         assertThat(suggestions.get(1).getSuggestedResult()).isEqualTo("term2");
     }
 
+    @Ignore("b/273733335")
     @Test
     public void testSearchSuggestion_invalidPrefix() throws Exception {
         // Insert schema just put something in the AppSearch to make it searchable.
diff --git a/autofill/autofill/api/current.txt b/autofill/autofill/api/current.txt
index 668e933..0b0a188 100644
--- a/autofill/autofill/api/current.txt
+++ b/autofill/autofill/api/current.txt
@@ -16,7 +16,12 @@
     field public static final String AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = "creditCardSecurityCode";
     field public static final String AUTOFILL_HINT_EMAIL_ADDRESS = "emailAddress";
     field public static final String AUTOFILL_HINT_EMAIL_OTP = "emailOTPCode";
+    field public static final String AUTOFILL_HINT_FLIGHT_CONFIRMATION_CODE = "flightConfirmationCode";
+    field public static final String AUTOFILL_HINT_FLIGHT_NUMBER = "flightNumber";
     field public static final String AUTOFILL_HINT_GENDER = "gender";
+    field public static final String AUTOFILL_HINT_GIFT_CARD_NUMBER = "giftCardNumber";
+    field public static final String AUTOFILL_HINT_GIFT_CARD_PIN = "giftCardPIN";
+    field public static final String AUTOFILL_HINT_LOYALTY_ACCOUNT_NUMBER = "loyaltyAccountNumber";
     field @Deprecated public static final String AUTOFILL_HINT_NAME = "name";
     field public static final String AUTOFILL_HINT_NEW_PASSWORD = "newPassword";
     field public static final String AUTOFILL_HINT_NEW_USERNAME = "newUsername";
diff --git a/autofill/autofill/api/public_plus_experimental_current.txt b/autofill/autofill/api/public_plus_experimental_current.txt
index 668e933..0b0a188 100644
--- a/autofill/autofill/api/public_plus_experimental_current.txt
+++ b/autofill/autofill/api/public_plus_experimental_current.txt
@@ -16,7 +16,12 @@
     field public static final String AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = "creditCardSecurityCode";
     field public static final String AUTOFILL_HINT_EMAIL_ADDRESS = "emailAddress";
     field public static final String AUTOFILL_HINT_EMAIL_OTP = "emailOTPCode";
+    field public static final String AUTOFILL_HINT_FLIGHT_CONFIRMATION_CODE = "flightConfirmationCode";
+    field public static final String AUTOFILL_HINT_FLIGHT_NUMBER = "flightNumber";
     field public static final String AUTOFILL_HINT_GENDER = "gender";
+    field public static final String AUTOFILL_HINT_GIFT_CARD_NUMBER = "giftCardNumber";
+    field public static final String AUTOFILL_HINT_GIFT_CARD_PIN = "giftCardPIN";
+    field public static final String AUTOFILL_HINT_LOYALTY_ACCOUNT_NUMBER = "loyaltyAccountNumber";
     field @Deprecated public static final String AUTOFILL_HINT_NAME = "name";
     field public static final String AUTOFILL_HINT_NEW_PASSWORD = "newPassword";
     field public static final String AUTOFILL_HINT_NEW_USERNAME = "newUsername";
diff --git a/autofill/autofill/api/restricted_current.txt b/autofill/autofill/api/restricted_current.txt
index d057b98..2ce5394 100644
--- a/autofill/autofill/api/restricted_current.txt
+++ b/autofill/autofill/api/restricted_current.txt
@@ -16,7 +16,12 @@
     field public static final String AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = "creditCardSecurityCode";
     field public static final String AUTOFILL_HINT_EMAIL_ADDRESS = "emailAddress";
     field public static final String AUTOFILL_HINT_EMAIL_OTP = "emailOTPCode";
+    field public static final String AUTOFILL_HINT_FLIGHT_CONFIRMATION_CODE = "flightConfirmationCode";
+    field public static final String AUTOFILL_HINT_FLIGHT_NUMBER = "flightNumber";
     field public static final String AUTOFILL_HINT_GENDER = "gender";
+    field public static final String AUTOFILL_HINT_GIFT_CARD_NUMBER = "giftCardNumber";
+    field public static final String AUTOFILL_HINT_GIFT_CARD_PIN = "giftCardPIN";
+    field public static final String AUTOFILL_HINT_LOYALTY_ACCOUNT_NUMBER = "loyaltyAccountNumber";
     field @Deprecated public static final String AUTOFILL_HINT_NAME = "name";
     field public static final String AUTOFILL_HINT_NEW_PASSWORD = "newPassword";
     field public static final String AUTOFILL_HINT_NEW_USERNAME = "newUsername";
diff --git a/autofill/autofill/src/main/java/androidx/autofill/HintConstants.java b/autofill/autofill/src/main/java/androidx/autofill/HintConstants.java
index ebc4328..12612a1 100644
--- a/autofill/autofill/src/main/java/androidx/autofill/HintConstants.java
+++ b/autofill/autofill/src/main/java/androidx/autofill/HintConstants.java
@@ -40,7 +40,7 @@
      * should be <code>{@value #AUTOFILL_HINT_EMAIL_ADDRESS}</code>).
      *
      * <p>See {@link android.view.View#setAutofillHints(String...)} for more info about autofill
-     * hints.
+     * hints.3
      */
     public static final String AUTOFILL_HINT_EMAIL_ADDRESS = "emailAddress";
 
@@ -680,4 +680,65 @@
      * hints.
      */
     public static final String AUTOFILL_HINT_UPI_VPA = "upiVirtualPaymentAddress";
+
+    /**
+     * Hint indicating that this view can be autofilled with a loyalty number.
+     *
+     * <p>Can be used with either {@link android.view.View#setAutofillHints(String[])} or <a
+     * href="#attr_android:autofillHint">{@code android:autofillHint}</a> (in which case the value
+     * should be <code>{@value #AUTOFILL_HINT_LOYALTY_ACCOUNT_NUMBER}</code>).
+     *
+     * <p>See {@link android.view.View#setAutofillHints(String...)} for more info about autofill
+     * hints.
+     */
+    public static final String AUTOFILL_HINT_LOYALTY_ACCOUNT_NUMBER = "loyaltyAccountNumber";
+
+    /**
+     * Hint indicating that this view can be autofilled with a gift card code.
+     *
+     * <p>Can be used with either {@link android.view.View#setAutofillHints(String[])} or <a
+     * href="#attr_android:autofillHint">{@code android:autofillHint}</a> (in which case the value
+     * should be <code>{@value #AUTOFILL_HINT_GIFT_CARD_NUMBER}</code>).
+     *
+     * <p>See {@link android.view.View#setAutofillHints(String...)} for more info about autofill
+     * hints.
+     */
+    public static final String AUTOFILL_HINT_GIFT_CARD_NUMBER = "giftCardNumber";
+
+    /**
+     * Hint indicating that this view can be autofilled with a gift card pin.
+     *
+     * <p>Can be used with either {@link android.view.View#setAutofillHints(String[])} or <a
+     * href="#attr_android:autofillHint">{@code android:autofillHint}</a> (in which case the value
+     * should be <code>{@value #AUTOFILL_HINT_GIFT_CARD_PIN}</code>).
+     *
+     * <p>See {@link android.view.View#setAutofillHints(String...)} for more info about autofill
+     * hints.
+     */
+    public static final String AUTOFILL_HINT_GIFT_CARD_PIN = "giftCardPIN";
+
+    /**
+     * Hint indicating that this view can be autofilled with a flight number.
+     * Examples: UA 355 (United Airlines), WN 355 (Southwest), AA 9158 (American Airlines)
+     *
+     * <p>Can be used with either {@link android.view.View#setAutofillHints(String[])} or <a
+     * href="#attr_android:autofillHint">{@code android:autofillHint}</a> (in which case the value
+     * should be <code>{@value #AUTOFILL_HINT_FLIGHT_NUMBER}</code>).
+     *
+     * <p>See {@link android.view.View#setAutofillHints(String...)} for more info about autofill
+     * hints.
+     */
+    public static final String AUTOFILL_HINT_FLIGHT_NUMBER = "flightNumber";
+
+    /**
+     * Hint indicating that this view can be autofilled with a flight confirmation code.
+     *
+     * <p>Can be used with either {@link android.view.View#setAutofillHints(String[])} or <a
+     * href="#attr_android:autofillHint">{@code android:autofillHint}</a> (in which case the value
+     * should be <code>{@value #AUTOFILL_HINT_FLIGHT_CONFIRMATION_CODE}</code>).
+     *
+     * <p>See {@link android.view.View#setAutofillHints(String...)} for more info about autofill
+     * hints.
+     */
+    public static final String AUTOFILL_HINT_FLIGHT_CONFIRMATION_CODE = "flightConfirmationCode";
 }
diff --git a/benchmark/baseline-profile-gradle-plugin/build.gradle b/benchmark/baseline-profile-gradle-plugin/build.gradle
index ebf6019..057bc2d1 100644
--- a/benchmark/baseline-profile-gradle-plugin/build.gradle
+++ b/benchmark/baseline-profile-gradle-plugin/build.gradle
@@ -74,3 +74,5 @@
         enableStricterValidation.set(true)
     }
 }
+
+afterEvaluate { tasks.named("test") { it.dependsOn(tasks.named("publish")) } }
diff --git a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/apptarget/BaselineProfileAppTargetPlugin.kt b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/apptarget/BaselineProfileAppTargetPlugin.kt
index db9d280..c0d95dd 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/apptarget/BaselineProfileAppTargetPlugin.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/apptarget/BaselineProfileAppTargetPlugin.kt
@@ -89,7 +89,7 @@
         // They're named `<BUILD_TYPE_BASELINE_PROFILE_PREFIX><originalBuildTypeName>`.
         createExtendedBuildTypes(
             project = project,
-            extension = extension,
+            extensionBuildTypes = extension.buildTypes,
             newBuildTypePrefix = BUILD_TYPE_BASELINE_PROFILE_PREFIX,
             filterBlock = {
                 // Create baseline profile build types only for non debuggable builds.
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 7a93406..a7cb238 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
@@ -136,7 +136,7 @@
         // flavors, so that the same variant names are created.
         createExtendedBuildTypes(
             project = project,
-            extension = extension,
+            extensionBuildTypes = extension.buildTypes,
             newBuildTypePrefix = BUILD_TYPE_BASELINE_PROFILE_PREFIX,
             extendedBuildTypeToOriginalBuildTypeMapping = extendedTypeToOriginalTypeMapping,
             configureBlock = configureBlock,
@@ -149,7 +149,7 @@
         )
         createBuildTypeIfNotExists(
             project = project,
-            extension = extension,
+            extensionBuildTypes = extension.buildTypes,
             buildTypeName = nonObfuscatedReleaseName,
             configureBlock = configureBlock
         )
diff --git a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/producer/tasks/CollectBaselineProfileTask.kt b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/producer/tasks/CollectBaselineProfileTask.kt
index aba5cd6..646a9dd 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/producer/tasks/CollectBaselineProfileTask.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/producer/tasks/CollectBaselineProfileTask.kt
@@ -26,9 +26,11 @@
 import org.gradle.api.GradleException
 import org.gradle.api.Project
 import org.gradle.api.file.ConfigurableFileCollection
-import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.provider.MapProperty
+import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.OutputDirectory
 import org.gradle.api.tasks.PathSensitive
 import org.gradle.api.tasks.PathSensitivity
 import org.gradle.api.tasks.TaskAction
@@ -44,6 +46,10 @@
 
     companion object {
         private const val COLLECT_TASK_NAME = "collect"
+        private const val PROP_KEY_PREFIX_INSTRUMENTATION_RUNNER_ARG =
+            "android.testInstrumentationRunnerArguments."
+        private const val PROP_KEY_INSTRUMENTATION_RUNNER_ARG_CLASS =
+            "${PROP_KEY_PREFIX_INSTRUMENTATION_RUNNER_ARG}class"
 
         internal fun registerForVariant(
             project: Project,
@@ -62,8 +68,7 @@
                 var outputDir = project
                     .layout
                     .buildDirectory
-                    .dir("$INTERMEDIATES_BASE_FOLDER/${variant.flavorName}/")
-
+                    .dir("$INTERMEDIATES_BASE_FOLDER/")
                 if (!flavorName.isNullOrBlank()) {
                     outputDir = outputDir.map { d -> d.dir(flavorName) }
                 }
@@ -72,10 +77,16 @@
                 }
 
                 // Sets the baseline-prof output path.
-                it.outputFile.set(outputDir.map { d -> d.file("baseline-prof.txt") })
+                it.outputDir.set(outputDir)
 
                 // Sets the test results inputs
                 it.testResultDirs.setFrom(testTaskDependencies.map { t -> t.resultsDir })
+
+                // Sets the project testInstrumentationRunnerArguments
+                it.testInstrumentationRunnerArguments.set(project
+                    .properties
+                    .filterKeys { k -> k.startsWith(PROP_KEY_PREFIX_INSTRUMENTATION_RUNNER_ARG) }
+                )
             }
         }
     }
@@ -89,12 +100,20 @@
     @get:PathSensitive(PathSensitivity.NONE)
     abstract val testResultDirs: ConfigurableFileCollection
 
-    @get:OutputFile
-    abstract val outputFile: RegularFileProperty
+    @get:Input
+    abstract val testInstrumentationRunnerArguments: MapProperty<String, Any>
+
+    @get:OutputDirectory
+    abstract val outputDir: DirectoryProperty
 
     @TaskAction
     fun exec() {
 
+        // Determines if this is a partial result based on whether the property
+        // `android.testInstrumentationRunnerArguments.class` is set
+        val isPartialResult = testInstrumentationRunnerArguments.get()
+            .containsKey(PROP_KEY_INSTRUMENTATION_RUNNER_ARG_CLASS)
+
         // Prepares list with test results to read. Note that these are the output directories
         // from the instrumentation task. We're interested only in `test-result.pb`.
         val testResultProtoFiles = testResultDirs.files.map { File(it, "test-result.pb") }
@@ -115,7 +134,7 @@
             )
         }
 
-        val profiles = mutableSetOf<String>()
+        val profileFiles = mutableSetOf<File>()
         testResultProtoFiles
             .map { TestSuiteResultProto.TestSuiteResult.parseFrom(it.readBytes()) }
             .forEach { testSuiteResult ->
@@ -139,18 +158,23 @@
                     // Merge each baseline profile file from the test results into the aggregated
                     // baseline file, removing duplicate lines.
                     for (baselineProfileFile in baselineProfileFiles) {
-                        profiles.addAll(baselineProfileFile.readLines())
+                        profileFiles.add(baselineProfileFile)
                     }
                 }
             }
 
-        if (profiles.isEmpty()) {
-            throw GradleException("No baseline profile found in test outputs.")
+        // If this is not a partial result delete the content of the output dir.
+        if (!isPartialResult) {
+            outputDir.get().asFile.apply {
+                deleteRecursively()
+                mkdirs()
+            }
         }
 
-        // Saves the merged baseline profile file in the final destination
-        val file = outputFile.get().asFile
-        file.writeText(profiles.joinToString(System.lineSeparator()))
-        logger.info("Aggregated baseline profile generated at ${file.absolutePath}")
+        // Saves the merged baseline profile file in the final destination. Existing tests are
+        // overwritten, in case this is a partial result that needs to update an existing profile.
+        profileFiles.forEach {
+            it.copyTo(outputDir.file(it.name).get().asFile, overwrite = true)
+        }
     }
 }
diff --git a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/BuildTypes.kt b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/BuildTypes.kt
index 3926416..5b8181c 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/BuildTypes.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/main/kotlin/androidx/baselineprofile/gradle/utils/BuildTypes.kt
@@ -16,19 +16,19 @@
 package androidx.baselineprofile.gradle.utils
 
 import com.android.build.api.dsl.BuildType
-import com.android.build.api.dsl.CommonExtension
 import org.gradle.api.GradleException
+import org.gradle.api.NamedDomainObjectContainer
 import org.gradle.api.Project
 
 internal inline fun <reified T : BuildType> createExtendedBuildTypes(
     project: Project,
-    extension: CommonExtension<*, T, *, *>,
+    extensionBuildTypes: NamedDomainObjectContainer<out T>,
     newBuildTypePrefix: String,
     crossinline filterBlock: (T) -> (Boolean),
     crossinline configureBlock: T.() -> (Unit),
     extendedBuildTypeToOriginalBuildTypeMapping: MutableMap<String, String> = mutableMapOf()
 ) {
-    extension.buildTypes.filter { buildType ->
+    extensionBuildTypes.filter { buildType ->
             if (buildType !is T) {
                 throw GradleException(
                     "Build type `${buildType.name}` is not of type ${T::class}"
@@ -40,14 +40,14 @@
             val newBuildTypeName = camelCase(newBuildTypePrefix, buildType.name)
 
             // Check in case the build type was created manually (to allow full customization)
-            if (extension.buildTypes.findByName(newBuildTypeName) != null) {
+            if (extensionBuildTypes.findByName(newBuildTypeName) != null) {
                 project.logger.info(
                     "Build type $newBuildTypeName won't be created because already exists."
                 )
             } else {
                 // If the new build type doesn't exist, create it simply extending the configured
                 // one (by default release).
-                extension.buildTypes.create(newBuildTypeName).apply {
+                extensionBuildTypes.create(newBuildTypeName).apply {
                     initWith(buildType)
                     matchingFallbacks += listOf(buildType.name)
                     configureBlock(this as T)
@@ -60,12 +60,12 @@
 
 internal inline fun <reified T : BuildType> createBuildTypeIfNotExists(
     project: Project,
-    extension: CommonExtension<*, T, *, *>,
+    extensionBuildTypes: NamedDomainObjectContainer<out T>,
     buildTypeName: String,
     crossinline configureBlock: T.() -> (Unit),
 ) {
     // Check in case the build type was created manually (to allow full customization)
-    if (extension.buildTypes.findByName(buildTypeName) != null) {
+    if (extensionBuildTypes.findByName(buildTypeName) != null) {
         project.logger.info(
             "Build type $buildTypeName won't be created because already exists."
         )
@@ -73,7 +73,7 @@
     }
     // If the new build type doesn't exist, create it simply extending the configured
     // one (by default release).
-    extension.buildTypes.create(buildTypeName).apply {
+    extensionBuildTypes.create(buildTypeName).apply {
         configureBlock(this)
     }
 }
diff --git a/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerPluginTest.kt b/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerPluginTest.kt
index d5974d9..d597426 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerPluginTest.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/consumer/BaselineProfileConsumerPluginTest.kt
@@ -24,7 +24,6 @@
 import androidx.baselineprofile.gradle.utils.VariantProfile
 import androidx.baselineprofile.gradle.utils.build
 import androidx.baselineprofile.gradle.utils.buildAndAssertThatOutput
-import androidx.baselineprofile.gradle.utils.buildAndFailAndAssertThatOutput
 import com.google.common.truth.Truth.assertThat
 import java.io.File
 import org.junit.Rule
@@ -63,18 +62,12 @@
             dependencyOnProducerProject = true,
             flavors = false
         )
-        projectSetup.producer.setup(
-            variantProfiles = listOf(
-                VariantProfile(
-                    flavor = null,
-                    buildType = "release",
-                    profileLines = listOf(
-                        Fixtures.CLASS_1_METHOD_1,
-                        Fixtures.CLASS_1,
-                        Fixtures.CLASS_2_METHOD_1,
-                        Fixtures.CLASS_2
-                    )
-                )
+        projectSetup.producer.setupWithoutFlavors(
+            releaseProfileLines = listOf(
+                Fixtures.CLASS_1_METHOD_1,
+                Fixtures.CLASS_1,
+                Fixtures.CLASS_2_METHOD_1,
+                Fixtures.CLASS_2
             )
         )
 
@@ -479,18 +472,13 @@
                 filter { include("nothing.**") }
             """.trimIndent()
         )
-        projectSetup.producer.setup(
-            variantProfiles = listOf(
-                VariantProfile(
-                    flavor = null,
-                    buildType = "release",
-                    profileLines = listOf(
-                        Fixtures.CLASS_1_METHOD_1,
-                        Fixtures.CLASS_1
-                    )
-                )
+        projectSetup.producer.setupWithoutFlavors(
+            releaseProfileLines = listOf(
+                Fixtures.CLASS_1_METHOD_1,
+                Fixtures.CLASS_1
             )
         )
+
         gradleRunner
             .withArguments("generateReleaseBaselineProfile", "--stacktrace")
             .buildAndFail()
@@ -510,17 +498,11 @@
         projectSetup.consumer.setup(
             androidPlugin = ANDROID_LIBRARY_PLUGIN
         )
-        projectSetup.producer.setup(
-            variantProfiles = listOf(
-                VariantProfile(
-                    flavor = null,
-                    buildType = "release",
-                    profileLines = listOf()
-                )
-            )
+        projectSetup.producer.setupWithoutFlavors(
+            releaseProfileLines = listOf()
         )
-        gradleRunner.buildAndFailAndAssertThatOutput("generateReleaseBaselineProfile") {
-            contains("No baseline profile found in test outputs.")
+        gradleRunner.buildAndAssertThatOutput("generateReleaseBaselineProfile") {
+            contains("No baseline profile rules were generated")
         }
     }
 
@@ -758,4 +740,96 @@
                 Fixtures.CLASS_1_METHOD_1,
             )
     }
+
+    @Test
+    fun testPartialResults() {
+        projectSetup.consumer.setup(
+            androidPlugin = ANDROID_APPLICATION_PLUGIN
+        )
+
+        // Function to setup the producer, run the generate profile command and assert output
+        val setupProducerGenerateAndAssert: (
+            Boolean,
+            Map<String, List<String>>,
+            List<String>
+        ) -> (Unit) = { partial, mapFileToProfile, finalProfileAssertList ->
+
+            projectSetup.producer.setup(
+                variantProfiles = listOf(
+                    VariantProfile(
+                        flavor = null,
+                        buildType = "release",
+                        profileFileLines = mapFileToProfile
+                    )
+                )
+            )
+
+            val args = mutableListOf("generateBaselineProfile")
+            if (partial) args.add("-Pandroid.testInstrumentationRunnerArguments.class=someClass")
+            projectSetup.consumer.gradleRunner.build(*args.toTypedArray()) { }
+
+            assertThat(readBaselineProfileFileContent("release"))
+                .containsExactly(*finalProfileAssertList.toTypedArray())
+        }
+
+        // Full generation, 2 new tests.
+        setupProducerGenerateAndAssert(
+            false,
+            mapOf(
+                "myTest1" to listOf(Fixtures.CLASS_1, Fixtures.CLASS_1_METHOD_1),
+                "myTest2" to listOf(Fixtures.CLASS_2, Fixtures.CLASS_2_METHOD_1)
+            ),
+            listOf(
+                Fixtures.CLASS_1,
+                Fixtures.CLASS_1_METHOD_1,
+                Fixtures.CLASS_2,
+                Fixtures.CLASS_2_METHOD_1
+            )
+        )
+
+        // Partial generation, modify 1 test.
+        setupProducerGenerateAndAssert(
+            true,
+            mapOf(
+                "myTest1" to listOf(Fixtures.CLASS_3, Fixtures.CLASS_3_METHOD_1)
+            ),
+            listOf(
+                Fixtures.CLASS_3,
+                Fixtures.CLASS_3_METHOD_1,
+                Fixtures.CLASS_2,
+                Fixtures.CLASS_2_METHOD_1
+            )
+        )
+
+        // Partial generation, add 1 test.
+        setupProducerGenerateAndAssert(
+            true,
+            mapOf(
+                "myTest3" to listOf(Fixtures.CLASS_4, Fixtures.CLASS_4_METHOD_1)
+            ),
+            listOf(
+                Fixtures.CLASS_3,
+                Fixtures.CLASS_3_METHOD_1,
+                Fixtures.CLASS_4,
+                Fixtures.CLASS_4_METHOD_1,
+                Fixtures.CLASS_2,
+                Fixtures.CLASS_2_METHOD_1
+            )
+        )
+
+        // Full generation, 2 new tests.
+        setupProducerGenerateAndAssert(
+            false,
+            mapOf(
+                "myTest1-new" to listOf(Fixtures.CLASS_1, Fixtures.CLASS_1_METHOD_1),
+                "myTest2-new" to listOf(Fixtures.CLASS_2, Fixtures.CLASS_2_METHOD_1)
+            ),
+            listOf(
+                Fixtures.CLASS_1,
+                Fixtures.CLASS_1_METHOD_1,
+                Fixtures.CLASS_2,
+                Fixtures.CLASS_2_METHOD_1
+            )
+        )
+    }
 }
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 18474e6..92b86fa 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
@@ -34,7 +34,7 @@
     private val emptyReleaseVariantProfile = VariantProfile(
         flavor = null,
         buildType = "release",
-        profileLines = listOf()
+        profileFileLines = mapOf()
     )
 
     @Test
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 d0df57b..606e105 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
@@ -36,7 +36,9 @@
 internal const val ANDROID_LIBRARY_PLUGIN = "com.android.library"
 internal const val ANDROID_TEST_PLUGIN = "com.android.test"
 
-class BaselineProfileProjectSetupRule : ExternalResource() {
+class BaselineProfileProjectSetupRule(
+    private val forceAgpVersion: String? = null
+) : ExternalResource() {
 
     /**
      * Root folder for the project setup that contains 3 modules.
@@ -116,6 +118,40 @@
             """.trimIndent()
             )
 
+            val repositoriesBlock = """
+                repositories {
+                    ${producerSetupRule.allRepositoryPaths.joinToString("\n") { """ maven { url "$it" } """ }}
+                }
+            """.trimIndent()
+
+            val agpDependency = if (forceAgpVersion == null) {
+                """"${appTargetSetupRule.props.agpDependency}""""
+            } else {
+                """("com.android.tools.build:gradle") { version { strictly "$forceAgpVersion" } }"""
+            }
+            rootFolder.newFile("build.gradle").writeText(
+                """
+                buildscript {
+                    $repositoriesBlock
+                    dependencies {
+
+                        // Specifies agp dependency
+                        classpath $agpDependency
+
+                        // Specifies plugin dependency
+                        classpath "androidx.baselineprofile.consumer:androidx.baselineprofile.consumer.gradle.plugin:+"
+                        classpath "androidx.baselineprofile.producer:androidx.baselineprofile.producer.gradle.plugin:+"
+                        classpath "androidx.baselineprofile.apptarget:androidx.baselineprofile.apptarget.gradle.plugin:+"
+                    }
+                }
+
+                allprojects {
+                    $repositoriesBlock
+                }
+
+            """.trimIndent()
+            )
+
             // Copies test project data
             mapOf(
                 "app-target" to appTargetSetupRule,
@@ -135,7 +171,7 @@
 data class VariantProfile(
     val flavor: String?,
     val buildType: String = "release",
-    val profileLines: List<String> = listOf()
+    val profileFileLines: Map<String, List<String>> = mapOf()
 ) {
     val nonMinifiedVariant = "${flavor ?: ""}NonMinified${buildType.capitalized()}"
 }
@@ -147,7 +183,7 @@
     val rootDir: File
         get() = rule.rootDir
     val gradleRunner: GradleRunner
-        get() = GradleRunner.create().withProjectDir(rule.rootDir).withPluginClasspath()
+        get() = GradleRunner.create().withProjectDir(rule.rootDir)
 
     fun setBuildGradle(buildGradleContent: String) =
         rule.writeDefaultBuildGradle(
@@ -194,27 +230,41 @@
                 VariantProfile(
                     flavor = "free",
                     buildType = "release",
-                    profileLines = freeReleaseProfileLines
+                    profileFileLines = mapOf("myTest" to freeReleaseProfileLines)
                 ),
                 VariantProfile(
                     flavor = "paid",
                     buildType = "release",
-                    profileLines = paidReleaseProfileLines
+                    profileFileLines = mapOf("myTest" to paidReleaseProfileLines)
                 ),
             )
         )
     }
 
+    fun setupWithoutFlavors(releaseProfileLines: List<String>) {
+        setup(
+            variantProfiles = listOf(
+                VariantProfile(
+                    flavor = null,
+                    buildType = "release",
+                    profileFileLines = mapOf("myTest" to releaseProfileLines)
+                )
+            )
+        )
+    }
+
     fun setup(
         variantProfiles: List<VariantProfile> = listOf(
             VariantProfile(
                 flavor = null,
                 buildType = "release",
-                profileLines = listOf(
-                    Fixtures.CLASS_1_METHOD_1,
-                    Fixtures.CLASS_2_METHOD_2,
-                    Fixtures.CLASS_2,
-                    Fixtures.CLASS_1
+                profileFileLines = mapOf(
+                    "myTest" to listOf(
+                        Fixtures.CLASS_1_METHOD_1,
+                        Fixtures.CLASS_2_METHOD_2,
+                        Fixtures.CLASS_2,
+                        Fixtures.CLASS_1
+                    )
                 )
             )
         ),
@@ -264,12 +314,17 @@
         val disableConnectedAndroidTestsBlock = variantProfiles.joinToString("\n") {
 
             // Creates a folder to use as results dir
-            val outputDir = File(tempFolder, it.nonMinifiedVariant).apply { mkdirs() }
+            val variantOutputDir = File(tempFolder, it.nonMinifiedVariant)
+            val testResultsOutputDir =
+                File(variantOutputDir, "testResultsOutDir").apply { mkdirs() }
+            val profilesOutputDir =
+                File(variantOutputDir, "profilesOutputDir").apply { mkdirs() }
 
             // Writes the fake test result proto in it, with the given lines
             writeFakeTestResultsProto(
-                outputDir = outputDir,
-                profileLines = it.profileLines
+                testResultsOutputDir = testResultsOutputDir,
+                profilesOutputDir = profilesOutputDir,
+                profileFileLines = it.profileFileLines
             )
 
             // Gradle script to injects a fake and disable the actual task execution for
@@ -277,7 +332,7 @@
             """
             afterEvaluate {
                 project.tasks.named("connected${it.nonMinifiedVariant.capitalized()}AndroidTest") {
-                    it.resultsDir.set(new File("${outputDir.absolutePath}"))
+                    it.resultsDir.set(new File("${testResultsOutputDir.absolutePath}"))
                     onlyIf { false }
                 }
             }
@@ -321,16 +376,19 @@
     }
 
     private fun writeFakeTestResultsProto(
-        outputDir: File,
-        profileLines: List<String>
+        testResultsOutputDir: File,
+        profilesOutputDir: File,
+        profileFileLines: Map<String, List<String>>
     ) {
 
-        val generatedProfileFile = File
-            .createTempFile("fake-baseline-prof-", ".txt")
-            .apply { writeText(profileLines.joinToString(System.lineSeparator())) }
+        val testResultProtoBuilder = TestResultProto.TestResult.newBuilder()
 
-        val testResultProto = TestResultProto.TestResult.newBuilder()
-            .addOutputArtifact(
+        // Writes a fake baseline profile for each item of the given map
+        profileFileLines.forEach {
+            val fakeProfileFile = File(profilesOutputDir, "fake-baseline-prof-${it.key}.txt")
+                .apply { writeText(it.value.joinToString(System.lineSeparator())) }
+
+            testResultProtoBuilder.addOutputArtifact(
                 TestArtifactProto.Artifact.newBuilder()
                     .setLabel(
                         LabelProto.Label.newBuilder()
@@ -339,19 +397,19 @@
                     )
                     .setSourcePath(
                         PathProto.Path.newBuilder()
-                            .setPath(generatedProfileFile.absolutePath)
+                            .setPath(fakeProfileFile.absolutePath)
                             .build()
                     )
                     .build()
             )
-            .build()
+        }
 
         val testSuiteResultProto = TestSuiteResultProto.TestSuiteResult.newBuilder()
             .setTestStatus(TestStatusProto.TestStatus.PASSED)
-            .addTestResult(testResultProto)
+            .addTestResult(testResultProtoBuilder.build())
             .build()
 
-        File(outputDir, "test-result.pb")
+        File(testResultsOutputDir, "test-result.pb")
             .apply { outputStream().use { testSuiteResultProto.writeTo(it) } }
     }
 }
diff --git a/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/Fixtures.kt b/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/Fixtures.kt
index f9ee2d5c..e69747b 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/Fixtures.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/Fixtures.kt
@@ -26,4 +26,8 @@
     const val CLASS_2_METHOD_3 = "HLcom/sample/Utils;->someOtherMethod()V"
     const val CLASS_2_METHOD_4 = "HSLcom/sample/Utils;->someOtherMethod()V"
     const val CLASS_2_METHOD_5 = "HSPLcom/sample/Utils;->someOtherMethod()V"
+    const val CLASS_3 = "Lcom/sample/Fragment;"
+    const val CLASS_3_METHOD_1 = "HSPLcom/sample/Fragment;-><init>()V"
+    const val CLASS_4 = "Lcom/sample/SomeOtherClass;"
+    const val CLASS_4_METHOD_1 = "HSPLcom/sample/SomeOtherClass;-><init>()V"
 }
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 051778b..8d29b74 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
@@ -26,6 +26,14 @@
         @TaskAction void exec() { println(getText().get()) }
     }
 
+    androidComponents {
+        def agpVersion = pluginVersion.major + "." + pluginVersion.minor + "." + pluginVersion.micro
+        if (pluginVersion.previewType != null) {
+            agpVersion += "-" + pluginVersion.previewType + pluginVersion.preview
+        }
+        println("agpVersion=" + agpVersion)
+    }
+
     """.trimIndent()
 
 internal fun GradleRunner.build(vararg arguments: String, block: (String) -> (Unit)) {
diff --git a/benchmark/benchmark-common/api/public_plus_experimental_current.txt b/benchmark/benchmark-common/api/public_plus_experimental_current.txt
index 1d9e4cd..a5a3dc8 100644
--- a/benchmark/benchmark-common/api/public_plus_experimental_current.txt
+++ b/benchmark/benchmark-common/api/public_plus_experimental_current.txt
@@ -31,7 +31,7 @@
   @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalPerfettoCaptureApi {
   }
 
-  @RequiresApi(21) @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public final class PerfettoTrace {
+  @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, 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);
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt
index 17134b9..95b10d3 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt
@@ -33,7 +33,7 @@
 import kotlin.test.assertTrue
 
 @LargeTest
-@SdkSuppress(minSdkVersion = 21)
+@SdkSuppress(minSdkVersion = 23)
 @RunWith(AndroidJUnit4::class)
 class PerfettoHelperTest {
     @Before
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoTraceTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoTraceTest.kt
index 73d01c3..8eba619 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoTraceTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoTraceTest.kt
@@ -32,7 +32,7 @@
 @OptIn(ExperimentalPerfettoCaptureApi::class)
 @LargeTest
 @RunWith(AndroidJUnit4::class)
-@SdkSuppress(minSdkVersion = 21)
+@SdkSuppress(minSdkVersion = 23)
 class PerfettoTraceTest {
     @Test
     fun record_basic() {
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
index 8cc6dde..26a4da0 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
@@ -42,13 +42,13 @@
  * @suppress
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-@RequiresApi(21)
+@RequiresApi(23)
 public class PerfettoCapture(
     /**
      * Bundled is available above API 28, but we default to using unbundled as well on API 29, as
      * ProcessStatsConfig.scan_all_processes_on_start isn't supported on the bundled version.
      */
-    unbundled: Boolean = Build.VERSION.SDK_INT in 21..29
+    unbundled: Boolean = Build.VERSION.SDK_INT <= 29
 ) {
 
     private val helper: PerfettoHelper = PerfettoHelper(unbundled)
@@ -98,7 +98,7 @@
      * Enables Perfetto SDK tracing in an app if present. Provides required binary dependencies to
      * the app if they're missing and the [provideBinariesIfMissing] parameter is set to `true`.
      */
-    @RequiresApi(Build.VERSION_CODES.R) // TODO(234351579): Support API < 30
+    @RequiresApi(30) // TODO(234351579): Support API < 30
     fun enableAndroidxTracingPerfetto(
         targetPackage: String,
         provideBinariesIfMissing: Boolean
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
index 9725c61..010cde4 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
@@ -28,7 +28,7 @@
 import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
 
 /**
- * Wrapper for [PerfettoCapture] which does nothing below L.
+ * Wrapper for [PerfettoCapture] which does nothing below API 23.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 class PerfettoCaptureWrapper {
@@ -36,7 +36,7 @@
     private val TRACE_ENABLE_PROP = "persist.traced.enable"
 
     init {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+        if (Build.VERSION.SDK_INT >= 23) {
             capture = PerfettoCapture()
         }
     }
@@ -52,16 +52,16 @@
         var inUse = false
     }
 
-    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+    @RequiresApi(23)
     private fun start(
         appTagPackages: List<String>,
         userspaceTracingPackage: String?
     ): Boolean {
         capture?.apply {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            if (Build.VERSION.SDK_INT >= 23) {
                 Log.d(LOG_TAG, "Recording perfetto trace")
                 if (userspaceTracingPackage != null &&
-                    Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
+                    Build.VERSION.SDK_INT >= 30
                 ) {
                     val result = enableAndroidxTracingPerfetto(
                         targetPackage = userspaceTracingPackage,
@@ -76,7 +76,7 @@
         return true
     }
 
-    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+    @RequiresApi(23)
     private fun stop(traceLabel: String): String {
         return Outputs.writeFile(
             fileName = "${traceLabel}_${dateToFileName()}.perfetto-trace",
@@ -100,7 +100,7 @@
         block: () -> Unit
     ): String? {
         // skip if Perfetto not supported, or on Cuttlefish (where tracing doesn't work)
-        if (Build.VERSION.SDK_INT < 21 || !isAbiSupported()) {
+        if (Build.VERSION.SDK_INT < 23 || !isAbiSupported()) {
             block()
             return null
         }
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
index e3a3d72..a2e8f73 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
@@ -37,7 +37,7 @@
  * @suppress
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-@RequiresApi(21)
+@RequiresApi(23)
 public class PerfettoHelper(
     private val unbundled: Boolean = Build.VERSION.SDK_INT < LOWEST_BUNDLED_VERSION_SUPPORTED
 ) {
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt
index 98ed9d8..5e2c29a 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt
@@ -20,7 +20,7 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import java.io.File
 
-@RequiresApi(21)
+@RequiresApi(23)
 @ExperimentalPerfettoCaptureApi
 class PerfettoTrace(
     /**
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/UiState.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/UiState.kt
index 207731a..4f0af98 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/UiState.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/UiState.kt
@@ -27,8 +27,8 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 fun UiState(
-    timelineStart: Long?,
-    timelineEnd: Long?,
+    timelineStart: Long? = null,
+    timelineEnd: Long? = null,
     highlightPackage: String?
 ) = UiState(
     timeline_start_ts = timelineStart,
diff --git a/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/PerfettoTraceRule.kt b/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/PerfettoTraceRule.kt
index aa35558..d8dcd99 100644
--- a/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/PerfettoTraceRule.kt
+++ b/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/PerfettoTraceRule.kt
@@ -87,7 +87,7 @@
     ): Statement = object : Statement() {
         override fun evaluate() {
             val thisPackage = InstrumentationRegistry.getInstrumentation().context.packageName
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            if (Build.VERSION.SDK_INT >= 23) {
                 val label = "${description.className}_${description.methodName}"
                 PerfettoTrace.record(
                     fileLabel = label,
diff --git a/benchmark/benchmark-macro/api/public_plus_experimental_current.txt b/benchmark/benchmark-macro/api/public_plus_experimental_current.txt
index 61577bd..86e2432 100644
--- a/benchmark/benchmark-macro/api/public_plus_experimental_current.txt
+++ b/benchmark/benchmark-macro/api/public_plus_experimental_current.txt
@@ -75,9 +75,50 @@
     property public final String packageName;
   }
 
+  @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> getResult(androidx.benchmark.macro.Metric.CaptureInfo captureInfo, androidx.benchmark.perfetto.PerfettoTraceProcessor.Session traceSession);
+  }
+
   public abstract sealed class Metric {
   }
 
+  @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;
+  }
+
+  @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 @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);
+  }
+
   @androidx.benchmark.macro.ExperimentalMetricApi public enum PowerCategory {
     method public static androidx.benchmark.macro.PowerCategory valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
     method public static androidx.benchmark.macro.PowerCategory[] values();
@@ -142,6 +183,11 @@
     ctor public StartupTimingMetric();
   }
 
+  @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> getResult(androidx.benchmark.macro.Metric.CaptureInfo captureInfo, androidx.benchmark.perfetto.PerfettoTraceProcessor.Session traceSession);
+  }
+
   @androidx.benchmark.macro.ExperimentalMetricApi public final class TraceSectionMetric extends androidx.benchmark.macro.Metric {
     ctor public TraceSectionMetric(String sectionName, optional androidx.benchmark.macro.TraceSectionMetric.Mode mode);
   }
@@ -173,7 +219,7 @@
 
   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 byte[] queryBytes(@org.intellij.lang.annotations.Language("sql") String query);
+    method public byte[] rawQuery(@org.intellij.lang.annotations.Language("sql") String query);
   }
 
   @androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi public final class Row implements kotlin.jvm.internal.markers.KMappedMarker java.util.Map<java.lang.String,java.lang.Object> {
diff --git a/benchmark/benchmark-macro/build.gradle b/benchmark/benchmark-macro/build.gradle
index d167114..a6b3f2f 100644
--- a/benchmark/benchmark-macro/build.gradle
+++ b/benchmark/benchmark-macro/build.gradle
@@ -60,7 +60,7 @@
 
     implementation(project(":benchmark:benchmark-common"))
     implementation("androidx.core:core:1.9.0")
-    implementation("androidx.profileinstaller:profileinstaller:1.0.3")
+    implementation("androidx.profileinstaller:profileinstaller:1.3.0")
     implementation("androidx.tracing:tracing-ktx:1.1.0-rc01")
     implementation(libs.testCore)
     implementation(libs.testUiautomator)
@@ -90,6 +90,7 @@
     kotlinOptions {
         // Enable using experimental APIs from within same version group
         freeCompilerArgs += [
+                "-opt-in=androidx.benchmark.macro.ExperimentalMetricApi",
                 "-opt-in=androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi",
                 "-opt-in=androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi"
         ]
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MetricResultExtensionsTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MetricResultExtensionsTest.kt
index ec10104..53fcdbf 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MetricResultExtensionsTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MetricResultExtensionsTest.kt
@@ -19,12 +19,11 @@
 import androidx.benchmark.MetricResult
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import org.junit.Test
-import org.junit.runner.RunWith
-import java.lang.IllegalStateException
 import kotlin.test.assertEquals
 import kotlin.test.assertFailsWith
 import kotlin.test.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
@@ -129,4 +128,25 @@
         }
         assertTrue(exception.message!!.contains("Iteration 1 didn't capture metric bar"))
     }
+
+    @Test
+    fun mergeMeasurements() {
+        val first = listOf(Metric.Measurement("foo", 1.0))
+        val second = listOf(Metric.Measurement("bar", 1.0))
+        assertEquals(first.merge(second), first + second)
+    }
+
+    @Test
+    fun mergeMeasurementsOverlappingKeys() {
+        val exception = assertFailsWith<IllegalStateException> {
+            val first = listOf(Metric.Measurement("foo", 1.0))
+            val second = listOf(Metric.Measurement("foo", 1.0))
+            first.merge(second)
+        }
+        assertTrue(
+            exception.message!!.contains(
+                "Multiple metrics produced measurements with overlapping names: [foo]"
+            )
+        )
+    }
 }
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/PowerMetricTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/PowerMetricTest.kt
index 5ab38e0..ccefa64 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/PowerMetricTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/PowerMetricTest.kt
@@ -18,6 +18,7 @@
 
 import android.os.Build
 import androidx.annotation.RequiresApi
+import androidx.benchmark.macro.Metric.Measurement
 import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
 import androidx.benchmark.perfetto.PerfettoTraceProcessor
 import kotlin.test.assertEquals
@@ -43,32 +44,32 @@
             .associateWith { PowerCategoryDisplayLevel.BREAKDOWN }
 
         val actualMetrics = PerfettoTraceProcessor.runSingleSessionServer(traceFile.absolutePath) {
-            PowerMetric(PowerMetric.Energy(categories)).getMetrics(captureInfo, this)
+            PowerMetric(PowerMetric.Energy(categories)).getResult(captureInfo, this)
         }
 
-        assertEquals(
-            IterationResult(
-                singleMetrics = mapOf(
-                    "energyComponentCpuBigUws" to 31935.0,
-                    "energyComponentCpuLittleUws" to 303264.0,
-                    "energyComponentCpuMidUws" to 55179.0,
-                    "energyComponentDisplayUws" to 1006934.0,
-                    "energyComponentGpuUws" to 66555.0,
-                    "energyComponentDdrAUws" to 48458.0,
-                    "energyComponentDdrBUws" to 54988.0,
-                    "energyComponentDdrCUws" to 100082.0,
-                    "energyComponentMemoryInterfaceUws" to 151912.0,
-                    "energyComponentTpuUws" to 50775.0,
-                    "energyComponentAocLogicUws" to 74972.0,
-                    "energyComponentAocMemoryUws" to 19601.0,
-                    "energyComponentModemUws" to 8369.0,
-                    "energyComponentRadioFrontendUws" to 0.0,
-                    "energyComponentWifiBtUws" to 493868.0,
-                    "energyComponentSystemFabricUws" to 122766.0,
-                    "energyTotalUws" to 2589658.0
-                ),
-                sampledMetrics = emptyMap()
-            ), actualMetrics)
+        assertEqualMeasurements(
+            expected = listOf(
+                Measurement("energyComponentCpuBigUws", 31935.0),
+                Measurement("energyComponentCpuLittleUws", 303264.0),
+                Measurement("energyComponentCpuMidUws", 55179.0),
+                Measurement("energyComponentDisplayUws", 1006934.0),
+                Measurement("energyComponentGpuUws", 66555.0),
+                Measurement("energyComponentDdrAUws", 48458.0),
+                Measurement("energyComponentDdrBUws", 54988.0),
+                Measurement("energyComponentDdrCUws", 100082.0),
+                Measurement("energyComponentMemoryInterfaceUws", 151912.0),
+                Measurement("energyComponentTpuUws", 50775.0),
+                Measurement("energyComponentAocLogicUws", 74972.0),
+                Measurement("energyComponentAocMemoryUws", 19601.0),
+                Measurement("energyComponentModemUws", 8369.0),
+                Measurement("energyComponentRadioFrontendUws", 0.0),
+                Measurement("energyComponentWifiBtUws", 493868.0),
+                Measurement("energyComponentSystemFabricUws", 122766.0),
+                Measurement("energyTotalUws", 2589658.0)
+            ),
+            observed = actualMetrics,
+            threshold = 0.1
+        )
     }
 
     @RequiresApi(Build.VERSION_CODES.Q)
@@ -81,23 +82,22 @@
             .associateWith { PowerCategoryDisplayLevel.TOTAL }
 
         val actualMetrics = PerfettoTraceProcessor.runSingleSessionServer(traceFile.absolutePath) {
-            PowerMetric(PowerMetric.Power(categories)).getMetrics(captureInfo, this)
+            PowerMetric(PowerMetric.Power(categories)).getResult(captureInfo, this)
         }
 
-        assertEquals(
-            IterationResult(
-                singleMetrics = mapOf(
-                    "powerCategoryCpuUw" to 80.94090814845532,
-                    "powerCategoryDisplayUw" to 208.77752436243003,
-                    "powerCategoryGpuUw" to 13.799502384408045,
-                    "powerCategoryMemoryUw" to 73.69686916856728,
-                    "powerCategoryMachineLearningUw" to 10.527679867302508,
-                    "powerCategoryNetworkUw" to 123.74248393116318,
-                    "powerUncategorizedUw" to 25.454281567489115,
-                    "powerTotalUw" to 536.9392494298155,
-                ),
-                sampledMetrics = emptyMap()
-            ), actualMetrics
+        assertEqualMeasurements(
+            expected = listOf(
+                Measurement("powerCategoryCpuUw", 80.94090814845532),
+                Measurement("powerCategoryDisplayUw", 208.77752436243003),
+                Measurement("powerCategoryGpuUw", 13.799502384408045),
+                Measurement("powerCategoryMemoryUw", 73.69686916856728),
+                Measurement("powerCategoryMachineLearningUw", 10.527679867302508),
+                Measurement("powerCategoryNetworkUw", 123.74248393116318),
+                Measurement("powerUncategorizedUw", 25.454281567489115),
+                Measurement("powerTotalUw", 536.9392494298155),
+            ),
+            observed = actualMetrics,
+            threshold = 0.00001
         )
     }
 
@@ -116,22 +116,21 @@
         )
 
         val actualMetrics = PerfettoTraceProcessor.runSingleSessionServer(traceFile.absolutePath) {
-            PowerMetric(PowerMetric.Power(categories)).getMetrics(captureInfo, this)
+            PowerMetric(PowerMetric.Power(categories)).getResult(captureInfo, this)
         }
 
-        assertEquals(
-            IterationResult(
-                singleMetrics = mapOf(
-                    "powerCategoryCpuUw" to 80.94090814845532,
-                    "powerCategoryDisplayUw" to 208.77752436243003,
-                    "powerCategoryMemoryUw" to 73.69686916856728,
-                    "powerCategoryNetworkUw" to 123.74248393116318,
-                    "powerComponentSystemFabricUw" to 25.454281567489115,
-                    "powerUnselectedUw" to 24.327182251710553,
-                    "powerTotalUw" to 536.9392494298155
-                ),
-                sampledMetrics = emptyMap()
-            ), actualMetrics
+        assertEqualMeasurements(
+            expected = listOf(
+                Measurement("powerCategoryCpuUw", 80.94090814845532),
+                Measurement("powerCategoryDisplayUw", 208.77752436243003),
+                Measurement("powerCategoryMemoryUw", 73.69686916856728),
+                Measurement("powerCategoryNetworkUw", 123.74248393116318),
+                Measurement("powerComponentSystemFabricUw", 25.454281567489115),
+                Measurement("powerUnselectedUw", 24.327182251710553),
+                Measurement("powerTotalUw", 536.9392494298155)
+            ),
+            observed = actualMetrics,
+            threshold = 0.00001
         )
     }
 
@@ -145,15 +144,10 @@
             .associateWith { PowerCategoryDisplayLevel.BREAKDOWN }
 
         val actualMetrics = PerfettoTraceProcessor.runSingleSessionServer(traceFile.absolutePath) {
-            PowerMetric(PowerMetric.Energy(categories)).getMetrics(captureInfo, this)
+            PowerMetric(PowerMetric.Energy(categories)).getResult(captureInfo, this)
         }
 
-        assertEquals(
-            IterationResult(
-                singleMetrics = emptyMap(),
-                sampledMetrics = emptyMap()
-            ), actualMetrics
-        )
+        assertEquals(emptyList(), actualMetrics)
     }
 
     @RequiresApi(Build.VERSION_CODES.Q)
@@ -164,18 +158,17 @@
         val traceFile = createTempFileFromAsset("api31_battery_discharge", ".perfetto-trace")
 
         val actualMetrics = PerfettoTraceProcessor.runSingleSessionServer(traceFile.absolutePath) {
-            PowerMetric(PowerMetric.Battery()).getMetrics(captureInfo, this)
+            PowerMetric(PowerMetric.Battery()).getResult(captureInfo, this)
         }
 
-        assertEquals(
-            IterationResult(
-                singleMetrics = mapOf(
-                    "batteryStartMah" to 1020.0,
-                    "batteryEndMah" to 1007.0,
-                    "batteryDiffMah" to 13.0
-                ),
-                sampledMetrics = emptyMap()
-            ), actualMetrics
+        assertEqualMeasurements(
+            expected = listOf(
+                Measurement("batteryStartMah", 1020.0),
+                Measurement("batteryEndMah", 1007.0),
+                Measurement("batteryDiffMah", 13.0)
+            ),
+            observed = actualMetrics,
+            threshold = 0.1
         )
     }
 }
\ No newline at end of file
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt
index 9f72b02..9e98054 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt
@@ -36,7 +36,6 @@
 import androidx.test.uiautomator.Until
 import java.io.File
 import kotlin.test.assertEquals
-import kotlin.test.assertNotNull
 import kotlin.test.assertTrue
 import org.junit.Assume.assumeFalse
 import org.junit.Assume.assumeTrue
@@ -52,10 +51,10 @@
     fun noResults() {
         assumeTrue(isAbiSupported())
         val packageName = "fake.package.fiction.nostartups"
-        val iterationResult = measureStartup(packageName, StartupMode.COLD) {
+        val measurements = measureStartup(packageName, StartupMode.COLD) {
             // Do nothing
         }
-        assertEquals(true, iterationResult.singleMetrics.isEmpty())
+        assertEquals(true, measurements.isEmpty())
     }
 
     @LargeTest
@@ -70,7 +69,7 @@
         val intent =
             Intent("androidx.benchmark.integration.macrobenchmark.target.TRIVIAL_STARTUP_ACTIVITY")
         val scope = MacrobenchmarkScope(packageName = packageName, launchWithClearTask = true)
-        val iterationResult = measureStartup(packageName, StartupMode.COLD) {
+        val measurements = measureStartup(packageName, StartupMode.COLD) {
             // Simulate a cold start
             scope.killProcess()
             scope.dropKernelPageCache()
@@ -79,10 +78,9 @@
         }
 
         assertEquals(
-            setOf("timeToInitialDisplayMs"),
-            iterationResult.singleMetrics.keys
+            listOf("timeToInitialDisplayMs"),
+            measurements.map { it.name }
         )
-        assertNotNull(iterationResult.timelineRangeNs)
     }
 
     /**
@@ -114,7 +112,7 @@
         }
 
         // measure the activity launch
-        val iterationResult = measureStartup(Packages.TEST, StartupMode.WARM) {
+        val measurements = measureStartup(Packages.TEST, StartupMode.WARM) {
             // Simulate a warm start, since it's our own process
             if (useInAppNav) {
                 // click the textview, which triggers an activity launch
@@ -148,12 +146,13 @@
         // validate
         assertEquals(
             setOf("timeToInitialDisplayMs", "timeToFullDisplayMs"),
-            iterationResult.singleMetrics.keys
+            measurements.map { it.name }.toSet()
         )
-        assertNotNull(iterationResult.timelineRangeNs)
 
-        val timeToInitialDisplayMs = iterationResult.singleMetrics["timeToInitialDisplayMs"]!!
-        val timeToFullDisplayMs = iterationResult.singleMetrics["timeToFullDisplayMs"]!!
+        val timeToInitialDisplayMs = measurements
+            .first { it.name == "timeToInitialDisplayMs" }.data.single()
+        val timeToFullDisplayMs = measurements
+            .first { it.name == "timeToFullDisplayMs" }.data.single()
 
         if (delayMs == 0L) {
             // since reportFullyDrawn is dispatched before startup is complete,
@@ -167,7 +166,6 @@
                     "ttid $timeToInitialDisplayMs, ttfd $timeToFullDisplayMs"
             )
         }
-        assertNotNull(iterationResult.timelineRangeNs)
     }
 
     @LargeTest
@@ -198,21 +196,21 @@
         validateStartup_fullyDrawn(delayMs = 100, useInAppNav = true)
     }
 
-    private fun getApi32WarmMetrics(metric: Metric): IterationResult {
+    private fun getApi32WarmMeasurements(metric: Metric): List<Metric.Measurement> {
         assumeTrue(isAbiSupported())
         val traceFile = createTempFileFromAsset("api32_startup_warm", ".perfetto-trace")
         val packageName = "androidx.benchmark.integration.macrobenchmark.target"
 
         metric.configure(packageName)
         return PerfettoTraceProcessor.runSingleSessionServer(traceFile.absolutePath) {
-            metric.getMetrics(
+            metric.getResult(
                 captureInfo = Metric.CaptureInfo(
                     targetPackageName = "androidx.benchmark.integration.macrobenchmark.target",
                     testPackageName = "androidx.benchmark.integration.macrobenchmark.test",
                     startupMode = StartupMode.WARM,
                     apiLevel = 32
                 ),
-                session = this
+                traceSession = this
             )
         }
     }
@@ -228,54 +226,57 @@
         val metric = StartupTimingMetric()
         metric.configure(Packages.TEST)
 
-        val metrics = PerfettoTraceProcessor.runSingleSessionServer(traceFile.absolutePath) {
-            metric.getMetrics(
+        val measurements = PerfettoTraceProcessor.runSingleSessionServer(traceFile.absolutePath) {
+            metric.getResult(
                 captureInfo = Metric.CaptureInfo(
                     targetPackageName = Packages.TEST,
                     testPackageName = Packages.TEST,
                     startupMode = StartupMode.WARM,
                     apiLevel = 24
                 ),
-                session = this
+                traceSession = this
             )
         }
 
-        // check known values
-        assertEquals(
-            setOf("timeToInitialDisplayMs", "timeToFullDisplayMs"),
-            metrics.singleMetrics.keys
+        assertEqualMeasurements(
+            expected = listOf(
+                Metric.Measurement("timeToInitialDisplayMs", 178.58525),
+                Metric.Measurement("timeToFullDisplayMs", 178.58525)
+            ),
+            observed = measurements,
+            threshold = 0.0001
         )
-        assertEquals(178.58525, metrics.singleMetrics["timeToInitialDisplayMs"]!!, 0.0001)
-        assertEquals(178.58525, metrics.singleMetrics["timeToFullDisplayMs"]!!, 0.0001)
-        assertEquals(1680207215350..1680385800600, metrics.timelineRangeNs)
     }
 
     @MediumTest
     @Test
     fun fixedStartupTraceMetrics() {
-        val metrics = getApi32WarmMetrics(StartupTimingMetric())
+        val measurements = getApi32WarmMeasurements(StartupTimingMetric())
 
-        // check known values
-        assertEquals(
-            setOf("timeToInitialDisplayMs", "timeToFullDisplayMs"),
-            metrics.singleMetrics.keys
+        assertEqualMeasurements(
+            expected = listOf(
+                Metric.Measurement("timeToInitialDisplayMs", 154.629883),
+                Metric.Measurement("timeToFullDisplayMs", 659.641358)
+            ),
+            observed = measurements,
+            threshold = 0.0001
         )
-        assertEquals(154.629883, metrics.singleMetrics["timeToInitialDisplayMs"]!!, 0.0001)
-        assertEquals(659.641358, metrics.singleMetrics["timeToFullDisplayMs"]!!, 0.0001)
-        assertEquals(157479786572825..157480446214183, metrics.timelineRangeNs)
     }
 
     @SuppressLint("NewApi") // suppressed for StartupTimingLegacyMetric, since data is fixed
     @MediumTest
     @Test
     fun fixedStartupTraceMetrics_legacy() {
-        val metrics = getApi32WarmMetrics(StartupTimingLegacyMetric())
+        val measurements = getApi32WarmMeasurements(StartupTimingLegacyMetric())
 
-        // check known values
-        assertEquals(setOf("startupMs", "fullyDrawnMs"), metrics.singleMetrics.keys)
-        assertEquals(156.515747, metrics.singleMetrics["startupMs"]!!, 0.0001)
-        assertEquals(644.613729, metrics.singleMetrics["fullyDrawnMs"]!!, 0.0001)
-        assertEquals(157479786566030..157479943081777, metrics.timelineRangeNs)
+        assertEqualMeasurements(
+            expected = listOf(
+                Metric.Measurement("startupMs", 156.515747),
+                Metric.Measurement("fullyDrawnMs", 644.613729)
+            ),
+            observed = measurements,
+            threshold = 0.0001
+        )
     }
 }
 
@@ -284,7 +285,7 @@
     packageName: String,
     startupMode: StartupMode,
     measureBlock: () -> Unit
-): IterationResult {
+): List<Metric.Measurement> {
     val metric = StartupTimingMetric()
     metric.configure(packageName)
     val tracePath = PerfettoCaptureWrapper().record(
@@ -301,14 +302,14 @@
     )!!
 
     return PerfettoTraceProcessor.runSingleSessionServer(tracePath) {
-        metric.getMetrics(
+        metric.getResult(
             captureInfo = Metric.CaptureInfo(
                 targetPackageName = packageName,
                 testPackageName = Packages.TEST,
                 startupMode = startupMode,
                 apiLevel = Build.VERSION.SDK_INT
             ),
-            session = this
+            traceSession = this
         )
     }
 }
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/TraceMetricTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/TraceMetricTest.kt
new file mode 100644
index 0000000..da73a55
--- /dev/null
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/TraceMetricTest.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.benchmark.macro
+
+import androidx.benchmark.perfetto.PerfettoHelper
+import androidx.benchmark.perfetto.PerfettoTraceProcessor
+import androidx.test.filters.MediumTest
+import org.junit.Assume.assumeTrue
+import org.junit.Test
+
+@MediumTest
+@OptIn(ExperimentalMetricApi::class)
+class TraceMetricTest {
+    private val api31HotStart = createTempFileFromAsset(
+        prefix = "api31_startup_hot",
+        suffix = ".perfetto-trace"
+    ).absolutePath
+
+    @Test
+    fun verifyActivityResume() = verifyActivityResume(
+        tracePath = api31HotStart,
+        expectedMs = 0.322
+    )
+
+    class ActivityResumeMetric : TraceMetric() {
+        override fun getResult(
+            captureInfo: CaptureInfo,
+            traceSession: PerfettoTraceProcessor.Session
+        ): List<Measurement> {
+            val rowSequence = traceSession.query(
+                """
+                SELECT
+                    slice.name as name,
+                    slice.ts as ts,
+                    slice.dur as dur
+                FROM slice
+                    INNER JOIN thread_track on slice.track_id = thread_track.id
+                    INNER JOIN thread USING(utid)
+                    INNER JOIN process USING(upid)
+                WHERE
+                    process.name LIKE "${captureInfo.targetPackageName}"
+                        AND slice.name LIKE "activityResume"
+                """.trimIndent()
+            )
+            val row = rowSequence.firstOrNull()
+            val activityResultNs = row?.long("dur")
+            println("ns $row, $activityResultNs")
+            return if (activityResultNs != null) {
+                listOf(Measurement("activityResumeMs", activityResultNs / 1_000_000.0))
+            } else {
+                emptyList()
+            }
+        }
+    }
+
+    companion object {
+        private val captureInfo = Metric.CaptureInfo(
+            targetPackageName = Packages.TARGET,
+            testPackageName = Packages.TEST,
+            startupMode = StartupMode.HOT,
+            apiLevel = 31
+        )
+
+        private fun verifyActivityResume(
+            tracePath: String,
+            @Suppress("SameParameterValue") expectedMs: Double
+        ) {
+            assumeTrue(PerfettoHelper.isAbiSupported())
+            val metric = ActivityResumeMetric()
+            metric.configure(packageName = Packages.TEST)
+
+            val result = PerfettoTraceProcessor.runSingleSessionServer(tracePath) {
+                metric.getResult(
+                    captureInfo = captureInfo,
+                    traceSession = this
+                )
+            }
+
+            assertEqualMeasurements(
+                expected = listOf(Metric.Measurement("activityResumeMs", expectedMs)),
+                observed = result,
+                threshold = 0.001
+            )
+        }
+    }
+}
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/TraceSectionMetricTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/TraceSectionMetricTest.kt
index 698ef07..b208517 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/TraceSectionMetricTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/TraceSectionMetricTest.kt
@@ -19,7 +19,6 @@
 import androidx.benchmark.perfetto.PerfettoHelper
 import androidx.benchmark.perfetto.PerfettoTraceProcessor
 import androidx.test.filters.MediumTest
-import kotlin.test.assertEquals
 import org.junit.Assume.assumeTrue
 import org.junit.Test
 
@@ -103,18 +102,20 @@
             assumeTrue(PerfettoHelper.isAbiSupported())
 
             val metric = TraceSectionMetric(sectionName, mode)
-            val expectedKey = sectionName + "Ms"
             metric.configure(packageName = packageName)
 
-            val iterationResult = PerfettoTraceProcessor.runSingleSessionServer(tracePath) {
-                metric.getMetrics(
+            val result = PerfettoTraceProcessor.runSingleSessionServer(tracePath) {
+                metric.getResult(
                     captureInfo = captureInfo,
-                    session = this
+                    traceSession = this
                 )
             }
 
-            assertEquals(setOf(expectedKey), iterationResult.singleMetrics.keys)
-            assertEquals(expectedMs, iterationResult.singleMetrics[expectedKey]!!, 0.001)
+            assertEqualMeasurements(
+                expected = listOf(Metric.Measurement(sectionName + "Ms", expectedMs)),
+                observed = result,
+                threshold = 0.001
+            )
         }
 
         private fun verifyFirstSum(
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/MemoryCountersQueryTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/MemoryCountersQueryTest.kt
new file mode 100644
index 0000000..df8c879
--- /dev/null
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/MemoryCountersQueryTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.benchmark.macro.perfetto
+
+import androidx.benchmark.macro.createTempFileFromAsset
+import androidx.benchmark.perfetto.PerfettoHelper
+import androidx.benchmark.perfetto.PerfettoTraceProcessor
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import kotlin.test.assertEquals
+import org.junit.Assume.assumeTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class MemoryCountersQueryTest {
+    @Test
+    @MediumTest
+    fun fixedTrace33() {
+        assumeTrue(PerfettoHelper.isAbiSupported())
+        val traceFile = createTempFileFromAsset("api31_startup_cold", ".perfetto-trace")
+        val metrics = PerfettoTraceProcessor.runSingleSessionServer(
+            traceFile.absolutePath
+        ) {
+            MemoryCountersQuery.getMemoryCounters(
+                this,
+                "androidx.benchmark.integration.macrobenchmark.target"
+            )
+        }
+        val expectedMetrics = MemoryCountersQuery.SubMetrics(
+            minorPageFaults = 3431.0,
+            majorPageFaults = 6.0,
+            pageFaultsBackedBySwapCache = 0.0,
+            pageFaultsBackedByReadIO = 8.0,
+            memoryCompactionEvents = 0.0,
+            memoryReclaimEvents = 0.0
+        )
+        assertEquals(expectedMetrics, metrics)
+    }
+}
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt
index fa830d2..c9c8608 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt
@@ -34,7 +34,7 @@
 @RunWith(AndroidJUnit4::class)
 class PerfettoCaptureTest {
     @SdkSuppress(
-        minSdkVersion = 21,
+        minSdkVersion = 23,
         maxSdkVersion = LOWEST_BUNDLED_VERSION_SUPPORTED - 1
     )
     @SmallTest
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/perfetto/PerfettoTraceProcessorTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/perfetto/PerfettoTraceProcessorTest.kt
index 2a7f619..ff8666e 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/perfetto/PerfettoTraceProcessorTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/perfetto/PerfettoTraceProcessorTest.kt
@@ -25,8 +25,10 @@
 import java.net.ConnectException
 import java.net.HttpURLConnection
 import java.net.URL
+import kotlin.test.assertContains
 import kotlin.test.assertEquals
 import kotlin.test.assertFailsWith
+import kotlin.test.assertNull
 import org.junit.Assert.assertTrue
 import org.junit.Assume.assumeFalse
 import org.junit.Assume.assumeTrue
@@ -125,6 +127,18 @@
     }
 
     @Test
+    fun query_syntaxError() {
+        assumeTrue(isAbiSupported())
+        val traceFile = createTempFileFromAsset("api31_startup_cold", ".perfetto-trace")
+        PerfettoTraceProcessor.runSingleSessionServer(traceFile.absolutePath) {
+            val error = assertFailsWith<IllegalStateException> {
+                query("SYNTAX ERROR, PLEASE!")
+            }
+            assertContains(error.message!!, "syntax error")
+        }
+    }
+
+    @Test
     fun query() {
         assumeTrue(isAbiSupported())
         val traceFile = createTempFileFromAsset("api31_startup_cold", ".perfetto-trace")
@@ -141,17 +155,6 @@
                     "SELECT name,ts,dur FROM slice WHERE name LIKE \"activityStart\""
                 ).toList(),
             )
-            query("""
-                    |SELECT
-                    |    slice.name,slice.ts,slice.dur
-                    |FROM slice
-                    |    INNER JOIN thread_track on slice.track_id = thread_track.id
-                    |    INNER JOIN thread USING(utid)
-                    |    INNER JOIN process USING(upid)
-                    |WHERE
-                    |    slice.name LIKE \"activityStart\"
-                """.trimMargin()
-            ).forEach { println(it) }
 
             // list of lists
             assertEquals(
@@ -190,17 +193,19 @@
         assumeTrue(isAbiSupported())
         val traceFile = createTempFileFromAsset("api31_startup_cold", ".perfetto-trace")
         PerfettoTraceProcessor.runSingleSessionServer(traceFile.absolutePath) {
-            val bytes = queryBytes(
-                "SELECT name,ts,dur FROM slice WHERE name LIKE \"activityStart\""
-            )
+            val query = "SELECT name,ts,dur FROM slice WHERE name LIKE \"activityStart\""
+            val bytes = rawQuery(query)
+            val queryResult = perfetto.protos.QueryResult.ADAPTER.decode(bytes)
+            assertNull(queryResult.error, "no error expected")
             assertEquals(
                 expected = listOf(
                     rowOf(
                         "name" to "activityStart",
                         "ts" to 186975009436431L,
-                        "dur" to 29580628L)
+                        "dur" to 29580628L
+                    )
                 ),
-                actual = QueryResultIterator(perfetto.protos.QueryResult.ADAPTER.decode(bytes))
+                actual = QueryResultIterator(queryResult)
                     .asSequence()
                     .toList(),
             )
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/IterationResult.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/IterationResult.kt
deleted file mode 100644
index 1adf20f..0000000
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/IterationResult.kt
+++ /dev/null
@@ -1,74 +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.benchmark.macro
-
-import androidx.benchmark.BenchmarkResult
-
-/**
- * Metric results from a single macrobenchmark iteration.
- */
-internal data class IterationResult(
-    /**
-     * Results for metrics that are measured once per iteration.
-     */
-    val singleMetrics: Map<String, Double>,
-
-    /**
-     * Results for metrics that are sampled multiple times per iteration, with all samples pooled.
-     */
-    val sampledMetrics: Map<String, List<Double>>,
-
-    /**
-     * Start of iteration relevant content, if easily provided, in trace-native nano timestamps.
-     *
-     * The union of all timelineRanges for a given iteration, if any are present, will determine
-     * default zoom for that iteration's trace in Studio / Perfetto UI.
-     */
-    val timelineRangeNs: LongRange? = null
-) {
-    operator fun plus(element: IterationResult) = IterationResult(
-        singleMetrics = singleMetrics + element.singleMetrics,
-        sampledMetrics = sampledMetrics + element.sampledMetrics,
-        timelineRangeNs = listOf(
-            element.timelineRangeNs,
-            this.timelineRangeNs
-        ).mergeTimelineRangeNs()
-    )
-
-    private fun List<LongRange?>.mergeTimelineRangeNs(): LongRange? {
-        filterNotNull().run {
-            return if (isNotEmpty()) {
-                (minOf { it.first })..(maxOf { it.last })
-            } else {
-                null
-            }
-        }
-    }
-
-    companion object {
-        val EMPTY = IterationResult(
-            singleMetrics = emptyMap(),
-            sampledMetrics = emptyMap(),
-            timelineRangeNs = null
-        )
-    }
-}
-
-internal fun List<IterationResult>.mergeIterationMeasurements() = BenchmarkResult.Measurements(
-    singleMetrics = this.map { it.singleMetrics }.mergeToSingleMetricResults(),
-    sampledMetrics = this.map { it.sampledMetrics }.mergeToSampledMetricResults()
-)
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
index 357acee..c220445 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
@@ -252,13 +252,13 @@
 
                 tracePaths.add(tracePath)
 
-                val iterationResult = loadTrace(PerfettoTrace(tracePath)) {
+                val measurementList = loadTrace(PerfettoTrace(tracePath)) {
                     // Extracts the metrics using the perfetto trace processor
                     userspaceTrace("extract metrics") {
                         metrics
-                            // capture list of Map<String,Long> per metric
+                            // capture list of Measurements
                             .map {
-                                it.getMetrics(
+                                it.getResult(
                                     Metric.CaptureInfo(
                                         targetPackageName = packageName,
                                         testPackageName = macrobenchPackageName,
@@ -268,15 +268,13 @@
                                     this
                                 )
                             }
-                            // merge into one map
-                            .reduce { sum, element -> sum + element }
+                            // merge together
+                            .reduce { sum, element -> sum.merge(element) }
                     }
                 }
 
                 // append UI state to trace, so tools opening trace will highlight relevant part in UI
                 val uiState = UiState(
-                    timelineStart = iterationResult.timelineRangeNs?.first,
-                    timelineEnd = iterationResult.timelineRangeNs?.last,
                     highlightPackage = packageName
                 )
                 File(tracePath).apply {
@@ -289,8 +287,8 @@
                 Log.d(TAG, "Iteration $iteration captured $uiState")
 
                 // report just the metrics
-                iterationResult
-            }.mergeIterationMeasurements()
+                measurementList
+            }.mergeMultiIterResults()
         }
 
         require(measurements.isNotEmpty()) {
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
index b3293ce..6c6c458 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
@@ -28,6 +28,7 @@
 import androidx.benchmark.macro.perfetto.BatteryDischargeQuery
 import androidx.benchmark.macro.perfetto.FrameTimingQuery
 import androidx.benchmark.macro.perfetto.FrameTimingQuery.SubMetric
+import androidx.benchmark.macro.perfetto.MemoryCountersQuery
 import androidx.benchmark.macro.perfetto.PowerQuery
 import androidx.benchmark.macro.perfetto.StartupTimingQuery
 import androidx.benchmark.macro.perfetto.camelCase
@@ -37,7 +38,7 @@
 /**
  * Metric interface.
  */
-public sealed class Metric {
+sealed class Metric {
 
     internal abstract fun configure(packageName: String)
 
@@ -51,17 +52,76 @@
      * TODO: takes package for package level filtering, but probably want a
      *  general config object coming into [start].
      */
-    internal abstract fun getMetrics(
+    internal abstract fun getResult(
         captureInfo: CaptureInfo,
-        session: PerfettoTraceProcessor.Session
-    ): IterationResult
+        traceSession: PerfettoTraceProcessor.Session
+    ): List<Measurement>
 
-    internal data class CaptureInfo(
+    @ExperimentalMetricApi
+    data class CaptureInfo(
         val apiLevel: Int,
         val targetPackageName: String,
         val testPackageName: String,
         val startupMode: StartupMode?
     )
+
+    /**
+     * Represents a Metric's measurement of a single iteration.
+     *
+     * To validate results in tests, use [assertEqualMeasurements]
+     */
+    @ExperimentalMetricApi
+    data class Measurement internal constructor(
+        /**
+         * Unique name of the metric, should be camel case with abbreviated suffix,
+         * e.g. `startTimeNs`
+         */
+        val name: String,
+        /**
+         * Measurement values captured by the metric, length constraints defined by
+         * [requireSingleValue].
+         */
+        val data: List<Double>,
+        /**
+         * True if the [data] param is a single value per measurement, false if it contains an
+         * arbitrary number of samples.
+         */
+        val requireSingleValue: Boolean
+    ) {
+
+        /**
+         * Represents a measurement with a single value captured per iteration.
+         *
+         * For example, in a startup Macrobenchmark, [StartupTimingMetric] returns a single
+         * measurement for `timeToInitialDisplayMs`.
+         */
+        constructor(name: String, data: Double) : this(
+            name,
+            listOf(data),
+            requireSingleValue = true
+        )
+
+        /**
+         * Represents a measurement with a value sampled an arbitrary number of times per iteration.
+         *
+         * For example, in a jank Macrobenchmark, [FrameTimingMetric] can return multiple
+         * measurements for `frameOverrunMs` - one for each observed frame.
+         *
+         * When measurements are merged across multiple iterations, percentiles are extracted from
+         * the total pool of samples: P50, P90, P95, and P99.
+         */
+        constructor(name: String, dataSamples: List<Double>) : this(
+            name,
+            dataSamples,
+            requireSingleValue = false
+        )
+
+        init {
+            require(!requireSingleValue || data.size == 1) {
+                "Metric.Measurement must be in multi-sample mode, or include only one data item"
+            }
+        }
+    }
 }
 
 private fun Long.nsToDoubleMs(): Double = this / 1_000_000.0
@@ -86,29 +146,25 @@
  */
 @ExperimentalMetricApi
 @Suppress("CanSealedSubClassBeObject")
-public class AudioUnderrunMetric : Metric() {
-    internal override fun configure(packageName: String) {
+class AudioUnderrunMetric : Metric() {
+    override fun configure(packageName: String) {
     }
 
-    internal override fun start() {
+    override fun start() {
     }
 
-    internal override fun stop() {
+    override fun stop() {
     }
 
-    internal override fun getMetrics(
+    override fun getResult(
         captureInfo: CaptureInfo,
-        session: PerfettoTraceProcessor.Session
-    ): IterationResult {
-        val subMetrics = AudioUnderrunQuery.getSubMetrics(session)
+        traceSession: PerfettoTraceProcessor.Session
+    ): List<Measurement> {
+        val subMetrics = AudioUnderrunQuery.getSubMetrics(traceSession)
 
-        return IterationResult(
-            singleMetrics = mapOf(
-                "audioTotalMs" to subMetrics.totalMs.toDouble(),
-                "audioUnderrunMs" to subMetrics.zeroMs.toDouble()
-            ),
-            sampledMetrics = emptyMap(),
-            timelineRangeNs = null
+        return listOf(
+            Measurement("audioTotalMs", subMetrics.totalMs.toDouble()),
+            Measurement("audioUnderrunMs", subMetrics.zeroMs.toDouble())
         )
     }
 }
@@ -127,37 +183,32 @@
  * Thread, and RenderThread.
  */
 @Suppress("CanSealedSubClassBeObject")
-public class FrameTimingMetric : Metric() {
-    internal override fun configure(packageName: String) {}
-    internal override fun start() {}
-    internal override fun stop() {}
+class FrameTimingMetric : Metric() {
+    override fun configure(packageName: String) {}
+    override fun start() {}
+    override fun stop() {}
 
     @SuppressLint("SyntheticAccessor")
-    internal override fun getMetrics(
+    override fun getResult(
         captureInfo: CaptureInfo,
-        session: PerfettoTraceProcessor.Session
-    ): IterationResult {
-        val subMetricsMsMap = FrameTimingQuery.getFrameSubMetrics(
-            session = session,
+        traceSession: PerfettoTraceProcessor.Session
+    ): List<Measurement> {
+        return FrameTimingQuery.getFrameSubMetrics(
+            session = traceSession,
             captureApiLevel = Build.VERSION.SDK_INT,
             packageName = captureInfo.targetPackageName
         )
             .filterKeys { it == SubMetric.FrameDurationCpuNs || it == SubMetric.FrameOverrunNs }
-            .mapKeys {
-                if (it.key == SubMetric.FrameDurationCpuNs) {
-                    "frameDurationCpuMs"
-                } else {
-                    "frameOverrunMs"
-                }
+            .map {
+                Measurement(
+                    name = if (it.key == SubMetric.FrameDurationCpuNs) {
+                        "frameDurationCpuMs"
+                    } else {
+                        "frameOverrunMs"
+                    },
+                    dataSamples = it.value.map { timeNs -> timeNs.nsToDoubleMs() }
+                )
             }
-            .mapValues { entry ->
-                entry.value.map { timeNs -> timeNs.nsToDoubleMs() }
-            }
-        return IterationResult(
-            singleMetrics = emptyMap(),
-            sampledMetrics = subMetricsMsMap,
-            timelineRangeNs = null
-        )
     }
 }
 
@@ -175,23 +226,23 @@
  * measurement may not be available prior to API 29.
  */
 @Suppress("CanSealedSubClassBeObject")
-public class StartupTimingMetric : Metric() {
-    internal override fun configure(packageName: String) {
+class StartupTimingMetric : Metric() {
+    override fun configure(packageName: String) {
     }
 
-    internal override fun start() {
+    override fun start() {
     }
 
-    internal override fun stop() {
+    override fun stop() {
     }
 
     @SuppressLint("SyntheticAccessor")
-    internal override fun getMetrics(
+    override fun getResult(
         captureInfo: CaptureInfo,
-        session: PerfettoTraceProcessor.Session
-    ): IterationResult {
+        traceSession: PerfettoTraceProcessor.Session
+    ): List<Measurement> {
         return StartupTimingQuery.getFrameSubMetrics(
-            session = session,
+            session = traceSession,
             captureApiLevel = captureInfo.apiLevel,
             targetPackageName = captureInfo.targetPackageName,
 
@@ -199,16 +250,14 @@
             // error if startup mode not defined
             startupMode = captureInfo.startupMode ?: StartupMode.COLD
         )?.run {
-            @Suppress("UNCHECKED_CAST")
-            IterationResult(
-                singleMetrics = mapOf(
-                    "timeToInitialDisplayMs" to timeToInitialDisplayNs.nsToDoubleMs(),
-                    "timeToFullDisplayMs" to timeToFullDisplayNs?.nsToDoubleMs()
-                ).filterValues { it != null } as Map<String, Double>,
-                sampledMetrics = emptyMap(),
-                timelineRangeNs = timelineRangeNs
-            )
-        } ?: IterationResult.EMPTY
+            mapOf(
+                "timeToInitialDisplayMs" to timeToInitialDisplayNs.nsToDoubleMs(),
+                "timeToFullDisplayMs" to timeToFullDisplayNs?.nsToDoubleMs()
+            ).filterValues { it != null }
+                .map {
+                    Measurement(it.key, it.value!!)
+                }
+        } ?: emptyList()
     }
 }
 
@@ -218,60 +267,115 @@
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
 @Suppress("CanSealedSubClassBeObject")
 @RequiresApi(29)
-public class StartupTimingLegacyMetric : Metric() {
-    internal override fun configure(packageName: String) {
+class StartupTimingLegacyMetric : Metric() {
+    override fun configure(packageName: String) {
     }
 
-    internal override fun start() {
+    override fun start() {
     }
 
-    internal override fun stop() {
+    override fun stop() {
     }
 
-    internal override fun getMetrics(
+    override fun getResult(
         captureInfo: CaptureInfo,
-        session: PerfettoTraceProcessor.Session
-    ): IterationResult {
-
+        traceSession: PerfettoTraceProcessor.Session
+    ): List<Measurement> {
         // Acquires perfetto metrics
-        val traceMetrics = session.getTraceMetrics("android_startup")
+        val traceMetrics = traceSession.getTraceMetrics("android_startup")
         val androidStartup = traceMetrics.android_startup
             ?: throw IllegalStateException("No android_startup metric found.")
         val appStartup =
             androidStartup.startup.firstOrNull { it.package_name == captureInfo.targetPackageName }
-                ?: throw IllegalStateException("Didn't find startup for pkg " +
-                    "${captureInfo.targetPackageName}, found startups for pkgs: " +
-                    "${androidStartup.startup.map {it.package_name}}")
+                ?: throw IllegalStateException(
+                    "Didn't find startup for pkg " +
+                        "${captureInfo.targetPackageName}, found startups for pkgs: " +
+                        "${androidStartup.startup.map { it.package_name }}"
+                )
 
         // Extract app startup
-        val metricMap = mutableMapOf<String, Double>()
+        val measurements = mutableListOf<Measurement>()
 
         val durMs = appStartup.to_first_frame?.dur_ms
         if (durMs != null) {
-            metricMap["startupMs"] = durMs
+            measurements.add(Measurement("startupMs", durMs))
         }
 
         val fullyDrawnMs = appStartup.report_fully_drawn?.dur_ms
         if (fullyDrawnMs != null) {
-            metricMap["fullyDrawnMs"] = fullyDrawnMs
+            measurements.add(Measurement("fullyDrawnMs", fullyDrawnMs))
         }
 
-        val timelineStart = appStartup.event_timestamps?.intent_received
-        val timelineEnd = appStartup.event_timestamps?.first_frame
-
-        return IterationResult(
-            singleMetrics = metricMap,
-            sampledMetrics = emptyMap(),
-            timelineRangeNs = if (timelineStart != null && timelineEnd != null) {
-                timelineStart..timelineEnd
-            } else {
-                null
-            }
-        )
+        return measurements
     }
 }
 
 /**
+ * Metric which captures results from a Perfetto trace with custom [PerfettoTraceProcessor] queries.
+ *
+ * This is a more customizable version of [TraceSectionMetric] which can perform arbitrary queries
+ * against the captured PerfettoTrace.
+ *
+ * Sample metric which finds the duration of the first "activityResume" trace section for the traced
+ * package:
+ * ```
+ * class ActivityResumeMetric : TraceMetric() {
+ *     override fun getResult(
+ *         captureInfo: CaptureInfo,
+ *         traceSession: PerfettoTraceProcessor.Session
+ *     ): Result {
+ *         val rowSequence = traceSession.query(
+ *             """
+ *             SELECT
+ *                 slice.name as name,
+ *                 slice.ts as ts,
+ *                 slice.dur as dur
+ *             FROM slice
+ *                 INNER JOIN thread_track on slice.track_id = thread_track.id
+ *                 INNER JOIN thread USING(utid)
+ *                 INNER JOIN process USING(upid)
+ *             WHERE
+ *                 process.name LIKE ${captureInfo.targetPackageName}
+ *                     AND slice.name LIKE "activityResume"
+ *             """.trimIndent()
+ *         )
+ *         // this metric queries a single slice type to produce submetrics, but could be extended
+ *         // to capture timing of every component of activity lifecycle
+ *         val activityResultNs = rowSequence.firstOrNull()?.double("dur")
+ *         return if (activityResultMs != null) {
+ *             Result("activityResumeMs", activityResultNs / 1_000_000.0)
+ *         } else {
+ *             Result()
+ *         }
+ *     }
+ * }
+ * ```
+ *
+ * @see PerfettoTraceProcessor
+ * @see PerfettoTraceProcessor.Session
+ * @see PerfettoTraceProcessor.Session.query
+ */
+@ExperimentalMetricApi
+abstract class TraceMetric : Metric() {
+    override fun configure(packageName: String) {
+    }
+
+    override fun start() {
+    }
+
+    override fun stop() {
+    }
+
+    /**
+     * Get the metric result for a given iteration given information about the target process and a TraceProcessor session
+     */
+    public abstract override fun getResult(
+        captureInfo: CaptureInfo,
+        traceSession: PerfettoTraceProcessor.Session
+    ): List<Measurement>
+}
+
+/**
  * Captures the time taken by named trace section - a named begin / end pair matching the provided
  * [sectionName].
  *
@@ -282,7 +386,7 @@
  * @see androidx.tracing.trace
  */
 @ExperimentalMetricApi
-public class TraceSectionMetric(
+class TraceSectionMetric(
     private val sectionName: String,
     private val mode: Mode = Mode.First
 ) : Metric() {
@@ -304,47 +408,42 @@
         Sum
     }
 
-    internal override fun configure(packageName: String) {
+    override fun configure(packageName: String) {
     }
 
-    internal override fun start() {
+    override fun start() {
     }
 
-    internal override fun stop() {
+    override fun stop() {
     }
 
     @SuppressLint("SyntheticAccessor")
-    internal override fun getMetrics(
+    override fun getResult(
         captureInfo: CaptureInfo,
-        session: PerfettoTraceProcessor.Session
-    ): IterationResult {
-        val slices = session.querySlices(sectionName)
+        traceSession: PerfettoTraceProcessor.Session
+    ): List<Measurement> {
+        val slices = traceSession.querySlices(sectionName)
 
         return when (mode) {
             Mode.First -> {
                 val slice = slices.firstOrNull()
                 if (slice == null) {
-                    IterationResult.EMPTY
-                } else IterationResult(
-                    singleMetrics = mapOf(
-                        sectionName + "Ms" to slice.dur / 1_000_000.0
-                    ),
-                    sampledMetrics = emptyMap(),
-                    timelineRangeNs = slice.ts..slice.endTs
+                    emptyList()
+                } else listOf(
+                    Measurement(
+                        name = sectionName + "Ms",
+                        data = slice.dur / 1_000_000.0
+                    )
                 )
             }
+
             Mode.Sum -> {
-                // note, this duration assumes non-reentrant slices
-                val durMs = slices.sumOf { it.dur } / 1_000_000.0
-                IterationResult(
-                    singleMetrics = mapOf(sectionName + "Ms" to durMs),
-                    sampledMetrics = emptyMap(),
-                    timelineRangeNs = if (slices.isEmpty()) {
-                        null
-                    } else {
-                        // parens added to make ktlint happy
-                        (slices.minOf { it.ts })..(slices.maxOf { it.endTs })
-                    }
+                listOf(
+                    Measurement(
+                        name = sectionName + "Ms",
+                        // note, this duration assumes non-reentrant slices
+                        data = slices.sumOf { it.dur } / 1_000_000.0
+                    )
                 )
             }
         }
@@ -404,18 +503,20 @@
  */
 @RequiresApi(29)
 @ExperimentalMetricApi
-public class PowerMetric(
+class PowerMetric(
     private val type: Type
 ) : Metric() {
 
     companion object {
         internal const val MEASURE_BLOCK_SECTION_NAME = "measureBlock"
 
+        @Suppress("FunctionName")
         @JvmStatic
         fun Battery(): Type.Battery {
             return Type.Battery()
         }
 
+        @Suppress("FunctionName")
         @JvmStatic
         fun Energy(
             categories: Map<PowerCategory, PowerCategoryDisplayLevel> = emptyMap()
@@ -423,6 +524,7 @@
             return Type.Energy(categories)
         }
 
+        @Suppress("FunctionName")
         @JvmStatic
         fun Power(
             categories: Map<PowerCategory, PowerCategoryDisplayLevel> = emptyMap()
@@ -452,7 +554,7 @@
         class Battery : Type()
     }
 
-    internal override fun configure(packageName: String) {
+    override fun configure(packageName: String) {
         if (type is Type.Energy || type is Type.Power) {
             hasMetrics(throwOnMissingMetrics = true)
         } else {
@@ -460,73 +562,63 @@
         }
     }
 
-    internal override fun start() {
+    override fun start() {
         if (type is Type.Battery) {
             Shell.executeScriptSilent("setprop power.battery_input.suspended true")
         }
     }
 
-    internal override fun stop() {
+    override fun stop() {
         if (type is Type.Battery) {
             Shell.executeScriptSilent("setprop power.battery_input.suspended false")
         }
     }
 
-    internal override fun getMetrics(
+    override fun getResult(
         captureInfo: CaptureInfo,
-        session: PerfettoTraceProcessor.Session
-    ): IterationResult {
+        traceSession: PerfettoTraceProcessor.Session
+    ): List<Measurement> {
         // collect metrics between trace point flags
-        val slice = session.querySlices(MEASURE_BLOCK_SECTION_NAME)
+        val slice = traceSession.querySlices(MEASURE_BLOCK_SECTION_NAME)
             .firstOrNull()
-            ?: return IterationResult.EMPTY
+            ?: return emptyList()
 
         if (type is Type.Battery) {
-            return getBatteryDischargeMetrics(session, slice)
+            return getBatteryDischargeMetrics(traceSession, slice)
         }
 
-        return getPowerMetrics(session, slice)
+        return getPowerMetrics(traceSession, slice)
     }
 
     private fun getBatteryDischargeMetrics(
         session: PerfettoTraceProcessor.Session,
         slice: Slice
-    ): IterationResult {
+    ): List<Measurement> {
         val metrics = BatteryDischargeQuery.getBatteryDischargeMetrics(
             session,
             slice
         )
-
-        val metricMap: Map<String, Double> = metrics.associate { measurement ->
-            getLabel(measurement.name) to measurement.chargeMah
+        return metrics.map { measurement ->
+            Measurement(getLabel(measurement.name), measurement.chargeMah)
         }
-
-        return IterationResult(
-            singleMetrics = metricMap,
-            sampledMetrics = emptyMap()
-        )
     }
 
     private fun getPowerMetrics(
         session: PerfettoTraceProcessor.Session,
         slice: Slice
-    ): IterationResult {
+    ): List<Measurement> {
         val metrics = PowerQuery.getPowerMetrics(session, slice)
 
         val metricMap: Map<String, Double> = getSpecifiedMetrics(metrics)
         if (metricMap.isEmpty()) {
-            return IterationResult(
-                singleMetrics = emptyMap(),
-                sampledMetrics = emptyMap()
-            )
+            return emptyList()
         }
 
         val extraMetrics: Map<String, Double> = getTotalAndUnselectedMetrics(metrics)
 
-        return IterationResult(
-            singleMetrics = metricMap + extraMetrics,
-            sampledMetrics = emptyMap()
-        )
+        return (metricMap + extraMetrics).map {
+            Measurement(it.key, it.value)
+        }
     }
 
     private fun getLabel(metricName: String, displayType: String = ""): String {
@@ -582,3 +674,28 @@
         }.flatten().associate { pair -> Pair(pair.first, pair.second) }
     }
 }
+
+/**
+ * Captures the number of page faults over time for a target package name.
+ */
+@ExperimentalMetricApi
+class MemoryCountersMetric : TraceMetric() {
+    override fun getResult(
+        captureInfo: CaptureInfo,
+        traceSession: PerfettoTraceProcessor.Session
+    ): List<Measurement> {
+        val metrics = MemoryCountersQuery.getMemoryCounters(
+            session = traceSession,
+            targetPackageName = captureInfo.targetPackageName
+        ) ?: return listOf()
+
+        return listOf(
+            Measurement("minorPageFaults", metrics.minorPageFaults),
+            Measurement("majorPageFaults", metrics.majorPageFaults),
+            Measurement("pageFaultsBackedBySwapCache", metrics.pageFaultsBackedBySwapCache),
+            Measurement("pageFaultsBackedByReadIO", metrics.pageFaultsBackedByReadIO),
+            Measurement("memoryCompactionEvents", metrics.memoryCompactionEvents),
+            Measurement("memoryReclaimEvents", metrics.memoryReclaimEvents),
+        )
+    }
+}
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MetricResultExtensions.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MetricResultExtensions.kt
index 364e6ae..dee66797 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MetricResultExtensions.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MetricResultExtensions.kt
@@ -17,7 +17,102 @@
 package androidx.benchmark.macro
 
 import android.util.Log
+import androidx.benchmark.BenchmarkResult
 import androidx.benchmark.MetricResult
+import kotlin.math.abs
+
+/**
+ * Asserts that the two lists of Measurements are equal with a threshold for data,
+ * ignoring list order.
+ *
+ * @throws AssertionError
+ */
+@ExperimentalMetricApi
+fun assertEqualMeasurements(
+    expected: List<Metric.Measurement>,
+    observed: List<Metric.Measurement>,
+    threshold: Double
+) {
+    val expectedSorted = expected.sortedBy { it.name }
+    val observedSorted = observed.sortedBy { it.name }
+    val expectedNames = listOf(expectedSorted.map { it.name })
+    val observedNames = listOf(observedSorted.map { it.name })
+    if (expectedNames != observedNames) {
+        throw AssertionError("expected same measurement names, " +
+            "expected = $expectedNames, observed = $observedNames")
+    }
+
+    var errorString = ""
+    expectedSorted.zip(observedSorted) { expectedMeasurement, observedMeasurement ->
+        val name = expectedMeasurement.name
+        if (expectedMeasurement.requireSingleValue !=
+            observedMeasurement.requireSingleValue
+        ) {
+            errorString += "expected value of requireSingleValue " +
+                "(${expectedMeasurement.requireSingleValue}) does not match observed " +
+                "value ${observedMeasurement.requireSingleValue}\n"
+        }
+
+        val expectedSamples = expectedMeasurement.data
+        val observedSamples = observedMeasurement.data
+        if (expectedSamples.size != observedSamples.size) {
+            errorString += "$name expected ${expectedSamples.size} samples," +
+                " observed ${observedSamples.size}\n"
+        } else {
+            expectedSamples.zip(observedSamples).forEachIndexed { index, pair ->
+                if (abs(pair.first - pair.second) > threshold) {
+                    errorString += "$name sample $index observed ${pair.first}" +
+                        " more than $threshold from expected ${pair.second}\n"
+                }
+            }
+        }
+    }
+
+    if (errorString.isNotBlank()) {
+        throw AssertionError(errorString)
+    }
+}
+
+internal fun List<Metric.Measurement>.merge(
+    other: List<Metric.Measurement>
+): List<Metric.Measurement> {
+    val nameSet = this.map { it.name }.toSet()
+    val otherNameSet = other.map { it.name }.toSet()
+    val intersectingNames = nameSet.intersect(otherNameSet)
+    if (intersectingNames.isNotEmpty()) {
+        throw IllegalStateException(
+            "Multiple metrics produced " +
+                "measurements with overlapping names: $intersectingNames"
+        )
+    }
+    return this + other
+}
+
+/**
+ * Takes a `List<List<Measurement>>`, one for each iteration, and transposes the data to be
+ * organized by Measurement name, with data merged into a `MetricResult`.
+ *
+ * For requireSingleValue Measurements, this becomes a MetricResult used to extract min/med/max.
+ *
+ * For !requireSingleValue SubResults, this becomes a MetricResult used to extract
+ * P50/P90/P95/P99 from a flattened list of all samples, pooled together.
+ */
+internal fun List<List<Metric.Measurement>>.mergeMultiIterResults() = BenchmarkResult.Measurements(
+    singleMetrics = this.map {
+        it.filter { measurement ->
+            measurement.requireSingleValue
+        }.associate { singleResult ->
+            singleResult.name to singleResult.data.first()
+        }
+    }.mergeToSingleMetricResults(),
+    sampledMetrics = this.map {
+        it.filter { measurement ->
+            !measurement.requireSingleValue
+        }.associate { singleResult ->
+            singleResult.name to singleResult.data
+        }
+    }.mergeToSampledMetricResults()
+)
 
 /**
  * Merge the Map<String, Long> results from each iteration into one List<MetricResult>
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/MemoryCountersQuery.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/MemoryCountersQuery.kt
new file mode 100644
index 0000000..1d9a6aa
--- /dev/null
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/MemoryCountersQuery.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.benchmark.macro.perfetto
+
+import android.util.Log
+import androidx.benchmark.macro.TAG
+import androidx.benchmark.perfetto.PerfettoTraceProcessor
+import androidx.benchmark.perfetto.processNameLikePkg
+import org.intellij.lang.annotations.Language
+
+internal object MemoryCountersQuery {
+    // https://perfetto.dev/docs/data-sources/memory-counters
+    @Language("sql")
+    internal fun getFullQuery(targetPackageName: String) = """
+        SELECT
+            track.name as counter_name,
+            process.name as process_name,
+            ts,
+            value
+        FROM counter
+            LEFT JOIN process_counter_track as track on counter.track_id = track.id
+            LEFT JOIN process using (upid)
+        WHERE
+            ${processNameLikePkg(targetPackageName)} AND
+            track.name LIKE 'mem.%.count'
+    """.trimIndent()
+
+    private const val MINOR_PAGE_FAULTS_COUNT = "mem.mm.min_flt.count"
+    private const val MAJOR_PAGE_FAULTS_COUNT = "mem.mm.maj_flt.count"
+    private const val PAGE_FAULTS_BACKED_BY_SWAP_CACHE_COUNT = "mem.mm.swp_flt.count"
+    private const val PAGE_FAULTS_BACKED_BY_READ_IO_COUNT = "mem.mm.read_io.count"
+    private const val MEMORY_COMPACTION_EVENTS_COUNT = "mem.mm.compaction.count"
+    private const val MEMORY_RECLAIM_EVENTS_COUNT = "mem.mm.reclaim.count"
+
+    data class SubMetrics(
+        // Minor Page Faults
+        val minorPageFaults: Double,
+        // Major Page Faults
+        val majorPageFaults: Double,
+        // Page Faults Served by Swap Cache
+        val pageFaultsBackedBySwapCache: Double,
+        // Read Page Faults backed by I/O
+        val pageFaultsBackedByReadIO: Double,
+        // Memory Compaction Events
+        val memoryCompactionEvents: Double,
+        // Memory Reclaim Events
+        val memoryReclaimEvents: Double
+    )
+
+    fun getMemoryCounters(
+        session: PerfettoTraceProcessor.Session,
+        targetPackageName: String
+    ): SubMetrics? {
+        val queryResultIterator = session.query(
+            query = getFullQuery(targetPackageName = targetPackageName)
+        )
+
+        var minorPageFaults = 0.0
+        var majorPageFaults = 0.0
+        var faultsBackedBySwapCache = 0.0
+        var faultsBackedByReadIO = 0.0
+        var memoryCompactionEvents = 0.0
+        var memoryReclaimEvents = 0.0
+
+        val rows = queryResultIterator.toList()
+        if (rows.isEmpty()) {
+            return null
+        } else {
+            rows.forEach { row ->
+                when (row.string("counter_name")) {
+
+                    MINOR_PAGE_FAULTS_COUNT -> {
+                        minorPageFaults += row.double("value")
+                    }
+
+                    MAJOR_PAGE_FAULTS_COUNT -> {
+                        majorPageFaults += row.double("value")
+                    }
+
+                    PAGE_FAULTS_BACKED_BY_SWAP_CACHE_COUNT -> {
+                        faultsBackedBySwapCache += row.double("value")
+                    }
+
+                    PAGE_FAULTS_BACKED_BY_READ_IO_COUNT -> {
+                        faultsBackedByReadIO += row.double("value")
+                    }
+
+                    MEMORY_COMPACTION_EVENTS_COUNT -> {
+                        memoryCompactionEvents += row.double("value")
+                    }
+
+                    MEMORY_RECLAIM_EVENTS_COUNT -> {
+                        memoryReclaimEvents += row.double("value")
+                    }
+
+                    else -> Log.d(TAG, "Unknown counter: $row")
+                }
+            }
+
+            return SubMetrics(
+                minorPageFaults = minorPageFaults,
+                majorPageFaults = majorPageFaults,
+                pageFaultsBackedBySwapCache = faultsBackedBySwapCache,
+                pageFaultsBackedByReadIO = faultsBackedByReadIO,
+                memoryCompactionEvents = memoryCompactionEvents,
+                memoryReclaimEvents = memoryReclaimEvents
+            )
+        }
+    }
+}
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 27b143c..de174db 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
@@ -24,7 +24,6 @@
 import androidx.benchmark.Shell
 import androidx.benchmark.ShellScript
 import androidx.benchmark.perfetto.PerfettoTraceProcessor
-import androidx.benchmark.perfetto.QueryResultIterator
 import androidx.benchmark.userspaceTrace
 import java.io.IOException
 import java.io.InputStream
@@ -36,7 +35,6 @@
 import perfetto.protos.ComputeMetricArgs
 import perfetto.protos.ComputeMetricResult
 import perfetto.protos.QueryArgs
-import perfetto.protos.QueryResult
 import perfetto.protos.StatusResult
 
 /**
@@ -187,8 +185,11 @@
 
     /**
      * Executes the given [sqlQuery] on a previously parsed trace with custom decoding.
+     *
+     * Note that this does not decode the query result, so it's the caller's responsibility to check
+     * for errors in the result.
      */
-    fun <T> query(sqlQuery: String, decodeBlock: (InputStream) -> T): T =
+    fun <T> rawQuery(sqlQuery: String, decodeBlock: (InputStream) -> T): T =
         httpRequest(
             method = METHOD_POST,
             url = PATH_QUERY,
@@ -197,14 +198,6 @@
         )
 
     /**
-     * Executes the given [sqlQuery] on a previously parsed trace and returns the result as a
-     * [QueryResultIterator]
-     */
-    fun query(sqlQuery: String): QueryResultIterator = query(sqlQuery) {
-        QueryResultIterator(QueryResult.ADAPTER.decode(it))
-    }
-
-    /**
      * Computes the given metrics on a previously parsed trace.
      */
     fun computeMetric(metrics: List<String>): ComputeMetricResult =
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/PerfettoTraceProcessor.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/PerfettoTraceProcessor.kt
index 5254f1c..64d2eef 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/PerfettoTraceProcessor.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/PerfettoTraceProcessor.kt
@@ -21,6 +21,7 @@
 import androidx.benchmark.macro.perfetto.server.PerfettoHttpServer
 import androidx.benchmark.userspaceTrace
 import java.io.File
+import java.io.InputStream
 import org.intellij.lang.annotations.Language
 import perfetto.protos.QueryResult
 import perfetto.protos.TraceMetrics
@@ -130,6 +131,11 @@
         return block.invoke(Session(this))
     }
 
+    /**
+     * Handle to query sql data from a [PerfettoTrace].
+     *
+     * @see query
+     */
     class Session internal constructor(
         private val traceProcessor: PerfettoTraceProcessor
     ) {
@@ -174,19 +180,33 @@
          *     }
          * }
          * ```
+         *
+         * @see PerfettoTraceProcessor
+         * @see PerfettoTraceProcessor.Session
          */
         fun query(@Language("sql") query: String): Sequence<Row> {
             userspaceTrace("PerfettoTraceProcessor#query $query".take(127)) {
                 require(traceProcessor.perfettoHttpServer.isRunning()) {
                     "Perfetto trace_shell_process is not running."
                 }
-                val queryResult = traceProcessor.perfettoHttpServer.query(query) {
-                    QueryResult.ADAPTER.decode(it)
+                val queryResult = traceProcessor.perfettoHttpServer.rawQuery(query) {
+                    // Note: check for errors as part of decode, so it's immediate
+                    // instead of lazily in QueryResultIterator
+                    QueryResult.decodeAndCheckError(query, it)
                 }
                 return Sequence { QueryResultIterator(queryResult) }
             }
         }
 
+        private fun QueryResult.Companion.decodeAndCheckError(
+            query: String,
+            inputStream: InputStream
+        ) = ADAPTER.decode(inputStream).also {
+            check(it.error == null) {
+                throw IllegalStateException("Error with query: --$query--, error=${it.error}")
+            }
+        }
+
         /**
          * Computes the given query on the currently loaded trace, returning the resulting protobuf
          * bytes as a [ByteArray].
@@ -197,14 +217,17 @@
          * [in the Perfetto project](https://github.com/google/perfetto/blob/master/protos/perfetto/trace_processor/trace_processor.proto),
          * which can be used to decode the result returned here with a protobuf parsing library.
          *
+         * Note that this method does not check for errors in the protobuf, that is the caller's
+         * responsibility.
+         *
          * @see Session.query
          */
-        fun queryBytes(@Language("sql") query: String): ByteArray {
+        fun rawQuery(@Language("sql") query: String): ByteArray {
             userspaceTrace("PerfettoTraceProcessor#query $query".take(127)) {
                 require(traceProcessor.perfettoHttpServer.isRunning()) {
                     "Perfetto trace_shell_process is not running."
                 }
-                return traceProcessor.perfettoHttpServer.query(query) { it.readBytes() }
+                return traceProcessor.perfettoHttpServer.rawQuery(query) { it.readBytes() }
             }
         }
 
@@ -224,15 +247,13 @@
                     "slice.name LIKE \"$it\""
                 }
 
-            val queryResultIterator = query(
+            return query(
                 query = """
-                SELECT slice.name,ts,dur
-                FROM slice
-                WHERE $whereClause
-            """.trimMargin()
-            )
-
-            return queryResultIterator.toSlices()
+                    SELECT slice.name,ts,dur
+                    FROM slice
+                    WHERE $whereClause
+                    """.trimMargin()
+            ).toSlices()
         }
     }
 
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/QueryResultIterator.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/QueryResultIterator.kt
index caabb5e..ba6eab3 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/QueryResultIterator.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/QueryResultIterator.kt
@@ -21,7 +21,7 @@
 /**
  * Iterator for results from a [PerfettoTraceProcessor] query.
  */
-internal class QueryResultIterator internal constructor(queryResult: QueryResult) : Iterator<Row> {
+internal class QueryResultIterator constructor(queryResult: QueryResult) : Iterator<Row> {
     private val dataLists = object {
         val stringBatches = mutableListOf<String>()
         val varIntBatches = mutableListOf<Long>()
diff --git a/biometric/biometric-ktx/build.gradle b/biometric/biometric-ktx/build.gradle
old mode 100755
new mode 100644
index 89d81fc..dec0bdd
--- a/biometric/biometric-ktx/build.gradle
+++ b/biometric/biometric-ktx/build.gradle
@@ -23,11 +23,6 @@
 }
 
 dependencies {
-    // Atomic group
-    constraints {
-        implementation(project(":biometric:biometric-ktx"))
-    }
-
     api(libs.kotlinStdlib)
     api(libs.kotlinCoroutinesCore)
     api(project(":biometric:biometric"))
diff --git a/biometric/biometric/build.gradle b/biometric/biometric/build.gradle
index 4243ce5..c295ae0 100644
--- a/biometric/biometric/build.gradle
+++ b/biometric/biometric/build.gradle
@@ -28,11 +28,6 @@
 }
 
 dependencies {
-    // Atomic group
-    constraints {
-        implementation(project(":biometric:biometric-ktx"))
-    }
-
     // Public API dependencies
     api("androidx.annotation:annotation:1.1.0")
     api("androidx.core:core:1.3.2")
diff --git a/buildSrc-tests/src/test/kotlin/androidx/build/checkapi/CheckApiTest.kt b/buildSrc-tests/src/test/kotlin/androidx/build/checkapi/CheckApiTest.kt
new file mode 100644
index 0000000..7bf5f5a
--- /dev/null
+++ b/buildSrc-tests/src/test/kotlin/androidx/build/checkapi/CheckApiTest.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.build.checkapi
+
+import androidx.build.Version
+import java.io.File
+import org.junit.Assert.assertEquals
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+
+class CheckApiTest {
+    @Rule
+    @JvmField
+    val tmpFolder = TemporaryFolder()
+
+    @Test
+    fun getRequiredCompatibilityApiFileFromDirTest() {
+        val apiDir = createTempDir("api", CORE_API_FILES)
+
+        assertEquals(
+            "1.1.0-beta02.txt",
+            getRequiredCompatibilityApiFileFromDir(
+                apiDir,
+                Version("1.1.0-beta03"),
+                ApiType.CLASSAPI
+            )?.name
+        )
+
+        assertEquals(
+            "1.3.0-beta01.txt",
+            getRequiredCompatibilityApiFileFromDir(
+                apiDir,
+                Version("1.4.0-alpha01"),
+                ApiType.CLASSAPI
+            )?.name
+        )
+    }
+
+    @Suppress("SameParameterValue")
+    private fun createTempDir(dirName: String, fileNames: List<String>): File =
+        tmpFolder.newFolder(dirName).also { apiDir ->
+            fileNames.forEach { fileName ->
+                File(apiDir, fileName).createNewFile()
+            }
+        }
+}
+
+/**
+ * List of API files representing `androidx.core:core:1.3.0-beta01`.
+ */
+private val CORE_API_FILES = listOf(
+    "1.1.0-beta01.txt",
+    "1.1.0-beta02.txt",
+    "1.1.0-rc01.txt",
+    "1.2.0-beta01.txt",
+    "1.2.0-beta02.txt",
+    "1.3.0-beta01.txt",
+    "api_lint.ignore",
+    "current.txt",
+    "public_plus_experimental_1.0.0.txt",
+    "public_plus_experimental_1.1.0-beta01.txt",
+    "public_plus_experimental_1.1.0-rc01.txt",
+    "public_plus_experimental_1.2.0-beta01.txt",
+    "public_plus_experimental_1.2.0-beta02.txt",
+    "public_plus_experimental_1.3.0-beta01.txt",
+    "res-1.1.0-beta01.txt",
+    "res-1.1.0-beta02.txt",
+    "res-1.1.0-rc01.txt",
+    "res-1.2.0-beta01.txt",
+    "res-1.2.0-beta02.txt",
+    "res-1.3.0-beta01.txt",
+    "res-current.txt",
+    "restricted_1.0.0.txt",
+    "restricted_1.1.0-beta01.txt",
+    "restricted_1.1.0-beta02.txt",
+    "restricted_1.1.0-rc01.txt",
+    "restricted_1.2.0-beta01.txt",
+    "restricted_1.2.0-beta02.txt",
+    "restricted_1.3.0-beta01.txt",
+    "restricted_current.txt"
+)
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
index d762bce..cca49a3 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
@@ -19,6 +19,7 @@
 import androidx.build.dependencyTracker.AffectedModuleDetector
 import org.gradle.api.GradleException
 import org.gradle.api.Project
+import org.gradle.api.provider.Provider
 
 /**
  * Whether to enable constraints for projects in same-version groups
@@ -151,6 +152,11 @@
  */
 const val ALLOW_CUSTOM_COMPILE_SDK = "androidx.allowCustomCompileSdk"
 
+/**
+ * If false, disable the CheckAarMetadata task. Default value is true.
+ */
+const val CHECK_AAR_METADATA = "androidx.checkAarMetadata"
+
 val ALL_ANDROIDX_PROPERTIES = setOf(
     ADD_GROUP_CONSTRAINTS,
     ALTERNATIVE_PROJECT_URL,
@@ -178,15 +184,15 @@
     ENABLED_KMP_TARGET_PLATFORMS,
     ALLOW_MISSING_LINT_CHECKS_PROJECT,
     XCODEGEN_DOWNLOAD_URI,
-    ALLOW_CUSTOM_COMPILE_SDK
+    ALLOW_CUSTOM_COMPILE_SDK,
+    CHECK_AAR_METADATA,
 )
 
 /**
  * Whether to enable constraints for projects in same-version groups
  * See the property definition for more details
  */
-fun Project.shouldAddGroupConstraints(): Boolean =
-    findBooleanProperty(ADD_GROUP_CONSTRAINTS) ?: false
+fun Project.shouldAddGroupConstraints() = booleanPropertyProvider(ADD_GROUP_CONSTRAINTS)
 
 /**
  * Returns alternative project url that will be used as "url" property
@@ -280,3 +286,15 @@
     findBooleanProperty(ALLOW_CUSTOM_COMPILE_SDK) ?: true
 
 fun Project.findBooleanProperty(propName: String) = (findProperty(propName) as? String)?.toBoolean()
+
+fun Project.booleanPropertyProvider(propName: String): Provider<Boolean> {
+    return project.providers.gradleProperty(propName).map { s ->
+        s.toBoolean()
+    }.orElse(false)
+}
+
+/**
+ * Returns whether the `CheckAarMetadata` task should be enabled.
+ */
+fun Project.isCheckAarMetadataEnabled() =
+    findBooleanProperty(CHECK_AAR_METADATA) ?: true
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index 0737dd3..89544d6 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -139,6 +139,13 @@
                 task -> configureJvmTestTask(project, task)
         }
 
+        // Disable AAR verification in the platform branch until b/271291033 is fixed.
+        if (!project.isCheckAarMetadataEnabled()) {
+            project.tasks.withType(CheckAarMetadataTask::class.java).configureEach { task ->
+                task.enabled = false
+            }
+        }
+
         project.configureTaskTimeouts()
         project.configureMavenArtifactUpload(extension, componentFactory)
         project.configureExternalDependencyLicenseCheck()
@@ -872,10 +879,17 @@
     private fun Project.configureConstraintsWithinGroup(
         extension: AndroidXExtension
     ) {
-        if (!project.shouldAddGroupConstraints()) {
+        if (!project.shouldAddGroupConstraints().get()) {
             return
         }
         project.afterEvaluate {
+            if (project.hasKotlinNativeTarget().get()) {
+                // KMP plugin cannot handle constraints properly for native targets
+                // b/274786186, YT: KT-57531
+                // It is expected to be fixed in Kotlin 1.9 after which, we should remove this check
+                return@afterEvaluate
+            }
+
             // make sure that the project has a group
             val projectGroup = extension.mavenGroup
             if (projectGroup == null)
@@ -939,6 +953,35 @@
                     dependencyConstraint
                 )
             }
+
+            // disallow duplicate constraints
+            project.configurations.all { config ->
+                // find all constraints contributed by this Configuration and its ancestors
+                val configurationConstraints: MutableSet<String> = mutableSetOf()
+                config.hierarchy.forEach { parentConfig ->
+                    parentConfig.dependencyConstraints.configureEach { dependencyConstraint ->
+                        dependencyConstraint.apply {
+                            if (
+                                versionConstraint.requiredVersion != "" &&
+                                versionConstraint.requiredVersion != "unspecified"
+                            ) {
+                                val key =
+                                    "${dependencyConstraint.group}:${dependencyConstraint.name}"
+                                if (configurationConstraints.contains(key)) {
+                                    throw GradleException(
+                                        "Constraint on $key was added multiple times in " +
+                                        "$config (version = " +
+                                        "${versionConstraint.requiredVersion}).\n\n" +
+                                        "This is unnecessary and can also trigger " +
+                                        "https://github.com/gradle/gradle/issues/24037 in " +
+                                        "builds trying to use the resulting artifacts.")
+                                }
+                                configurationConstraints.add(key)
+                            }
+                        }
+                    }
+                }
+            }
         }
     }
 
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
index 6597c53..6c1b432 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
@@ -22,6 +22,7 @@
 import org.gradle.api.Project
 import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
 import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper
+import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
 import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
 import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
 import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset
@@ -51,6 +52,12 @@
     val targets: NamedDomainObjectCollection<KotlinTarget>
         get() = kotlinExtension.targets
 
+    internal fun hasNativeTarget(): Boolean {
+        // it is important to check initialized here not to trigger initialization
+        return kotlinExtensionDelegate.isInitialized() && targets.any {
+            it.platformType == KotlinPlatformType.native
+        }
+    }
     fun sourceSets(closure: Closure<*>) {
         if (kotlinExtensionDelegate.isInitialized()) {
             kotlinExtension.sourceSets.configure(closure)
@@ -197,3 +204,11 @@
         const val EXTENSION_NAME = "androidXMultiplatform"
     }
 }
+
+/**
+ * Returns a provider that is set to true if and only if this project has at least 1 kotlin native
+ * target (mac, linux, ios).
+ */
+internal fun Project.hasKotlinNativeTarget() = project.provider {
+    project.extensions.getByType(AndroidXMultiplatformExtension::class.java).hasNativeTarget()
+}
\ No newline at end of file
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
index d9ff238..f7bd29a 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
@@ -91,8 +91,6 @@
         )
         buildOnServerTask.cacheEvenIfNoOutputs()
         buildOnServerTask.distributionDirectory = getDistributionDirectory()
-        buildOnServerTask.repositoryDirectory = getRepositoryDirectory()
-        buildOnServerTask.buildId = getBuildId()
         buildOnServerTask.dependsOn(
             tasks.register(
                 CREATE_AGGREGATE_BUILD_INFO_FILES_TASK,
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/BuildOnServerTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/BuildOnServerTask.kt
index 62d83d0..006a4fc 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/BuildOnServerTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/BuildOnServerTask.kt
@@ -17,17 +17,13 @@
 package androidx.build
 
 import org.gradle.api.DefaultTask
-import org.gradle.api.tasks.InputDirectory
 import org.gradle.api.tasks.InputFiles
 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 java.io.File
-import java.io.FileInputStream
 import java.io.FileNotFoundException
-import java.util.zip.ZipEntry
-import java.util.zip.ZipInputStream
 import org.gradle.api.tasks.CacheableTask
 
 /**
@@ -48,12 +44,6 @@
     @Internal
     lateinit var distributionDirectory: File
 
-    @Internal
-    lateinit var buildId: String
-
-    @InputDirectory @PathSensitive(PathSensitivity.RELATIVE)
-    lateinit var repositoryDirectory: File
-
     @InputFiles @PathSensitive(PathSensitivity.RELATIVE)
     fun getRequiredFiles(): List<File> {
         return mutableListOf(
@@ -74,40 +64,5 @@
             val missingFileString = missingFiles.reduce { acc, s -> "$acc, $s" }
             throw FileNotFoundException("buildOnServer required output missing: $missingFileString")
         }
-
-        verifyVersionFilesPresent()
-    }
-
-    private fun verifyVersionFilesPresent() {
-        repositoryDirectory.walk().forEach { file ->
-            if (file.extension == "aar") {
-                val inputStream = FileInputStream(file)
-                val aarFileInputStream = ZipInputStream(inputStream)
-                var entry: ZipEntry? = aarFileInputStream.nextEntry
-                while (entry != null) {
-                    if (entry.name == "classes.jar") {
-                        var foundVersionFile = false
-                        val classesJarInputStream = ZipInputStream(aarFileInputStream)
-                        var jarEntry = classesJarInputStream.nextEntry
-                        while (jarEntry != null) {
-                            if (jarEntry.name.startsWith("META-INF/androidx.") &&
-                                jarEntry.name.endsWith(".version")
-                            ) {
-                                foundVersionFile = true
-                                break
-                            }
-                            jarEntry = classesJarInputStream.nextEntry
-                        }
-                        if (!foundVersionFile) {
-                            throw Exception(
-                                "Missing META-INF/ version file in ${file.absolutePath}"
-                            )
-                        }
-                        break
-                    }
-                    entry = aarFileInputStream.nextEntry
-                }
-            }
-        }
     }
 }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/Ktlint.kt b/buildSrc/private/src/main/kotlin/androidx/build/Ktlint.kt
index b878c8b..3290247 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/Ktlint.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/Ktlint.kt
@@ -233,6 +233,7 @@
             javaExecSpec.mainClass.set(MainClass)
             javaExecSpec.classpath = ktlintClasspath
             javaExecSpec.args = getArgsList(shouldFormat = true)
+            javaExecSpec.jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
             overrideDirectory?.let { javaExecSpec.workingDir = it }
         }
     }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/Release.kt b/buildSrc/private/src/main/kotlin/androidx/build/Release.kt
index ba8cc8b..bd9c72e0 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/Release.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/Release.kt
@@ -15,19 +15,23 @@
  */
 package androidx.build
 
+import androidx.build.uptodatedness.cacheEvenIfNoOutputs
 import com.android.build.gradle.LibraryExtension
 import org.gradle.api.Action
+import org.gradle.api.DefaultTask
 import org.gradle.api.GradleException
 import org.gradle.api.Project
-import org.gradle.api.Task
 import org.gradle.api.file.DuplicatesStrategy
+import org.gradle.api.provider.Provider
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.TaskAction
 import org.gradle.api.tasks.TaskProvider
 import org.gradle.api.tasks.bundling.Zip
 import org.gradle.plugin.devel.GradlePluginDevelopmentExtension
 import org.gradle.work.DisableCachingByDefault
 import java.io.File
+import java.io.FileNotFoundException
 import java.util.Locale
 
 /**
@@ -54,21 +58,6 @@
     init {
         // multiple artifacts in the same group might have the same maven-metadata.xml
         duplicatesStrategy = DuplicatesStrategy.EXCLUDE
-        if (!project.shouldAddGroupConstraints() && !isSnapshotBuild()) {
-            doFirst {
-                throw GradleException(
-                    """
-                    Cannot publish artifacts without setting -P$ADD_GROUP_CONSTRAINTS=true
-
-                    This property is required when building artifacts to publish
-
-                    (but this property can reduce remote cache usage so it is disabled by default)
-
-                    See AndroidXGradleProperties.kt for more information about this property
-                    """.trimIndent()
-                )
-            }
-        }
     }
 
     /**
@@ -95,7 +84,8 @@
         )
         // We specifically pass the subdirectory into 'from' so that changes in other artifacts
         // won't cause this task to become out of date
-        from("$androidxRepoOut/$projectSubdir") { spec ->
+        val fromDir = project.file("$androidxRepoOut/$projectSubdir")
+        from(fromDir) { spec ->
             spec.into("m2repository/$projectSubdir")
             for (inclusion in includes) {
                 include(inclusion)
@@ -204,8 +194,9 @@
         }
         val version = project.version
 
+        val projectZipTask = getProjectZipTask(project)
         val zipTasks = listOf(
-            getProjectZipTask(project),
+            projectZipTask,
             getGroupReleaseZipTask(project, mavenGroup),
             getGlobalFullZipTask(project)
         )
@@ -234,6 +225,29 @@
                 zipTask.dependsOn(publishTask)
             }
         }
+
+        val verifyInputs = getVerifyProjectZipInputsTask(project)
+        verifyInputs.configure { verifyTask ->
+            verifyTask.dependsOn(publishTask)
+            artifacts.forEach { artifact ->
+                verifyTask.addCandidate(artifact)
+            }
+        }
+        val verifyOutputs = getVerifyProjectZipOutputsTask(project)
+        verifyOutputs.configure { verifyTask ->
+            verifyTask.dependsOn(projectZipTask)
+            artifacts.forEach { artifact ->
+                verifyTask.addCandidate(artifact)
+            }
+        }
+        projectZipTask.configure { zipTask ->
+            if (!isPresubmitBuild()) {
+                zipTask.dependsOn(verifyInputs)
+                zipTask.finalizedBy(verifyOutputs)
+            }
+            val verifyOutputsTask = verifyOutputs.get()
+            verifyOutputsTask.addFile(zipTask.archiveFile.get().getAsFile())
+        }
     }
 
     /**
@@ -271,11 +285,13 @@
      * Registers an archive task as a dependency of the anchor task
      */
     private fun Project.addToAnchorTask(task: TaskProvider<GMavenZipTask>) {
-        val archiveAnchorTask = project.rootProject.maybeRegister(
+        val archiveAnchorTask: TaskProvider<VerifyVersionFilesTask> =
+        project.rootProject.maybeRegister(
             name = ALL_ARCHIVES_TASK_NAME,
-            onConfigure = { archiveTask: Task ->
+            onConfigure = { archiveTask: VerifyVersionFilesTask ->
                 archiveTask.group = "Distribution"
                 archiveTask.description = "Builds all archives for publishing"
+                archiveTask.repositoryDirectory = project.rootProject.getRepositoryDirectory()
             },
             onRegister = {
             }
@@ -353,6 +369,107 @@
         project.addToAnchorTask(taskProvider)
         return taskProvider
     }
+
+    private fun getVerifyProjectZipInputsTask(
+        project: Project
+    ): TaskProvider<VerifyGMavenZipTask> {
+        val taskProvider = project.tasks.register(
+            "verifyInputs" + PROJECT_ARCHIVE_ZIP_TASK_NAME,
+            VerifyGMavenZipTask::class.java
+        )
+        return taskProvider
+    }
+
+    private fun getVerifyProjectZipOutputsTask(
+        project: Project
+    ): TaskProvider<VerifyGMavenZipTask> {
+        val taskProvider = project.tasks.register(
+            "verifyOutputs" + PROJECT_ARCHIVE_ZIP_TASK_NAME,
+            VerifyGMavenZipTask::class.java
+        )
+        return taskProvider
+    }
+}
+
+// b/273294710
+@DisableCachingByDefault(
+    because = "This task only checks the existence of files and isn't worth caching"
+)
+open class VerifyGMavenZipTask : DefaultTask() {
+    @Input
+    val filesToVerify = mutableListOf<File>()
+
+    /**
+     * Whether this build adds automatic constraints between projects in the same group
+     */
+    @get:Input
+    val shouldAddGroupConstraints: Provider<Boolean>
+
+    init {
+        cacheEvenIfNoOutputs()
+        shouldAddGroupConstraints = project.shouldAddGroupConstraints()
+    }
+
+    fun addFile(file: File) {
+        filesToVerify.add(file)
+    }
+
+    fun addCandidate(artifact: Artifact) {
+        val groupSubdir = artifact.mavenGroup.replace('.', '/')
+        val projectSubdir = File("$groupSubdir/${artifact.projectName}")
+        val androidxRepoOut = project.getRepositoryDirectory()
+        val fromDir = project.file("$androidxRepoOut/$projectSubdir")
+        addFile(File(fromDir, "${artifact.version}"))
+    }
+
+    @TaskAction
+    fun execute() {
+        verifySettings()
+        verifyFiles()
+    }
+
+    fun verifySettings() {
+        if (!shouldAddGroupConstraints.get() && !isSnapshotBuild()) {
+            throw GradleException(
+                """
+                Cannot publish artifacts without setting -P$ADD_GROUP_CONSTRAINTS=true
+
+                This property is required when building artifacts to publish
+
+                (but this property can reduce remote cache usage so it is disabled by default)
+
+                See AndroidXGradleProperties.kt for more information about this property
+                """.trimIndent()
+            )
+        }
+    }
+
+    fun verifyFiles() {
+        val missingFiles = mutableListOf<String>()
+        val emptyDirs = mutableListOf<String>()
+        filesToVerify.forEach { file ->
+            if (!file.exists()) {
+                missingFiles.add(file.path)
+            } else {
+                if (file.isDirectory) {
+                    if (file.listFiles().isEmpty()) {
+                        emptyDirs.add(file.path)
+                    }
+                }
+            }
+        }
+
+        if (missingFiles.isNotEmpty() || emptyDirs.isNotEmpty()) {
+            val checkedFilesString = filesToVerify.toString()
+            val missingFileString = missingFiles.toString()
+            val emptyDirsString = emptyDirs.toString()
+            throw FileNotFoundException(
+                "GMavenZip ${missingFiles.size} missing files: $missingFileString, " +
+                    "${emptyDirs.size} empty dirs: $emptyDirsString. " +
+                    "Checked files: $checkedFilesString"
+            )
+        }
+    }
 }
 
 /**
@@ -395,9 +512,10 @@
 
 private val AndroidXExtension.publishPlatforms: List<String>
     get() {
-        val declaredTargets = project.multiplatformExtension?.targets?.asMap?.keys?.map {
+        val potentialTargets = project.multiplatformExtension?.targets?.asMap?.keys?.map {
             it.lowercase()
         } ?: emptySet()
+        val declaredTargets = potentialTargets.filter { it != "metadata" }
         return declaredTargets.toList()
     }
 
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/VerifyVersionFilesTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/VerifyVersionFilesTask.kt
new file mode 100644
index 0000000..14eacb9
--- /dev/null
+++ b/buildSrc/private/src/main/kotlin/androidx/build/VerifyVersionFilesTask.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.build
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.tasks.InputDirectory
+import org.gradle.api.tasks.PathSensitive
+import org.gradle.api.tasks.PathSensitivity
+import org.gradle.api.tasks.TaskAction
+import java.io.File
+import java.io.FileInputStream
+import java.util.zip.ZipEntry
+import java.util.zip.ZipInputStream
+import org.gradle.api.tasks.CacheableTask
+
+/**
+ * Task for verifying version files in Androidx artifacts
+ *
+ */
+@CacheableTask
+open class VerifyVersionFilesTask : DefaultTask() {
+
+    @InputDirectory @PathSensitive(PathSensitivity.RELATIVE)
+    lateinit var repositoryDirectory: File
+
+    @TaskAction
+    fun verifyVersionFilesPresent() {
+        repositoryDirectory.walk().forEach { file ->
+            var expectedPrefix = "androidx"
+            if (file.path.contains("/libyuv/"))
+                expectedPrefix = "libyuv_libyuv" // external library that we don't publish
+            if (file.extension == "aar") {
+                val inputStream = FileInputStream(file)
+                val aarFileInputStream = ZipInputStream(inputStream)
+                var entry: ZipEntry? = aarFileInputStream.nextEntry
+                while (entry != null) {
+                    if (entry.name == "classes.jar") {
+                        var foundVersionFile = false
+                        val classesJarInputStream = ZipInputStream(aarFileInputStream)
+                        var jarEntry = classesJarInputStream.nextEntry
+                        while (jarEntry != null) {
+                            if (jarEntry.name.startsWith("META-INF/$expectedPrefix.") &&
+                                jarEntry.name.endsWith(".version")
+                            ) {
+                                foundVersionFile = true
+                                break
+                            }
+                            jarEntry = classesJarInputStream.nextEntry
+                        }
+                        if (!foundVersionFile) {
+                            throw Exception(
+                                "Missing classes.jar/META-INF/$expectedPrefix.*version " +
+                                "file in ${file.absolutePath}"
+                            )
+                        }
+                        break
+                    }
+                    entry = aarFileInputStream.nextEntry
+                }
+            }
+        }
+    }
+}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiLocation.kt b/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiLocation.kt
index 9f10512..d9587ea 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiLocation.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/checkapi/ApiLocation.kt
@@ -84,8 +84,8 @@
             return fromBaseName(apiFileDir, CURRENT)
         }
 
-        fun isResourceApiFile(apiFile: File): Boolean {
-            return apiFile.name.startsWith(PREFIX_RESOURCE)
+        fun isResourceApiFilename(filename: String): Boolean {
+            return filename.startsWith(PREFIX_RESOURCE)
         }
 
         private fun fromBaseName(apiFileDir: File, baseName: String): ApiLocation {
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/checkapi/CheckApi.kt b/buildSrc/private/src/main/kotlin/androidx/build/checkapi/CheckApi.kt
index b08f626..9fec2fd 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/checkapi/CheckApi.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/checkapi/CheckApi.kt
@@ -17,11 +17,14 @@
 package androidx.build.checkapi
 
 import androidx.build.Version
-import androidx.build.checkapi.ApiLocation.Companion.isResourceApiFile
+import androidx.build.checkapi.ApiLocation.Companion.isResourceApiFilename
 import androidx.build.version
 import org.gradle.api.GradleException
 import org.gradle.api.Project
 import java.io.File
+import java.nio.file.Files
+import java.nio.file.Path
+import kotlin.io.path.name
 
 enum class ApiType {
     CLASSAPI,
@@ -88,28 +91,30 @@
  */
 fun getRequiredCompatibilityApiFileFromDir(
     apiDir: File,
-    version: Version,
+    apiVersion: Version,
     apiType: ApiType
 ): File? {
-    var lastFile: File? = null
-    var lastVersion: Version? = null
-    apiDir.listFiles()
-        ?.filter { file ->
-            (apiType == ApiType.RESOURCEAPI && isResourceApiFile(file)) ||
-                (apiType == ApiType.CLASSAPI && !isResourceApiFile(file))
-        }
-        ?.forEach { file ->
-            val parsed = Version.parseOrNull(file)
-            parsed?.let { otherVersion ->
-                if ((lastFile == null || lastVersion!! < otherVersion) &&
-                    (otherVersion < version) &&
-                    (otherVersion.isFinalApi()) &&
-                    (otherVersion.major == version.major)
-                ) {
-                    lastFile = file
-                    lastVersion = otherVersion
-                }
+    var highestPath: Path? = null
+    var highestVersion: Version? = null
+
+    // Find the path with highest version that is lower than the current API version.
+    Files.newDirectoryStream(apiDir.toPath()).forEach { path ->
+        val pathName = path.name
+        if ((apiType == ApiType.RESOURCEAPI && isResourceApiFilename(pathName)) ||
+            (apiType == ApiType.CLASSAPI && !isResourceApiFilename(pathName))
+        ) {
+            val pathVersion = Version.parseFilenameOrNull(pathName)
+            if (pathVersion != null &&
+                (highestVersion == null || pathVersion > highestVersion!!) &&
+                pathVersion < apiVersion &&
+                pathVersion.isFinalApi() &&
+                pathVersion.major == apiVersion.major
+            ) {
+                highestPath = path
+                highestVersion = pathVersion
             }
         }
-    return lastFile
+    }
+
+    return highestPath?.toFile()
 }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
index 68a974b..cab8d67 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
@@ -752,6 +752,9 @@
 private fun Project.getPrebuiltsExternalPath() =
     File(project.getCheckoutRoot(), "prebuilts/androidx/external/")
 
+private val PLATFORMS =
+    listOf("linuxx64", "macosarm64", "macosx64", "iosx64", "iossimulatorarm64", "iosarm64")
+
 private fun Project.getExtraCommonDependencies(): FileCollection = files(
     arrayOf(
         File(
@@ -763,8 +766,13 @@
             "org/jetbrains/kotlinx/atomicfu/0.17.0/atomicfu-0.17.0.jar"
         ),
         File(
-           getPrebuiltsExternalPath(),
+            getPrebuiltsExternalPath(),
             "com/squareup/okio/okio-jvm/3.1.0/okio-jvm-3.1.0.jar"
         )
-    )
+    ) + PLATFORMS.map {
+        File(
+            getPrebuiltsExternalPath(),
+            "com/squareup/okio/okio-$it/3.1.0/okio-$it-3.1.0.klib"
+        )
+    }
 )
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 d6a880e..bc4fc47 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt
@@ -48,6 +48,8 @@
 val ALLOW_RERUNNING_TASKS = setOf(
     "buildOnServer",
     "checkExternalLicenses",
+    // caching disabled for now while we look for a fix for b/273294710
+    "createAllArchives",
     // https://youtrack.jetbrains.com/issue/KT-52632
     "commonizeNativeDistribution",
     "createDiffArchiveForAll",
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/Version.kt b/buildSrc/public/src/main/kotlin/androidx/build/Version.kt
index 33825d2..d385d9b 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/Version.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/Version.kt
@@ -92,7 +92,14 @@
          */
         fun parseOrNull(file: File): Version? {
             if (!file.isFile) return null
-            val matcher = VERSION_FILE_REGEX.matcher(file.name)
+            return parseFilenameOrNull(file.name)
+        }
+
+        /**
+         * @return Version or null, if a name of the given file doesn't match
+         */
+        fun parseFilenameOrNull(filename: String): Version? {
+            val matcher = VERSION_FILE_REGEX.matcher(filename)
             return if (matcher.matches()) parseOrNull(matcher.group(2)) else null
         }
 
diff --git a/busytown/androidx_multiplatform_mac.sh b/busytown/androidx_multiplatform_mac.sh
index b159ca6..9f2dfbd 100755
--- a/busytown/androidx_multiplatform_mac.sh
+++ b/busytown/androidx_multiplatform_mac.sh
@@ -15,7 +15,7 @@
 # Setup simulators
 impl/androidx-native-mac-simulator-setup.sh
 
-impl/build.sh buildOnServer :docs-kmp:zipCombinedKmpDocs --no-configuration-cache -Pandroidx.displayTestOutput=false
+impl/build.sh buildOnServer :docs-kmp:zipCombinedKmpDocs --no-configuration-cache -Pandroidx.displayTestOutput=false createAllArchives -Pandroidx.constraints=true
 
 # run a separate createAllArchives task to prepare a repository
 # folder in DIST.
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManagerTest.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManagerTest.kt
index 7bbc024..02991ed 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManagerTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManagerTest.kt
@@ -58,7 +58,7 @@
     @Test
     fun previewSizeAreaIsWithinMaxPreviewArea() {
         // Act & Assert
-        val previewSize = displayInfoManager.previewSize
+        val previewSize = displayInfoManager.getPreviewSize()
         assertTrue("$previewSize has larger area than 1920 * 1080",
             previewSize.width * previewSize.height <= 1920 * 1080)
     }
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 7492aa0..048c265 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
@@ -21,6 +21,8 @@
 import android.annotation.SuppressLint
 import android.hardware.camera2.CameraCharacteristics
 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
@@ -39,6 +41,13 @@
 import androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop
 import androidx.camera.core.CameraSelector
 import androidx.camera.core.CameraState
+import androidx.camera.core.DynamicRange
+import androidx.camera.core.DynamicRange.BIT_DEPTH_10_BIT
+import androidx.camera.core.DynamicRange.BIT_DEPTH_8_BIT
+import androidx.camera.core.DynamicRange.FORMAT_DOLBY_VISION
+import androidx.camera.core.DynamicRange.FORMAT_HDR10
+import androidx.camera.core.DynamicRange.FORMAT_HDR10_PLUS
+import androidx.camera.core.DynamicRange.FORMAT_HLG
 import androidx.camera.core.ExposureState
 import androidx.camera.core.FocusMeteringAction
 import androidx.camera.core.ZoomState
@@ -177,4 +186,47 @@
         Log.warn { "TODO: isPrivateReprocessingSupported are not yet supported." }
         return false
     }
+
+    @SuppressLint("ClassVerificationFailure")
+    override fun getSupportedDynamicRanges(): Set<DynamicRange> {
+        // TODO: use DynamicRangesCompat instead after it is migrates from camera-camera2.
+        if (Build.VERSION.SDK_INT >= 33) {
+            val availableProfiles = cameraProperties.metadata[
+                CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES]!!
+            return profileSetToDynamicRangeSet(availableProfiles.supportedProfiles)
+        }
+        return setOf(DynamicRange.SDR)
+    }
+
+    private fun profileSetToDynamicRangeSet(profileSet: Set<Long>): Set<DynamicRange> {
+        return profileSet.map { profileToDynamicRange(it) }.toSet()
+    }
+
+    private fun profileToDynamicRange(profile: Long): DynamicRange {
+        return checkNotNull(PROFILE_TO_DR_MAP[profile]) {
+            "Dynamic range profile cannot be converted to a DynamicRange object: $profile"
+        }
+    }
+
+    companion object {
+        private val DR_HLG10 = DynamicRange(FORMAT_HLG, BIT_DEPTH_10_BIT)
+        private val DR_HDR10 = DynamicRange(FORMAT_HDR10, BIT_DEPTH_10_BIT)
+        private val DR_HDR10_PLUS = DynamicRange(FORMAT_HDR10_PLUS, BIT_DEPTH_10_BIT)
+        private val DR_DOLBY_VISION_10_BIT = DynamicRange(FORMAT_DOLBY_VISION, BIT_DEPTH_10_BIT)
+        private val DR_DOLBY_VISION_8_BIT = DynamicRange(FORMAT_DOLBY_VISION, BIT_DEPTH_8_BIT)
+        private val PROFILE_TO_DR_MAP: Map<Long, DynamicRange> = mapOf(
+            DynamicRangeProfiles.STANDARD to DynamicRange.SDR,
+            DynamicRangeProfiles.HLG10 to DR_HLG10,
+            DynamicRangeProfiles.HDR10 to DR_HDR10,
+            DynamicRangeProfiles.HDR10_PLUS to DR_HDR10_PLUS,
+            DynamicRangeProfiles.DOLBY_VISION_10B_HDR_OEM to DR_DOLBY_VISION_10_BIT,
+            DynamicRangeProfiles.DOLBY_VISION_10B_HDR_OEM_PO to DR_DOLBY_VISION_10_BIT,
+            DynamicRangeProfiles.DOLBY_VISION_10B_HDR_REF to DR_DOLBY_VISION_10_BIT,
+            DynamicRangeProfiles.DOLBY_VISION_10B_HDR_REF_PO to DR_DOLBY_VISION_10_BIT,
+            DynamicRangeProfiles.DOLBY_VISION_8B_HDR_OEM to DR_DOLBY_VISION_8_BIT,
+            DynamicRangeProfiles.DOLBY_VISION_8B_HDR_OEM_PO to DR_DOLBY_VISION_8_BIT,
+            DynamicRangeProfiles.DOLBY_VISION_8B_HDR_REF to DR_DOLBY_VISION_8_BIT,
+            DynamicRangeProfiles.DOLBY_VISION_8B_HDR_REF_PO to DR_DOLBY_VISION_8_BIT,
+        )
+    }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraStateAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraStateAdapter.kt
index ed435db..268c9a4 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraStateAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraStateAdapter.kt
@@ -56,7 +56,7 @@
         postCameraState(CameraInternal.State.CLOSED)
     }
 
-    public fun onGraphUpdated(cameraGraph: CameraGraph) = synchronized(lock) {
+    fun onGraphUpdated(cameraGraph: CameraGraph) = synchronized(lock) {
         Log.debug { "Camera graph updated from $currentGraph to $cameraGraph" }
         if (currentCameraInternalState != CameraInternal.State.CLOSED) {
             postCameraState(CameraInternal.State.CLOSING)
@@ -66,7 +66,7 @@
         currentCameraInternalState = CameraInternal.State.CLOSED
     }
 
-    public fun onGraphStateUpdated(cameraGraph: CameraGraph, graphState: GraphState) =
+    fun onGraphStateUpdated(cameraGraph: CameraGraph, graphState: GraphState) =
         synchronized(lock) {
             Log.debug { "$cameraGraph state updated to $graphState" }
             handleStateTransition(cameraGraph, graphState)
@@ -131,12 +131,17 @@
                             graphState.cameraError.toCameraStateError()
                         )
                     } else {
-                        // TODO(b/263201241): Add transition to PENDING_OPEN once we handle errors
-                        //  while a camera is already opened.
-                        CombinedCameraState(
-                            CameraInternal.State.CLOSING,
-                            graphState.cameraError.toCameraStateError()
-                        )
+                        if (isRecoverableError(graphState.cameraError)) {
+                            CombinedCameraState(
+                                CameraInternal.State.PENDING_OPEN,
+                                graphState.cameraError.toCameraStateError()
+                            )
+                        } else {
+                            CombinedCameraState(
+                                CameraInternal.State.CLOSING,
+                                graphState.cameraError.toCameraStateError()
+                            )
+                        }
                     }
 
                 GraphStateStopping -> CombinedCameraState(CameraInternal.State.CLOSING)
@@ -148,8 +153,19 @@
             when (graphState) {
                 GraphStateStopping -> CombinedCameraState(CameraInternal.State.CLOSING)
                 GraphStateStopped -> CombinedCameraState(CameraInternal.State.CLOSED)
-                // TODO(b/263201241): Transition to appropriate states once we handle errors while
-                //  a camera is already opened.
+                is GraphStateError ->
+                    if (isRecoverableError(graphState.cameraError)) {
+                        CombinedCameraState(
+                            CameraInternal.State.PENDING_OPEN,
+                            graphState.cameraError.toCameraStateError()
+                        )
+                    } else {
+                        CombinedCameraState(
+                            CameraInternal.State.CLOSED,
+                            graphState.cameraError.toCameraStateError()
+                        )
+                    }
+
                 else -> null
             }
 
@@ -157,10 +173,34 @@
             when (graphState) {
                 GraphStateStopped -> CombinedCameraState(CameraInternal.State.CLOSED)
                 GraphStateStarting -> CombinedCameraState(CameraInternal.State.OPENING)
+                is GraphStateError -> CombinedCameraState(
+                    CameraInternal.State.CLOSING,
+                    graphState.cameraError.toCameraStateError()
+                )
+
                 else -> null
             }
 
-        // TODO(b/263201241): Add PENDING_OPEN transitions once we have active recovery.
+        CameraInternal.State.PENDING_OPEN ->
+            when (graphState) {
+                GraphStateStarting -> CombinedCameraState(CameraInternal.State.OPENING)
+                GraphStateStarted -> CombinedCameraState(CameraInternal.State.OPEN)
+                is GraphStateError ->
+                    if (isRecoverableError(graphState.cameraError)) {
+                        CombinedCameraState(
+                            CameraInternal.State.PENDING_OPEN,
+                            graphState.cameraError.toCameraStateError()
+                        )
+                    } else {
+                        CombinedCameraState(
+                            CameraInternal.State.CLOSED,
+                            graphState.cameraError.toCameraStateError()
+                        )
+                    }
+
+                else -> null
+            }
+
         else ->
             null
     }
@@ -184,10 +224,14 @@
                         CameraState.ERROR_OTHER_RECOVERABLE_ERROR
 
                     CameraError.ERROR_ILLEGAL_ARGUMENT_EXCEPTION ->
-                        CameraState.ERROR_OTHER_RECOVERABLE_ERROR
+                        CameraState.ERROR_CAMERA_FATAL_ERROR
 
                     CameraError.ERROR_SECURITY_EXCEPTION ->
-                        CameraState.ERROR_OTHER_RECOVERABLE_ERROR
+                        CameraState.ERROR_CAMERA_FATAL_ERROR
+
+                    CameraError.ERROR_GRAPH_CONFIG -> CameraState.ERROR_STREAM_CONFIG
+                    CameraError.ERROR_DO_NOT_DISTURB_ENABLED ->
+                        CameraState.ERROR_DO_NOT_DISTURB_MODE_ENABLED
 
                     else -> throw IllegalArgumentException("Unexpected CameraError: $this")
                 }
@@ -203,6 +247,12 @@
             else -> throw IllegalArgumentException("Unexpected CameraInternal state: $this")
         }
 
+        internal fun isRecoverableError(cameraError: CameraError) =
+            cameraError == CameraError.ERROR_CAMERA_DISCONNECTED ||
+                cameraError == CameraError.ERROR_CAMERA_IN_USE ||
+                cameraError == CameraError.ERROR_CAMERA_LIMIT_EXCEEDED ||
+                cameraError == CameraError.ERROR_CAMERA_DEVICE
+
         internal fun MutableLiveData<CameraState>.setOrPostValue(cameraState: CameraState) {
             if (Looper.myLooper() == Looper.getMainLooper()) {
                 this.value = cameraState
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt
index ad5975f..ccf2838 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt
@@ -71,42 +71,16 @@
     }
 
     /**
-     * Check whether the input surface configuration list is under the capability of any combination
-     * of this object.
-     *
-     * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
-     * @param cameraId          the camera id of the camera device to be compared
-     * @param surfaceConfigList the surface configuration list to be compared
-     * @return the check result that whether it could be supported
-     */
-    override fun checkSupported(
-        isConcurrentCameraModeOn: Boolean,
-        cameraId: String,
-        surfaceConfigList: List<SurfaceConfig>?
-    ): Boolean {
-        if (surfaceConfigList == null || surfaceConfigList.isEmpty()) {
-            return true
-        }
-
-        if (!checkIfSupportedCombinationExist(cameraId)) {
-            return false
-        }
-
-        return supportedSurfaceCombinationMap[cameraId]!!.checkSupported(
-            isConcurrentCameraModeOn, surfaceConfigList)
-    }
-
-    /**
      * Transform to a SurfaceConfig object with cameraId, image format and size info
      *
-     * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
+     * @param cameraMode  the working camera mode.
      * @param cameraId    the camera id of the camera device to transform the object
      * @param imageFormat the image format info for the surface configuration object
      * @param size        the size info for the surface configuration object
      * @return new {@link SurfaceConfig} object
      */
     override fun transformSurfaceConfig(
-        isConcurrentCameraModeOn: Boolean,
+        cameraMode: Int,
         cameraId: String,
         imageFormat: Int,
         size: Size
@@ -114,7 +88,7 @@
         checkIfSupportedCombinationExist(cameraId)
 
         return supportedSurfaceCombinationMap[cameraId]!!.transformSurfaceConfig(
-            isConcurrentCameraModeOn,
+            cameraMode,
             imageFormat, size)
     }
 
@@ -130,7 +104,7 @@
     /**
      * Retrieves a map of suggested stream specifications for the given list of use cases.
      *
-     * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
+     * @param cameraMode        the working camera mode.
      * @param cameraId          the camera id of the camera device used by the use cases
      * @param existingSurfaces  list of surfaces already configured and used by the camera. The
      *                          resolutions for these surface can not change.
@@ -144,7 +118,7 @@
      *                                  is not a valid id.
      */
     override fun getSuggestedStreamSpecs(
-        isConcurrentCameraModeOn: Boolean,
+        cameraMode: Int,
         cameraId: String,
         existingSurfaces: List<AttachedSurfaceInfo>,
         newUseCaseConfigsSupportedSizeMap: Map<UseCaseConfig<*>, List<Size>>
@@ -157,7 +131,7 @@
         }
 
         return supportedSurfaceCombinationMap[cameraId]!!.getSuggestedStreamSpecifications(
-            isConcurrentCameraModeOn,
+            cameraMode,
             existingSurfaces,
             newUseCaseConfigsSupportedSizeMap
         )
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
index 91b1c4f..825e6d9 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
@@ -35,6 +35,7 @@
 import androidx.camera.core.impl.Config
 import androidx.camera.core.impl.ImageCaptureConfig
 import androidx.camera.core.impl.ImageOutputConfig
+import androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR
 import androidx.camera.core.impl.MutableOptionsBundle
 import androidx.camera.core.impl.OptionsBundle
 import androidx.camera.core.impl.PreviewConfig
@@ -42,6 +43,8 @@
 import androidx.camera.core.impl.UseCaseConfig
 import androidx.camera.core.impl.UseCaseConfigFactory
 import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType
+import androidx.camera.core.resolutionselector.ResolutionSelector
+import androidx.camera.core.resolutionselector.ResolutionStrategy
 
 /**
  * This class builds [Config] objects for a given [UseCaseConfigFactory.CaptureType].
@@ -127,9 +130,19 @@
         )
 
         if (captureType == CaptureType.PREVIEW) {
+            val previewSize = displayInfoManager.getPreviewSize()
             mutableConfig.insertOption(
                 ImageOutputConfig.OPTION_MAX_RESOLUTION,
-                displayInfoManager.previewSize
+                previewSize
+            )
+            mutableConfig.insertOption(
+                OPTION_RESOLUTION_SELECTOR,
+                ResolutionSelector.Builder().setResolutionStrategy(
+                    ResolutionStrategy.create(
+                        previewSize,
+                        ResolutionStrategy.FALLBACK_RULE_CLOSEST_LOWER
+                    )
+                ).build()
             )
         }
 
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/GuaranteedConfigurationsUtil.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/GuaranteedConfigurationsUtil.kt
index 1a78536..b383e7a 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/GuaranteedConfigurationsUtil.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/GuaranteedConfigurationsUtil.kt
@@ -414,6 +414,199 @@
     }
 
     @JvmStatic
+    fun getUltraHighResolutionSupportedCombinationList(): List<SurfaceCombination> {
+        val combinationList: MutableList<SurfaceCombination> = ArrayList()
+
+        // (YUV, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (PRIV, RECORD)
+        // Covers (YUV, ULTRA_MAXIMUM) + (PRIV, PREVIEW) in the guaranteed table.
+        SurfaceCombination().apply {
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.YUV, ConfigSize.ULTRA_MAXIMUM)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.RECORD)
+            )
+        }.also {
+            combinationList.add(it)
+        }
+
+        // (JPEG, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (PRIV, RECORD)
+        // Covers (JPEG, ULTRA_MAXIMUM) + (PRIV, PREVIEW) in the guaranteed table.
+        SurfaceCombination().apply {
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.JPEG, ConfigSize.ULTRA_MAXIMUM)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.RECORD)
+            )
+        }.also {
+            combinationList.add(it)
+        }
+
+        // (RAW, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (PRIV, RECORD)
+        // Covers (RAW, ULTRA_MAXIMUM) + (PRIV, PREVIEW) in the guaranteed table.
+        SurfaceCombination().apply {
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.RAW, ConfigSize.ULTRA_MAXIMUM)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.RECORD)
+            )
+        }.also {
+            combinationList.add(it)
+        }
+
+        // (YUV, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (JPEG, MAXIMUM)
+        SurfaceCombination().apply {
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.YUV, ConfigSize.ULTRA_MAXIMUM)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM)
+            )
+        }.also {
+            combinationList.add(it)
+        }
+
+        // (JPEG, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (JPEG, MAXIMUM)
+        SurfaceCombination().apply {
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.JPEG, ConfigSize.ULTRA_MAXIMUM)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM)
+            )
+        }.also {
+            combinationList.add(it)
+        }
+
+        // (RAW, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (JPEG, MAXIMUM)
+        SurfaceCombination().apply {
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.RAW, ConfigSize.ULTRA_MAXIMUM)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM)
+            )
+        }.also {
+            combinationList.add(it)
+        }
+
+        // (YUV, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (YUV, MAXIMUM)
+        // Covers (YUV, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (YUV, RECORD) in the guaranteed table.
+        SurfaceCombination().apply {
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.YUV, ConfigSize.ULTRA_MAXIMUM)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.YUV, ConfigSize.MAXIMUM)
+            )
+        }.also {
+            combinationList.add(it)
+        }
+
+        // (JPEG, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (YUV, MAXIMUM)
+        // Covers (JPEG, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (YUV, RECORD) in the guaranteed table.
+        SurfaceCombination().apply {
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.JPEG, ConfigSize.ULTRA_MAXIMUM)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.YUV, ConfigSize.MAXIMUM)
+            )
+        }.also {
+            combinationList.add(it)
+        }
+
+        // (RAW, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (YUV, MAXIMUM)
+        // Covers (RAW, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (YUV, RECORD) in the guaranteed table.
+        SurfaceCombination().apply {
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.RAW, ConfigSize.ULTRA_MAXIMUM)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.YUV, ConfigSize.MAXIMUM)
+            )
+        }.also {
+            combinationList.add(it)
+        }
+
+        // (YUV, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (RAW, MAXIMUM)
+        SurfaceCombination().apply {
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.YUV, ConfigSize.ULTRA_MAXIMUM)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.RAW, ConfigSize.MAXIMUM)
+            )
+        }.also {
+            combinationList.add(it)
+        }
+
+        // (JPEG, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (RAW, MAXIMUM)
+        SurfaceCombination().apply {
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.JPEG, ConfigSize.ULTRA_MAXIMUM)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.RAW, ConfigSize.MAXIMUM)
+            )
+        }.also {
+            combinationList.add(it)
+        }
+
+        // (RAW, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (RAW, MAXIMUM)
+        SurfaceCombination().apply {
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.RAW, ConfigSize.ULTRA_MAXIMUM)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW)
+            )
+            addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.RAW, ConfigSize.MAXIMUM)
+            )
+        }.also {
+            combinationList.add(it)
+        }
+
+        return combinationList
+    }
+
+    @JvmStatic
     fun getConcurrentSupportedCombinationList(): List<SurfaceCombination> {
         val combinationList: MutableList<SurfaceCombination> = ArrayList()
         // (YUV, s1440p)
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt
index c8a98dc0..6c3d9b3 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt
@@ -16,8 +16,10 @@
 
 package androidx.camera.camera2.pipe.integration.adapter
 
+import android.annotation.SuppressLint
 import android.content.Context
 import android.content.pm.PackageManager.FEATURE_CAMERA_CONCURRENT
+import android.graphics.ImageFormat
 import android.graphics.Point
 import android.graphics.SurfaceTexture
 import android.hardware.camera2.CameraCharacteristics
@@ -25,13 +27,18 @@
 import android.hardware.display.DisplayManager
 import android.media.CamcorderProfile
 import android.media.MediaRecorder
+import android.os.Build
 import android.util.Size
 import android.view.Display
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.CameraMetadata
 import androidx.camera.camera2.pipe.integration.compat.StreamConfigurationMapCompat
+import androidx.camera.camera2.pipe.integration.compat.workaround.ExtraSupportedSurfaceCombinationsContainer
 import androidx.camera.camera2.pipe.integration.compat.workaround.OutputSizesCorrector
+import androidx.camera.camera2.pipe.integration.compat.workaround.ResolutionCorrector
+import androidx.camera.camera2.pipe.integration.impl.DisplayInfoManager
 import androidx.camera.core.impl.AttachedSurfaceInfo
+import androidx.camera.core.impl.CameraMode
 import androidx.camera.core.impl.EncoderProfilesProxy
 import androidx.camera.core.impl.ImageFormatConstants
 import androidx.camera.core.impl.StreamSpec
@@ -40,8 +47,11 @@
 import androidx.camera.core.impl.SurfaceSizeDefinition
 import androidx.camera.core.impl.UseCaseConfig
 import androidx.camera.core.impl.utils.CompareSizesByArea
+import androidx.camera.core.internal.utils.SizeUtil
 import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_1080P
+import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_1440P
 import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_480P
+import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_720P
 import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_VGA
 import java.util.Arrays
 import java.util.Collections
@@ -67,22 +77,46 @@
     private val hardwareLevel =
         cameraMetadata[CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL]
             ?: CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
-    private val concurrentSurfaceCombinations: MutableList<SurfaceCombination> = ArrayList()
-    private val surfaceCombinations: MutableList<SurfaceCombination> = ArrayList()
-    private val outputSizesCache: MutableMap<Int, Array<Size>> = HashMap()
+    private val concurrentSurfaceCombinations: MutableList<SurfaceCombination> = mutableListOf()
+    private val surfaceCombinations: MutableList<SurfaceCombination> = mutableListOf()
+    private val ultraHighSurfaceCombinations: MutableList<SurfaceCombination> = mutableListOf()
+    private val cameraModeToSupportedCombinationsMap: MutableMap<Int, List<SurfaceCombination>> =
+        mutableMapOf()
     private var isRawSupported = false
     private var isBurstCaptureSupported = false
+    private var isConcurrentCameraModeSupported = false
+    private var isUltraHighResolutionSensorSupported = false
+    private val sizeDefinitionFormats = mutableListOf(
+        ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE, ImageFormat.JPEG,
+        ImageFormat.YUV_420_888
+    )
     internal lateinit var surfaceSizeDefinition: SurfaceSizeDefinition
     private val displayManager: DisplayManager =
         (context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager)
     private val streamConfigurationMapCompat = getStreamConfigurationMapCompat()
+    private val extraSupportedSurfaceCombinationsContainer =
+        ExtraSupportedSurfaceCombinationsContainer()
+    private val displayInfoManager = DisplayInfoManager(context)
+    private val resolutionCorrector = ResolutionCorrector()
 
     init {
         checkCapabilities()
         generateSupportedCombinationList()
-        if (context.packageManager.hasSystemFeature(FEATURE_CAMERA_CONCURRENT)) {
+        if (isUltraHighResolutionSensorSupported) {
+            generateUltraHighResolutionSupportedCombinationList()
+        }
+        isConcurrentCameraModeSupported =
+            context.packageManager.hasSystemFeature(FEATURE_CAMERA_CONCURRENT)
+        if (isConcurrentCameraModeSupported) {
             generateConcurrentSupportedCombinationList()
         }
+        if (isRawSupported) {
+            // In CameraDevice's javadoc, RAW refers to the ImageFormat.RAW_SENSOR format. But
+            // a test in ImageCaptureTest using RAW10 to do the test. Adding the RAW10 format to
+            // make sure this is compatible with the original users.
+            sizeDefinitionFormats.add(ImageFormat.RAW_SENSOR)
+            sizeDefinitionFormats.add(ImageFormat.RAW10)
+        }
         generateSurfaceSizeDefinition()
     }
 
@@ -90,17 +124,16 @@
      * Check whether the input surface configuration list is under the capability of any combination
      * of this object.
      *
-     * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
+     * @param cameraMode        the working camera mode.
      * @param surfaceConfigList the surface configuration list to be compared
      * @return the check result that whether it could be supported
      */
     fun checkSupported(
-        isConcurrentCameraModeOn: Boolean,
+        cameraMode: Int,
         surfaceConfigList: List<SurfaceConfig>
     ): Boolean {
         // TODO(b/262772650): camera-pipe support for concurrent camera
-        val targetSurfaceCombinations = if (isConcurrentCameraModeOn)
-            concurrentSurfaceCombinations else surfaceCombinations
+        val targetSurfaceCombinations = getSurfaceCombinationsByCameraMode(cameraMode)
         for (surfaceCombination in targetSurfaceCombinations) {
             if (surfaceCombination.isSupported(surfaceConfigList)) {
                 return true
@@ -110,28 +143,51 @@
     }
 
     /**
+     * Returns the supported surface combinations according to the specified camera mode.
+     */
+    private fun getSurfaceCombinationsByCameraMode(
+        @CameraMode.Mode cameraMode: Int
+    ): List<SurfaceCombination> {
+        if (cameraModeToSupportedCombinationsMap.containsKey(cameraMode)) {
+            return cameraModeToSupportedCombinationsMap[cameraMode]!!
+        }
+        var supportedSurfaceCombinations: MutableList<SurfaceCombination> = mutableListOf()
+        when (cameraMode) {
+            CameraMode.CONCURRENT_CAMERA -> supportedSurfaceCombinations =
+                concurrentSurfaceCombinations
+
+            CameraMode.ULTRA_HIGH_RESOLUTION_CAMERA -> {
+                supportedSurfaceCombinations.addAll(ultraHighSurfaceCombinations)
+                supportedSurfaceCombinations.addAll(surfaceCombinations)
+            }
+
+            else -> supportedSurfaceCombinations.addAll(surfaceCombinations)
+        }
+        cameraModeToSupportedCombinationsMap[cameraMode] = supportedSurfaceCombinations
+        return supportedSurfaceCombinations
+    }
+
+    /**
      * Transform to a SurfaceConfig object with image format and size info
      *
-     * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
+     * @param cameraMode  the working camera mode.
      * @param imageFormat the image format info for the surface configuration object
      * @param size        the size info for the surface configuration object
      * @return new [SurfaceConfig] object
      */
     fun transformSurfaceConfig(
-        isConcurrentCameraModeOn: Boolean,
+        cameraMode: Int,
         imageFormat: Int,
         size: Size
     ): SurfaceConfig {
-        val maxOutputSizeForConcurrentMode = if (isConcurrentCameraModeOn)
-            getMaxOutputSizeByFormat(imageFormat) else null
-        return SurfaceConfig.transformSurfaceConfig(isConcurrentCameraModeOn,
-            imageFormat, size, surfaceSizeDefinition, maxOutputSizeForConcurrentMode)
+        return SurfaceConfig.transformSurfaceConfig(cameraMode,
+            imageFormat, size, surfaceSizeDefinition)
     }
 
     /**
      * Finds the suggested stream specification of the newly added UseCaseConfig.
      *
-     * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
+     * @param cameraMode        the working camera mode.
      * @param existingSurfaces  the existing surfaces.
      * @param newUseCaseConfigsSupportedSizeMap newly added UseCaseConfig to supported output sizes
      * map.
@@ -141,7 +197,7 @@
      * found. This may be due to no available output size or no available surface combination.
      */
     fun getSuggestedStreamSpecifications(
-        isConcurrentCameraModeOn: Boolean,
+        cameraMode: Int,
         existingSurfaces: List<AttachedSurfaceInfo>,
         newUseCaseConfigsSupportedSizeMap: Map<UseCaseConfig<*>, List<Size>>
     ): Map<UseCaseConfig<*>, StreamSpec> {
@@ -154,20 +210,17 @@
         // Use the small size (640x480) for new use cases to check whether there is any possible
         // supported combination first
         for (useCaseConfig in newUseCaseConfigs) {
-            val maxOutputSizeForConcurrentMode = if (isConcurrentCameraModeOn)
-                getMaxOutputSizeByFormat(useCaseConfig.inputFormat) else null
             surfaceConfigs.add(
                 SurfaceConfig.transformSurfaceConfig(
-                    isConcurrentCameraModeOn,
+                    cameraMode,
                     useCaseConfig.inputFormat,
                     RESOLUTION_VGA,
-                    surfaceSizeDefinition,
-                    maxOutputSizeForConcurrentMode
+                    surfaceSizeDefinition
                 )
             )
         }
 
-        if (!checkSupported(isConcurrentCameraModeOn, surfaceConfigs)) {
+        if (!checkSupported(cameraMode, surfaceConfigs)) {
             throw java.lang.IllegalArgumentException(
                 "No supported surface combination is found for camera device - Id : " + cameraId +
                     ".  May be attempting to bind too many use cases. " + "Existing surfaces: " +
@@ -182,8 +235,12 @@
 
         // Collect supported output sizes for all use cases
         for (index in useCasesPriorityOrder) {
-            val supportedOutputSizes: List<Size> =
+            var supportedOutputSizes: List<Size> =
                 newUseCaseConfigsSupportedSizeMap[newUseCaseConfigs[index]]!!
+            supportedOutputSizes = resolutionCorrector.insertOrPrioritize(
+                SurfaceConfig.getConfigType(newUseCaseConfigs[index].inputFormat),
+                supportedOutputSizes
+            )
             supportedOutputSizesList.add(supportedOutputSizes)
         }
         // Get all possible size arrangements
@@ -204,28 +261,27 @@
             for (i in possibleSizeList.indices) {
                 val size = possibleSizeList[i]
                 val newUseCase = newUseCaseConfigs[useCasesPriorityOrder[i]]
-                val maxOutputSizeForConcurrentMode = if (isConcurrentCameraModeOn)
-                    getMaxOutputSizeByFormat(newUseCase.inputFormat) else null
                 surfaceConfigList.add(
                     SurfaceConfig.transformSurfaceConfig(
-                        isConcurrentCameraModeOn,
+                        cameraMode,
                         newUseCase.inputFormat,
                         size,
-                        surfaceSizeDefinition,
-                        maxOutputSizeForConcurrentMode
+                        surfaceSizeDefinition
                     )
                 )
             }
 
             // Check whether the SurfaceConfig combination can be supported
-            if (checkSupported(isConcurrentCameraModeOn, surfaceConfigList)) {
+            if (checkSupported(cameraMode, surfaceConfigList)) {
                 suggestedStreamSpecMap = HashMap()
                 for (useCaseConfig in newUseCaseConfigs) {
                     suggestedStreamSpecMap.put(
                         useCaseConfig,
-                        StreamSpec.builder(possibleSizeList[useCasesPriorityOrder.indexOf(
-                            newUseCaseConfigs.indexOf(useCaseConfig)
-                        )]).build()
+                        StreamSpec.builder(
+                            possibleSizeList[useCasesPriorityOrder.indexOf(
+                                newUseCaseConfigs.indexOf(useCaseConfig)
+                            )]
+                        ).build()
                     )
                 }
                 break
@@ -250,14 +306,21 @@
      * Refresh Preview Size based on current display configurations.
      */
     private fun refreshPreviewSize() {
-        val previewSize: Size = calculatePreviewSize()
-        surfaceSizeDefinition = SurfaceSizeDefinition.create(
-            surfaceSizeDefinition.analysisSize,
-            surfaceSizeDefinition.s720pSize,
-            previewSize,
-            surfaceSizeDefinition.s1440pSize,
-            surfaceSizeDefinition.recordSize
-        )
+        displayInfoManager.refresh()
+        if (!::surfaceSizeDefinition.isInitialized) {
+            generateSurfaceSizeDefinition()
+        } else {
+            val previewSize: Size = displayInfoManager.getPreviewSize()
+            surfaceSizeDefinition = SurfaceSizeDefinition.create(
+                surfaceSizeDefinition.analysisSize,
+                surfaceSizeDefinition.s720pSizeMap,
+                previewSize,
+                surfaceSizeDefinition.s1440pSizeMap,
+                surfaceSizeDefinition.recordSize,
+                surfaceSizeDefinition.maximumSizeMap,
+                surfaceSizeDefinition.ultraMaximumSizeMap
+            )
+        }
     }
 
     /**
@@ -271,6 +334,10 @@
             isRawSupported = contains(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
             isBurstCaptureSupported =
                 contains(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)
+            isUltraHighResolutionSensorSupported = contains(
+                CameraCharacteristics
+                    .REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR
+            )
         }
     }
 
@@ -284,30 +351,107 @@
                 isRawSupported, isBurstCaptureSupported
             )
         )
-        // TODO(b/246609101): ExtraSupportedSurfaceCombinationsQuirk is supposed to be here to add additional
-        //  surface combinations to the list
+        surfaceCombinations.addAll(
+            extraSupportedSurfaceCombinationsContainer[cameraId, hardwareLevel]
+        )
+    }
+
+    private fun generateUltraHighResolutionSupportedCombinationList() {
+        ultraHighSurfaceCombinations.addAll(
+            GuaranteedConfigurationsUtil.getUltraHighResolutionSupportedCombinationList()
+        )
     }
 
     private fun generateConcurrentSupportedCombinationList() {
         concurrentSurfaceCombinations.addAll(
-            GuaranteedConfigurationsUtil.generateConcurrentSupportedCombinationList())
+            GuaranteedConfigurationsUtil.generateConcurrentSupportedCombinationList()
+        )
     }
 
     /**
-     * Generation the size definition for VGA, s720p, PREVIEW, s1440p, and RECORD.
+     * Generation the size definition for VGA, s720p, PREVIEW, s1440p, RECORD, MAXIMUM and
+     * ULTRA_MAXIMUM.
      */
     private fun generateSurfaceSizeDefinition() {
-        val vgaSize = Size(640, 480)
-        // s720p is not a fixed size, it refers to 720p (1280 x 720) or the maximum supported
-        // resolution for the particular format returned by
-        // {@link StreamConfigurationMap#getOutputSizes(int)}, whichever is smaller.
-        // Same for s1440p.
-        val s720pSize = Size(1280, 720)
-        val s1440pSize = Size(1920, 1440)
-        val previewSize: Size = calculatePreviewSize()
+        val previewSize: Size = displayInfoManager.getPreviewSize()
         val recordSize: Size = getRecordSize()
-        surfaceSizeDefinition = SurfaceSizeDefinition.create(vgaSize, s720pSize, previewSize,
-            s1440pSize, recordSize)
+        surfaceSizeDefinition = SurfaceSizeDefinition.create(
+            RESOLUTION_VGA,
+            createS720pOrS1440pSizeMap(RESOLUTION_720P),
+            previewSize,
+            createS720pOrS1440pSizeMap(RESOLUTION_1440P),
+            recordSize,
+            createMaximumSizeMap(),
+            createUltraMaximumSizeMap()
+        )
+    }
+
+    /**
+     * Creates the format to s720p or s720p size map.
+     *
+     * <p>s720p refers to the 720p (1280 x 720) or the maximum supported resolution for the
+     * particular format returned by {@link StreamConfigurationMap#getOutputSizes(int)},
+     * whichever is smaller.
+     *
+     * <p>s1440p refers to the 1440p (1920 x 1440) or the maximum supported resolution for the
+     * particular format returned by {@link StreamConfigurationMap#getOutputSizes(int)},
+     * whichever is smaller.
+     *
+     * @param targetSize the target size to create the map.
+     * @return the format to s720p or s720p size map.
+     */
+    private fun createS720pOrS1440pSizeMap(targetSize: Size): Map<Int, Size> {
+        val resultMap: MutableMap<Int, Size> = mutableMapOf()
+        if (!isConcurrentCameraModeSupported) {
+            return resultMap
+        }
+
+        val compareSizesByArea = CompareSizesByArea()
+        val originalMap = streamConfigurationMapCompat.toStreamConfigurationMap()
+        for (format in sizeDefinitionFormats) {
+            val maxOutputSize = getMaxOutputSizeByFormat(originalMap, format, false)
+            resultMap[format] = if (maxOutputSize == null) {
+                targetSize
+            } else {
+                Collections.min(
+                    listOf(
+                        targetSize,
+                        maxOutputSize
+                    ), compareSizesByArea
+                )
+            }
+        }
+        return resultMap
+    }
+
+    private fun createMaximumSizeMap(): Map<Int, Size> {
+        val resultMap: MutableMap<Int, Size> = mutableMapOf()
+        val originalMap = streamConfigurationMapCompat.toStreamConfigurationMap()
+        for (format in sizeDefinitionFormats) {
+            getMaxOutputSizeByFormat(originalMap, format, true)?.let {
+                resultMap[format] = it
+            }
+        }
+        return resultMap
+    }
+
+    private fun createUltraMaximumSizeMap(): Map<Int, Size> {
+        val resultMap: MutableMap<Int, Size> = mutableMapOf()
+        // Maximum resolution mode is supported since API level 31
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S ||
+            !isUltraHighResolutionSensorSupported
+        ) {
+            return resultMap
+        }
+        val maximumResolutionMap =
+            cameraMetadata[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION]
+                ?: return resultMap
+        for (format in sizeDefinitionFormats) {
+            getMaxOutputSizeByFormat(maximumResolutionMap, format, true)?.let {
+                resultMap[format] = it
+            }
+        }
+        return resultMap
     }
 
     /**
@@ -337,7 +481,7 @@
     private fun getStreamConfigurationMapCompat(): StreamConfigurationMapCompat {
         val map = cameraMetadata[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP]
             ?: throw IllegalArgumentException("Cannot retrieve SCALER_STREAM_CONFIGURATION_MAP")
-        return StreamConfigurationMapCompat(map, OutputSizesCorrector(cameraMetadata))
+        return StreamConfigurationMapCompat(map, OutputSizesCorrector(cameraMetadata, map))
     }
 
     /**
@@ -347,7 +491,9 @@
      * @return Maximum supported video size.
      */
     private fun getRecordSizeFromStreamConfigurationMapCompat(): Size {
-        val videoSizeArr = streamConfigurationMapCompat.getOutputSizes(
+        val map: StreamConfigurationMap =
+            streamConfigurationMapCompat.toStreamConfigurationMap()
+        val videoSizeArr = map.getOutputSizes(
             MediaRecorder::class.java
         ) ?: return RESOLUTION_480P
         Arrays.sort(videoSizeArr, CompareSizesByArea(true))
@@ -394,31 +540,6 @@
     }
 
     /**
-     * Calculates the size for preview. If the max size is larger than 1080p, use 1080p.
-     */
-    @SuppressWarnings("deprecation")
-    /* getRealSize */
-    private fun calculatePreviewSize(): Size {
-        val displaySize = Point()
-        val display: Display = getMaxSizeDisplay()
-        display.getRealSize(displaySize)
-        var displayViewSize: Size
-        displayViewSize = if (displaySize.x > displaySize.y) {
-            Size(displaySize.x, displaySize.y)
-        } else {
-            Size(displaySize.y, displaySize.x)
-        }
-        if (displayViewSize.width * displayViewSize.height
-            > RESOLUTION_1080P.width * RESOLUTION_1080P.height
-        ) {
-            displayViewSize = RESOLUTION_1080P
-        }
-        // TODO(b/245619094): Use ExtraCroppingQuirk to potentially override this with select
-        //  resolution
-        return displayViewSize
-    }
-
-    /**
      * Retrieves the display which has the max size among all displays.
      */
     private fun getMaxSizeDisplay(): Display {
@@ -478,15 +599,18 @@
     /**
      * Get max supported output size for specific image format
      *
+     * @param map the original stream configuration map without quirks applied.
      * @param imageFormat the image format info
+     * @param highResolutionIncluded whether high resolution output sizes are included
      * @return the max supported output size for the image format
      */
-    internal fun getMaxOutputSizeByFormat(imageFormat: Int): Size {
-        // Needs to retrieve the output size from the original stream configuration map without
-        // quirks applied.
-        val map: StreamConfigurationMap =
-            streamConfigurationMapCompat.toStreamConfigurationMap()
-        val outputSizes: Array<Size> =
+    @SuppressLint("ClassVerificationFailure")
+    internal fun getMaxOutputSizeByFormat(
+        map: StreamConfigurationMap,
+        imageFormat: Int,
+        highResolutionIncluded: Boolean
+    ): Size? {
+        val outputSizes: Array<Size>? =
             if (imageFormat == ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE) {
                 // This is a little tricky that 0x22 that is internal defined in
                 // StreamConfigurationMap.java to be equal to ImageFormat.PRIVATE that is public
@@ -497,7 +621,22 @@
             } else {
                 map.getOutputSizes(imageFormat)
             }
-        return Collections.max(outputSizes.asList(), CompareSizesByArea())
+        if (outputSizes.isNullOrEmpty()) {
+            return null
+        }
+        val compareSizesByArea = CompareSizesByArea()
+        val maxSize = Collections.max(outputSizes.asList(), compareSizesByArea)
+        var maxHighResolutionSize = SizeUtil.RESOLUTION_ZERO
+
+        if (Build.VERSION.SDK_INT >= 23 && highResolutionIncluded) {
+            val highResolutionOutputSizes = map.getHighResolutionOutputSizes(imageFormat)
+            if (highResolutionOutputSizes != null && highResolutionOutputSizes.isNotEmpty()) {
+                maxHighResolutionSize =
+                    Collections.max(highResolutionOutputSizes.asList(), compareSizesByArea)
+            }
+        }
+
+        return Collections.max(Arrays.asList(maxSize, maxHighResolutionSize), compareSizesByArea)
     }
 
     /**
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt
index 612133c..4c61ecc 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt
@@ -21,6 +21,7 @@
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.integration.compat.workaround.AutoFlashAEModeDisabler
 import androidx.camera.camera2.pipe.integration.compat.workaround.MeteringRegionCorrection
+import androidx.camera.camera2.pipe.integration.compat.workaround.UseTorchAsFlash
 import dagger.Module
 
 /** Dependency bindings for adding camera compat related classes (e.g. workarounds, quirks etc.) */
@@ -28,6 +29,7 @@
     includes = [
         AutoFlashAEModeDisabler.Bindings::class,
         MeteringRegionCorrection.Bindings::class,
+        UseTorchAsFlash.Bindings::class,
     ],
 )
 abstract class CameraCompatModule
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/ZoomCompat.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/ZoomCompat.kt
index a9e1348..4c4a936 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/ZoomCompat.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/ZoomCompat.kt
@@ -24,6 +24,7 @@
 import android.os.Build
 import android.util.Range
 import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.integration.compat.workaround.getControlZoomRatioRangeSafely
 import androidx.camera.camera2.pipe.integration.impl.CameraProperties
 import androidx.camera.camera2.pipe.integration.impl.UseCaseCamera
 import dagger.Module
@@ -51,8 +52,7 @@
             @Provides
             fun provideZoomRatio(cameraProperties: CameraProperties): ZoomCompat {
                 return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
-                    val range =
-                        cameraProperties.metadata[CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE]
+                    val range = cameraProperties.metadata.getControlZoomRatioRangeSafely()
                     if (range != null) {
                         AndroidRZoomCompat(cameraProperties, range)
                     } else {
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/AspectRatioLegacyApi21Quirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/AspectRatioLegacyApi21Quirk.kt
new file mode 100644
index 0000000..c46b711
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/AspectRatioLegacyApi21Quirk.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.camera.camera2.pipe.integration.compat.quirk
+
+import android.hardware.camera2.CameraCharacteristics
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.CameraMetadata
+import androidx.camera.camera2.pipe.integration.compat.workaround.TargetAspectRatio
+import androidx.camera.core.impl.Quirk
+
+/**
+ *
+ * QuirkSummary
+ * Bug Id: b/128924712
+ * Description: Quirk that produces stretched use cases on all the legacy API 21 devices. If
+ * the device is LEGACY + Android 5.0, then return the same aspect ratio as maximum JPEG
+ * resolution. The Camera2 LEGACY mode API always sends the HAL a configure call with the
+ * same aspect ratio as the maximum JPEG resolution, and do the cropping/scaling before
+ * returning the output. There is a bug because of a flipped scaling factor in the
+ * intermediate texture transform matrix, and it was fixed in L MR1.
+ * Device(s): All the legacy API 21 devices
+ * @see androidx.camera.camera2.internal.compat.workaround.TargetAspectRatio
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+class AspectRatioLegacyApi21Quirk : Quirk {
+    /**
+     * Get the corrected aspect ratio.
+     */
+    @TargetAspectRatio.Ratio
+    fun getCorrectedAspectRatio(): Int {
+        return TargetAspectRatio.RATIO_MAX_JPEG
+    }
+
+    companion object {
+        fun load(cameraMetadata: CameraMetadata): Boolean {
+            val level: Int? = cameraMetadata[CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL]
+            return level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY &&
+                Build.VERSION.SDK_INT == 21
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraNoResponseWhenEnablingFlashQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraNoResponseWhenEnablingFlashQuirk.kt
new file mode 100644
index 0000000..92d37e5
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraNoResponseWhenEnablingFlashQuirk.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.camera.camera2.pipe.integration.compat.quirk
+
+import android.annotation.SuppressLint
+import android.hardware.camera2.CameraCharacteristics
+import android.hardware.camera2.CameraMetadata.LENS_FACING_BACK
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.CameraMetadata
+
+/**
+ * Camera gets stuck when taking pictures with flash ON or AUTO in dark environment.
+ *
+ * QuirkSummary
+ * - Bug Id: 193336562, 194046401
+ * - Description: Camera HAL get stuck when taking pictures with flash ON or AUTO in dark
+ *   environment.
+ * - Device(s): SM-N9200 and all Samsung Galaxy Note 5 devices, SM-J510FN
+ *
+ * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
+ */
+@SuppressLint("CameraXQuirksClassDetector")
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+class CameraNoResponseWhenEnablingFlashQuirk : UseTorchAsFlashQuirk {
+
+    companion object {
+        val AFFECTED_MODELS = listOf(
+            // Enables on all Samsung Galaxy Note 5 devices.
+            "SM-N9200",
+            "SM-N9208",
+            "SAMSUNG-SM-N920A",
+            "SM-N920C",
+            "SM-N920F",
+            "SM-N920G",
+            "SM-N920I",
+            "SM-N920K",
+            "SM-N920L",
+            "SM-N920P",
+            "SM-N920R4",
+            "SM-N920R6",
+            "SM-N920R7",
+            "SM-N920S",
+            "SM-N920T",
+            "SM-N920V",
+            "SM-N920W8",
+            "SM-N920X",
+            "SM-J510FN" // Galaxy J5
+        )
+
+        fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
+            return AFFECTED_MODELS.contains(Build.MODEL.uppercase()) &&
+                cameraMetadata[CameraCharacteristics.LENS_FACING] == LENS_FACING_BACK
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt
index c0c7158..522e38d 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt
@@ -45,12 +45,30 @@
         if (AfRegionFlipHorizontallyQuirk.isEnabled(cameraMetadata)) {
             quirks.add(AfRegionFlipHorizontallyQuirk())
         }
+        if (AspectRatioLegacyApi21Quirk.load(cameraMetadata)) {
+            quirks.add(AspectRatioLegacyApi21Quirk())
+        }
         if (CamcorderProfileResolutionQuirk.isEnabled(cameraMetadata)) {
             quirks.add(CamcorderProfileResolutionQuirk(streamConfigurationMapCompat))
         }
+        if (CameraNoResponseWhenEnablingFlashQuirk.isEnabled(cameraMetadata)) {
+            quirks.add(CameraNoResponseWhenEnablingFlashQuirk())
+        }
+        if (FlashTooSlowQuirk.isEnabled(cameraMetadata)) {
+            quirks.add(FlashTooSlowQuirk())
+        }
         if (ImageCaptureFailWithAutoFlashQuirk.isEnabled(cameraMetadata)) {
             quirks.add(ImageCaptureFailWithAutoFlashQuirk())
         }
+        if (ImageCaptureFlashNotFireQuirk.isEnabled(cameraMetadata)) {
+            quirks.add(ImageCaptureFlashNotFireQuirk())
+        }
+        if (ImageCaptureWashedOutImageQuirk.isEnabled(cameraMetadata)) {
+            quirks.add(ImageCaptureWashedOutImageQuirk())
+        }
+        if (ImageCaptureWithFlashUnderexposureQuirk.isEnabled(cameraMetadata)) {
+            quirks.add(ImageCaptureWithFlashUnderexposureQuirk())
+        }
         if (JpegHalCorruptImageQuirk.isEnabled()) {
             quirks.add(JpegHalCorruptImageQuirk())
         }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ControlZoomRatioRangeAssertionErrorQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ControlZoomRatioRangeAssertionErrorQuirk.kt
new file mode 100644
index 0000000..518a228
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ControlZoomRatioRangeAssertionErrorQuirk.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.camera.camera2.pipe.integration.compat.quirk
+
+import android.annotation.SuppressLint
+import android.hardware.camera2.CameraCharacteristics
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.camera.core.impl.Quirk
+import java.nio.BufferUnderflowException
+
+/**
+ * A quirk for devices that throw a [BufferUnderflowException] when querying the flash availability.
+ *
+ * QuirkSummary
+ * - Bug Id: 231701345
+ * - Description: When attempting to retrieve the
+ *   [CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE] characteristic, an
+ *   [AssertionError] is thrown. This is an undocumented exception
+ *   on the [CameraCharacteristics.get] method, so this violates the API contract.
+ * - Device(s): Jio JioPhone Next, Samsung Galaxy A02s, Vivo V2039
+ *
+ * @see androidx.camera.camera2.pipe.integration.compat.workaround.getControlZoomRatioRangeSafely
+ */
+@SuppressLint("CameraXQuirksClassDetector") // TODO(b/270421716): enable when kotlin is supported.
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+class ControlZoomRatioRangeAssertionErrorQuirk : Quirk {
+    companion object {
+        fun isEnabled() = isJioPhoneNext() || isSamsungA2s() || isVivo2039()
+
+        private fun isJioPhoneNext() = Build.BRAND.equals("JIO", ignoreCase = true) &&
+            Build.MODEL.startsWith("LS1542QW", ignoreCase = true)
+
+        private fun isSamsungA2s() = Build.BRAND.equals("SAMSUNG", ignoreCase = true) && (
+            Build.MODEL.startsWith("SM-A025", ignoreCase = true) ||
+            Build.MODEL.equals("SM-S124DL", ignoreCase = true)
+        )
+
+        private fun isVivo2039() = Build.BRAND.equals("VIVO", ignoreCase = true) &&
+            Build.MODEL.equals("VIVO 2039", ignoreCase = true)
+    }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/DeviceQuirksLoader.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/DeviceQuirksLoader.kt
index 55b7820..32be169 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/DeviceQuirksLoader.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/DeviceQuirksLoader.kt
@@ -38,6 +38,10 @@
             quirks.add(CrashWhenTakingPhotoWithAutoFlashAEModeQuirk())
         }
 
+        if (ControlZoomRatioRangeAssertionErrorQuirk.isEnabled()) {
+            quirks.add(ControlZoomRatioRangeAssertionErrorQuirk())
+        }
+
         if (FlashAvailabilityBufferUnderflowQuirk.isEnabled()) {
             quirks.add(FlashAvailabilityBufferUnderflowQuirk())
         }
@@ -51,9 +55,18 @@
         if (ExcludedSupportedSizesQuirk.load()) {
             quirks.add(ExcludedSupportedSizesQuirk())
         }
+        if (ExtraCroppingQuirk.load()) {
+            quirks.add(ExtraCroppingQuirk())
+        }
         if (ExtraSupportedOutputSizeQuirk.load()) {
             quirks.add(ExtraSupportedOutputSizeQuirk())
         }
+        if (ExtraSupportedSurfaceCombinationsQuirk.load()) {
+            quirks.add(ExtraSupportedSurfaceCombinationsQuirk())
+        }
+        if (Nexus4AndroidLTargetAspectRatioQuirk.load()) {
+            quirks.add(Nexus4AndroidLTargetAspectRatioQuirk())
+        }
         if (PreviewPixelHDRnetQuirk.isEnabled()) {
             quirks.add(PreviewPixelHDRnetQuirk())
         }
@@ -63,6 +76,9 @@
         if (StillCaptureFlashStopRepeatingQuirk.isEnabled()) {
             quirks.add(StillCaptureFlashStopRepeatingQuirk())
         }
+        if (TorchIsClosedAfterImageCapturingQuirk.isEnabled()) {
+            quirks.add(TorchIsClosedAfterImageCapturingQuirk())
+        }
 
         return quirks
     }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraCroppingQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraCroppingQuirk.kt
new file mode 100644
index 0000000..f391079
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraCroppingQuirk.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.camera.camera2.pipe.integration.compat.quirk
+
+import android.os.Build
+import android.util.Range
+import android.util.Size
+import androidx.annotation.RequiresApi
+import androidx.camera.core.impl.Quirk
+import androidx.camera.core.impl.SurfaceConfig.ConfigType
+
+/**
+ * Quirk that requires specific resolutions as the workaround.
+ *
+ * QuirkSummary
+ * Bug Id: 190203334
+ * Description: The symptom of these devices is that the output of one or many streams,
+ * including PRIV, JPEG and/or YUV, can have an unintended 25% crop, and the
+ * cropped image is stretched to fill the Surface, which results in a distorted
+ * output. The streams can also have an unintended 25% double crop, in which
+ * case the stretched image will not be distorted, but the FOV is smaller than
+ * it should be. The behavior is inconsistent in a way that the extra cropping
+ * depends on the resolution of the streams. The existence of the issue also
+ * depends on API level and/or build number. See discussion in
+ * go/samsung-camera-distortion.
+ * Device(s): Samsung Galaxy Tab A (2016) SM-T580, Samsung Galaxy J7 (2016) SM-J710MN,
+ * Samsung Galaxy A3 (2017) SM-A320FL, Samsung Galaxy J5 Prime SM-G570M,
+ * Samsung Galaxy J7 Prime SM-G610F, Samsung Galaxy J7 Prime SM-G610M
+ */
+@RequiresApi(21)
+class ExtraCroppingQuirk : Quirk {
+    /**
+     * Get a verified resolution that is guaranteed to work.
+     *
+     *  The selected resolution have been manually tested by CameraX team. It is known to
+     * work for the given device/stream.
+     *
+     * @return null if no resolution provided, in which case the calling code should fallback to
+     * user provided target resolution.
+     */
+    fun getVerifiedResolution(configType: ConfigType): Size? {
+        return if (isSamsungDistortion) {
+            // The following resolutions are needed for both the front and the back camera.
+            when (configType) {
+                ConfigType.PRIV -> Size(1920, 1080)
+                ConfigType.YUV -> Size(1280, 720)
+                ConfigType.JPEG -> Size(3264, 1836)
+                else -> null
+            }
+        } else null
+    }
+
+    companion object {
+        private val SAMSUNG_DISTORTION_MODELS_TO_API_LEVEL_MAP: MutableMap<String, Range<Int>?> =
+            mutableMapOf(
+                "SM-T580" to null,
+                "SM-J710MN" to Range(21, 26),
+                "SM-A320FL" to null,
+                "SM-G570M" to null,
+                "SM-G610F" to null,
+                "SM-G610M" to Range(21, 26)
+            )
+
+        fun load(): Boolean {
+            return isSamsungDistortion
+        }
+
+        /**
+         * Checks for device model with Samsung output distortion bug (b/190203334).
+         */
+        internal val isSamsungDistortion: Boolean
+            get() {
+                val isDeviceModelContained = ("samsung".equals(Build.BRAND, ignoreCase = true) &&
+                    SAMSUNG_DISTORTION_MODELS_TO_API_LEVEL_MAP.containsKey(
+                        Build.MODEL.uppercase()
+                    ))
+                if (!isDeviceModelContained) {
+                    return false
+                }
+                val apiLevelRange =
+                    SAMSUNG_DISTORTION_MODELS_TO_API_LEVEL_MAP[Build.MODEL.uppercase()]
+                return apiLevelRange?.contains(Build.VERSION.SDK_INT) ?: true
+            }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedSurfaceCombinationsQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedSurfaceCombinationsQuirk.kt
new file mode 100644
index 0000000..5a63102
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedSurfaceCombinationsQuirk.kt
@@ -0,0 +1,352 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.compat.quirk
+
+import android.hardware.camera2.CameraCharacteristics
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.camera.core.impl.Quirk
+import androidx.camera.core.impl.SurfaceCombination
+import androidx.camera.core.impl.SurfaceConfig
+
+/**
+ *
+ * QuirkSummary
+ * Bug Id: b/194149215
+ * Description: Quirk required to include extra supported surface combinations which are
+ * additional to the guaranteed supported configurations. An example is the
+ * Samsung S7's LIMITED-level camera device can support additional YUV/640x480 +
+ * PRIV/PREVIEW + YUV/MAXIMUM combination. Some other Samsung devices can
+ * support additional YUV/640x480 + PRIV/PREVIEW + YUV/MAXIMUM and YUV/640x480 +
+ * YUV/PREVIEW + YUV/MAXIMUM configurations.
+ * Device(s): Some Samsung devices
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+
+class ExtraSupportedSurfaceCombinationsQuirk : Quirk {
+    /**
+     * Returns the extra supported surface combinations for specific camera on the device.
+     */
+    fun getExtraSupportedSurfaceCombinations(
+        cameraId: String,
+        hardwareLevel: Int
+    ): List<SurfaceCombination> {
+        if (isSamsungS7) {
+            return getSamsungS7ExtraCombinations(cameraId)
+        }
+        if (supportExtraFullConfigurationsSamsungDevice()) {
+            return getLimitedDeviceExtraSupportedFullConfigurations(hardwareLevel)
+        }
+        return if (supportExtraLevel3ConfigurationsGoogleDevice()) {
+            listOf(LEVEL_3_LEVEL_PRIV_PRIV_YUV_RAW_CONFIGURATION)
+        } else emptyList()
+    }
+
+    private fun getSamsungS7ExtraCombinations(cameraId: String): List<SurfaceCombination> {
+        val extraCombinations: MutableList<SurfaceCombination> = ArrayList()
+        if (cameraId == "1") {
+            // (YUV, ANALYSIS) + (PRIV, PREVIEW) + (YUV, MAXIMUM)
+            extraCombinations.add(FULL_LEVEL_YUV_PRIV_YUV_CONFIGURATION)
+        }
+        return extraCombinations
+    }
+
+    private fun getLimitedDeviceExtraSupportedFullConfigurations(
+        hardwareLevel: Int
+    ): List<SurfaceCombination> {
+        val extraCombinations: MutableList<SurfaceCombination> = ArrayList()
+        if (hardwareLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED) {
+            // (YUV, ANALYSIS) + (PRIV, PREVIEW) + (YUV, MAXIMUM)
+            extraCombinations.add(FULL_LEVEL_YUV_PRIV_YUV_CONFIGURATION)
+            // (YUV, ANALYSIS) + (YUV, PREVIEW) + (YUV, MAXIMUM)
+            extraCombinations.add(FULL_LEVEL_YUV_YUV_YUV_CONFIGURATION)
+        }
+        return extraCombinations
+    }
+
+    companion object {
+        private const val TAG = "ExtraSupportedSurfaceCombinationsQuirk"
+        private val FULL_LEVEL_YUV_PRIV_YUV_CONFIGURATION = createFullYuvPrivYuvConfiguration()
+        private val FULL_LEVEL_YUV_YUV_YUV_CONFIGURATION = createFullYuvYuvYuvConfiguration()
+        private val LEVEL_3_LEVEL_PRIV_PRIV_YUV_RAW_CONFIGURATION =
+            createLevel3PrivPrivYuvRawConfiguration()
+        private val SUPPORT_EXTRA_FULL_CONFIGURATIONS_SAMSUNG_MODELS: Set<String> =
+            setOf(
+                "SM-A515F", // Galaxy A51
+                "SM-A515U", // Galaxy A51
+                "SM-A515U1", // Galaxy A51
+                "SM-A515W", // Galaxy A51
+                "SM-S515DL", // Galaxy A51
+                "SC-54A", // Galaxy A51 5G
+                "SCG07", // Galaxy A51 5G
+                "SM-A5160", // Galaxy A51 5G
+                "SM-A516B", // Galaxy A51 5G
+                "SM-A516N", // Galaxy A51 5G
+                "SM-A516U", // Galaxy A51 5G
+                "SM-A516U1", // Galaxy A51 5G
+                "SM-A516V", // Galaxy A51 5G
+                "SM-A715F", // Galaxy A71
+                "SM-A715W", // Galaxy A71
+                "SM-A7160", // Galaxy A71 5G
+                "SM-A716B", // Galaxy A71 5G
+                "SM-A716U", // Galaxy A71 5G
+                "SM-A716U1", // Galaxy A71 5G
+                "SM-A716V", // Galaxy A71 5G
+                "SM-A8050", // Galaxy A80
+                "SM-A805F", // Galaxy A80
+                "SM-A805N", // Galaxy A80
+                "SCV44", // Galaxy Fold
+                "SM-F9000", // Galaxy Fold
+                "SM-F900F", // Galaxy Fold
+                "SM-F900U", // Galaxy Fold
+                "SM-F900U1", // Galaxy Fold
+                "SM-F900W", // Galaxy Fold
+                "SM-F907B", // Galaxy Fold 5G
+                "SM-F907N", // Galaxy Fold 5G
+                "SM-N970F", // Galaxy Note10
+                "SM-N9700", // Galaxy Note10
+                "SM-N970U", // Galaxy Note10
+                "SM-N970U1", // Galaxy Note10
+                "SM-N970W", // Galaxy Note10
+                "SM-N971N", // Galaxy Note10 5G
+                "SM-N770F", // Galaxy Note10 Lite
+                "SC-01M", // Galaxy Note10+
+                "SCV45", // Galaxy Note10+
+                "SM-N9750", // Galaxy Note10+
+                "SM-N975C", // Galaxy Note10+
+                "SM-N975U", // Galaxy Note10+
+                "SM-N975U1", // Galaxy Note10+
+                "SM-N975W", // Galaxy Note10+
+                "SM-N975F", // Galaxy Note10+
+                "SM-N976B", // Galaxy Note10+ 5G
+                "SM-N976N", // Galaxy Note10+ 5G
+                "SM-N9760", // Galaxy Note10+ 5G
+                "SM-N976Q", // Galaxy Note10+ 5G
+                "SM-N976V", // Galaxy Note10+ 5G
+                "SM-N976U", // Galaxy Note10+ 5G
+                "SM-N9810", // Galaxy Note20 5G
+                "SM-N981N", // Galaxy Note20 5G
+                "SM-N981U", // Galaxy Note20 5G
+                "SM-N981U1", // Galaxy Note20 5G
+                "SM-N981W", // Galaxy Note20 5G
+                "SM-N981B", // Galaxy Note20 5G
+                "SC-53A", // Galaxy Note20 Ultra 5G
+                "SCG06", // Galaxy Note20 Ultra 5G
+                "SM-N9860", // Galaxy Note20 Ultra 5G
+                "SM-N986N", // Galaxy Note20 Ultra 5G
+                "SM-N986U", // Galaxy Note20 Ultra 5G
+                "SM-N986U1", // Galaxy Note20 Ultra 5G
+                "SM-N986W", // Galaxy Note20 Ultra 5G
+                "SM-N986B", // Galaxy Note20 Ultra 5G
+                "SC-03L", // Galaxy S10
+                "SCV41", // Galaxy S10
+                "SM-G973F", // Galaxy S10
+                "SM-G973N", // Galaxy S10
+                "SM-G9730", // Galaxy S10
+                "SM-G9738", // Galaxy S10
+                "SM-G973C", // Galaxy S10
+                "SM-G973U", // Galaxy S10
+                "SM-G973U1", // Galaxy S10
+                "SM-G973W", // Galaxy S10
+                "SM-G977B", // Galaxy S10 5G
+                "SM-G977N", // Galaxy S10 5G
+                "SM-G977P", // Galaxy S10 5G
+                "SM-G977T", // Galaxy S10 5G
+                "SM-G977U", // Galaxy S10 5G
+                "SM-G770F", // Galaxy S10 Lite
+                "SM-G770U1", // Galaxy S10 Lite
+                "SC-04L", // Galaxy S10+
+                "SCV42", // Galaxy S10+
+                "SM-G975F", // Galaxy S10+
+                "SM-G975N", // Galaxy S10+
+                "SM-G9750", // Galaxy S10+
+                "SM-G9758", // Galaxy S10+
+                "SM-G975U", // Galaxy S10+
+                "SM-G975U1", // Galaxy S10+
+                "SM-G975W", // Galaxy S10+
+                "SC-05L", // Galaxy S10+ Olympic Games Edition
+                "SM-G970F", // Galaxy S10e
+                "SM-G970N", // Galaxy S10e
+                "SM-G9700", // Galaxy S10e
+                "SM-G9708", // Galaxy S10e
+                "SM-G970U", // Galaxy S10e
+                "SM-G970U1", // Galaxy S10e
+                "SM-G970W", // Galaxy S10e
+                "SC-51A", // Galaxy S20 5G
+                "SC51Aa", // Galaxy S20 5G
+                "SCG01", // Galaxy S20 5G
+                "SM-G9810", // Galaxy S20 5G
+                "SM-G981N", // Galaxy S20 5G
+                "SM-G981U", // Galaxy S20 5G
+                "SM-G981U1", // Galaxy S20 5G
+                "SM-G981V", // Galaxy S20 5G
+                "SM-G981W", // Galaxy S20 5G
+                "SM-G981B", // Galaxy S20 5G
+                "SCG03", // Galaxy S20 Ultra 5G
+                "SM-G9880", // Galaxy S20 Ultra 5G
+                "SM-G988N", // Galaxy S20 Ultra 5G
+                "SM-G988Q", // Galaxy S20 Ultra 5G
+                "SM-G988U", // Galaxy S20 Ultra 5G
+                "SM-G988U1", // Galaxy S20 Ultra 5G
+                "SM-G988W", // Galaxy S20 Ultra 5G
+                "SM-G988B", // Galaxy S20 Ultra 5G
+                "SC-52A", // Galaxy S20+ 5G
+                "SCG02", // Galaxy S20+ 5G
+                "SM-G9860", // Galaxy S20+ 5G
+                "SM-G986N", // Galaxy S20+ 5G
+                "SM-G986U", // Galaxy S20+ 5G
+                "SM-G986U1", // Galaxy S20+ 5G
+                "SM-G986W", // Galaxy S20+ 5G
+                "SM-G986B", // Galaxy S20+ 5G
+                "SCV47", // Galaxy Z Flip
+                "SM-F7000", // Galaxy Z Flip
+                "SM-F700F", // Galaxy Z Flip
+                "SM-F700N", // Galaxy Z Flip
+                "SM-F700U", // Galaxy Z Flip
+                "SM-F700U1", // Galaxy Z Flip
+                "SM-F700W", // Galaxy Z Flip
+                "SCG04", // Galaxy Z Flip 5G
+                "SM-F7070", // Galaxy Z Flip 5G
+                "SM-F707B", // Galaxy Z Flip 5G
+                "SM-F707N", // Galaxy Z Flip 5G
+                "SM-F707U", // Galaxy Z Flip 5G
+                "SM-F707U1", // Galaxy Z Flip 5G
+                "SM-F707W", // Galaxy Z Flip 5G
+                "SM-F9160", // Galaxy Z Fold2 5G
+                "SM-F916B", // Galaxy Z Fold2 5G
+                "SM-F916N", // Galaxy Z Fold2 5G
+                "SM-F916Q", // Galaxy Z Fold2 5G
+                "SM-F916U", // Galaxy Z Fold2 5G
+                "SM-F916U1", // Galaxy Z Fold2 5G
+                "SM-F916W" // Galaxy Z Fold2 5G
+            )
+        private val SUPPORT_EXTRA_LEVEL_3_CONFIGURATIONS_GOOGLE_MODELS: Set<String> =
+            setOf(
+                "PIXEL 6",
+                "PIXEL 6 PRO",
+                "PIXEL 7",
+                "PIXEL 7 PRO"
+            )
+
+        fun load(): Boolean {
+            return (isSamsungS7 || supportExtraFullConfigurationsSamsungDevice() ||
+                supportExtraLevel3ConfigurationsGoogleDevice())
+        }
+
+        internal val isSamsungS7: Boolean
+            get() = "heroqltevzw".equals(
+                Build.DEVICE,
+                ignoreCase = true
+            ) || "heroqltetmo".equals(
+                Build.DEVICE, ignoreCase = true
+            )
+
+        internal fun supportExtraFullConfigurationsSamsungDevice(): Boolean {
+            if (!"samsung".equals(Build.BRAND, ignoreCase = true)) {
+                return false
+            }
+            val capitalModelName = Build.MODEL.uppercase()
+            return SUPPORT_EXTRA_FULL_CONFIGURATIONS_SAMSUNG_MODELS.contains(capitalModelName)
+        }
+
+        internal fun supportExtraLevel3ConfigurationsGoogleDevice(): Boolean {
+            if (!"google".equals(Build.BRAND, ignoreCase = true)) {
+                return false
+            }
+            val capitalModelName = Build.MODEL.uppercase()
+            return SUPPORT_EXTRA_LEVEL_3_CONFIGURATIONS_GOOGLE_MODELS.contains(capitalModelName)
+        }
+
+        internal fun createFullYuvPrivYuvConfiguration(): SurfaceCombination {
+            // (YUV, ANALYSIS) + (PRIV, PREVIEW) + (YUV, MAXIMUM)
+            val surfaceCombination = SurfaceCombination()
+            surfaceCombination.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.YUV,
+                    SurfaceConfig.ConfigSize.VGA
+                )
+            )
+            surfaceCombination.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.PRIV,
+                    SurfaceConfig.ConfigSize.PREVIEW
+                )
+            )
+            surfaceCombination.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.YUV,
+                    SurfaceConfig.ConfigSize.MAXIMUM
+                )
+            )
+            return surfaceCombination
+        }
+
+        internal fun createFullYuvYuvYuvConfiguration(): SurfaceCombination {
+            // (YUV, ANALYSIS) + (YUV, PREVIEW) + (YUV, MAXIMUM)
+            val surfaceCombination = SurfaceCombination()
+            surfaceCombination.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.YUV,
+                    SurfaceConfig.ConfigSize.VGA
+                )
+            )
+            surfaceCombination.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.YUV,
+                    SurfaceConfig.ConfigSize.PREVIEW
+                )
+            )
+            surfaceCombination.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.YUV,
+                    SurfaceConfig.ConfigSize.MAXIMUM
+                )
+            )
+            return surfaceCombination
+        }
+
+        internal fun createLevel3PrivPrivYuvRawConfiguration(): SurfaceCombination {
+            // (PRIV, PREVIEW) + (PRIV, ANALYSIS) + (YUV, MAXIMUM) + (RAW, MAXIMUM)
+            val surfaceCombination = SurfaceCombination()
+            surfaceCombination.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.PRIV,
+                    SurfaceConfig.ConfigSize.PREVIEW
+                )
+            )
+            surfaceCombination.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.PRIV,
+                    SurfaceConfig.ConfigSize.VGA
+                )
+            )
+            surfaceCombination.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.YUV,
+                    SurfaceConfig.ConfigSize.MAXIMUM
+                )
+            )
+            surfaceCombination.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.RAW,
+                    SurfaceConfig.ConfigSize.MAXIMUM
+                )
+            )
+            return surfaceCombination
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FlashTooSlowQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FlashTooSlowQuirk.kt
new file mode 100644
index 0000000..6f591d2
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FlashTooSlowQuirk.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.camera.camera2.pipe.integration.compat.quirk
+
+import android.annotation.SuppressLint
+import android.hardware.camera2.CameraCharacteristics.LENS_FACING
+import android.hardware.camera2.CameraMetadata.LENS_FACING_BACK
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.CameraMetadata
+
+/**
+ * Quirks that denotes the device has a slow flash sequence that could result in blurred pictures.
+ *
+ * QuirkSummary
+ * - Bug Id: 211474332
+ * - Description: When capturing still photos in auto flash mode, it needs more than 1 second to
+ *   flash and therefore it easily results in blurred pictures.
+ * - Device(s): Pixel 3a / Pixel 3a XL
+ *
+ * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
+ */
+@SuppressLint("CameraXQuirksClassDetector")
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+class FlashTooSlowQuirk : UseTorchAsFlashQuirk {
+
+    companion object {
+
+        // List of devices with the issue. See b/181966663.
+        private val AFFECTED_MODELS = listOf(
+            "PIXEL 3A",
+            "PIXEL 3A XL"
+        )
+
+        fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
+            return AFFECTED_MODELS.contains(Build.MODEL.uppercase()) &&
+                cameraMetadata[LENS_FACING] == LENS_FACING_BACK
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFlashNotFireQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFlashNotFireQuirk.kt
new file mode 100644
index 0000000..7ddffbb
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFlashNotFireQuirk.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.camera.camera2.pipe.integration.compat.quirk
+
+import android.annotation.SuppressLint
+import android.hardware.camera2.CameraCharacteristics.LENS_FACING
+import android.hardware.camera2.CameraMetadata.LENS_FACING_FRONT
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.CameraMetadata
+
+/**
+ * A quirk to denote the camera never fire the flash while taking picture with flash ON/AUTO mode.
+ *
+ * QuirkSummary
+ * - Bug Id: 228800360
+ * - Description: The flash doesn't fire while taking picture with flash ON/AUTO mode.
+ * - Device(s): Itel w6004, Samsung Galaxy J7 (sm-j700f, sm-j710f) front camera
+ *
+ * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
+ */
+@SuppressLint("CameraXQuirksClassDetector")
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+class ImageCaptureFlashNotFireQuirk : UseTorchAsFlashQuirk {
+
+    companion object {
+        // List of devices with the issue. See b/228800360.
+        private val BUILD_MODELS = listOf(
+            "itel w6004" // Itel W6004
+        )
+        private val BUILD_MODELS_FRONT_CAMERA = listOf(
+            "sm-j700f", // Samsung Galaxy J7
+            "sm-j710f", // Samsung Galaxy J7
+        )
+
+        fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
+            val isFrontCameraAffected =
+                BUILD_MODELS_FRONT_CAMERA.contains(Build.MODEL.lowercase()) &&
+                    cameraMetadata[LENS_FACING] == LENS_FACING_FRONT
+            val isAffected = BUILD_MODELS.contains(Build.MODEL.lowercase())
+            return isFrontCameraAffected || isAffected
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureWashedOutImageQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureWashedOutImageQuirk.kt
new file mode 100644
index 0000000..64cd4ab
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureWashedOutImageQuirk.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.camera.camera2.pipe.integration.compat.quirk
+
+import android.annotation.SuppressLint
+import android.hardware.camera2.CameraCharacteristics.LENS_FACING
+import android.hardware.camera2.CameraMetadata.LENS_FACING_BACK
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.CameraMetadata
+
+/**
+ * QuirkSummary
+ * - Bug Id: 176399765, 181966663
+ * - Description: Quirk that prevents from getting washed out image while taking picture with
+ *   flash ON/AUTO mode.
+ * - Device(s): Galaxy S7, Galaxy S7+
+ * @see UseTorchAsFlashQuirk
+ *
+ * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
+ */
+@SuppressLint("CameraXQuirksClassDetector")
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+class ImageCaptureWashedOutImageQuirk : UseTorchAsFlashQuirk {
+
+    companion object {
+        val BUILD_MODELS = listOf(
+            // List of devices with the issue. See b/181966663.
+            "SM-G9300", // Galaxy S7
+            "SM-G930R",
+            "SM-G930A",
+            "SM-G930V",
+            "SM-G930T",
+            "SM-G930U",
+            "SM-G930P", // Galaxy S7+
+            "SM-SC02H",
+            "SM-SCV33",
+            "SM-G9350",
+            "SM-G935R",
+            "SM-G935A",
+            "SM-G935V",
+            "SM-G935T",
+            "SM-G935U",
+            "SM-G935P"
+        )
+
+        fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
+            return BUILD_MODELS.contains(Build.MODEL.uppercase()) &&
+                cameraMetadata[LENS_FACING] == LENS_FACING_BACK
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureWithFlashUnderexposureQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureWithFlashUnderexposureQuirk.kt
new file mode 100644
index 0000000..d6c1817
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureWithFlashUnderexposureQuirk.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.camera.camera2.pipe.integration.compat.quirk
+
+import android.annotation.SuppressLint
+import android.hardware.camera2.CameraCharacteristics.LENS_FACING
+import android.hardware.camera2.CameraMetadata.LENS_FACING_BACK
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.CameraMetadata
+
+/**
+ * A quirk to denote even when the camera uses flash ON/AUTO mode, but the captured image is
+ * still underexposed.
+ *
+ * QuirkSummary
+ * - Bug Id: 228800282
+ * - Description: While the flash is in ON/AUTO mode and the camera fires the flash in a dark
+ *   environment, the captured photos are underexposed after continuously capturing 2
+ *   or more photos.
+ * - Device(s): Samsung Galaxy A2 Core (sm-a260f), Samsung Galaxy J5 (sm-j530f), Samsung Galaxy
+ *   J6 (sm-j600g), Samsung Galaxy J7 Neo (sm-j701f), Samsung Galaxy J7 Prime
+ *   (sm-g610f), Samsung Galaxy J7 (sm-j710mn)
+ *
+ * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
+ */
+@SuppressLint("CameraXQuirksClassDetector")
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+class ImageCaptureWithFlashUnderexposureQuirk : UseTorchAsFlashQuirk {
+    companion object {
+        // List of devices with the issue. See b/228800282.
+        private val BUILD_MODELS = listOf(
+            "sm-a260f", // Samsung Galaxy A2 Core
+            "sm-j530f", // Samsung Galaxy J5
+            "sm-j600g", // Samsung Galaxy J6
+            "sm-j701f", // Samsung Galaxy J7 Neo
+            "sm-g610f", // Samsung Galaxy J7 Prime
+            "sm-j710mn", // Samsung Galaxy J7
+        )
+
+        fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
+            return BUILD_MODELS.contains(Build.MODEL.lowercase()) &&
+                cameraMetadata[LENS_FACING] == LENS_FACING_BACK
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/Nexus4AndroidLTargetAspectRatioQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/Nexus4AndroidLTargetAspectRatioQuirk.kt
new file mode 100644
index 0000000..7147ca8
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/Nexus4AndroidLTargetAspectRatioQuirk.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.camera.camera2.pipe.integration.compat.quirk
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.integration.compat.workaround.TargetAspectRatio
+import androidx.camera.core.impl.Quirk
+
+/**
+ *
+ * QuirkSummary
+ * Bug Id: b/19606058
+ * Description: Quirk that produces stretched preview on Nexus 4 devices running Android L
+ * (API levels 21 and 22). There is a Camera1/HAL1 issue on the Nexus 4. The preview will be
+ * stretched when configuring a JPEG that doesn't actually have the same aspect ratio as the
+ * maximum JPEG resolution.
+ * Device(s): Google Nexus 4
+ * @see androidx.camera.camera2.internal.compat.workaround.TargetAspectRatio
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+
+class Nexus4AndroidLTargetAspectRatioQuirk : Quirk {
+    /**
+     * Get the corrected aspect ratio.
+     */
+    @TargetAspectRatio.Ratio
+    fun getCorrectedAspectRatio(): Int {
+        return TargetAspectRatio.RATIO_MAX_JPEG
+    }
+
+    companion object {
+        // List of devices with the issue.
+        private val DEVICE_MODELS = listOf(
+            "NEXUS 4" // b/158749159
+        )
+
+        fun load(): Boolean {
+            return "GOOGLE".equals(
+                Build.BRAND,
+                ignoreCase = true
+            ) && Build.VERSION.SDK_INT < 23 && DEVICE_MODELS.contains(
+                Build.MODEL.uppercase()
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TorchIsClosedAfterImageCapturingQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TorchIsClosedAfterImageCapturingQuirk.kt
new file mode 100644
index 0000000..52c7c81
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TorchIsClosedAfterImageCapturingQuirk.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.compat.quirk
+
+import android.annotation.SuppressLint
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.camera.core.impl.Quirk
+
+/**
+ * QuirkSummary
+ * - Bug Id: 228272227
+ * - Description: The Torch is unexpectedly turned off after taking a picture.
+ * - Device(s): Redmi 4X, Redmi 5A, Mi A1, Mi A2, Mi A2 lite and Redmi 6 Pro.
+ */
+@SuppressLint("CameraXQuirksClassDetector") // TODO(b/270421716): enable when kotlin is supported.
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+class TorchIsClosedAfterImageCapturingQuirk : Quirk {
+
+    companion object {
+        // List of devices with the issue. See b/228272227.
+        private val BUILD_MODELS = listOf(
+            "mi a1", // Xiaomi Mi A1
+            "mi a2", // Xiaomi Mi A2
+            "mi a2 lite", // Xiaomi Mi A2 Lite
+            "redmi 4x", // Xiaomi Redmi 4X
+            "redmi 5a", // Xiaomi Redmi 5A
+            "redmi 6 pro", // Xiaomi Redmi 6 Pro
+        )
+
+        fun isEnabled(): Boolean {
+            return BUILD_MODELS.contains(Build.MODEL.lowercase())
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/UseTorchAsFlashQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/UseTorchAsFlashQuirk.kt
new file mode 100644
index 0000000..bc57376
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/UseTorchAsFlashQuirk.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.compat.quirk
+
+import androidx.annotation.RequiresApi
+import androidx.camera.core.impl.Quirk
+
+/**
+ * A quirk interface which denotes CameraX should use torch for flash when flashMode is
+ * ON or AUTO.
+ *
+ * Subclasses of this interface can denote the reason why torch is required instead of AE
+ * pre-capture.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+interface UseTorchAsFlashQuirk : Quirk
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/CameraMetadataSafeGetter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/CameraMetadataSafeGetter.kt
new file mode 100644
index 0000000..6597f3d
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/CameraMetadataSafeGetter.kt
@@ -0,0 +1,89 @@
+/*
+ * 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:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+
+package androidx.camera.camera2.pipe.integration.compat.workaround
+
+import android.hardware.camera2.CameraCharacteristics
+import android.os.Build
+import android.util.Range
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.CameraMetadata
+import androidx.camera.camera2.pipe.core.Log
+import androidx.camera.camera2.pipe.integration.compat.quirk.ControlZoomRatioRangeAssertionErrorQuirk
+import androidx.camera.camera2.pipe.integration.compat.quirk.DeviceQuirks
+
+/**
+ * Gets a [CameraCharacteristics] value with additional error handling.
+ *
+ * @param T the type of the characteristic value
+ * @param key the [CameraCharacteristics.Key] of the characteristic
+ * @return the value of the characteristic
+ */
+fun <T> CameraMetadata.getSafely(key: CameraCharacteristics.Key<T>): T? {
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
+        key == CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE
+    ) {
+        @Suppress("UNCHECKED_CAST") // T is guaranteed to be Range<Float>
+        return getControlZoomRatioRangeSafely() as T?
+    }
+
+    return get(key)
+}
+
+/**
+ * Gets the value of [CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE] with additional error
+ * handling.
+ *
+ * Some devices may throw [AssertionError] when getting the value of
+ * [CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE]. Here, the error is caught and logged before
+ * returning null as workaround.
+ *
+ * Ref: b/231701345
+ *
+ * @return the CONTROL_ZOOM_RATIO_RANGE characteristic value, null in case of [AssertionError].
+ */
+@RequiresApi(Build.VERSION_CODES.R)
+fun CameraMetadata.getControlZoomRatioRangeSafely(): Range<Float>? = try {
+    get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE)
+} catch (e: AssertionError) {
+    if (DeviceQuirks[ControlZoomRatioRangeAssertionErrorQuirk::class.java] != null) {
+        Log.debug {
+            "Device is known to throw an exception while retrieving" +
+                " the value for CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE." +
+                " CONTROL_ZOOM_RATIO_RANGE is not supported." +
+                " [Manufacturer: ${Build.MANUFACTURER}, Model:" +
+                " ${Build.MODEL}, API Level: ${Build.VERSION.SDK_INT}]."
+        }
+    } else {
+        Log.error(e) {
+            "Exception thrown while retrieving the value for" +
+                " CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE on devices not known to " +
+                "throw exceptions during this operation. Please file an issue at " +
+                "https://issuetracker.google.com/issues/new?component=618491&template=1257717" +
+                " with this error message [Manufacturer: ${Build.MANUFACTURER}, Model:" +
+                " ${Build.MODEL}, API Level: ${Build.VERSION.SDK_INT}]." +
+                " CONTROL_ZOOM_RATIO_RANGE is not available."
+        }
+    }
+
+    Log.warn(e) {
+        "AssertionError: " +
+            "failed to get CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE"
+    }
+    null
+}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/CapturePipelineTorchCorrection.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/CapturePipelineTorchCorrection.kt
new file mode 100644
index 0000000..0525273
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/CapturePipelineTorchCorrection.kt
@@ -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.
+ */
+
+@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+
+package androidx.camera.camera2.pipe.integration.compat.workaround
+
+import android.hardware.camera2.CameraDevice
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.Request
+import androidx.camera.camera2.pipe.RequestTemplate
+import androidx.camera.camera2.pipe.core.Log
+import androidx.camera.camera2.pipe.integration.compat.quirk.DeviceQuirks
+import androidx.camera.camera2.pipe.integration.compat.quirk.TorchIsClosedAfterImageCapturingQuirk
+import androidx.camera.camera2.pipe.integration.config.UseCaseCameraScope
+import androidx.camera.camera2.pipe.integration.impl.CapturePipeline
+import androidx.camera.camera2.pipe.integration.impl.CapturePipelineImpl
+import androidx.camera.camera2.pipe.integration.impl.TorchControl
+import androidx.camera.camera2.pipe.integration.impl.UseCaseThreads
+import androidx.camera.core.TorchState
+import javax.inject.Inject
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.joinAll
+import kotlinx.coroutines.launch
+
+/**
+ * This is a workaround for b/228272227 where the Torch is unexpectedly closed after a single
+ * capturing.
+ *
+ * If the Torch is enabled before performing a single capture, this workaround may turn the Torch
+ * OFF then ON after the capturing.
+ */
+@UseCaseCameraScope
+class CapturePipelineTorchCorrection @Inject constructor(
+    private val capturePipelineImpl: CapturePipelineImpl,
+    private val threads: UseCaseThreads,
+    private val torchControl: TorchControl,
+) : CapturePipeline {
+
+    override suspend fun submitStillCaptures(
+        requests: List<Request>,
+        captureMode: Int,
+        flashType: Int,
+        flashMode: Int
+    ): List<Deferred<Void?>> {
+        val needCorrectTorchState = isCorrectionRequired(requests)
+
+        // Forward the capture request to capturePipelineImpl
+        val deferredResults =
+            capturePipelineImpl.submitStillCaptures(requests, captureMode, flashType, flashMode)
+
+        if (needCorrectTorchState) {
+            threads.sequentialScope.launch {
+                deferredResults.joinAll()
+                Log.debug { "Re-enable Torch to correct the Torch state" }
+                torchControl.setTorchAsync(torch = false).join()
+                torchControl.setTorchAsync(torch = true).join()
+                Log.debug { "Re-enable Torch to correct the Torch state, done" }
+            }
+        }
+
+        return deferredResults
+    }
+
+    override var template: Int = CameraDevice.TEMPLATE_PREVIEW
+        set(value) {
+            capturePipelineImpl.template = value
+            field = value
+        }
+
+    /**
+     * Return true means the Torch will be unexpectedly closed, and it requires turning on the
+     * Torch again after the capturing.
+     */
+    private fun isCorrectionRequired(requests: Collection<Request>): Boolean {
+        return requests.contains(stillCaptureTemplate) && isTorchOn()
+    }
+
+    private fun Collection<Request>.contains(template: RequestTemplate): Boolean {
+        forEach { request ->
+            if (request.template == template)
+                return true
+        }
+        return false
+    }
+
+    private fun isTorchOn() = torchControl.torchStateLiveData.value == TorchState.ON
+
+    companion object {
+        val isEnabled = DeviceQuirks[TorchIsClosedAfterImageCapturingQuirk::class.java] != null
+        private val stillCaptureTemplate = RequestTemplate(CameraDevice.TEMPLATE_STILL_CAPTURE)
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ExtraSupportedSurfaceCombinationsContainer.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ExtraSupportedSurfaceCombinationsContainer.kt
new file mode 100644
index 0000000..b56f416
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ExtraSupportedSurfaceCombinationsContainer.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.compat.workaround
+
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.integration.compat.quirk.DeviceQuirks
+import androidx.camera.camera2.pipe.integration.compat.quirk.ExtraSupportedSurfaceCombinationsQuirk
+import androidx.camera.core.impl.SurfaceCombination
+
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+class ExtraSupportedSurfaceCombinationsContainer {
+    private val quirk: ExtraSupportedSurfaceCombinationsQuirk? =
+        DeviceQuirks[ExtraSupportedSurfaceCombinationsQuirk::class.java]
+
+    /**
+     * Retrieves the extra surface combinations which can be supported on the device.
+     */
+    operator fun get(cameraId: String, hardwareLevel: Int): List<SurfaceCombination> {
+        return quirk?.getExtraSupportedSurfaceCombinations(cameraId, hardwareLevel) ?: listOf()
+    }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/MaxPreviewSize.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/MaxPreviewSize.kt
new file mode 100644
index 0000000..93a724e
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/MaxPreviewSize.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.compat.workaround
+
+import android.util.Size
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.integration.compat.quirk.DeviceQuirks
+import androidx.camera.camera2.pipe.integration.compat.quirk.ExtraCroppingQuirk
+import androidx.camera.core.impl.SurfaceConfig
+
+/**
+ * Helper class that overrides the maximum preview size used in surface combination check.
+ */
+@RequiresApi(21)
+class MaxPreviewSize constructor(
+    private val extraCroppingQuirk: ExtraCroppingQuirk? =
+        DeviceQuirks[ExtraCroppingQuirk::class.java]
+) {
+
+    /**
+     * Gets the max preview resolution based on the default preview max resolution.
+     *
+     *
+     *  If select resolution is larger than the default resolution, return the select
+     * resolution. The select resolution has been manually tested on the device. Otherwise,
+     * return the default max resolution.
+     */
+    fun getMaxPreviewResolution(defaultMaxPreviewResolution: Size): Size {
+        if (extraCroppingQuirk == null) {
+            return defaultMaxPreviewResolution
+        }
+        val selectResolution: Size = extraCroppingQuirk.getVerifiedResolution(
+            SurfaceConfig.ConfigType.PRIV
+        ) ?: return defaultMaxPreviewResolution
+        val isSelectResolutionLarger = (selectResolution.width * selectResolution.height >
+            defaultMaxPreviewResolution.width * defaultMaxPreviewResolution.height)
+        return if (isSelectResolutionLarger) selectResolution else defaultMaxPreviewResolution
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/OutputSizesCorrector.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/OutputSizesCorrector.kt
index a873e6b..723c840 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/OutputSizesCorrector.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/OutputSizesCorrector.kt
@@ -16,31 +16,40 @@
 
 package androidx.camera.camera2.pipe.integration.compat.workaround
 
+import android.hardware.camera2.params.StreamConfigurationMap
+import android.util.Rational
 import android.util.Size
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.CameraMetadata
-import androidx.camera.camera2.pipe.integration.config.CameraScope
-import javax.inject.Inject
+import androidx.camera.camera2.pipe.integration.compat.StreamConfigurationMapCompat
 import androidx.camera.camera2.pipe.integration.compat.quirk.DeviceQuirks
 import androidx.camera.camera2.pipe.integration.compat.quirk.ExcludedSupportedSizesQuirk
 import androidx.camera.camera2.pipe.integration.compat.quirk.ExtraSupportedOutputSizeQuirk
+import androidx.camera.camera2.pipe.integration.config.CameraScope
+import androidx.camera.core.impl.utils.AspectRatioUtil
+import androidx.camera.core.impl.utils.CompareSizesByArea
+import java.util.Collections
+import javax.inject.Inject
 
 /**
  * Helper class to provide the StreamConfigurationMap output sizes related correction functions.
  *
  * 1. ExtraSupportedOutputSizeQuirk
  * 2. ExcludedSupportedSizesContainer
- * 3. TargetAspectRatio
+ * 3. Nexus4AndroidLTargetAspectRatioQuirk
+ * 4. AspectRatioLegacyApi21Quirk
  */
 @CameraScope
 @RequiresApi(21)
 class OutputSizesCorrector @Inject constructor(
-    private val cameraMetadata: CameraMetadata
+    private val cameraMetadata: CameraMetadata,
+    private val streamConfigurationMap: StreamConfigurationMap
 ) {
     private val excludedSupportedSizesQuirk: ExcludedSupportedSizesQuirk? =
         DeviceQuirks[ExcludedSupportedSizesQuirk::class.java]
     private val extraSupportedOutputSizeQuirk: ExtraSupportedOutputSizeQuirk? =
         DeviceQuirks[ExtraSupportedOutputSizeQuirk::class.java]
+    private val targetAspectRatio: TargetAspectRatio = TargetAspectRatio()
 
     /**
      * Applies the output sizes related quirks onto the input sizes array.
@@ -132,8 +141,51 @@
      * Excludes output sizes by TargetAspectRatio.
      */
     private fun excludeOutputSizesByTargetAspectRatioWorkaround(sizes: Array<Size>?): Array<Size>? {
-        // TODO(b/245622117): Nexus4AndroidLTargetAspectRatioQuirk and AspectRatioLegacyApi21Quirk
-        return sizes
+        if (sizes == null) {
+            return null
+        }
+
+        val targetAspectRatio: Int =
+            targetAspectRatio[
+                cameraMetadata,
+                StreamConfigurationMapCompat(streamConfigurationMap, this)
+            ]
+
+        var ratio: Rational? = null
+
+        when (targetAspectRatio) {
+            TargetAspectRatio.RATIO_4_3 -> ratio =
+                AspectRatioUtil.ASPECT_RATIO_4_3
+
+            TargetAspectRatio.RATIO_16_9 -> ratio =
+                AspectRatioUtil.ASPECT_RATIO_16_9
+
+            TargetAspectRatio.RATIO_MAX_JPEG -> {
+                val maxJpegSize = Collections.max(sizes.asList(), CompareSizesByArea())
+                ratio = Rational(maxJpegSize.width, maxJpegSize.height)
+            }
+
+            TargetAspectRatio.RATIO_ORIGINAL -> ratio =
+                null
+        }
+
+        if (ratio == null) {
+            return sizes
+        }
+
+        val resultList: MutableList<Size> = java.util.ArrayList()
+
+        for (size in sizes) {
+            if (AspectRatioUtil.hasMatchingAspectRatio(size, ratio)) {
+                resultList.add(size)
+            }
+        }
+
+        return if (resultList.isEmpty()) {
+            null
+        } else {
+            resultList.toTypedArray()
+        }
     }
 
     private fun concatNullableSizeLists(
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ResolutionCorrector.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ResolutionCorrector.kt
new file mode 100644
index 0000000..c3cee29
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ResolutionCorrector.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.compat.workaround
+
+import android.util.Size
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.integration.compat.quirk.DeviceQuirks
+import androidx.camera.camera2.pipe.integration.compat.quirk.ExtraCroppingQuirk
+import androidx.camera.core.impl.SurfaceConfig.ConfigType
+
+/**
+ * Helper class that overrides user configured resolution with resolution selected based on device
+ * quirks.
+ */
+@RequiresApi(21)
+class ResolutionCorrector {
+    private val extraCroppingQuirk = DeviceQuirks[ExtraCroppingQuirk::class.java]
+    /**
+     * Returns a new list of resolution with the selected resolution inserted or prioritized.
+     *
+     *
+     *  If the list contains the selected resolution, move it to be the first element; if it
+     * does not contain the selected resolution, insert it as the first element; if there is no
+     * device quirk, return the original list.
+     *
+     * @param configType           the config type based on which the supported resolution is
+     * calculated.
+     * @param supportedResolutions a ordered list of resolutions calculated by CameraX.
+     */
+    fun insertOrPrioritize(
+        configType: ConfigType,
+        supportedResolutions: List<Size>,
+
+    ): List<Size> {
+        if (extraCroppingQuirk == null) {
+            return supportedResolutions
+        }
+        val selectResolution: Size = extraCroppingQuirk.getVerifiedResolution(configType)
+            ?: return supportedResolutions
+        val newResolutions: MutableList<Size> = mutableListOf()
+        newResolutions.add(selectResolution)
+        for (size in supportedResolutions) {
+            if (size != selectResolution) {
+                newResolutions.add(size)
+            }
+        }
+        return newResolutions
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/TargetAspectRatio.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/TargetAspectRatio.kt
new file mode 100644
index 0000000..99c6d19
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/TargetAspectRatio.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.camera.camera2.pipe.integration.compat.workaround
+
+import androidx.annotation.IntDef
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+import androidx.camera.camera2.pipe.CameraMetadata
+import androidx.camera.camera2.pipe.integration.compat.StreamConfigurationMapCompat
+import androidx.camera.camera2.pipe.integration.compat.quirk.AspectRatioLegacyApi21Quirk
+import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
+import androidx.camera.camera2.pipe.integration.compat.quirk.DeviceQuirks
+import androidx.camera.camera2.pipe.integration.compat.quirk.Nexus4AndroidLTargetAspectRatioQuirk
+
+/**
+ * Workaround to get corrected target aspect ratio.
+ *
+ * @see Nexus4AndroidLTargetAspectRatioQuirk
+ *
+ * @see AspectRatioLegacyApi21Quirk
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+
+class TargetAspectRatio {
+    /**
+     * Gets corrected target aspect ratio based on device and camera quirks.
+     */
+    @Ratio
+    operator fun get(
+        cameraMetadata: CameraMetadata,
+        streamConfigurationMapCompat: StreamConfigurationMapCompat
+    ): Int {
+        val cameraQuirks = CameraQuirks(
+            cameraMetadata,
+            streamConfigurationMapCompat
+        )
+        val nexus4AndroidLTargetAspectRatioQuirk =
+            DeviceQuirks[Nexus4AndroidLTargetAspectRatioQuirk::class.java]
+        if (nexus4AndroidLTargetAspectRatioQuirk != null) {
+            return nexus4AndroidLTargetAspectRatioQuirk.getCorrectedAspectRatio()
+        }
+
+        val aspectRatioLegacyApi21Quirk =
+            cameraQuirks.quirks[AspectRatioLegacyApi21Quirk::class.java]
+        return aspectRatioLegacyApi21Quirk?.getCorrectedAspectRatio() ?: RATIO_ORIGINAL
+    }
+
+    /**
+     */
+    @IntDef(RATIO_4_3, RATIO_16_9, RATIO_MAX_JPEG, RATIO_ORIGINAL)
+    @Retention(AnnotationRetention.SOURCE)
+    @RestrictTo(
+        RestrictTo.Scope.LIBRARY_GROUP
+    )
+    annotation class Ratio
+    companion object {
+        /** 4:3 standard aspect ratio.  */
+        const val RATIO_4_3 = 0
+
+        /** 16:9 standard aspect ratio.  */
+        const val RATIO_16_9 = 1
+
+        /** The same aspect ratio as the maximum JPEG resolution.  */
+        const val RATIO_MAX_JPEG = 2
+
+        /** No correction is needed.  */
+        const val RATIO_ORIGINAL = 3
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/UseTorchAsFlash.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/UseTorchAsFlash.kt
new file mode 100644
index 0000000..4522456
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/UseTorchAsFlash.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+
+package androidx.camera.camera2.pipe.integration.compat.workaround
+
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
+import androidx.camera.camera2.pipe.integration.compat.quirk.UseTorchAsFlashQuirk
+import dagger.Module
+import dagger.Provides
+
+/**
+ * Workaround to use torch as flash.
+ *
+ * @see UseTorchAsFlashQuirk
+ */
+interface UseTorchAsFlash {
+    fun shouldUseTorchAsFlash(): Boolean
+
+    @Module
+    abstract class Bindings {
+        companion object {
+            @Provides
+            fun provideUseTorchAsFlash(cameraQuirks: CameraQuirks): UseTorchAsFlash =
+                if (cameraQuirks.quirks.contains(UseTorchAsFlashQuirk::class.java))
+                    UseTorchAsFlashImpl
+                else
+                    NotUseTorchAsFlash
+        }
+    }
+}
+
+object UseTorchAsFlashImpl : UseTorchAsFlash {
+    /** Returns true for torch should be used as flash. */
+    override fun shouldUseTorchAsFlash() = true
+}
+
+object NotUseTorchAsFlash : UseTorchAsFlash {
+    override fun shouldUseTorchAsFlash() = false
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt
index ca15c6e..5e7a7b4 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt
@@ -30,8 +30,10 @@
 import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
 import androidx.camera.camera2.pipe.integration.adapter.SessionConfigAdapter
 import androidx.camera.camera2.pipe.integration.adapter.SessionConfigAdapter.Companion.toCamera2ImplConfig
+import androidx.camera.camera2.pipe.integration.compat.workaround.CapturePipelineTorchCorrection
 import androidx.camera.camera2.pipe.integration.impl.CameraCallbackMap
 import androidx.camera.camera2.pipe.integration.impl.CameraInteropStateCallbackRepository
+import androidx.camera.camera2.pipe.integration.impl.CapturePipeline
 import androidx.camera.camera2.pipe.integration.impl.CapturePipelineImpl
 import androidx.camera.camera2.pipe.integration.impl.ComboRequestListener
 import androidx.camera.camera2.pipe.integration.impl.UseCaseCamera
@@ -52,14 +54,27 @@
 /** Dependency bindings for building a [UseCaseCamera] */
 @Module(
     includes = [
-        CapturePipelineImpl.Bindings::class,
         UseCaseCameraImpl.Bindings::class,
         UseCaseCameraRequestControlImpl.Bindings::class,
     ]
 )
 abstract class UseCaseCameraModule {
     // Used for dagger provider methods that are static.
-    companion object
+    companion object {
+
+        @UseCaseCameraScope
+        @Provides
+        fun provideCapturePipeline(
+            capturePipelineImpl: CapturePipelineImpl,
+            capturePipelineTorchCorrection: CapturePipelineTorchCorrection
+        ): CapturePipeline {
+            if (CapturePipelineTorchCorrection.isEnabled) {
+                return capturePipelineTorchCorrection
+            }
+
+            return capturePipelineImpl
+        }
+    }
 }
 
 /** Dagger module for binding the [UseCase]'s to the [UseCaseCamera]. */
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt
index 3b944d4..9da5b32 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt
@@ -49,6 +49,7 @@
 import androidx.camera.camera2.pipe.core.Log
 import androidx.camera.camera2.pipe.integration.compat.workaround.isFlashAvailable
 import androidx.camera.camera2.pipe.integration.compat.workaround.shouldStopRepeatingBeforeCapture
+import androidx.camera.camera2.pipe.integration.compat.workaround.UseTorchAsFlash
 import androidx.camera.camera2.pipe.integration.config.UseCaseCameraScope
 import androidx.camera.camera2.pipe.integration.config.UseCaseGraphConfig
 import androidx.camera.core.ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY
@@ -62,8 +63,6 @@
 import androidx.camera.core.ImageCapture.FlashType
 import androidx.camera.core.ImageCaptureException
 import androidx.camera.core.TorchState
-import dagger.Binds
-import dagger.Module
 import java.util.concurrent.TimeUnit
 import javax.inject.Inject
 import kotlinx.coroutines.CompletableDeferred
@@ -94,6 +93,7 @@
     private val torchControl: TorchControl,
     private val threads: UseCaseThreads,
     private val requestListener: ComboRequestListener,
+    private val useTorchAsFlash: UseTorchAsFlash,
     cameraProperties: CameraProperties,
     private val useCaseCameraState: UseCaseCameraState,
     useCaseGraphConfig: UseCaseGraphConfig,
@@ -314,18 +314,10 @@
         }
     }.result.await()
 
-    // TODO(b/209383160): Sync TorchAsFlash Quirk
     private fun isTorchAsFlash(@FlashType flashType: Int): Boolean {
         return template == CameraDevice.TEMPLATE_RECORD ||
-            flashType == FLASH_TYPE_USE_TORCH_AS_FLASH /* ||
-            mUseTorchAsFlash.shouldUseTorchAsFlash() */
-    }
-
-    @Module
-    abstract class Bindings {
-        @UseCaseCameraScope
-        @Binds
-        abstract fun provideCapturePipeline(capturePipeline: CapturePipelineImpl): CapturePipeline
+            flashType == FLASH_TYPE_USE_TORCH_AS_FLASH ||
+            useTorchAsFlash.shouldUseTorchAsFlash()
     }
 }
 
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManager.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManager.kt
index c7745f6..c1ea143 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManager.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManager.kt
@@ -25,6 +25,7 @@
 import android.util.Size
 import android.view.Display
 import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.integration.compat.workaround.MaxPreviewSize
 import javax.inject.Inject
 import javax.inject.Singleton
 
@@ -33,6 +34,7 @@
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 class DisplayInfoManager @Inject constructor(context: Context) {
     private val MAX_PREVIEW_SIZE = Size(1920, 1080)
+    private val maxPreviewSize: MaxPreviewSize = MaxPreviewSize()
 
     companion object {
         private var lazyMaxDisplay: Display? = null
@@ -69,8 +71,27 @@
     val defaultDisplay: Display
         get() = getMaxSizeDisplay()
 
-    val previewSize: Size
-        get() = calculatePreviewSize()
+    private var previewSize: Size? = null
+
+    /**
+     * Update the preview size according to current display size.
+     */
+    fun refresh() {
+        previewSize = calculatePreviewSize()
+    }
+
+    /**
+     * PREVIEW refers to the best size match to the device's screen resolution, or to 1080p
+     * (1920x1080), whichever is smaller.
+     */
+    fun getPreviewSize(): Size {
+        // Use cached value to speed up since this would be called multiple times.
+        if (previewSize != null) {
+            return previewSize as Size
+        }
+        previewSize = calculatePreviewSize()
+        return previewSize as Size
+    }
 
     private fun getMaxSizeDisplay(): Display {
         lazyMaxDisplay?.let { return it }
@@ -130,7 +151,7 @@
         ) {
             displayViewSize = MAX_PREVIEW_SIZE
         }
-        // TODO(b/230402463): Migrate extra cropping quirk from CameraX.
+        displayViewSize = maxPreviewSize.getMaxPreviewResolution(displayViewSize)
 
         return displayViewSize.also { lazyPreviewSize = displayViewSize }
     }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt
index 5541a40..ed6acf5 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt
@@ -27,6 +27,7 @@
 import androidx.camera.camera2.pipe.AeMode
 import androidx.camera.camera2.pipe.AfMode
 import androidx.camera.camera2.pipe.CameraGraph.Constants3A.METERING_REGIONS_DEFAULT
+import androidx.camera.camera2.pipe.Lock3ABehavior
 import androidx.camera.camera2.pipe.Result3A
 import androidx.camera.camera2.pipe.integration.adapter.asListenableFuture
 import androidx.camera.camera2.pipe.integration.adapter.propagateTo
@@ -43,11 +44,11 @@
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoSet
+import java.util.concurrent.TimeUnit
 import javax.inject.Inject
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.Deferred
 import kotlinx.coroutines.launch
-import kotlinx.coroutines.withTimeoutOrNull
 
 /**
  * Implementation of focus and metering controls exposed by [CameraControlInternal].
@@ -110,7 +111,7 @@
         val signal = CompletableDeferred<FocusMeteringResult>()
 
         useCaseCamera?.let { useCaseCamera ->
-            val job = threads.sequentialScope.launch {
+            threads.sequentialScope.launch {
                 cancelSignal?.setCancelException("Cancelled by another startFocusAndMetering()")
                 updateSignal?.setCancelException("Cancelled by another startFocusAndMetering()")
                 updateSignal = signal
@@ -158,50 +159,54 @@
                 } else {
                     (false to autoFocusTimeoutMs)
                 }
-                withTimeoutOrNull(timeout) {
-                    /**
-                     * If device does not support a 3A region, we should not update it at all.
-                     * If device does support but a region list is empty, it means any previously
-                     * set region should be removed, so the no-op METERING_REGIONS_DEFAULT is used.
-                     */
-                    useCaseCamera.requestControl.startFocusAndMeteringAsync(
-                        aeRegions = if (maxAeRegionCount > 0)
-                            aeRectangles.ifEmpty { METERING_REGIONS_DEFAULT.toList() }
-                        else null,
-                        afRegions = if (maxAfRegionCount > 0)
-                            afRectangles.ifEmpty { METERING_REGIONS_DEFAULT.toList() }
-                        else null,
-                        awbRegions = if (maxAwbRegionCount > 0)
-                            awbRectangles.ifEmpty { METERING_REGIONS_DEFAULT.toList() }
-                        else null,
-                        afTriggerStartAeMode = cameraProperties.getSupportedAeMode(AeMode.ON)
-                    ).await()
-                }.let { result3A ->
-                    if (result3A != null) {
-                        if (result3A.status == Result3A.Status.SUBMIT_FAILED) {
-                            signal.completeExceptionally(
-                                OperationCanceledException("Camera is not active.")
-                            )
-                        } else {
-                            signal.complete(result3A.toFocusMeteringResult(
-                                shouldTriggerAf = afRectangles.isNotEmpty()
-                            ))
+
+                /**
+                 * If device does not support a 3A region, we should not update it at all.
+                 * If device does support but a region list is empty, it means any previously
+                 * set region should be removed, so the no-op METERING_REGIONS_DEFAULT is used.
+                 */
+                val result3A = useCaseCamera.requestControl.startFocusAndMeteringAsync(
+                    aeRegions = if (maxAeRegionCount > 0)
+                        aeRectangles.ifEmpty { METERING_REGIONS_DEFAULT.toList() }
+                    else null,
+                    afRegions = if (maxAfRegionCount > 0)
+                        afRectangles.ifEmpty { METERING_REGIONS_DEFAULT.toList() }
+                    else null,
+                    awbRegions = if (maxAwbRegionCount > 0)
+                        awbRectangles.ifEmpty { METERING_REGIONS_DEFAULT.toList() }
+                    else null,
+                    aeLockBehavior = if (maxAeRegionCount > 0)
+                        Lock3ABehavior.AFTER_NEW_SCAN
+                    else null,
+                    afLockBehavior = if (maxAfRegionCount > 0)
+                        Lock3ABehavior.AFTER_NEW_SCAN
+                    else null,
+                    awbLockBehavior = if (maxAwbRegionCount > 0)
+                        Lock3ABehavior.AFTER_NEW_SCAN
+                    else null,
+                    afTriggerStartAeMode = cameraProperties.getSupportedAeMode(AeMode.ON),
+                    timeLimitNs = TimeUnit.NANOSECONDS.convert(
+                        timeout,
+                        TimeUnit.MILLISECONDS
+                    )
+                ).await()
+
+                if (result3A.status == Result3A.Status.SUBMIT_FAILED) {
+                    signal.completeExceptionally(
+                        OperationCanceledException("Camera is not active.")
+                    )
+                } else if (result3A.status == Result3A.Status.TIME_LIMIT_REACHED) {
+                    if (isCancelEnabled) {
+                        if (signal.isActive) {
+                            cancelFocusAndMeteringNowAsync(useCaseCamera, signal)
                         }
                     } else {
-                        if (isCancelEnabled) {
-                            if (signal.isActive) {
-                                cancelFocusAndMeteringNowAsync(useCaseCamera, signal)
-                            }
-                        } else {
-                            signal.complete(FocusMeteringResult.create(false))
-                        }
+                        signal.complete(FocusMeteringResult.create(false))
                     }
-                }
-            }
-
-            signal.invokeOnCompletion { throwable ->
-                if (throwable is OperationCanceledException) {
-                    job.cancel()
+                } else {
+                    signal.complete(result3A.toFocusMeteringResult(
+                        shouldTriggerAf = afRectangles.isNotEmpty()
+                    ))
                 }
             }
         } ?: run {
@@ -311,7 +316,8 @@
          * in priority. On the other hand, resultAfState == null matters only if the result comes
          * from a submitted request, so it should be checked after frameMetadata == null.
          *
-         * Ref: [FocusMeteringAction] and [Controller3A.lock3A] documentations.
+         * @see FocusMeteringAction
+         * @see androidx.camera.camera2.pipe.graph.Controller3A.lock3A
          */
         val isFocusSuccessful = when {
             !shouldTriggerAf -> false
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt
index 9f2844a..00d5f2c4 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt
@@ -166,7 +166,7 @@
 
         // Find maximum supported resolution that is <= min(VGA, display resolution)
         // Using minimum supported size could cause some issue on certain devices.
-        val previewSize = displayInfoManager.previewSize
+        val previewSize = displayInfoManager.getPreviewSize()
         val maxSizeProduct =
             min(640L * 480L, previewSize.width.toLong() * previewSize.height.toLong())
 
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt
index 3e96a048..bfa1fc7 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt
@@ -26,6 +26,7 @@
 import androidx.camera.camera2.pipe.AeMode
 import androidx.camera.camera2.pipe.AfMode
 import androidx.camera.camera2.pipe.AwbMode
+import androidx.camera.camera2.pipe.CameraGraph
 import androidx.camera.camera2.pipe.CameraGraph.Constants3A.METERING_REGIONS_DEFAULT
 import androidx.camera.camera2.pipe.Lock3ABehavior
 import androidx.camera.camera2.pipe.Request
@@ -132,7 +133,11 @@
         aeRegions: List<MeteringRectangle>? = null,
         afRegions: List<MeteringRectangle>? = null,
         awbRegions: List<MeteringRectangle>? = null,
-        afTriggerStartAeMode: AeMode? = null
+        aeLockBehavior: Lock3ABehavior? = null,
+        afLockBehavior: Lock3ABehavior? = null,
+        awbLockBehavior: Lock3ABehavior? = null,
+        afTriggerStartAeMode: AeMode? = null,
+        timeLimitNs: Long = CameraGraph.Constants3A.DEFAULT_TIME_LIMIT_NS,
     ): Deferred<Result3A>
 
     suspend fun cancelFocusAndMeteringAsync(): Deferred<Result3A>
@@ -219,14 +224,21 @@
         aeRegions: List<MeteringRectangle>?,
         afRegions: List<MeteringRectangle>?,
         awbRegions: List<MeteringRectangle>?,
-        afTriggerStartAeMode: AeMode?
+        aeLockBehavior: Lock3ABehavior?,
+        afLockBehavior: Lock3ABehavior?,
+        awbLockBehavior: Lock3ABehavior?,
+        afTriggerStartAeMode: AeMode?,
+        timeLimitNs: Long,
     ): Deferred<Result3A> = graph.acquireSession().use {
         it.lock3A(
             aeRegions = aeRegions,
             afRegions = afRegions,
             awbRegions = awbRegions,
-            afLockBehavior = Lock3ABehavior.AFTER_NEW_SCAN,
-            afTriggerStartAeMode = afTriggerStartAeMode
+            aeLockBehavior = aeLockBehavior,
+            afLockBehavior = afLockBehavior,
+            awbLockBehavior = awbLockBehavior,
+            afTriggerStartAeMode = afTriggerStartAeMode,
+            timeLimitNs = timeLimitNs,
         )
     }
 
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 209c893..2372dd3 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
@@ -20,6 +20,7 @@
 import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
 import androidx.camera.camera2.pipe.integration.adapter.CameraInfoAdapter
+import androidx.camera.camera2.pipe.integration.compat.workaround.getSafely
 import androidx.camera.camera2.pipe.integration.impl.CameraProperties
 import androidx.camera.core.CameraInfo
 import androidx.core.util.Preconditions
@@ -45,9 +46,9 @@
      * @return the value of the characteristic.
     </T> */
     fun <T> getCameraCharacteristic(
-        @Suppress("UNUSED_PARAMETER") key: CameraCharacteristics.Key<T>
+        key: CameraCharacteristics.Key<T>
     ): T? {
-        return cameraProperties.metadata[key]
+        return cameraProperties.metadata.getSafely(key)
     }
 
     /**
@@ -103,4 +104,4 @@
         @JvmStatic
         fun create(cameraProperties: CameraProperties) = Camera2CameraInfo(cameraProperties)
     }
-}
\ No newline at end of file
+}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraStateAdapterTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraStateAdapterTest.kt
index 4f159bd..bc7dddf 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraStateAdapterTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraStateAdapterTest.kt
@@ -25,6 +25,9 @@
 import androidx.camera.camera2.pipe.GraphState.GraphStateStopping
 import androidx.camera.camera2.pipe.integration.testing.FakeCameraGraph
 import androidx.camera.core.CameraState
+import androidx.camera.core.CameraState.ERROR_CAMERA_DISABLED
+import androidx.camera.core.CameraState.ERROR_MAX_CAMERAS_IN_USE
+import androidx.camera.core.CameraState.ERROR_OTHER_RECOVERABLE_ERROR
 import androidx.camera.core.impl.CameraInternal
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
@@ -78,12 +81,16 @@
                 CameraInternal.State.OPENING,
                 GraphStateError(CameraError.ERROR_CAMERA_LIMIT_EXCEEDED, willAttemptRetry = true)
             )
-        // TODO(b/263201241): Add tests for PENDING_OPEN transitions.
-        val nextStateWhenGraphStateErrorWillNotRetry =
+        val nextStateWhenGraphStateErrorRecoverableWillNotRetry =
             cameraStateAdapter.calculateNextState(
                 CameraInternal.State.OPENING,
                 GraphStateError(CameraError.ERROR_CAMERA_LIMIT_EXCEEDED, willAttemptRetry = false)
             )
+        val nextStateWhenGraphStateErrorUnrecoverableWillNotRetry =
+            cameraStateAdapter.calculateNextState(
+                CameraInternal.State.OPENING,
+                GraphStateError(CameraError.ERROR_CAMERA_DISABLED, willAttemptRetry = false)
+            )
 
         assertThat(nextStateWhenGraphStateStarting).isEqualTo(null)
         assertThat(nextStateWhenGraphStateStarted!!.state).isEqualTo(CameraInternal.State.OPEN)
@@ -92,15 +99,12 @@
         assertThat(nextStateWhenGraphStateErrorWillRetry!!.state).isEqualTo(
             CameraInternal.State.OPENING
         )
-        assertThat(nextStateWhenGraphStateErrorWillRetry.error!!.code).isEqualTo(
-            CameraState.ERROR_MAX_CAMERAS_IN_USE
+        assertThat(nextStateWhenGraphStateErrorRecoverableWillNotRetry!!.state).isEqualTo(
+            CameraInternal.State.PENDING_OPEN
         )
-        assertThat(nextStateWhenGraphStateErrorWillNotRetry!!.state).isEqualTo(
+        assertThat(nextStateWhenGraphStateErrorUnrecoverableWillNotRetry!!.state).isEqualTo(
             CameraInternal.State.CLOSING
         )
-        assertThat(nextStateWhenGraphStateErrorWillNotRetry.error!!.code).isEqualTo(
-            CameraState.ERROR_MAX_CAMERAS_IN_USE
-        )
     }
 
     @Test
@@ -113,18 +117,27 @@
             cameraStateAdapter.calculateNextState(CameraInternal.State.OPEN, GraphStateStopping)
         val nextStateWhenGraphStateStopped =
             cameraStateAdapter.calculateNextState(CameraInternal.State.OPEN, GraphStateStopped)
-        // TODO(b/263201241): Add tests when we handle errors while camera is already opened.
-        val nextStateWhenGraphStateError =
+        val nextStateWhenGraphStateErrorRecoverable =
             cameraStateAdapter.calculateNextState(
                 CameraInternal.State.OPEN,
                 GraphStateError(CameraError.ERROR_CAMERA_LIMIT_EXCEEDED, true)
             )
+        val nextStateWhenGraphStateErrorUnrecoverable =
+            cameraStateAdapter.calculateNextState(
+                CameraInternal.State.OPEN,
+                GraphStateError(CameraError.ERROR_CAMERA_DISABLED, true)
+            )
 
         assertThat(nextStateWhenGraphStateStarting).isEqualTo(null)
         assertThat(nextStateWhenGraphStateStarted).isEqualTo(null)
         assertThat(nextStateWhenGraphStateStopping!!.state).isEqualTo(CameraInternal.State.CLOSING)
         assertThat(nextStateWhenGraphStateStopped!!.state).isEqualTo(CameraInternal.State.CLOSED)
-        assertThat(nextStateWhenGraphStateError).isEqualTo(null)
+        assertThat(nextStateWhenGraphStateErrorRecoverable!!.state).isEqualTo(
+            CameraInternal.State.PENDING_OPEN
+        )
+        assertThat(nextStateWhenGraphStateErrorUnrecoverable!!.state).isEqualTo(
+            CameraInternal.State.CLOSED
+        )
     }
 
     @Test
@@ -147,7 +160,8 @@
         assertThat(nextStateWhenGraphStateStarted).isEqualTo(null)
         assertThat(nextStateWhenGraphStateStopping).isEqualTo(null)
         assertThat(nextStateWhenGraphStateStopped!!.state).isEqualTo(CameraInternal.State.CLOSED)
-        assertThat(nextStateWhenGraphStateError).isEqualTo(null)
+        assertThat(nextStateWhenGraphStateError!!.state).isEqualTo(CameraInternal.State.CLOSING)
+        assertThat(nextStateWhenGraphStateError.error?.code).isEqualTo(ERROR_MAX_CAMERAS_IN_USE)
     }
 
     @Test
@@ -205,4 +219,115 @@
         cameraStateAdapter.onGraphStateUpdated(cameraGraph1, GraphStateStarting)
         assertThat(cameraStateAdapter.cameraState.value?.type).isEqualTo(CameraState.Type.OPEN)
     }
+
+    @Test
+    fun testStateTransitionsOnRecoverableErrorsWhenOpening() {
+        cameraStateAdapter.onGraphUpdated(cameraGraph1)
+        assertThat(cameraStateAdapter.cameraState.value?.type).isEqualTo(CameraState.Type.CLOSED)
+
+        cameraStateAdapter.onGraphStateUpdated(cameraGraph1, GraphStateStarting)
+        assertThat(cameraStateAdapter.cameraState.value?.type).isEqualTo(CameraState.Type.OPENING)
+
+        // We should transition to OPENING with an error code if we encounter errors during opening.
+        cameraStateAdapter.onGraphStateUpdated(
+            cameraGraph1,
+            GraphStateError(CameraError.ERROR_CAMERA_DISCONNECTED, willAttemptRetry = true)
+        )
+        val cameraStateWillRetry = cameraStateAdapter.cameraState.value!!
+        assertThat(cameraStateWillRetry.type).isEqualTo(CameraState.Type.OPENING)
+        assertThat(cameraStateWillRetry.error?.code).isEqualTo(ERROR_OTHER_RECOVERABLE_ERROR)
+
+        // Now assume we've exceeded retries and will no longer retry.
+        cameraStateAdapter.onGraphStateUpdated(
+            cameraGraph1,
+            GraphStateError(CameraError.ERROR_CAMERA_DISCONNECTED, willAttemptRetry = false)
+        )
+        val cameraStateNotRetry = cameraStateAdapter.cameraState.value!!
+        assertThat(cameraStateNotRetry.type).isEqualTo(CameraState.Type.PENDING_OPEN)
+        assertThat(cameraStateNotRetry.error?.code).isEqualTo(ERROR_OTHER_RECOVERABLE_ERROR)
+
+        // Now we make sure we transition to OPENING and OPEN when the camera does eventually open
+        // when it becomes available.
+        cameraStateAdapter.onGraphStateUpdated(cameraGraph1, GraphStateStarting)
+        assertThat(cameraStateAdapter.cameraState.value?.type).isEqualTo(CameraState.Type.OPENING)
+
+        cameraStateAdapter.onGraphStateUpdated(cameraGraph1, GraphStateStarted)
+        assertThat(cameraStateAdapter.cameraState.value?.type).isEqualTo(CameraState.Type.OPEN)
+    }
+
+    @Test
+    fun testStateTransitionsOnUnrecoverableErrorsWhenOpening() {
+        cameraStateAdapter.onGraphUpdated(cameraGraph1)
+        assertThat(cameraStateAdapter.cameraState.value?.type).isEqualTo(CameraState.Type.CLOSED)
+
+        cameraStateAdapter.onGraphStateUpdated(cameraGraph1, GraphStateStarting)
+        assertThat(cameraStateAdapter.cameraState.value?.type).isEqualTo(CameraState.Type.OPENING)
+
+        cameraStateAdapter.onGraphStateUpdated(
+            cameraGraph1,
+            GraphStateError(CameraError.ERROR_CAMERA_DISABLED, willAttemptRetry = false)
+        )
+        val cameraState = cameraStateAdapter.cameraState.value!!
+        assertThat(cameraState.type).isEqualTo(CameraState.Type.CLOSING)
+        assertThat(cameraState.error?.code).isEqualTo(ERROR_CAMERA_DISABLED)
+    }
+
+    @Test
+    fun testStateTransitionsOnRecoverableErrorsWhenOpen() {
+        cameraStateAdapter.onGraphUpdated(cameraGraph1)
+        assertThat(cameraStateAdapter.cameraState.value?.type).isEqualTo(CameraState.Type.CLOSED)
+
+        cameraStateAdapter.onGraphStateUpdated(cameraGraph1, GraphStateStarting)
+        cameraStateAdapter.onGraphStateUpdated(cameraGraph1, GraphStateStarted)
+        assertThat(cameraStateAdapter.cameraState.value?.type).isEqualTo(CameraState.Type.OPEN)
+
+        // We should transition to OPENING with an error code if we encounter errors during opening.
+        cameraStateAdapter.onGraphStateUpdated(
+            cameraGraph1,
+            GraphStateError(CameraError.ERROR_CAMERA_DISCONNECTED, willAttemptRetry = false)
+        )
+        val cameraState = cameraStateAdapter.cameraState.value!!
+        assertThat(cameraState.type).isEqualTo(CameraState.Type.PENDING_OPEN)
+        assertThat(cameraState.error?.code).isEqualTo(ERROR_OTHER_RECOVERABLE_ERROR)
+    }
+
+    @Test
+    fun testStateTransitionsOnUnrecoverableErrorsWhenOpen() {
+        cameraStateAdapter.onGraphUpdated(cameraGraph1)
+        assertThat(cameraStateAdapter.cameraState.value?.type).isEqualTo(CameraState.Type.CLOSED)
+
+        cameraStateAdapter.onGraphStateUpdated(cameraGraph1, GraphStateStarting)
+        cameraStateAdapter.onGraphStateUpdated(cameraGraph1, GraphStateStarted)
+        assertThat(cameraStateAdapter.cameraState.value?.type).isEqualTo(CameraState.Type.OPEN)
+
+        cameraStateAdapter.onGraphStateUpdated(
+            cameraGraph1,
+            GraphStateError(CameraError.ERROR_CAMERA_DISABLED, willAttemptRetry = false)
+        )
+        val cameraState = cameraStateAdapter.cameraState.value!!
+        assertThat(cameraState.type).isEqualTo(CameraState.Type.CLOSED)
+        assertThat(cameraState.error?.code).isEqualTo(ERROR_CAMERA_DISABLED)
+    }
+
+    @Test
+    fun testStateTransitionsOnErrorsWhenClosing() {
+        cameraStateAdapter.onGraphUpdated(cameraGraph1)
+        assertThat(cameraStateAdapter.cameraState.value?.type).isEqualTo(CameraState.Type.CLOSED)
+
+        cameraStateAdapter.onGraphStateUpdated(cameraGraph1, GraphStateStarting)
+        cameraStateAdapter.onGraphStateUpdated(cameraGraph1, GraphStateStarted)
+        assertThat(cameraStateAdapter.cameraState.value?.type).isEqualTo(CameraState.Type.OPEN)
+
+        cameraStateAdapter.onGraphStateUpdated(cameraGraph1, GraphStateStopping)
+        assertThat(cameraStateAdapter.cameraState.value?.type).isEqualTo(CameraState.Type.CLOSING)
+
+        // We should update the CLOSING state to include an error code.
+        cameraStateAdapter.onGraphStateUpdated(
+            cameraGraph1,
+            GraphStateError(CameraError.ERROR_CAMERA_DISCONNECTED, willAttemptRetry = false)
+        )
+        val cameraState = cameraStateAdapter.cameraState.value!!
+        assertThat(cameraState.type).isEqualTo(CameraState.Type.CLOSING)
+        assertThat(cameraState.error?.code).isEqualTo(ERROR_OTHER_RECOVERABLE_ERROR)
+    }
 }
\ No newline at end of file
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 77f4fe0..3b0b4c2 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
@@ -29,6 +29,7 @@
 import android.util.Size
 import androidx.camera.camera2.pipe.CameraGraph
 import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.Lock3ABehavior
 import androidx.camera.camera2.pipe.Result3A
 import androidx.camera.camera2.pipe.integration.compat.StreamConfigurationMapCompat
 import androidx.camera.camera2.pipe.integration.compat.ZoomCompat
@@ -60,7 +61,6 @@
 import androidx.camera.testing.SurfaceTextureProvider
 import androidx.camera.testing.fakes.FakeCamera
 import androidx.camera.testing.fakes.FakeUseCase
-import androidx.concurrent.futures.await
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
@@ -361,6 +361,20 @@
     }
 
     @Test
+    fun startFocusAndMetering_defaultPoint_3ALocksAreCorrect() = runBlocking {
+        startFocusMeteringAndAwait(FocusMeteringAction.Builder(point1).build())
+
+        with(fakeRequestControl.focusMeteringCalls.last()) {
+            assertWithMessage("Wrong lock behavior for AE")
+                .that(aeLockBehavior).isEqualTo(Lock3ABehavior.AFTER_NEW_SCAN)
+            assertWithMessage("Wrong lock behavior for AF")
+                .that(afLockBehavior).isEqualTo(Lock3ABehavior.AFTER_NEW_SCAN)
+            assertWithMessage("Wrong lock behavior for AWB")
+                .that(awbLockBehavior).isEqualTo(Lock3ABehavior.AFTER_NEW_SCAN)
+        }
+    }
+
+    @Test
     fun startFocusAndMetering_multiplePoints_3ARectsAreCorrect() = runBlocking {
         // Camera 0 i.e. Max AF count = 3, Max AE count = 3, Max AWB count = 1
         startFocusMeteringAndAwait(
@@ -1268,20 +1282,28 @@
     }
 
     @Test
-    fun startFocusMetering_onlyAfSupported_unsupportedRegionsNotSet() {
+    fun startFocusMetering_onlyAfSupported_unsupportedRegionsNotConfigured() {
         // camera 5 supports 1 AF and 0 AE/AWB regions
         focusMeteringControl = initFocusMeteringControl(cameraId = CAMERA_ID_5)
 
-        startFocusMeteringAndAwait(FocusMeteringAction.Builder(
-            point1,
-            FocusMeteringAction.FLAG_AF or FocusMeteringAction.FLAG_AE or
-                FocusMeteringAction.FLAG_AWB
-        ).build())
+        startFocusMeteringAndAwait(
+            FocusMeteringAction.Builder(
+                point1,
+                FocusMeteringAction.FLAG_AF or FocusMeteringAction.FLAG_AE or
+                    FocusMeteringAction.FLAG_AWB
+            ).build()
+        )
 
         with(fakeRequestControl.focusMeteringCalls.last()) {
             assertWithMessage("Wrong number of AE regions").that(aeRegions).isNull()
+            assertWithMessage("Wrong lock behavior for AE").that(aeLockBehavior).isNull()
+
             assertWithMessage("Wrong number of AF regions").that(afRegions?.size).isEqualTo(1)
+            assertWithMessage("Wrong lock behavior for AE")
+                .that(afLockBehavior).isEqualTo(Lock3ABehavior.AFTER_NEW_SCAN)
+
             assertWithMessage("Wrong number of AWB regions").that(awbRegions).isNull()
+            assertWithMessage("Wrong lock behavior for AWB").that(awbLockBehavior).isNull()
         }
     }
 
@@ -1398,7 +1420,10 @@
                 cameraPropertiesMap[cameraId]!!.metadata,
                 StreamConfigurationMapCompat(
                     StreamConfigurationMapBuilder.newBuilder().build(),
-                    OutputSizesCorrector(cameraPropertiesMap[cameraId]!!.metadata)
+                    OutputSizesCorrector(
+                        cameraPropertiesMap[cameraId]!!.metadata,
+                        StreamConfigurationMapBuilder.newBuilder().build()
+                    ),
                 )
             )
         ),
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt
index 2f82f0a..c43b7f8 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt
@@ -21,6 +21,7 @@
 import android.graphics.SurfaceTexture
 import android.hardware.camera2.CameraCharacteristics
 import android.hardware.camera2.CameraManager
+import android.hardware.camera2.CameraMetadata
 import android.hardware.camera2.params.StreamConfigurationMap
 import android.media.CamcorderProfile.QUALITY_1080P
 import android.media.CamcorderProfile.QUALITY_2160P
@@ -50,6 +51,7 @@
 import androidx.camera.core.UseCase
 import androidx.camera.core.concurrent.CameraCoordinator
 import androidx.camera.core.impl.AttachedSurfaceInfo
+import androidx.camera.core.impl.CameraMode
 import androidx.camera.core.impl.CameraThreadConfig
 import androidx.camera.core.impl.EncoderProfilesProxy
 import androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy
@@ -60,6 +62,7 @@
 import androidx.camera.core.impl.UseCaseConfigFactory
 import androidx.camera.core.internal.utils.SizeUtil
 import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_1440P
+import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_720P
 import androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_VGA
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.CameraXUtil
@@ -130,6 +133,19 @@
         Size(800, 450), // 16:9
         Size(640, 480), // 4:3
     )
+    private val highResolutionMaximumSize = Size(6000, 4500)
+    private val highResolutionSupportedSizes = arrayOf(
+        Size(6000, 4500), // 4:3
+        Size(6000, 3375), // 16:9
+    )
+    private val ultraHighMaximumSize = Size(8000, 6000)
+    private val maximumResolutionSupportedSizes = arrayOf(
+        Size(7200, 5400), // 4:3
+        Size(7200, 4050), // 16:9
+    )
+    private val maximumResolutionHighResolutionSupportedSizes = arrayOf(
+        Size(8000, 6000)
+    )
     private val context = InstrumentationRegistry.getInstrumentation().context
     private var cameraFactory: FakeCameraFactory? = null
     private var useCaseConfigFactory: UseCaseConfigFactory = mock()
@@ -180,7 +196,8 @@
         for (combination in combinationList) {
             val isSupported =
                 supportedSurfaceCombination.checkSupported(
-                    false, combination.surfaceConfigList)
+                    CameraMode.DEFAULT, combination.surfaceConfigList
+                )
             assertThat(isSupported).isTrue()
         }
     }
@@ -194,7 +211,8 @@
         )
         val combinationList = getLegacySupportedCombinationList()
         val isSupported = isAllSubConfigListSupported(
-            false, supportedSurfaceCombination, combinationList)
+            CameraMode.DEFAULT, supportedSurfaceCombination, combinationList
+        )
         assertThat(isSupported).isTrue()
     }
 
@@ -209,7 +227,8 @@
         for (combination in combinationList) {
             val isSupported =
                 supportedSurfaceCombination.checkSupported(
-                    false, combination.surfaceConfigList)
+                    CameraMode.DEFAULT, combination.surfaceConfigList
+                )
             assertThat(isSupported).isFalse()
         }
     }
@@ -225,7 +244,8 @@
         for (combination in combinationList) {
             val isSupported =
                 supportedSurfaceCombination.checkSupported(
-                    false, combination.surfaceConfigList)
+                    CameraMode.DEFAULT, combination.surfaceConfigList
+                )
             assertThat(isSupported).isFalse()
         }
     }
@@ -241,7 +261,8 @@
         for (combination in combinationList) {
             val isSupported =
                 supportedSurfaceCombination.checkSupported(
-                    false, combination.surfaceConfigList)
+                    CameraMode.DEFAULT, combination.surfaceConfigList
+                )
             assertThat(isSupported).isFalse()
         }
     }
@@ -257,7 +278,8 @@
         for (combination in combinationList) {
             val isSupported =
                 supportedSurfaceCombination.checkSupported(
-                    false, combination.surfaceConfigList)
+                    CameraMode.DEFAULT, combination.surfaceConfigList
+                )
             assertThat(isSupported).isTrue()
         }
     }
@@ -271,7 +293,8 @@
         )
         val combinationList = getLimitedSupportedCombinationList()
         val isSupported = isAllSubConfigListSupported(
-            false, supportedSurfaceCombination, combinationList)
+            CameraMode.DEFAULT, supportedSurfaceCombination, combinationList
+        )
         assertThat(isSupported).isTrue()
     }
 
@@ -286,7 +309,8 @@
         for (combination in combinationList) {
             val isSupported =
                 supportedSurfaceCombination.checkSupported(
-                    false, combination.surfaceConfigList)
+                    CameraMode.DEFAULT, combination.surfaceConfigList
+                )
             assertThat(isSupported).isFalse()
         }
     }
@@ -302,7 +326,8 @@
         for (combination in combinationList) {
             val isSupported =
                 supportedSurfaceCombination.checkSupported(
-                    false, combination.surfaceConfigList)
+                    CameraMode.DEFAULT, combination.surfaceConfigList
+                )
             assertThat(isSupported).isFalse()
         }
     }
@@ -318,7 +343,8 @@
         for (combination in combinationList) {
             val isSupported =
                 supportedSurfaceCombination.checkSupported(
-                    false, combination.surfaceConfigList)
+                    CameraMode.DEFAULT, combination.surfaceConfigList
+                )
             assertThat(isSupported).isTrue()
         }
     }
@@ -332,7 +358,8 @@
         )
         val combinationList = getFullSupportedCombinationList()
         val isSupported = isAllSubConfigListSupported(
-            false, supportedSurfaceCombination, combinationList)
+            CameraMode.DEFAULT, supportedSurfaceCombination, combinationList
+        )
         assertThat(isSupported).isTrue()
     }
 
@@ -347,7 +374,8 @@
         for (combination in combinationList) {
             val isSupported =
                 supportedSurfaceCombination.checkSupported(
-                    false, combination.surfaceConfigList)
+                    CameraMode.DEFAULT, combination.surfaceConfigList
+                )
             assertThat(isSupported).isFalse()
         }
     }
@@ -355,9 +383,8 @@
     @Test
     fun checkLimitedSurfaceCombinationSupportedInRawDevice() {
         setupCamera(
-            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, intArrayOf(
-                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW
-            )
+            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
         )
         val supportedSurfaceCombination = SupportedSurfaceCombination(
             context, fakeCameraMetadata,
@@ -367,7 +394,8 @@
         for (combination in combinationList) {
             val isSupported =
                 supportedSurfaceCombination.checkSupported(
-                    false, combination.surfaceConfigList)
+                    CameraMode.DEFAULT, combination.surfaceConfigList
+                )
             assertThat(isSupported).isTrue()
         }
     }
@@ -375,9 +403,8 @@
     @Test
     fun checkLegacySurfaceCombinationSupportedInRawDevice() {
         setupCamera(
-            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, intArrayOf(
-                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW
-            )
+            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
         )
         val supportedSurfaceCombination = SupportedSurfaceCombination(
             context, fakeCameraMetadata,
@@ -387,7 +414,8 @@
         for (combination in combinationList) {
             val isSupported =
                 supportedSurfaceCombination.checkSupported(
-                    false, combination.surfaceConfigList)
+                    CameraMode.DEFAULT, combination.surfaceConfigList
+                )
             assertThat(isSupported).isTrue()
         }
     }
@@ -395,9 +423,8 @@
     @Test
     fun checkFullSurfaceCombinationSupportedInRawDevice() {
         setupCamera(
-            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, intArrayOf(
-                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW
-            )
+            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
         )
         val supportedSurfaceCombination = SupportedSurfaceCombination(
             context, fakeCameraMetadata,
@@ -407,7 +434,8 @@
         for (combination in combinationList) {
             val isSupported =
                 supportedSurfaceCombination.checkSupported(
-                    false, combination.surfaceConfigList)
+                    CameraMode.DEFAULT, combination.surfaceConfigList
+                )
             assertThat(isSupported).isTrue()
         }
     }
@@ -415,9 +443,8 @@
     @Test
     fun checkRawSurfaceCombinationSupportedInRawDevice() {
         setupCamera(
-            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, intArrayOf(
-                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW
-            )
+            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
         )
         val supportedSurfaceCombination = SupportedSurfaceCombination(
             context, fakeCameraMetadata,
@@ -427,7 +454,8 @@
         for (combination in combinationList) {
             val isSupported =
                 supportedSurfaceCombination.checkSupported(
-                    false, combination.surfaceConfigList)
+                    CameraMode.DEFAULT, combination.surfaceConfigList
+                )
             assertThat(isSupported).isTrue()
         }
     }
@@ -443,7 +471,8 @@
         for (combination in combinationList) {
             val isSupported =
                 supportedSurfaceCombination.checkSupported(
-                    false, combination.surfaceConfigList)
+                    CameraMode.DEFAULT, combination.surfaceConfigList
+                )
             assertThat(isSupported).isTrue()
         }
     }
@@ -457,14 +486,16 @@
         )
         val combinationList = getLevel3SupportedCombinationList()
         val isSupported = isAllSubConfigListSupported(
-            false, supportedSurfaceCombination, combinationList)
+            CameraMode.DEFAULT, supportedSurfaceCombination, combinationList
+        )
         assertThat(isSupported).isTrue()
     }
 
     @Test
     fun checkConcurrentSurfaceCombinationSupportedInConcurrentCameraMode() {
         Shadows.shadowOf(context.packageManager).setSystemFeature(
-            PackageManager.FEATURE_CAMERA_CONCURRENT, true)
+            PackageManager.FEATURE_CAMERA_CONCURRENT, true
+        )
         setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3)
         val supportedSurfaceCombination = SupportedSurfaceCombination(
             context, fakeCameraMetadata,
@@ -474,7 +505,8 @@
         for (combination in combinationList) {
             val isSupported =
                 supportedSurfaceCombination.checkSupported(
-                    true, combination.surfaceConfigList)
+                    CameraMode.CONCURRENT_CAMERA, combination.surfaceConfigList
+                )
             assertThat(isSupported).isTrue()
         }
     }
@@ -482,7 +514,8 @@
     @Test
     fun checkConcurrentSurfaceCombinationSubListSupportedInConcurrentCameraMode() {
         Shadows.shadowOf(context.packageManager).setSystemFeature(
-            PackageManager.FEATURE_CAMERA_CONCURRENT, true)
+            PackageManager.FEATURE_CAMERA_CONCURRENT, true
+        )
         setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3)
         val supportedSurfaceCombination = SupportedSurfaceCombination(
             context, fakeCameraMetadata,
@@ -490,10 +523,59 @@
         )
         val combinationList = getConcurrentSupportedCombinationList()
         val isSupported = isAllSubConfigListSupported(
-            true, supportedSurfaceCombination, combinationList)
+            CameraMode.CONCURRENT_CAMERA, supportedSurfaceCombination, combinationList
+        )
         assertThat(isSupported).isTrue()
     }
 
+    @Test
+    @Config(minSdk = Build.VERSION_CODES.S)
+    fun checkUltraHighResolutionSurfaceCombinationSupportedInUltraHighCameraMode() {
+        setupCamera(
+            maximumResolutionSupportedSizes = maximumResolutionSupportedSizes,
+            maximumResolutionHighResolutionSupportedSizes =
+            maximumResolutionHighResolutionSupportedSizes,
+            capabilities = intArrayOf(
+                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR
+            )
+        )
+        val supportedSurfaceCombination = SupportedSurfaceCombination(
+            context, fakeCameraMetadata,
+            mockEncoderProfilesAdapter
+        )
+        GuaranteedConfigurationsUtil.getUltraHighResolutionSupportedCombinationList().forEach {
+            assertThat(
+                supportedSurfaceCombination.checkSupported(
+                    CameraMode.ULTRA_HIGH_RESOLUTION_CAMERA, it.surfaceConfigList
+                )
+            ).isTrue()
+        }
+    }
+
+    @Test
+    @Config(minSdk = Build.VERSION_CODES.S)
+    fun checkUltraHighResolutionSurfaceCombinationSubListSupportedInUltraHighCameraMode() {
+        setupCamera(
+            maximumResolutionSupportedSizes = maximumResolutionSupportedSizes,
+            maximumResolutionHighResolutionSupportedSizes =
+            maximumResolutionHighResolutionSupportedSizes,
+            capabilities = intArrayOf(
+                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR
+            )
+        )
+        val supportedSurfaceCombination = SupportedSurfaceCombination(
+            context, fakeCameraMetadata,
+            mockEncoderProfilesAdapter
+        )
+        GuaranteedConfigurationsUtil.getUltraHighResolutionSupportedCombinationList().also {
+            assertThat(
+                isAllSubConfigListSupported(
+                    CameraMode.ULTRA_HIGH_RESOLUTION_CAMERA, supportedSurfaceCombination, it
+                )
+            ).isTrue()
+        }
+    }
+
     // //////////////////////////////////////////////////////////////////////////////////////////
     //
     // Surface config transformation tests
@@ -508,7 +590,7 @@
             mockEncoderProfilesAdapter
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            false,
+            CameraMode.DEFAULT,
             ImageFormat.YUV_420_888, vgaSize
         )
         val expectedSurfaceConfig =
@@ -524,7 +606,7 @@
             mockEncoderProfilesAdapter
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            false,
+            CameraMode.DEFAULT,
             ImageFormat.YUV_420_888, previewSize
         )
         val expectedSurfaceConfig =
@@ -540,7 +622,7 @@
             mockEncoderProfilesAdapter
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            false,
+            CameraMode.DEFAULT,
             ImageFormat.YUV_420_888, recordSize
         )
         val expectedSurfaceConfig =
@@ -556,7 +638,7 @@
             mockEncoderProfilesAdapter
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            false,
+            CameraMode.DEFAULT,
             ImageFormat.YUV_420_888, maximumSize
         )
         val expectedSurfaceConfig =
@@ -572,7 +654,7 @@
             mockEncoderProfilesAdapter
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            false,
+            CameraMode.DEFAULT,
             ImageFormat.JPEG, vgaSize
         )
         val expectedSurfaceConfig =
@@ -588,7 +670,7 @@
             mockEncoderProfilesAdapter
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            false,
+            CameraMode.DEFAULT,
             ImageFormat.JPEG, previewSize
         )
         val expectedSurfaceConfig =
@@ -604,7 +686,7 @@
             mockEncoderProfilesAdapter
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            false,
+            CameraMode.DEFAULT,
             ImageFormat.JPEG, recordSize
         )
         val expectedSurfaceConfig =
@@ -620,7 +702,7 @@
             mockEncoderProfilesAdapter
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            false,
+            CameraMode.DEFAULT,
             ImageFormat.JPEG, maximumSize
         )
         val expectedSurfaceConfig =
@@ -638,7 +720,7 @@
             mockEncoderProfilesAdapter
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            true,
+            CameraMode.CONCURRENT_CAMERA,
             ImageFormat.PRIVATE, SizeUtil.RESOLUTION_720P
         )
         val expectedSurfaceConfig =
@@ -656,7 +738,7 @@
             mockEncoderProfilesAdapter
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            true,
+            CameraMode.CONCURRENT_CAMERA,
             ImageFormat.YUV_420_888, SizeUtil.RESOLUTION_720P
         )
         val expectedSurfaceConfig =
@@ -674,7 +756,7 @@
             mockEncoderProfilesAdapter
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            true,
+            CameraMode.CONCURRENT_CAMERA,
             ImageFormat.JPEG, SizeUtil.RESOLUTION_720P
         )
         val expectedSurfaceConfig =
@@ -692,7 +774,7 @@
             mockEncoderProfilesAdapter
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            true,
+            CameraMode.CONCURRENT_CAMERA,
             ImageFormat.PRIVATE, RESOLUTION_1440P
         )
         val expectedSurfaceConfig =
@@ -710,7 +792,7 @@
             mockEncoderProfilesAdapter
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            true,
+            CameraMode.CONCURRENT_CAMERA,
             ImageFormat.YUV_420_888, RESOLUTION_1440P
         )
         val expectedSurfaceConfig =
@@ -728,7 +810,7 @@
             mockEncoderProfilesAdapter
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            true,
+            CameraMode.CONCURRENT_CAMERA,
             ImageFormat.JPEG, RESOLUTION_1440P
         )
         val expectedSurfaceConfig =
@@ -736,6 +818,56 @@
         assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
     }
 
+    @Test
+    @Config(minSdk = 31)
+    fun transformSurfaceConfigWithUltraHighResolution() {
+        setupCamera(
+            maximumResolutionSupportedSizes = maximumResolutionSupportedSizes,
+            maximumResolutionHighResolutionSupportedSizes =
+            maximumResolutionHighResolutionSupportedSizes,
+            capabilities = intArrayOf(
+                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR
+            )
+        )
+        val supportedSurfaceCombination = SupportedSurfaceCombination(
+            context, fakeCameraMetadata,
+            mockEncoderProfilesAdapter
+        )
+        assertThat(
+            supportedSurfaceCombination.transformSurfaceConfig(
+                CameraMode.DEFAULT,
+                ImageFormat.PRIVATE, ultraHighMaximumSize
+            )
+        ).isEqualTo(
+            SurfaceConfig.create(
+                SurfaceConfig.ConfigType.PRIV,
+                SurfaceConfig.ConfigSize.ULTRA_MAXIMUM
+            )
+        )
+        assertThat(
+            supportedSurfaceCombination.transformSurfaceConfig(
+                CameraMode.DEFAULT,
+                ImageFormat.YUV_420_888, ultraHighMaximumSize
+            )
+        ).isEqualTo(
+            SurfaceConfig.create(
+                SurfaceConfig.ConfigType.YUV,
+                SurfaceConfig.ConfigSize.ULTRA_MAXIMUM
+            )
+        )
+        assertThat(
+            supportedSurfaceCombination.transformSurfaceConfig(
+                CameraMode.DEFAULT,
+                ImageFormat.JPEG, ultraHighMaximumSize
+            )
+        ).isEqualTo(
+            SurfaceConfig.create(
+                SurfaceConfig.ConfigType.JPEG,
+                SurfaceConfig.ConfigSize.ULTRA_MAXIMUM
+            )
+        )
+    }
+
     // //////////////////////////////////////////////////////////////////////////////////////////
     //
     // Resolution selection tests for LEGACY-level guaranteed configurations
@@ -1163,7 +1295,8 @@
         }
         getSuggestedSpecsAndVerify(
             useCaseExpectedResultMap,
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
         )
     }
 
@@ -1184,7 +1317,8 @@
         }
         getSuggestedSpecsAndVerify(
             useCaseExpectedResultMap,
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3,
+            capabilities = intArrayOf(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
         )
     }
 
@@ -1434,7 +1568,7 @@
         val useCaseConfigToOutputSizesMap =
             getUseCaseConfigToOutputSizesMap(useCaseConfigMap.values.toList())
         val suggestedStreamSpecs = supportedSurfaceCombination.getSuggestedStreamSpecifications(
-            false,
+            CameraMode.DEFAULT,
             attachedSurfaceInfoList,
             useCaseConfigToOutputSizesMap
         )
@@ -1486,18 +1620,91 @@
     // //////////////////////////////////////////////////////////////////////////////////////////
 
     @Test
-    fun maximumSizeForImageFormat() {
-        setupCamera(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY)
+    fun generateCorrectSurfaceDefinition() {
+        Shadows.shadowOf(context.packageManager).setSystemFeature(
+            PackageManager.FEATURE_CAMERA_CONCURRENT, true
+        )
+        setupCamera()
         val supportedSurfaceCombination = SupportedSurfaceCombination(
             context, fakeCameraMetadata,
             mockEncoderProfilesAdapter
         )
-        val maximumYUVSize =
-            supportedSurfaceCombination.getMaxOutputSizeByFormat(ImageFormat.YUV_420_888)
-        assertThat(maximumYUVSize).isEqualTo(maximumSize)
-        val maximumJPEGSize =
-            supportedSurfaceCombination.getMaxOutputSizeByFormat(ImageFormat.JPEG)
-        assertThat(maximumJPEGSize).isEqualTo(maximumSize)
+        val imageFormat = ImageFormat.JPEG
+        val surfaceSizeDefinition = supportedSurfaceCombination.surfaceSizeDefinition
+        assertThat(surfaceSizeDefinition.s720pSizeMap[imageFormat]).isEqualTo(RESOLUTION_720P)
+        assertThat(surfaceSizeDefinition.previewSize).isEqualTo(previewSize)
+        assertThat(surfaceSizeDefinition.s1440pSizeMap[imageFormat]).isEqualTo(RESOLUTION_1440P)
+        assertThat(surfaceSizeDefinition.recordSize).isEqualTo(recordSize)
+        assertThat(surfaceSizeDefinition.maximumSizeMap[imageFormat]).isEqualTo(maximumSize)
+        assertThat(surfaceSizeDefinition.ultraMaximumSizeMap).isEmpty()
+    }
+
+    @Test
+    fun correctS720pSize_withSmallerOutputSizes() {
+        Shadows.shadowOf(context.packageManager).setSystemFeature(
+            PackageManager.FEATURE_CAMERA_CONCURRENT, true
+        )
+        setupCamera(supportedSizes = arrayOf(RESOLUTION_VGA))
+        val supportedSurfaceCombination = SupportedSurfaceCombination(
+            context, fakeCameraMetadata,
+            mockEncoderProfilesAdapter
+        )
+        val imageFormat = ImageFormat.JPEG
+        val surfaceSizeDefinition = supportedSurfaceCombination.surfaceSizeDefinition
+        assertThat(surfaceSizeDefinition.s720pSizeMap[imageFormat])
+            .isEqualTo(RESOLUTION_VGA)
+    }
+
+    @Test
+    fun correctS1440pSize_withSmallerOutputSizes() {
+        Shadows.shadowOf(context.packageManager).setSystemFeature(
+            PackageManager.FEATURE_CAMERA_CONCURRENT, true
+        )
+        setupCamera(supportedSizes = arrayOf(RESOLUTION_VGA))
+        val supportedSurfaceCombination = SupportedSurfaceCombination(
+            context, fakeCameraMetadata,
+            mockEncoderProfilesAdapter
+        )
+        val imageFormat = ImageFormat.JPEG
+        val surfaceSizeDefinition = supportedSurfaceCombination.surfaceSizeDefinition
+        assertThat(surfaceSizeDefinition.s1440pSizeMap[imageFormat]).isEqualTo(RESOLUTION_VGA)
+    }
+
+    @Test
+    @Config(minSdk = 23)
+    fun correctMaximumSize_withHighResolutionOutputSizes() {
+        setupCamera(highResolutionSupportedSizes = highResolutionSupportedSizes)
+        val supportedSurfaceCombination = SupportedSurfaceCombination(
+            context, fakeCameraMetadata,
+            mockEncoderProfilesAdapter
+        )
+        val imageFormat = ImageFormat.JPEG
+        val surfaceSizeDefinition = supportedSurfaceCombination.surfaceSizeDefinition
+        assertThat(surfaceSizeDefinition.maximumSizeMap[imageFormat]).isEqualTo(
+            highResolutionMaximumSize
+        )
+    }
+
+    @Test
+    @Config(minSdk = 32)
+    fun correctUltraMaximumSize_withMaximumResolutionMap() {
+        setupCamera(
+            maximumResolutionSupportedSizes = maximumResolutionSupportedSizes,
+            maximumResolutionHighResolutionSupportedSizes =
+            maximumResolutionHighResolutionSupportedSizes,
+            capabilities = intArrayOf(
+                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR
+            )
+        )
+        val supportedSurfaceCombination = SupportedSurfaceCombination(
+            context, fakeCameraMetadata,
+            mockEncoderProfilesAdapter
+        )
+        val imageFormat = ImageFormat.JPEG
+        val surfaceSizeDefinition = supportedSurfaceCombination.surfaceSizeDefinition
+        assertThat(surfaceSizeDefinition.ultraMaximumSizeMap[imageFormat]).isEqualTo(
+            ultraHighMaximumSize
+        )
     }
 
     @Test
@@ -1520,26 +1727,14 @@
         )
     }
 
-    private fun setupCamera(hardwareLevel: Int, capabilities: IntArray) {
-        setupCamera(
-            hardwareLevel, sensorOrientation90, landscapePixelArraySize,
-            supportedSizes, capabilities
-        )
-    }
-
-    private fun setupCamera(hardwareLevel: Int, supportedSizes: Array<Size>) {
-        setupCamera(
-            hardwareLevel, sensorOrientation90, landscapePixelArraySize,
-            supportedSizes, null
-        )
-    }
-
     private fun setupCamera(
-        hardwareLevel: Int,
+        hardwareLevel: Int = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
         sensorOrientation: Int = sensorOrientation90,
         pixelArraySize: Size = landscapePixelArraySize,
-        supportedSizes: Array<Size> =
-            this.supportedSizes,
+        supportedSizes: Array<Size> = this.supportedSizes,
+        highResolutionSupportedSizes: Array<Size>? = null,
+        maximumResolutionSupportedSizes: Array<Size>? = null,
+        maximumResolutionHighResolutionSupportedSizes: Array<Size>? = null,
         capabilities: IntArray? = null,
         cameraId: CameraId = CameraId.fromCamera1Id(0)
     ) {
@@ -1564,18 +1759,34 @@
         }
 
         val mockMap: StreamConfigurationMap = mock()
+        val mockMaximumResolutionMap: StreamConfigurationMap? =
+            if (maximumResolutionSupportedSizes != null ||
+                maximumResolutionHighResolutionSupportedSizes != null
+            ) {
+                mock()
+            } else {
+                null
+            }
+
+        val characteristicsMap = mutableMapOf(
+            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL to hardwareLevel,
+            CameraCharacteristics.SENSOR_ORIENTATION to sensorOrientation,
+            CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE to pixelArraySize,
+            CameraCharacteristics.LENS_FACING to CameraCharacteristics.LENS_FACING_BACK,
+            CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES to capabilities,
+            CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP to mockMap,
+        ).also { characteristicsMap ->
+            mockMaximumResolutionMap?.let {
+                characteristicsMap[
+                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION] =
+                    mockMaximumResolutionMap
+            }
+        }
 
         // set up FakeCafakeCameraMetadatameraMetadata
         fakeCameraMetadata = FakeCameraMetadata(
             cameraId = cameraId,
-            characteristics = mapOf(
-                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL to hardwareLevel,
-                CameraCharacteristics.SENSOR_ORIENTATION to sensorOrientation,
-                CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE to pixelArraySize,
-                CameraCharacteristics.LENS_FACING to CameraCharacteristics.LENS_FACING_BACK,
-                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES to capabilities,
-                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP to mockMap
-            )
+            characteristics = characteristicsMap
         )
 
         val cameraManager = ApplicationProvider.getApplicationContext<Context>()
@@ -1597,7 +1808,28 @@
                 MediaRecorder::class.java
             )
         ).thenReturn(supportedSizes)
+        // This is setup for high resolution output sizes
+        highResolutionSupportedSizes?.let {
+            whenever(mockMap.getHighResolutionOutputSizes(ArgumentMatchers.anyInt())).thenReturn(it)
+        }
         shadowCharacteristics.set(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP, mockMap)
+        mockMaximumResolutionMap?.let {
+            whenever(mockMaximumResolutionMap.getOutputSizes(ArgumentMatchers.anyInt()))
+                .thenReturn(maximumResolutionSupportedSizes)
+            whenever(mockMaximumResolutionMap.getOutputSizes(SurfaceTexture::class.java))
+                .thenReturn(maximumResolutionSupportedSizes)
+            whenever(
+                mockMaximumResolutionMap.getHighResolutionOutputSizes(
+                    ArgumentMatchers.anyInt()
+                )
+            ).thenReturn(
+                maximumResolutionHighResolutionSupportedSizes
+            )
+            shadowCharacteristics.set(
+                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION,
+                mockMaximumResolutionMap
+            )
+        }
         @LensFacing val lensFacingEnum = CameraUtil.getLensFacingEnumFromInt(
             CameraCharacteristics.LENS_FACING_BACK
         )
@@ -1652,7 +1884,7 @@
     }
 
     private fun isAllSubConfigListSupported(
-        isConcurrentCameraModeOn: Boolean,
+        cameraMode: Int,
         supportedSurfaceCombination: SupportedSurfaceCombination,
         combinationList: List<SurfaceCombination>
     ): Boolean {
@@ -1666,7 +1898,8 @@
                 val subConfigurationList: MutableList<SurfaceConfig> = ArrayList(configList)
                 subConfigurationList.removeAt(index)
                 val isSupported = supportedSurfaceCombination.checkSupported(
-                    isConcurrentCameraModeOn, subConfigurationList)
+                    cameraMode, subConfigurationList
+                )
                 if (!isSupported) {
                     return false
                 }
@@ -1679,11 +1912,13 @@
         captureType: UseCaseConfigFactory.CaptureType,
         targetFrameRate: Range<Int>? = null
     ): UseCase {
-        val builder = FakeUseCaseConfig.Builder(captureType, when (captureType) {
-            UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE -> ImageFormat.JPEG
-            UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS -> ImageFormat.YUV_420_888
-            else -> ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE
-        })
+        val builder = FakeUseCaseConfig.Builder(
+            captureType, when (captureType) {
+                UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE -> ImageFormat.JPEG
+                UseCaseConfigFactory.CaptureType.IMAGE_ANALYSIS -> ImageFormat.YUV_420_888
+                else -> ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE
+            }
+        )
         targetFrameRate?.let {
             builder.mutableConfig.insertOption(UseCaseConfig.OPTION_TARGET_FRAME_RATE, it)
         }
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/OutputSizesCorrectorTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/OutputSizesCorrectorTest.kt
index b81db73..63199bb 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/OutputSizesCorrectorTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/OutputSizesCorrectorTest.kt
@@ -40,6 +40,8 @@
 private const val MOTOROLA_E5_PLAY_MODEL_NAME = "moto e5 play"
 private const val SAMSUNG_BRAND_NAME = "SAMSUNG"
 private const val SAMSUNG_J7_DEVICE_NAME = "J7XELTE"
+private const val FAKE_BRAND_NAME = "Fake-Brand"
+private const val FAKE_DEVICE_NAME = "Fake-Device"
 
 private val outputSizes = arrayOf(
     // Samsung J7 API 27 above excluded sizes
@@ -206,6 +208,98 @@
         ).inOrder()
     }
 
+    @Test
+    fun canExcludeApi21LegacyLevelProblematicSizesByFormat() {
+        val outputSizesCorrector = createOutputSizesCorrector(
+            FAKE_BRAND_NAME,
+            FAKE_DEVICE_NAME,
+            null,
+            CAMERA_ID_0,
+            CameraCharacteristics.LENS_FACING_BACK,
+            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
+        )
+
+        val resultList = outputSizesCorrector.applyQuirks(
+            outputSizes,
+            ImageFormat.YUV_420_888
+        )!!.toList()
+
+        val expectedList = if (Build.VERSION.SDK_INT == 21) {
+            // non-4:3 sizes are removed
+            listOf(
+                Size(4128, 3096),
+                Size(3264, 2448),
+                Size(2048, 1536),
+                Size(1280, 960),
+                Size(640, 480),
+                Size(320, 240),
+            )
+        } else {
+            listOf(
+                Size(4128, 3096),
+                Size(4128, 2322),
+                Size(3088, 3088),
+                Size(3264, 2448),
+                Size(3264, 1836),
+                Size(2048, 1536),
+                Size(2048, 1152),
+                Size(1920, 1080),
+                Size(1280, 960),
+                Size(1280, 720),
+                Size(640, 480),
+                Size(320, 240),
+            )
+        }
+
+        Truth.assertThat(resultList).containsExactlyElementsIn(expectedList).inOrder()
+    }
+
+    @Test
+    fun canExcludeApi21LegacyLevelProblematicSizesByClass() {
+        val outputSizesCorrector = createOutputSizesCorrector(
+            FAKE_BRAND_NAME,
+            FAKE_DEVICE_NAME,
+            null,
+            CAMERA_ID_0,
+            CameraCharacteristics.LENS_FACING_BACK,
+            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
+        )
+
+        val resultList = outputSizesCorrector.applyQuirks(
+            outputSizes,
+            SurfaceTexture::class.java
+        )!!.toList()
+
+        val expectedList = if (Build.VERSION.SDK_INT == 21) {
+            // non-4:3 sizes are removed
+            listOf(
+                Size(4128, 3096),
+                Size(3264, 2448),
+                Size(2048, 1536),
+                Size(1280, 960),
+                Size(640, 480),
+                Size(320, 240),
+            )
+        } else {
+            listOf(
+                Size(4128, 3096),
+                Size(4128, 2322),
+                Size(3088, 3088),
+                Size(3264, 2448),
+                Size(3264, 1836),
+                Size(2048, 1536),
+                Size(2048, 1152),
+                Size(1920, 1080),
+                Size(1280, 960),
+                Size(1280, 720),
+                Size(640, 480),
+                Size(320, 240),
+            )
+        }
+
+        Truth.assertThat(resultList).containsExactlyElementsIn(expectedList).inOrder()
+    }
+
     private fun createOutputSizesCorrector(
         brand: String,
         device: String?,
@@ -222,20 +316,22 @@
             ReflectionHelpers.setStaticField(Build::class.java, "MODEL", it)
         }
 
+        val map = StreamConfigurationMapBuilder.newBuilder()
+            .apply {
+                outputSizes.forEach { outputSize ->
+                    addOutputSize(outputSize)
+                }
+            }.build()
+
         return OutputSizesCorrector(
             FakeCameraMetadata(
                 cameraId = CameraId(cameraId), characteristics = mapOf(
                     CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL to hardwareLevel,
                     CameraCharacteristics.LENS_FACING to lensFacing,
-                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP to
-                        StreamConfigurationMapBuilder.newBuilder()
-                            .apply {
-                                outputSizes.forEach { outputSize ->
-                                    addOutputSize(outputSize)
-                                }
-                            }.build()
+                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP to map
                 )
-            )
+            ),
+            map
         )
     }
 }
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatTest.kt
index f972ee6..ef595571 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatTest.kt
@@ -60,7 +60,7 @@
         streamConfigurationMapCompat =
             StreamConfigurationMapCompat(
                 builder.build(),
-            OutputSizesCorrector(FakeCameraMetadata())
+                OutputSizesCorrector(FakeCameraMetadata(), builder.build())
             )
     }
 
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/ZoomCompatTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/ZoomCompatTest.kt
new file mode 100644
index 0000000..a194ad5
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/ZoomCompatTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.compat
+
+import android.hardware.camera2.CameraCharacteristics
+import android.hardware.camera2.CaptureRequest
+import android.hardware.camera2.CaptureResult
+import android.os.Build
+import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.CameraMetadata
+import androidx.camera.camera2.pipe.Metadata
+import androidx.camera.camera2.pipe.integration.adapter.RobolectricCameraPipeTestRunner
+import androidx.camera.camera2.pipe.integration.impl.CameraProperties
+import com.google.common.truth.Truth.assertThat
+import kotlin.reflect.KClass
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+
+@RunWith(RobolectricCameraPipeTestRunner::class)
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+@DoNotInstrument
+class ZoomCompatTest {
+    @Test
+    @Config(minSdk = 30)
+    fun canProvideZoomCompat_whenGettingControlZoomRatioThrowsError() {
+        assertThat(ZoomCompat.Bindings.provideZoomRatio(throwingCameraProperties))
+            .isInstanceOf(CropRegionZoomCompat::class.java)
+    }
+
+    private val throwingCameraProperties = object : CameraProperties {
+        override val cameraId: CameraId
+            get() = TODO("Not yet implemented")
+        override val metadata: CameraMetadata
+            get() = throwingCameraMetadata
+    }
+
+    private val throwingCameraMetadata = object : CameraMetadata {
+        override val camera: CameraId
+            get() = TODO("Not yet implemented")
+        override val isRedacted: Boolean
+            get() = TODO("Not yet implemented")
+        override val keys: Set<CameraCharacteristics.Key<*>>
+            get() = TODO("Not yet implemented")
+        override val physicalCameraIds: Set<CameraId>
+            get() = TODO("Not yet implemented")
+        override val physicalRequestKeys: Set<CaptureRequest.Key<*>>
+            get() = TODO("Not yet implemented")
+        override val requestKeys: Set<CaptureRequest.Key<*>>
+            get() = TODO("Not yet implemented")
+        override val resultKeys: Set<CaptureResult.Key<*>>
+            get() = TODO("Not yet implemented")
+        override val sessionKeys: Set<CaptureRequest.Key<*>>
+            get() = TODO("Not yet implemented")
+
+        override fun awaitPhysicalMetadata(cameraId: CameraId): CameraMetadata {
+            TODO("Not yet implemented")
+        }
+
+        override fun <T> get(key: CameraCharacteristics.Key<T>): T? {
+            println("throwingCameraMetadata get: key = $key")
+            if (key == CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE) {
+                throw AssertionError()
+            }
+            TODO("Not yet implemented")
+        }
+
+        override fun <T> get(key: Metadata.Key<T>): T? {
+            TODO("Not yet implemented")
+        }
+
+        override fun <T> getOrDefault(key: CameraCharacteristics.Key<T>, default: T): T {
+            TODO("Not yet implemented")
+        }
+
+        override fun <T> getOrDefault(key: Metadata.Key<T>, default: T): T {
+            TODO("Not yet implemented")
+        }
+
+        override suspend fun getPhysicalMetadata(cameraId: CameraId): CameraMetadata {
+            TODO("Not yet implemented")
+        }
+
+        override fun <T : Any> unwrapAs(type: KClass<T>): T? {
+            TODO("Not yet implemented")
+        }
+    }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/AfRegionFlipHorizontallyQuirkTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/AfRegionFlipHorizontallyQuirkTest.kt
index 5f4c468..9e3a6be 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/AfRegionFlipHorizontallyQuirkTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/AfRegionFlipHorizontallyQuirkTest.kt
@@ -73,7 +73,10 @@
             cameraMetadata,
             StreamConfigurationMapCompat(
                 StreamConfigurationMapBuilder.newBuilder().build(),
-                OutputSizesCorrector(cameraMetadata)
+                OutputSizesCorrector(
+                    cameraMetadata,
+                    StreamConfigurationMapBuilder.newBuilder().build()
+                )
             )
         ).quirks
     }
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/CamcorderProfileResolutionQuirkTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/CamcorderProfileResolutionQuirkTest.kt
index 7b2f9e2..5f0a2267 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/CamcorderProfileResolutionQuirkTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/CamcorderProfileResolutionQuirkTest.kt
@@ -74,7 +74,10 @@
         val quirk = CamcorderProfileResolutionQuirk(
             StreamConfigurationMapCompat(
                 cameraMetadata[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP]!!,
-                OutputSizesCorrector(cameraMetadata)
+                OutputSizesCorrector(
+                    cameraMetadata,
+                    cameraMetadata[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP]!!
+                )
             )
         )
 
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/ControlZoomRatioRangeAssertionErrorQuirkTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/ControlZoomRatioRangeAssertionErrorQuirkTest.kt
new file mode 100644
index 0000000..7a13679
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/ControlZoomRatioRangeAssertionErrorQuirkTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.compat.quirk
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.ParameterizedRobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+import org.robolectric.shadows.ShadowBuild
+
+@RunWith(ParameterizedRobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = 21)
+class ControlZoomRatioRangeAssertionErrorQuirkTest(
+    private val brand: String,
+    private val model: String,
+    private val quirkEnablingExpected: Boolean
+) {
+    @Test
+    fun canEnableControlZoomRatioRangeAssertionErrorQuirkCorrectly() {
+        ShadowBuild.setBrand(brand)
+        ShadowBuild.setModel(model)
+
+        assertThat(DeviceQuirks[ControlZoomRatioRangeAssertionErrorQuirk::class.java] != null)
+            .isEqualTo(quirkEnablingExpected)
+    }
+
+    companion object {
+        @JvmStatic
+        @ParameterizedRobolectricTestRunner.Parameters(name = "Brand: {0}, Model: {1}")
+        fun data() = listOf(
+            arrayOf("jio", "LS1542QWN", true),
+            arrayOf("samsung", "SM-A025M/DS", true),
+            arrayOf("Samsung", "SM-S124DL", true),
+            arrayOf("vivo", "vivo 2039", true),
+            arrayOf("motorola", "MotoG100", false),
+        )
+    }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraCroppingQuirkTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraCroppingQuirkTest.kt
new file mode 100644
index 0000000..42b703a
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraCroppingQuirkTest.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.compat.quirk
+
+import android.os.Build
+import android.util.Size
+import androidx.camera.core.impl.SurfaceConfig
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+import org.robolectric.util.ReflectionHelpers
+
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class ExtraCroppingQuirkTest {
+    private val quirk = ExtraCroppingQuirk()
+
+    @Test
+    fun deviceModelNotContained_returnsNull() {
+        ReflectionHelpers.setStaticField(Build::class.java, "MODEL", "non-existent")
+        assert(!ExtraCroppingQuirk.load())
+        Truth.assertThat(quirk.getVerifiedResolution(SurfaceConfig.ConfigType.PRIV)).isNull()
+    }
+
+    @Test
+    fun deviceBrandNotSamsung_returnsNull() {
+        ReflectionHelpers.setStaticField(Build::class.java, "BRAND", "non-existent")
+        assert(!ExtraCroppingQuirk.load())
+        Truth.assertThat(quirk.getVerifiedResolution(SurfaceConfig.ConfigType.PRIV)).isNull()
+    }
+
+    @Test
+    fun osVersionNotInRange_returnsNull() {
+        ReflectionHelpers.setStaticField(Build::class.java, "BRAND", "samsung")
+        ReflectionHelpers.setStaticField(Build::class.java, "MODEL", "SM-J710MN")
+        ReflectionHelpers.setStaticField(Build.VERSION::class.java, "SDK_INT", 20)
+        assert(!ExtraCroppingQuirk.load())
+        Truth.assertThat(quirk.getVerifiedResolution(SurfaceConfig.ConfigType.PRIV)).isNull()
+    }
+
+    @Test
+    fun rawConfigType_returnsNull() {
+        ReflectionHelpers.setStaticField(Build::class.java, "BRAND", "samsung")
+        ReflectionHelpers.setStaticField(Build::class.java, "MODEL", "SM-J710MN")
+        ReflectionHelpers.setStaticField(Build.VERSION::class.java, "SDK_INT", 22)
+        assert(ExtraCroppingQuirk.load())
+        Truth.assertThat(quirk.getVerifiedResolution(SurfaceConfig.ConfigType.RAW)).isNull()
+    }
+
+    @Test
+    fun privConfigType_returnsSize() {
+        ReflectionHelpers.setStaticField(Build::class.java, "BRAND", "samsung")
+        ReflectionHelpers.setStaticField(Build::class.java, "MODEL", "SM-J710MN")
+        ReflectionHelpers.setStaticField(Build.VERSION::class.java, "SDK_INT", 22)
+        assert(ExtraCroppingQuirk.load())
+        Truth.assertThat(quirk.getVerifiedResolution(SurfaceConfig.ConfigType.PRIV))
+            .isEqualTo(Size(1920, 1080))
+    }
+
+    @Test
+    fun yuvConfigType_returnsSize() {
+        ReflectionHelpers.setStaticField(Build::class.java, "BRAND", "samsung")
+        ReflectionHelpers.setStaticField(Build::class.java, "MODEL", "SM-J710MN")
+        ReflectionHelpers.setStaticField(Build.VERSION::class.java, "SDK_INT", 22)
+        assert(ExtraCroppingQuirk.load())
+        Truth.assertThat(quirk.getVerifiedResolution(SurfaceConfig.ConfigType.YUV))
+            .isEqualTo(Size(1280, 720))
+    }
+
+    @Test
+    fun jpegConfigType_returnsSize() {
+        ReflectionHelpers.setStaticField(Build::class.java, "BRAND", "samsung")
+        ReflectionHelpers.setStaticField(Build::class.java, "MODEL", "SM-J710MN")
+        ReflectionHelpers.setStaticField(Build.VERSION::class.java, "SDK_INT", 22)
+        assert(ExtraCroppingQuirk.load())
+        Truth.assertThat(quirk.getVerifiedResolution(SurfaceConfig.ConfigType.JPEG))
+            .isEqualTo(Size(3264, 1836))
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..88b54d5
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedSurfaceCombinationsQuirkTest.kt
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.compat.quirk
+
+import android.hardware.camera2.CameraCharacteristics
+import android.os.Build
+import androidx.camera.core.impl.SurfaceCombination
+import androidx.camera.core.impl.SurfaceConfig
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.ParameterizedRobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+import org.robolectric.util.ReflectionHelpers
+
+@RunWith(ParameterizedRobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class ExtraSupportedSurfaceCombinationsQuirkTest(private val config: Config) {
+
+    @Test
+    fun checkExtraSupportedSurfaceCombinations() {
+        // Set up brand properties
+        if (config.brand != null) {
+            ReflectionHelpers.setStaticField(Build::class.java, "BRAND", config.brand)
+        }
+
+        // Set up device properties
+        if (config.device != null) {
+            ReflectionHelpers.setStaticField(Build::class.java, "DEVICE", config.device)
+        }
+
+        // Set up model properties
+        if (config.model != null) {
+            ReflectionHelpers.setStaticField(Build::class.java, "MODEL", config.model)
+        }
+
+        // Initializes ExtraSupportedSurfaceCombinationsContainer instance with camera id
+        val quirk =
+            ExtraSupportedSurfaceCombinationsQuirk()
+
+        // Gets the extra supported surface combinations on the device
+        val extraSurfaceCombinations: List<SurfaceCombination> =
+            quirk.getExtraSupportedSurfaceCombinations(
+                config.cameraId,
+                config.hardwareLevel
+            )
+        for (expectedSupportedSurfaceCombination in config.expectedSupportedSurfaceCombinations) {
+            var isSupported = false
+
+            // Checks the combination is supported by the list retrieved from the
+            // ExtraSupportedSurfaceCombinationsContainer.
+            for (extraSurfaceCombination in extraSurfaceCombinations) {
+                if (extraSurfaceCombination.isSupported(
+                        expectedSupportedSurfaceCombination.surfaceConfigList
+                    )
+                ) {
+                    isSupported = true
+                    break
+                }
+            }
+            Truth.assertThat(isSupported).isTrue()
+        }
+    }
+
+    companion object {
+        @JvmStatic
+        @ParameterizedRobolectricTestRunner.Parameters(name = "{0}")
+        fun data() = listOf(
+            Config(
+                null,
+                "heroqltevzw",
+                null,
+                "0",
+                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+            ),
+            Config(
+                null,
+                "heroqltevzw",
+                null,
+                "1",
+                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+                createFullLevelYPYSupportedCombinations()
+            ),
+            // Tests for Samsung S7 case
+            Config(
+                null,
+                "heroqltetmo",
+                null,
+                "0",
+                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+            ),
+            Config(
+                null,
+                "heroqltetmo",
+                null, "1",
+                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+                createFullLevelYPYSupportedCombinations()
+            ),
+            // Tests for Samsung limited device case
+            Config(
+                "samsung",
+                null,
+                "sm-g9860",
+                "0",
+                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+            ),
+            Config(
+                "samsung",
+                null,
+                "sm-g9860",
+                "1",
+                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+                createFullLevelYPYAndYYYSupportedCombinations()
+            ),
+            // Tests for FULL Pixel devices
+            Config(
+                "Google",
+                null,
+                "Pixel 6",
+                "0",
+                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+                createLevel3PrivPrivYuvRawConfiguration()
+            ),
+            Config(
+                "Google",
+                null,
+                "Pixel 6",
+                "1",
+                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+                createLevel3PrivPrivYuvRawConfiguration()
+            ),
+            Config(
+                "Google",
+                null,
+                "Pixel 6 Pro",
+                "0",
+                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+                createLevel3PrivPrivYuvRawConfiguration()
+            ),
+            Config(
+                "Google",
+                null,
+                "Pixel 6 Pro",
+                "1",
+                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+                createLevel3PrivPrivYuvRawConfiguration()
+            ),
+            Config(
+                "Google",
+                null,
+                "Pixel 7",
+                "0",
+                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+                createLevel3PrivPrivYuvRawConfiguration()
+            ),
+            Config(
+                "Google",
+                null,
+                "Pixel 6 Pro",
+                "1",
+                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+                createLevel3PrivPrivYuvRawConfiguration()
+            ),
+            Config(
+                "Google",
+                null,
+                "Pixel 7 Pro",
+                "0",
+                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+                createLevel3PrivPrivYuvRawConfiguration()
+            ),
+            Config(
+                "Google",
+                null,
+                "Pixel 7 Pro",
+                "1",
+                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+                createLevel3PrivPrivYuvRawConfiguration()
+            ),
+            // Other cases
+            Config(
+                null,
+                null,
+                null,
+                "0",
+                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
+            )
+        )
+
+        private fun createFullLevelYPYSupportedCombinations(): Array<SurfaceCombination> {
+            // (YUV, ANALYSIS) + (PRIV, PREVIEW) + (YUV, MAXIMUM)
+            val surfaceCombination = SurfaceCombination()
+            surfaceCombination.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.YUV,
+                    SurfaceConfig.ConfigSize.VGA
+                )
+            )
+            surfaceCombination.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.PRIV,
+                    SurfaceConfig.ConfigSize.PREVIEW
+                )
+            )
+            surfaceCombination.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.YUV,
+                    SurfaceConfig.ConfigSize.MAXIMUM
+                )
+            )
+            return arrayOf(surfaceCombination)
+        }
+
+        private fun createFullLevelYPYAndYYYSupportedCombinations(): Array<SurfaceCombination> {
+            // (YUV, ANALYSIS) + (PRIV, PREVIEW) + (YUV, MAXIMUM)
+            val surfaceCombination1 = SurfaceCombination()
+            surfaceCombination1.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.YUV,
+                    SurfaceConfig.ConfigSize.VGA
+                )
+            )
+            surfaceCombination1.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.PRIV,
+                    SurfaceConfig.ConfigSize.PREVIEW
+                )
+            )
+            surfaceCombination1.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.YUV,
+                    SurfaceConfig.ConfigSize.MAXIMUM
+                )
+            )
+
+            // (YUV, ANALYSIS) + (YUV, PREVIEW) + (YUV, MAXIMUM)
+            val surfaceCombination2 = SurfaceCombination()
+            surfaceCombination2.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.YUV,
+                    SurfaceConfig.ConfigSize.VGA
+                )
+            )
+            surfaceCombination2.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.YUV,
+                    SurfaceConfig.ConfigSize.PREVIEW
+                )
+            )
+            surfaceCombination2.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.YUV,
+                    SurfaceConfig.ConfigSize.MAXIMUM
+                )
+            )
+            return arrayOf(surfaceCombination1, surfaceCombination2)
+        }
+
+        private fun createLevel3PrivPrivYuvRawConfiguration(): Array<SurfaceCombination> {
+            // (PRIV, PREVIEW) + (PRIV, ANALYSIS) + (YUV, MAXIMUM) + (RAW, MAXIMUM)
+            val surfaceCombination = SurfaceCombination()
+            surfaceCombination.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.PRIV,
+                    SurfaceConfig.ConfigSize.PREVIEW
+                )
+            )
+            surfaceCombination.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.PRIV,
+                    SurfaceConfig.ConfigSize.VGA
+                )
+            )
+            surfaceCombination.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.YUV,
+                    SurfaceConfig.ConfigSize.MAXIMUM
+                )
+            )
+            surfaceCombination.addSurfaceConfig(
+                SurfaceConfig.create(
+                    SurfaceConfig.ConfigType.RAW,
+                    SurfaceConfig.ConfigSize.MAXIMUM
+                )
+            )
+            return arrayOf(surfaceCombination)
+        }
+    }
+
+    class Config(
+        val brand: String?,
+        val device: String?,
+        val model: String?,
+        val cameraId: String,
+        val hardwareLevel: Int,
+        val expectedSupportedSurfaceCombinations: Array<SurfaceCombination> = arrayOf()
+    )
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/JpegHalCorruptImageQuirkTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/JpegHalCorruptImageQuirkTest.kt
index bc371e4..edd60eb 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/JpegHalCorruptImageQuirkTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/JpegHalCorruptImageQuirkTest.kt
@@ -55,7 +55,10 @@
             FakeCameraMetadata(),
             StreamConfigurationMapCompat(
                 StreamConfigurationMapBuilder.newBuilder().build(),
-                OutputSizesCorrector(FakeCameraMetadata())
+                OutputSizesCorrector(
+                    FakeCameraMetadata(),
+                    StreamConfigurationMapBuilder.newBuilder().build()
+                )
             )
         ).quirks
 
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/YuvImageOnePixelShiftQuirkTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/YuvImageOnePixelShiftQuirkTest.kt
index 43d8e7f..c7b452a 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/YuvImageOnePixelShiftQuirkTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/quirk/YuvImageOnePixelShiftQuirkTest.kt
@@ -46,7 +46,10 @@
             FakeCameraMetadata(),
             StreamConfigurationMapCompat(
                 StreamConfigurationMapBuilder.newBuilder().build(),
-                OutputSizesCorrector(FakeCameraMetadata())
+                OutputSizesCorrector(
+                    FakeCameraMetadata(),
+                    StreamConfigurationMapBuilder.newBuilder().build()
+                )
             )
         ).quirks
 
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/workaround/AutoFlashAEModeDisablerTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/workaround/AutoFlashAEModeDisablerTest.kt
index 04025fc..f7ad78c 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/workaround/AutoFlashAEModeDisablerTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/workaround/AutoFlashAEModeDisablerTest.kt
@@ -113,7 +113,10 @@
             CameraQuirks(
                 metadata, StreamConfigurationMapCompat(
                     StreamConfigurationMapBuilder.newBuilder().build(),
-                    OutputSizesCorrector(FakeCameraMetadata())
+                    OutputSizesCorrector(
+                        FakeCameraMetadata(),
+                        StreamConfigurationMapBuilder.newBuilder().build()
+                    )
                 )
             )
         )
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/workaround/ResolutionCorrectorTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/workaround/ResolutionCorrectorTest.kt
new file mode 100644
index 0000000..ca3adf66
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/workaround/ResolutionCorrectorTest.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.camera.camera2.pipe.integration.compat.workaround
+
+import android.os.Build
+import android.util.Size
+import androidx.camera.core.impl.SurfaceConfig
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+import org.robolectric.util.ReflectionHelpers
+
+private val RESOLUTION_1 = Size(101, 100)
+private val RESOLUTION_2 = Size(102, 100)
+
+private val SELECT_RESOLUTION_PRIV = Size(1920, 1080)
+private val SELECT_RESOLUTION_YUV = Size(1280, 720)
+private val SELECT_RESOLUTION_JPEG = Size(3264, 1836)
+
+private val SUPPORTED_RESOLUTIONS = listOf(RESOLUTION_1, RESOLUTION_2)
+
+/**
+ * Unit test for [ResolutionCorrector].
+ */
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class ResolutionCorrectorTest {
+
+    private var mResolutionCorrector: ResolutionCorrector? = null
+
+    @Before
+    fun setup() {
+        ReflectionHelpers.setStaticField(Build::class.java, "BRAND", "samsung")
+        ReflectionHelpers.setStaticField(Build::class.java, "MODEL", "SM-J710MN")
+        ReflectionHelpers.setStaticField(Build.VERSION::class.java, "SDK_INT", 22)
+        mResolutionCorrector = ResolutionCorrector()
+    }
+
+    @Test
+    fun hasPrivResolution_prioritized() {
+        hasResolution_prioritized(SurfaceConfig.ConfigType.PRIV, SELECT_RESOLUTION_PRIV)
+    }
+
+    @Test
+    fun hasYuvResolution_prioritized() {
+        hasResolution_prioritized(SurfaceConfig.ConfigType.YUV, SELECT_RESOLUTION_YUV)
+    }
+
+    @Test
+    fun hasJpegResolution_prioritized() {
+        hasResolution_prioritized(SurfaceConfig.ConfigType.JPEG, SELECT_RESOLUTION_JPEG)
+    }
+
+    private fun hasResolution_prioritized(
+        configType: SurfaceConfig.ConfigType,
+        resolution: Size
+    ) {
+        val resolutions: MutableList<Size> = ArrayList<Size>(SUPPORTED_RESOLUTIONS)
+        resolutions.add(resolution)
+        Truth.assertThat(mResolutionCorrector!!.insertOrPrioritize(configType, resolutions))
+            .containsExactly(resolution, RESOLUTION_1, RESOLUTION_2).inOrder()
+    }
+
+    @Test
+    fun noPrivResolution_inserted() {
+        noResolution_inserted(SurfaceConfig.ConfigType.PRIV, SELECT_RESOLUTION_PRIV)
+    }
+
+    @Test
+    fun noYuvResolution_inserted() {
+        noResolution_inserted(SurfaceConfig.ConfigType.YUV, SELECT_RESOLUTION_YUV)
+    }
+
+    @Test
+    fun noJpegResolution_inserted() {
+        noResolution_inserted(SurfaceConfig.ConfigType.JPEG, SELECT_RESOLUTION_JPEG)
+    }
+
+    private fun noResolution_inserted(
+        configType: SurfaceConfig.ConfigType,
+        resolution: Size
+    ) {
+        Truth.assertThat(
+            mResolutionCorrector!!.insertOrPrioritize(
+                configType,
+                SUPPORTED_RESOLUTIONS
+            )
+        )
+            .containsExactly(resolution, RESOLUTION_1, RESOLUTION_2).inOrder()
+    }
+
+    @Test
+    fun noQuirk_returnsOriginalSupportedResolutions() {
+        ReflectionHelpers.setStaticField(Build::class.java, "BRAND", "notsamsung")
+        val resolutionCorrector = ResolutionCorrector()
+        val result = resolutionCorrector.insertOrPrioritize(
+            SurfaceConfig.ConfigType.PRIV,
+            SUPPORTED_RESOLUTIONS
+        )
+        Truth.assertThat(result).containsExactlyElementsIn(SUPPORTED_RESOLUTIONS)
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/workaround/UseTorchAsFlashTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/workaround/UseTorchAsFlashTest.kt
new file mode 100644
index 0000000..4322730
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/workaround/UseTorchAsFlashTest.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.compat.workaround
+
+import android.hardware.camera2.CameraCharacteristics
+import android.os.Build
+import androidx.camera.camera2.pipe.integration.adapter.RobolectricCameraPipeTestRunner
+import androidx.camera.camera2.pipe.integration.compat.StreamConfigurationMapCompat
+import androidx.camera.camera2.pipe.integration.compat.quirk.CameraNoResponseWhenEnablingFlashQuirk
+import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
+import androidx.camera.camera2.pipe.integration.compat.quirk.ImageCaptureWashedOutImageQuirk
+import androidx.camera.camera2.pipe.testing.FakeCameraMetadata
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+import org.robolectric.shadows.ShadowBuild
+import org.robolectric.shadows.StreamConfigurationMapBuilder
+
+@RunWith(RobolectricCameraPipeTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class UseTorchAsFlashTest {
+
+    @Test
+    fun shouldUseTorchAsFlash_default_isFalse() {
+        val useTorchAsFlash = createUseTorchAsFlash()
+
+        assertThat(useTorchAsFlash.shouldUseTorchAsFlash()).isFalse()
+    }
+
+    @Test
+    fun shouldUseTorchAsFlash_withCameraNoResponseWhenEnablingFlashQuirk_isTrue() {
+        CameraNoResponseWhenEnablingFlashQuirk.AFFECTED_MODELS.forEach { model ->
+            ShadowBuild.setModel(model)
+            val useTorchAsFlash = createUseTorchAsFlash()
+
+            assertThat(useTorchAsFlash.shouldUseTorchAsFlash()).isTrue()
+        }
+    }
+
+    @Test
+    fun shouldUseTorchAsFlash_withImageCaptureWashedOutImageQuirk_isTrue() {
+        ImageCaptureWashedOutImageQuirk.BUILD_MODELS.forEach { model ->
+            ShadowBuild.setModel(model)
+            val useTorchAsFlash = createUseTorchAsFlash()
+
+            assertThat(useTorchAsFlash.shouldUseTorchAsFlash()).isTrue()
+        }
+    }
+
+    @Test
+    fun shouldUseTorchAsFlash_lensFacingFront_isFalse() {
+        CameraNoResponseWhenEnablingFlashQuirk.AFFECTED_MODELS.forEach { model ->
+            ShadowBuild.setModel(model)
+            val useTorchAsFlash =
+                createUseTorchAsFlash(lensFacing = CameraCharacteristics.LENS_FACING_FRONT)
+
+            assertThat(useTorchAsFlash.shouldUseTorchAsFlash()).isFalse()
+        }
+    }
+
+    private fun createUseTorchAsFlash(
+        lensFacing: Int = CameraCharacteristics.LENS_FACING_BACK
+    ): UseTorchAsFlash {
+        val metadata = FakeCameraMetadata(mapOf(CameraCharacteristics.LENS_FACING to lensFacing))
+
+        return UseTorchAsFlash.Bindings.provideUseTorchAsFlash(
+            CameraQuirks(
+                metadata,
+                StreamConfigurationMapCompat(
+                    StreamConfigurationMapBuilder.newBuilder().build(),
+                    OutputSizesCorrector(
+                        FakeCameraMetadata(),
+                        StreamConfigurationMapBuilder.newBuilder().build()
+                    )
+                )
+            )
+        )
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
index c04fcb5..34b3175 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
@@ -37,7 +37,10 @@
 import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
 import androidx.camera.camera2.pipe.integration.adapter.RobolectricCameraPipeTestRunner
 import androidx.camera.camera2.pipe.integration.adapter.asListenableFuture
+import androidx.camera.camera2.pipe.integration.compat.workaround.CapturePipelineTorchCorrection
 import androidx.camera.camera2.pipe.integration.compat.workaround.NoOpAutoFlashAEModeDisabler
+import androidx.camera.camera2.pipe.integration.compat.workaround.NotUseTorchAsFlash
+import androidx.camera.camera2.pipe.integration.compat.workaround.UseTorchAsFlashImpl
 import androidx.camera.camera2.pipe.integration.config.UseCaseGraphConfig
 import androidx.camera.camera2.pipe.integration.testing.FakeCameraGraph
 import androidx.camera.camera2.pipe.integration.testing.FakeCameraGraphSession
@@ -62,12 +65,13 @@
 import kotlinx.coroutines.Deferred
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.awaitAll
 import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withTimeoutOrNull
 import org.junit.After
 import org.junit.AfterClass
 import org.junit.Assert
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.mock
@@ -112,7 +116,7 @@
     }
     private val comboRequestListener = ComboRequestListener()
     private val fakeCameraGraphSession = object : FakeCameraGraphSession() {
-        var requestHandler: (List<Request>) -> Unit = { _ -> }
+        var requestHandler: (List<Request>) -> Unit = { requests -> requests.complete() }
         val lock3ASemaphore = Semaphore(0)
         val unlock3ASemaphore = Semaphore(0)
         val lock3AForCaptureSemaphore = Semaphore(0)
@@ -140,7 +144,9 @@
         override suspend fun unlock3A(
             ae: Boolean?,
             af: Boolean?,
-            awb: Boolean?
+            awb: Boolean?,
+            frameLimit: Int,
+            timeLimitNs: Long
         ): Deferred<Result3A> {
             unlock3ASemaphore.release()
             return CompletableDeferred(Result3A(Result3A.Status.OK))
@@ -176,6 +182,11 @@
             mapOf(CameraCharacteristics.FLASH_INFO_AVAILABLE to true),
         )
     )
+    private val fakeUseCaseGraphConfig = UseCaseGraphConfig(
+        graph = FakeCameraGraph(fakeCameraGraphSession = fakeCameraGraphSession),
+        surfaceToStreamMap = emptyMap(),
+        cameraStateAdapter = CameraStateAdapter(),
+    )
     private var runningRepeatingStream: ScheduledFuture<*>? = null
         set(value) {
             runningRepeatingStream?.cancel(false)
@@ -183,7 +194,7 @@
         }
 
     private lateinit var torchControl: TorchControl
-    private lateinit var capturePipeline: CapturePipeline
+    private lateinit var capturePipeline: CapturePipelineImpl
 
     private lateinit var fakeUseCaseCameraState: UseCaseCameraState
 
@@ -210,24 +221,19 @@
             fakeRequestControl.torchUpdateEventList.clear()
         }
 
-        val fakeUseCaseGraphConfig = UseCaseGraphConfig(
-            graph = FakeCameraGraph(fakeCameraGraphSession = fakeCameraGraphSession),
-            surfaceToStreamMap = emptyMap(),
-            cameraStateAdapter = CameraStateAdapter(),
-        )
-
         fakeUseCaseCameraState = UseCaseCameraState(
             fakeUseCaseGraphConfig,
             fakeUseCaseThreads
         )
 
         capturePipeline = CapturePipelineImpl(
-            torchControl = torchControl,
-            threads = fakeUseCaseThreads,
-            requestListener = comboRequestListener,
             cameraProperties = fakeCameraProperties,
+            requestListener = comboRequestListener,
+            threads = fakeUseCaseThreads,
+            torchControl = torchControl,
             useCaseGraphConfig = fakeUseCaseGraphConfig,
-            useCaseCameraState = fakeUseCaseCameraState
+            useCaseCameraState = fakeUseCaseCameraState,
+            useTorchAsFlash = NotUseTorchAsFlash,
         )
     }
 
@@ -237,13 +243,11 @@
     }
 
     @Test
-    @Ignore // b/216788724
     fun miniLatency_flashOn_shouldTriggerAePreCapture(): Unit = runBlocking {
         flashOn_shouldTriggerAePreCapture(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
     }
 
     @Test
-    @Ignore // b/216788724
     fun maxQuality_flashOn_shouldTriggerAePreCapture(): Unit = runBlocking {
         flashOn_shouldTriggerAePreCapture(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
     }
@@ -279,13 +283,11 @@
     }
 
     @Test
-    @Ignore // b/216788724
     fun miniLatency_flashAutoFlashRequired_shouldTriggerAePreCapture(): Unit = runBlocking {
         flashAutoFlashRequired_shouldTriggerAePreCapture(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
     }
 
     @Test
-    @Ignore // b/216788724
     fun maxQuality_flashAutoFlashRequired_shouldTriggerAePreCapture(): Unit = runBlocking {
         flashAutoFlashRequired_shouldTriggerAePreCapture(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
     }
@@ -329,17 +331,66 @@
         ).isTrue()
     }
 
-    // TODO(wenhungteng@): Porting miniLatency_withTorchAsFlashQuirk_shouldOpenTorch,
-    //  maxQuality_withTorchAsFlashQuirk_shouldOpenTorch
+    @Test
+    fun miniLatency_withTorchAsFlashQuirk_shouldOpenTorch(): Unit = runBlocking {
+        withTorchAsFlashQuirk_shouldOpenTorch(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
+    }
 
     @Test
-    @Ignore // b/216788724
+    fun maxQuality_withTorchAsFlashQuirk_shouldOpenTorch(): Unit = runBlocking {
+        withTorchAsFlashQuirk_shouldOpenTorch(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
+    }
+
+    private suspend fun withTorchAsFlashQuirk_shouldOpenTorch(imageCaptureMode: Int) {
+        // Arrange.
+        capturePipeline = CapturePipelineImpl(
+            cameraProperties = fakeCameraProperties,
+            requestListener = comboRequestListener,
+            threads = fakeUseCaseThreads,
+            torchControl = torchControl,
+            useCaseGraphConfig = fakeUseCaseGraphConfig,
+            useCaseCameraState = fakeUseCaseCameraState,
+            useTorchAsFlash = UseTorchAsFlashImpl,
+        )
+
+        val requestList = mutableListOf<Request>()
+        fakeCameraGraphSession.requestHandler = { requests ->
+            requestList.addAll(requests)
+        }
+
+        // Act.
+        capturePipeline.submitStillCaptures(
+            requests = listOf(singleRequest),
+            captureMode = imageCaptureMode,
+            flashMode = ImageCapture.FLASH_MODE_ON,
+            flashType = ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
+        )
+
+        // Assert 1, torch should be turned on.
+        assertThat(
+            fakeRequestControl.setTorchSemaphore.tryAcquire(5, TimeUnit.SECONDS)
+        ).isTrue()
+        assertThat(fakeRequestControl.torchUpdateEventList.size).isEqualTo(1)
+        assertThat(fakeRequestControl.torchUpdateEventList.removeFirst()).isTrue()
+
+        // Complete the capture request.
+        assertThat(fakeCameraGraphSession.submitSemaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+        requestList.complete()
+
+        // Assert 2, torch should be turned off.
+        assertThat(
+            fakeRequestControl.setTorchSemaphore.tryAcquire(5, TimeUnit.SECONDS)
+        ).isTrue()
+        assertThat(fakeRequestControl.torchUpdateEventList.size).isEqualTo(1)
+        assertThat(fakeRequestControl.torchUpdateEventList.removeFirst()).isFalse()
+    }
+
+    @Test
     fun miniLatency_withTemplateRecord_shouldOpenTorch(): Unit = runBlocking {
         withTemplateRecord_shouldOpenTorch(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
     }
 
     @Test
-    @Ignore // b/216788724
     fun maxQuality_withTemplateRecord_shouldOpenTorch(): Unit = runBlocking {
         withTemplateRecord_shouldOpenTorch(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
     }
@@ -381,13 +432,11 @@
     }
 
     @Test
-    @Ignore // b/216788724
     fun miniLatency_withFlashTypeTorch_shouldOpenTorch(): Unit = runBlocking {
         withFlashTypeTorch_shouldOpenTorch(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
     }
 
     @Test
-    @Ignore // b/216788724
     fun maxQuality_withFlashTypeTorch_shouldOpenTorch(): Unit = runBlocking {
         withFlashTypeTorch_shouldOpenTorch(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
     }
@@ -427,7 +476,6 @@
     }
 
     @Test
-    @Ignore // b/216788724
     fun miniLatency_flashRequired_withFlashTypeTorch_shouldLock3A(): Unit = runBlocking {
         withFlashTypeTorch_shouldLock3A(
             ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
@@ -436,7 +484,6 @@
     }
 
     @Test
-    @Ignore // b/216788724
     fun maxQuality_withFlashTypeTorch_shouldLock3A(): Unit = runBlocking {
         withFlashTypeTorch_shouldLock3A(
             ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY,
@@ -464,7 +511,7 @@
             fakeCameraGraphSession.lock3ASemaphore.tryAcquire(5, TimeUnit.SECONDS)
         ).isTrue()
         assertThat(
-            fakeCameraGraphSession.unlock3ASemaphore.tryAcquire(2, TimeUnit.SECONDS)
+            fakeCameraGraphSession.unlock3ASemaphore.tryAcquire(1, TimeUnit.SECONDS)
         ).isFalse()
 
         // Complete the capture request.
@@ -478,7 +525,6 @@
     }
 
     @Test
-    @Ignore // b/216788724
     fun miniLatency_withFlashTypeTorch_shouldNotLock3A(): Unit = runBlocking {
         // Act.
         capturePipeline.submitStillCaptures(
@@ -486,16 +532,15 @@
             captureMode = ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
             flashMode = ImageCapture.FLASH_MODE_OFF,
             flashType = ImageCapture.FLASH_TYPE_USE_TORCH_AS_FLASH,
-        )
+        ).awaitAllWithTimeout()
 
         // Assert, there is no invocation on lock3A().
         assertThat(
-            fakeCameraGraphSession.lock3ASemaphore.tryAcquire(2, TimeUnit.SECONDS)
+            fakeCameraGraphSession.lock3ASemaphore.tryAcquire(1, TimeUnit.SECONDS)
         ).isFalse()
     }
 
     @Test
-    @Ignore // b/216788724
     fun withFlashTypeTorch_torchAlreadyOn_skipTurnOnTorch(): Unit = runBlocking {
         // Arrange.
         // Ensure the torch is already turned on before capturing.
@@ -510,16 +555,15 @@
             captureMode = ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
             flashMode = ImageCapture.FLASH_MODE_ON,
             flashType = ImageCapture.FLASH_TYPE_USE_TORCH_AS_FLASH,
-        )
+        ).awaitAllWithTimeout()
 
         // Assert, there is no invocation on setTorch().
         assertThat(
-            fakeRequestControl.setTorchSemaphore.tryAcquire(2, TimeUnit.SECONDS)
+            fakeRequestControl.setTorchSemaphore.tryAcquire(1, TimeUnit.SECONDS)
         ).isFalse()
     }
 
     @Test
-    @Ignore // b/216788724
     fun miniLatency_shouldNotAePreCapture(): Unit = runBlocking {
         // Act.
         capturePipeline.submitStillCaptures(
@@ -527,16 +571,15 @@
             captureMode = ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
             flashMode = ImageCapture.FLASH_MODE_OFF,
             flashType = ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
-        )
+        ).awaitAllWithTimeout()
 
         // Assert, there is only 1 single capture request.
         assertThat(
-            fakeCameraGraphSession.lock3AForCaptureSemaphore.tryAcquire(2, TimeUnit.SECONDS)
+            fakeCameraGraphSession.lock3AForCaptureSemaphore.tryAcquire(1, TimeUnit.SECONDS)
         ).isFalse()
     }
 
     @Test
-    @Ignore // b/216788724
     fun captureFailure_taskShouldFailure(): Unit = runBlocking {
         // Arrange.
         fakeCameraGraphSession.requestHandler = { requests ->
@@ -561,19 +604,13 @@
         )
 
         // Assert.
-        val exception = Assert.assertThrows(ExecutionException::class.java) {
-            Futures.allAsList(resultDeferredList.map {
-                it.asListenableFuture()
-            }).get(2, TimeUnit.SECONDS)
+        val exception = Assert.assertThrows(ImageCaptureException::class.java) {
+            runBlocking { resultDeferredList.awaitAllWithTimeout() }
         }
-        Assert.assertTrue(exception.cause is ImageCaptureException)
-        assertThat((exception.cause as ImageCaptureException).imageCaptureError).isEqualTo(
-            ImageCapture.ERROR_CAPTURE_FAILED
-        )
+        assertThat(exception.imageCaptureError).isEqualTo(ImageCapture.ERROR_CAPTURE_FAILED)
     }
 
     @Test
-    @Ignore // b/216788724
     fun captureCancel_taskShouldFailureWithCAMERA_CLOSED(): Unit = runBlocking {
         // Arrange.
         fakeCameraGraphSession.requestHandler = { requests ->
@@ -671,7 +708,7 @@
 
         // Assert, repeating should not be stopped when quirk not enabled.
         assertThat(
-            fakeCameraGraphSession.stopRepeatingSemaphore.tryAcquire(5, TimeUnit.SECONDS)
+            fakeCameraGraphSession.stopRepeatingSemaphore.tryAcquire(2, TimeUnit.SECONDS)
         ).isFalse()
 
         assertThat(
@@ -685,10 +722,62 @@
         submittedRequestList.complete()
 
         assertThat(
-            fakeCameraGraphSession.repeatingRequestSemaphore.tryAcquire(5, TimeUnit.SECONDS)
+            fakeCameraGraphSession.repeatingRequestSemaphore.tryAcquire(2, TimeUnit.SECONDS)
         ).isFalse()
     }
 
+    @Test
+    fun torchAsFlash_torchCorrection_shouldTurnsTorchOffOn(): Unit = runBlocking {
+        torchStateCorrectionTest(ImageCapture.FLASH_TYPE_USE_TORCH_AS_FLASH)
+    }
+
+    @Test
+    fun defaultCapture_torchCorrection_shouldTurnsTorchOffOn(): Unit = runBlocking {
+        torchStateCorrectionTest(ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH)
+    }
+
+    private suspend fun torchStateCorrectionTest(flashType: Int) {
+        // Arrange.
+        torchControl.setTorchAsync(torch = true).join()
+        verifyTorchState(true)
+
+        val requestList = mutableListOf<Request>()
+        fakeCameraGraphSession.requestHandler = { requests ->
+            requestList.addAll(requests)
+        }
+        val capturePipelineTorchCorrection = CapturePipelineTorchCorrection(
+            capturePipelineImpl = capturePipeline,
+            threads = fakeUseCaseThreads,
+            torchControl = torchControl,
+        )
+
+        // Act.
+        capturePipelineTorchCorrection.submitStillCaptures(
+            requests = listOf(singleRequest),
+            captureMode = ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
+            flashMode = ImageCapture.FLASH_MODE_ON,
+            flashType = flashType,
+        )
+
+        assertThat(fakeCameraGraphSession.submitSemaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+        assertThat(fakeRequestControl.setTorchSemaphore.tryAcquire(1, TimeUnit.SECONDS)).isFalse()
+        // Complete the capture request.
+        requestList.complete()
+
+        // Assert, the Torch should be turned off, and then turned on.
+        verifyTorchState(false)
+        verifyTorchState(true)
+        // No more invocation to set Torch mode.
+        assertThat(fakeRequestControl.torchUpdateEventList.size).isEqualTo(0)
+    }
+
+    private fun verifyTorchState(state: Boolean) {
+        assertThat(
+            fakeRequestControl.setTorchSemaphore.tryAcquire(5, TimeUnit.SECONDS)
+        ).isTrue()
+        assertThat(fakeRequestControl.torchUpdateEventList.removeFirst() == state).isTrue()
+    }
+
     // TODO(wenhungteng@): Porting overrideAeModeForStillCapture_quirkAbsent_notOverride,
     //  overrideAeModeForStillCapture_aePrecaptureStarted_override,
     //  overrideAeModeForStillCapture_aePrecaptureFinish_notOverride,
@@ -728,4 +817,10 @@
             }, 0, period, TimeUnit.MILLISECONDS)
         }, initialDelay, TimeUnit.MILLISECONDS)
     }
+
+    private suspend fun <T> Collection<Deferred<T>>.awaitAllWithTimeout(
+        timeMillis: Long = TimeUnit.SECONDS.toMillis(5)
+    ) = checkNotNull(withTimeoutOrNull(timeMillis) {
+        awaitAll()
+    }) { "Cannot complete the Deferred within $timeMillis" }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManagerTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManagerTest.kt
index 1d12d9b..02a742b 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManagerTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManagerTest.kt
@@ -198,7 +198,7 @@
         addDisplay(480, 640)
 
         // Act & Assert
-        assertEquals(Size(640, 480), displayInfoManager.previewSize)
+        assertEquals(Size(640, 480), displayInfoManager.getPreviewSize())
     }
 
     @Test
@@ -207,7 +207,7 @@
         addDisplay(2000, 3000)
 
         // Act & Assert
-        assertEquals(Size(1920, 1080), displayInfoManager.previewSize)
+        assertEquals(Size(1920, 1080), displayInfoManager.getPreviewSize())
     }
 
     @Test
@@ -216,10 +216,11 @@
         addDisplay(480, 640)
 
         // Act
-        displayInfoManager.previewSize
+        displayInfoManager.getPreviewSize()
         addDisplay(2000, 3000)
+        displayInfoManager.refresh()
 
         // Assert
-        assertEquals(Size(1920, 1080), displayInfoManager.previewSize)
+        assertEquals(Size(1920, 1080), displayInfoManager.getPreviewSize())
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfoTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfoTest.kt
index 8da683c..1ffe0a0 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfoTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfoTest.kt
@@ -27,6 +27,7 @@
 import androidx.camera.camera2.pipe.integration.testing.FakeCameraProperties
 import androidx.camera.camera2.pipe.testing.FakeCameraMetadata
 import androidx.camera.core.CameraState
+import androidx.camera.core.DynamicRange
 import androidx.camera.core.ExposureState
 import androidx.camera.core.ZoomState
 import androidx.camera.core.impl.CameraCaptureCallback
@@ -173,6 +174,10 @@
             override fun getSupportedHighResolutions(format: Int): MutableList<Size> {
                 throw NotImplementedError("Not used in testing")
             }
+
+            override fun getSupportedDynamicRanges(): MutableSet<DynamicRange> {
+                throw NotImplementedError("Not used in testing")
+            }
         }
         Camera2CameraInfo.from(wrongCameraInfo)
     }
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraphSession.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraphSession.kt
index a69eb5a2..66174ca 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraphSession.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraphSession.kt
@@ -96,7 +96,13 @@
         throw NotImplementedError("Not used in testing")
     }
 
-    override suspend fun unlock3A(ae: Boolean?, af: Boolean?, awb: Boolean?): Deferred<Result3A> {
+    override suspend fun unlock3A(
+        ae: Boolean?,
+        af: Boolean?,
+        awb: Boolean?,
+        frameLimit: Int,
+        timeLimitNs: Long
+    ): Deferred<Result3A> {
         throw NotImplementedError("Not used in testing")
     }
 
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraInfoAdapterCreator.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraInfoAdapterCreator.kt
index b687bf0..f0b520c 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraInfoAdapterCreator.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraInfoAdapterCreator.kt
@@ -100,7 +100,7 @@
         }
         val fakeStreamConfigurationMap = StreamConfigurationMapCompat(
             streamConfigurationMap,
-            OutputSizesCorrector(cameraProperties.metadata)
+            OutputSizesCorrector(cameraProperties.metadata, streamConfigurationMap)
         )
         val fakeCameraQuirks = CameraQuirks(
             cameraProperties.metadata,
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 215e70a..64d3363 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
@@ -19,6 +19,8 @@
 import android.hardware.camera2.CaptureRequest
 import android.hardware.camera2.params.MeteringRectangle
 import androidx.camera.camera2.pipe.AeMode
+import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.Lock3ABehavior
 import androidx.camera.camera2.pipe.Request
 import androidx.camera.camera2.pipe.RequestTemplate
 import androidx.camera.camera2.pipe.Result3A
@@ -31,9 +33,11 @@
 import androidx.camera.core.UseCase
 import androidx.camera.core.impl.CaptureConfig
 import androidx.camera.core.impl.Config
+import java.util.concurrent.TimeUnit
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.Deferred
 import kotlinx.coroutines.Job
+import kotlinx.coroutines.withTimeoutOrNull
 
 class FakeUseCaseCameraComponentBuilder : UseCaseCameraComponent.Builder {
     private var config: UseCaseCameraConfig = UseCaseCameraConfig(emptyList(), CameraStateAdapter())
@@ -101,11 +105,27 @@
         aeRegions: List<MeteringRectangle>?,
         afRegions: List<MeteringRectangle>?,
         awbRegions: List<MeteringRectangle>?,
-        afTriggerStartAeMode: AeMode?
+        aeLockBehavior: Lock3ABehavior?,
+        afLockBehavior: Lock3ABehavior?,
+        awbLockBehavior: Lock3ABehavior?,
+        afTriggerStartAeMode: AeMode?,
+        timeLimitNs: Long,
     ): Deferred<Result3A> {
         focusMeteringCalls.add(
-            FocusMeteringParams(aeRegions, afRegions, awbRegions, afTriggerStartAeMode)
+            FocusMeteringParams(
+                aeRegions, afRegions, awbRegions,
+                aeLockBehavior, afLockBehavior, awbLockBehavior,
+                afTriggerStartAeMode,
+                timeLimitNs
+            )
         )
+        withTimeoutOrNull(TimeUnit.MILLISECONDS.convert(timeLimitNs, TimeUnit.NANOSECONDS)) {
+            focusMeteringResult.await()
+        }.let { result3A ->
+            if (result3A == null) {
+                focusMeteringResult.complete(Result3A(status = Result3A.Status.TIME_LIMIT_REACHED))
+            }
+        }
         return focusMeteringResult
     }
 
@@ -127,7 +147,11 @@
         val aeRegions: List<MeteringRectangle>? = null,
         val afRegions: List<MeteringRectangle>? = null,
         val awbRegions: List<MeteringRectangle>? = null,
-        val afTriggerStartAeMode: AeMode? = null
+        val aeLockBehavior: Lock3ABehavior? = null,
+        val afLockBehavior: Lock3ABehavior? = null,
+        val awbLockBehavior: Lock3ABehavior? = null,
+        val afTriggerStartAeMode: AeMode? = null,
+        val timeLimitNs: Long = CameraGraph.Constants3A.DEFAULT_TIME_LIMIT_NS,
     )
 
     data class RequestParameters(
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 6f6f637..a8b6841 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
@@ -22,6 +22,7 @@
 import androidx.camera.camera2.pipe.CameraController
 import androidx.camera.camera2.pipe.CameraGraph
 import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.CameraStatusMonitor
 import androidx.camera.camera2.pipe.GraphState.GraphStateError
 import androidx.camera.camera2.pipe.StreamGraph
 import androidx.camera.camera2.pipe.StreamId
@@ -163,7 +164,7 @@
         }
     }
 
-    override fun tryRestart() {
+    override fun tryRestart(cameraStatus: CameraStatusMonitor.CameraStatus) {
         synchronized(lock) {
             check(!closed) {
                 "Attempted to invoke restart after close."
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 29dee61..2271494 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
@@ -53,10 +53,10 @@
 
     /**
      * Restart the current session. This should basically perform stop() then start(). However, the
-     * implementation should handle its internal states correctly, and only restart when the
-     * conditions are appropriate.
+     * implementation should handle its internal states correctly, and only restart under the right
+     * [CameraStatusMonitor.CameraStatus] and [ControllerState].
      */
-    fun tryRestart()
+    fun tryRestart(cameraStatus: CameraStatusMonitor.CameraStatus)
 
     /**
      * Close this instance. [start] and [stop] should not be invoked, and any additional calls will
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraError.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraError.kt
index aac3027..b191c82 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraError.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraError.kt
@@ -23,6 +23,7 @@
 import android.hardware.camera2.CameraAccessException.CAMERA_IN_USE
 import android.hardware.camera2.CameraAccessException.MAX_CAMERAS_IN_USE
 import android.hardware.camera2.CameraDevice.StateCallback
+import android.os.Build
 import androidx.annotation.RequiresApi
 
 @JvmInline
@@ -87,13 +88,30 @@
         /** This indicates that we received SecurityException while opening the camera. */
         val ERROR_SECURITY_EXCEPTION = CameraError(8)
 
+        /**
+         * This indicates we've encountered an error while configuring our camera graph, such as
+         * creating, finalizing capture sessions, and creating, submitting capture requests.
+         */
+        val ERROR_GRAPH_CONFIG = CameraError(9)
+
+        /**
+         * The camera cannot be opened because Do Not Disturb (DND) mode is on. This is actually
+         * a quirk for legacy devices on P, where we encounter RuntimeExceptions while opening the
+         * camera when it tries to enable shutter sound.
+         */
+        val ERROR_DO_NOT_DISTURB_ENABLED = CameraError(10)
+
         internal fun from(throwable: Throwable) =
             when (throwable) {
                 is CameraAccessException -> from(throwable)
                 is IllegalArgumentException -> ERROR_ILLEGAL_ARGUMENT_EXCEPTION
                 is SecurityException -> ERROR_SECURITY_EXCEPTION
                 else -> {
-                    throw IllegalArgumentException("Unexpected throwable: $throwable")
+                    if (Build.VERSION.SDK_INT == 28 && isDoNotDisturbException(throwable)) {
+                        ERROR_DO_NOT_DISTURB_ENABLED
+                    } else {
+                        throw IllegalArgumentException("Unexpected throwable: $throwable")
+                    }
                 }
             }
 
@@ -124,5 +142,28 @@
                     )
                 }
             }
+
+        /**
+         * The full stack trace of the Do Not Disturb exception on API level 28 is as follows:
+         *
+         * java.lang.RuntimeException: Camera is being used after Camera.release() was called
+         *  at android.hardware.Camera._enableShutterSound(Native Method)
+         *  at android.hardware.Camera.updateAppOpsPlayAudio(Camera.java:1770)
+         *  at android.hardware.Camera.initAppOps(Camera.java:582)
+         *  at android.hardware.Camera.<init>(Camera.java:575)
+         *  at android.hardware.Camera.getEmptyParameters(Camera.java:2130)
+         *  at android.hardware.camera2.legacy.LegacyMetadataMapper.createCharacteristics
+         *  (LegacyMetadataMapper.java:151)
+         *  at android.hardware.camera2.CameraManager.getCameraCharacteristics
+         *  (CameraManager.java:274)
+         *
+         * This function checks whether the method name of the top element is "_enableShutterSound".
+         */
+        private fun isDoNotDisturbException(throwable: Throwable): Boolean {
+            if (throwable !is RuntimeException) return false
+            val topMethodName =
+                throwable.stackTrace.let { if (it.isNotEmpty()) it[0].methodName else null }
+            return topMethodName == "_enableShutterSound"
+        }
     }
 }
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 7522c6a..37d48e4e 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
@@ -297,13 +297,20 @@
          * that component, i.e. if it was locked earlier it will stay locked and if it was already
          * unlocked, it will stay unlocked.
          *
+         * @param frameLimit the maximum number of frames to wait before we give up waiting for this
+         *   operation to complete.
+         * @param timeLimitNs the maximum time limit in ms we wait before we give up waiting for
+         *   this operation to complete.
+         *
          * @return [Result3A], which will contain the latest frame number at which the auto-focus,
          *   auto-exposure, auto-white balance were unlocked as per the method arguments.
          */
         suspend fun unlock3A(
             ae: Boolean? = null,
             af: Boolean? = null,
-            awb: Boolean? = null
+            awb: Boolean? = null,
+            frameLimit: Int = DEFAULT_FRAME_LIMIT,
+            timeLimitNs: Long = DEFAULT_TIME_LIMIT_NS
         ): Deferred<Result3A>
 
         /**
@@ -373,5 +380,8 @@
      * will retry opening the camera (and creating a capture session).
      */
     class GraphStateError(val cameraError: CameraError, val willAttemptRetry: Boolean) :
-        GraphState()
-}
+        GraphState() {
+        override fun toString(): String =
+            super.toString() + "(cameraError = $cameraError, willAttemptRetry = $willAttemptRetry)"
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CaptureSequenceProcessor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CaptureSequenceProcessor.kt
index 9c6cc07..facca9b 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CaptureSequenceProcessor.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CaptureSequenceProcessor.kt
@@ -51,7 +51,7 @@
     ): TCaptureSequence?
 
     /** Issue a previously created [CaptureSequence] to the active camera instance. */
-    fun submit(captureSequence: TCaptureSequence): Int
+    fun submit(captureSequence: TCaptureSequence): Int?
 
     /**
      * Opportunistically abort any ongoing captures by the camera. This may or may not complete
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 5ddaf29..0a4c5a5 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
@@ -24,6 +24,7 @@
 import androidx.camera.camera2.pipe.CameraError
 import androidx.camera.camera2.pipe.CameraGraph
 import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.CameraStatusMonitor.CameraStatus
 import androidx.camera.camera2.pipe.CameraSurfaceManager
 import androidx.camera.camera2.pipe.StreamId
 import androidx.camera.camera2.pipe.config.Camera2ControllerScope
@@ -66,6 +67,9 @@
     @GuardedBy("lock")
     private var controllerState: ControllerState = ControllerState.STOPPED
 
+    @GuardedBy("lock")
+    private var lastCameraError: CameraError? = null
+
     private var currentCamera: VirtualCamera? = null
     private var currentSession: CaptureSessionState? = null
     private var currentSurfaceMap: Map<StreamId, Surface>? = null
@@ -80,6 +84,7 @@
             Log.warn { "Ignoring start(): Camera2CameraController is already started" }
             return
         }
+        lastCameraError = null
         val camera = virtualCameraManager.open(
             config.camera,
             config.flags.allowMultipleActiveCameras,
@@ -135,11 +140,30 @@
         }
     }
 
-    override fun tryRestart(): Unit = synchronized(lock) {
-        if (controllerState != ControllerState.DISCONNECTED) {
-            Log.debug { "Ignoring restart(): CameraController is $controllerState" }
+    override fun tryRestart(cameraStatus: CameraStatus): Unit = synchronized(lock) {
+        var shouldRestart = false
+        when (controllerState) {
+            ControllerState.DISCONNECTED ->
+                if (cameraStatus is CameraStatus.CameraAvailable ||
+                    cameraStatus is CameraStatus.CameraPrioritiesChanged
+                ) {
+                    shouldRestart = true
+                }
+
+            ControllerState.ERROR ->
+                if (cameraStatus is CameraStatus.CameraAvailable &&
+                    lastCameraError == CameraError.ERROR_CAMERA_DEVICE
+                ) {
+                    shouldRestart = true
+                }
+        }
+        if (!shouldRestart) {
+            Log.debug {
+                "Ignoring tryRestart(): state = $controllerState, cameraStatus = $cameraStatus"
+            }
             return
         }
+        Log.debug { "Restarting Camera2CameraController" }
         stop()
         start()
     }
@@ -222,6 +246,7 @@
                         "unrecoverable error: ${cameraState.cameraErrorCode}"
                 }
             }
+            lastCameraError = cameraState.cameraErrorCode
         } else {
             controllerState = ControllerState.STOPPED
         }
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 b6be643..73418c4 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
@@ -124,10 +124,8 @@
             // null
             // if the CameraDevice has been closed or disconnected. If this fails, indicate that the
             // request was not submitted.
-            val requestBuilder: CaptureRequest.Builder
-            try {
-                requestBuilder = session.device.createCaptureRequest(requestTemplate)
-            } catch (exception: ObjectUnavailableException) {
+            val requestBuilder = session.device.createCaptureRequest(requestTemplate)
+            if (requestBuilder == null) {
                 Log.info { "  Failed to create a CaptureRequest.Builder from $requestTemplate!" }
                 return null
             }
@@ -247,7 +245,7 @@
         )
     }
 
-    override fun submit(captureSequence: Camera2CaptureSequence): Int {
+    override fun submit(captureSequence: Camera2CaptureSequence): Int? {
         val captureCallback = captureSequence as CameraCaptureSession.CaptureCallback
         // TODO: Update these calls to use executors on newer versions of the OS
         return if (captureSequence.captureRequestList.size == 1 &&
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2ErrorProcessor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2ErrorProcessor.kt
new file mode 100644
index 0000000..bfae20a
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2ErrorProcessor.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.compat
+
+import androidx.annotation.GuardedBy
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.CameraError
+import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.GraphState
+import androidx.camera.camera2.pipe.internal.CameraErrorListener
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * A class responsible for reporting camera errors with a particular [CameraId]. When
+ * [androidx.camera.camera2.pipe.compat.VirtualCameraManager] processes a camera open request, it
+ * should update CameraErrorProcessor with the [VirtualCameraState] that came with the open request.
+ */
+@Singleton
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+class Camera2ErrorProcessor @Inject constructor() : CameraErrorListener {
+    private val lock = Any()
+
+    @GuardedBy("lock")
+    private val virtualCameraStateMap = mutableMapOf<CameraId, VirtualCameraState>()
+
+    override fun onCameraError(
+        cameraId: CameraId,
+        cameraError: CameraError,
+        willAttemptRetry: Boolean
+    ) {
+        val virtualCameraState = synchronized(lock) {
+            virtualCameraStateMap[cameraId]
+        } ?: return
+        virtualCameraState.graphListener.onGraphError(
+            GraphState.GraphStateError(
+                cameraError,
+                willAttemptRetry
+            )
+        )
+    }
+
+    /**
+     * Sets the current active [VirtualCameraState] to report the camera error to. Any attempt to
+     * acquire an open camera creates a [VirtualCameraState], and it's important to keep
+     * Camera2ErrorProcessor updated with the latest [VirtualCameraState].
+     */
+    internal fun setActiveVirtualCamera(
+        cameraId: CameraId,
+        virtualCameraState: VirtualCameraState
+    ) = synchronized(lock) {
+        virtualCameraStateMap[cameraId] = virtualCameraState
+    }
+}
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CameraDeviceWrapper.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CameraDeviceWrapper.kt
index a4461dd..27722eb 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CameraDeviceWrapper.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CameraDeviceWrapper.kt
@@ -37,6 +37,7 @@
 import androidx.camera.camera2.pipe.core.SystemTimeSource
 import androidx.camera.camera2.pipe.core.Timestamps
 import androidx.camera.camera2.pipe.core.Timestamps.formatMs
+import androidx.camera.camera2.pipe.internal.CameraErrorListener
 import androidx.camera.camera2.pipe.writeParameter
 import kotlin.reflect.KClass
 import kotlinx.atomicfu.atomic
@@ -52,64 +53,56 @@
     val cameraId: CameraId
 
     /** @see CameraDevice.createCaptureRequest */
-    @Throws(ObjectUnavailableException::class)
-    fun createCaptureRequest(template: RequestTemplate): CaptureRequest.Builder
+    fun createCaptureRequest(template: RequestTemplate): CaptureRequest.Builder?
 
     /** @see CameraDevice.createReprocessCaptureRequest */
     @RequiresApi(Build.VERSION_CODES.M)
-    @Throws(ObjectUnavailableException::class)
-    fun createReprocessCaptureRequest(inputResult: TotalCaptureResult): CaptureRequest.Builder
+    fun createReprocessCaptureRequest(inputResult: TotalCaptureResult): CaptureRequest.Builder?
 
     /** @see CameraDevice.createCaptureSession */
-    @Throws(ObjectUnavailableException::class)
     fun createCaptureSession(
         outputs: List<Surface>,
         stateCallback: CameraCaptureSessionWrapper.StateCallback,
         handler: Handler?
-    )
+    ): Boolean
 
     /** @see CameraDevice.createReprocessableCaptureSession */
     @RequiresApi(Build.VERSION_CODES.M)
-    @Throws(ObjectUnavailableException::class)
     fun createReprocessableCaptureSession(
         input: InputConfiguration,
         outputs: List<Surface>,
         stateCallback: CameraCaptureSessionWrapper.StateCallback,
         handler: Handler?
-    )
+    ): Boolean
 
     /** @see CameraDevice.createConstrainedHighSpeedCaptureSession */
     @RequiresApi(Build.VERSION_CODES.M)
-    @Throws(ObjectUnavailableException::class)
     fun createConstrainedHighSpeedCaptureSession(
         outputs: List<Surface>,
         stateCallback: CameraCaptureSessionWrapper.StateCallback,
         handler: Handler?
-    )
+    ): Boolean
 
     /** @see CameraDevice.createCaptureSessionByOutputConfigurations */
     @RequiresApi(Build.VERSION_CODES.N)
-    @Throws(ObjectUnavailableException::class)
     fun createCaptureSessionByOutputConfigurations(
         outputConfigurations: List<OutputConfigurationWrapper>,
         stateCallback: CameraCaptureSessionWrapper.StateCallback,
         handler: Handler?
-    )
+    ): Boolean
 
     /** @see CameraDevice.createReprocessableCaptureSessionByConfigurations */
     @RequiresApi(Build.VERSION_CODES.N)
-    @Throws(ObjectUnavailableException::class)
     fun createReprocessableCaptureSessionByConfigurations(
         inputConfig: InputConfigData,
         outputs: List<OutputConfigurationWrapper>,
         stateCallback: CameraCaptureSessionWrapper.StateCallback,
         handler: Handler?
-    )
+    ): Boolean
 
     /** @see CameraDevice.createCaptureSession */
     @RequiresApi(Build.VERSION_CODES.P)
-    @Throws(ObjectUnavailableException::class)
-    fun createCaptureSession(config: SessionConfigData)
+    fun createCaptureSession(config: SessionConfigData): Boolean
 
     /** Invoked when the [CameraDevice] has been closed */
     fun onDeviceClosed()
@@ -138,6 +131,7 @@
     private val cameraMetadata: CameraMetadata,
     private val cameraDevice: CameraDevice,
     override val cameraId: CameraId,
+    private val cameraErrorListener: CameraErrorListener,
     private val interopSessionStateCallback: CameraCaptureSession.StateCallback? = null
 ) : CameraDeviceWrapper {
     private val _lastStateCallback = atomic<CameraCaptureSessionWrapper.StateCallback?>(null)
@@ -146,7 +140,7 @@
         outputs: List<Surface>,
         stateCallback: CameraCaptureSessionWrapper.StateCallback,
         handler: Handler?
-    ) = rethrowCamera2Exceptions {
+    ): Boolean = catchAndReportCameraExceptions(cameraId, cameraErrorListener) {
         val previousStateCallback = _lastStateCallback.value
         check(_lastStateCallback.compareAndSet(previousStateCallback, stateCallback))
 
@@ -156,11 +150,15 @@
         cameraDevice.createCaptureSession(
             outputs,
             AndroidCaptureSessionStateCallback(
-                this, stateCallback, previousStateCallback, interopSessionStateCallback
+                this,
+                stateCallback,
+                previousStateCallback,
+                cameraErrorListener,
+                interopSessionStateCallback
             ),
             handler
         )
-    }
+    } != null
 
     @RequiresApi(23)
     override fun createReprocessableCaptureSession(
@@ -168,7 +166,7 @@
         outputs: List<Surface>,
         stateCallback: CameraCaptureSessionWrapper.StateCallback,
         handler: Handler?
-    ) = rethrowCamera2Exceptions {
+    ): Boolean = catchAndReportCameraExceptions(cameraId, cameraErrorListener) {
         val previousStateCallback = _lastStateCallback.value
         check(_lastStateCallback.compareAndSet(previousStateCallback, stateCallback))
 
@@ -179,18 +177,22 @@
             input,
             outputs,
             AndroidCaptureSessionStateCallback(
-                this, stateCallback, previousStateCallback, interopSessionStateCallback
+                this,
+                stateCallback,
+                previousStateCallback,
+                cameraErrorListener,
+                interopSessionStateCallback
             ),
             handler
         )
-    }
+    } != null
 
     @RequiresApi(23)
     override fun createConstrainedHighSpeedCaptureSession(
         outputs: List<Surface>,
         stateCallback: CameraCaptureSessionWrapper.StateCallback,
         handler: Handler?
-    ) = rethrowCamera2Exceptions {
+    ): Boolean = catchAndReportCameraExceptions(cameraId, cameraErrorListener) {
         val previousStateCallback = _lastStateCallback.value
         check(_lastStateCallback.compareAndSet(previousStateCallback, stateCallback))
 
@@ -200,18 +202,22 @@
             cameraDevice,
             outputs,
             AndroidCaptureSessionStateCallback(
-                this, stateCallback, previousStateCallback, interopSessionStateCallback
+                this,
+                stateCallback,
+                previousStateCallback,
+                cameraErrorListener,
+                interopSessionStateCallback
             ),
             handler
         )
-    }
+    } != null
 
     @RequiresApi(24)
     override fun createCaptureSessionByOutputConfigurations(
         outputConfigurations: List<OutputConfigurationWrapper>,
         stateCallback: CameraCaptureSessionWrapper.StateCallback,
         handler: Handler?
-    ) = rethrowCamera2Exceptions {
+    ): Boolean = catchAndReportCameraExceptions(cameraId, cameraErrorListener) {
         val previousStateCallback = _lastStateCallback.value
         check(_lastStateCallback.compareAndSet(previousStateCallback, stateCallback))
 
@@ -221,11 +227,15 @@
             cameraDevice,
             outputConfigurations.map { it.unwrapAs(OutputConfiguration::class) },
             AndroidCaptureSessionStateCallback(
-                this, stateCallback, previousStateCallback, interopSessionStateCallback
+                this,
+                stateCallback,
+                previousStateCallback,
+                cameraErrorListener,
+                interopSessionStateCallback
             ),
             handler
         )
-    }
+    } != null
 
     @RequiresApi(24)
     override fun createReprocessableCaptureSessionByConfigurations(
@@ -233,7 +243,7 @@
         outputs: List<OutputConfigurationWrapper>,
         stateCallback: CameraCaptureSessionWrapper.StateCallback,
         handler: Handler?
-    ) = rethrowCamera2Exceptions {
+    ): Boolean = catchAndReportCameraExceptions(cameraId, cameraErrorListener) {
         val previousStateCallback = _lastStateCallback.value
         check(_lastStateCallback.compareAndSet(previousStateCallback, stateCallback))
 
@@ -246,66 +256,75 @@
             ),
             outputs.map { it.unwrapAs(OutputConfiguration::class) },
             AndroidCaptureSessionStateCallback(
-                this, stateCallback, previousStateCallback, interopSessionStateCallback
+                this,
+                stateCallback,
+                previousStateCallback,
+                cameraErrorListener,
+                interopSessionStateCallback
             ),
             handler
         )
-    }
+    } != null
 
     @RequiresApi(28)
-    override fun createCaptureSession(config: SessionConfigData) = rethrowCamera2Exceptions {
-        val stateCallback = config.stateCallback
-        val previousStateCallback = _lastStateCallback.value
-        check(_lastStateCallback.compareAndSet(previousStateCallback, stateCallback))
+    override fun createCaptureSession(config: SessionConfigData): Boolean =
+        catchAndReportCameraExceptions(cameraId, cameraErrorListener) {
+            val stateCallback = config.stateCallback
+            val previousStateCallback = _lastStateCallback.value
+            check(_lastStateCallback.compareAndSet(previousStateCallback, stateCallback))
 
-        val sessionConfig =
-            Api28Compat.newSessionConfiguration(
-                config.sessionType,
-                config.outputConfigurations.map { it.unwrapAs(OutputConfiguration::class) },
-                config.executor,
-                AndroidCaptureSessionStateCallback(
-                    this, stateCallback, previousStateCallback, interopSessionStateCallback
+            val sessionConfig =
+                Api28Compat.newSessionConfiguration(
+                    config.sessionType,
+                    config.outputConfigurations.map { it.unwrapAs(OutputConfiguration::class) },
+                    config.executor,
+                    AndroidCaptureSessionStateCallback(
+                        this,
+                        stateCallback,
+                        previousStateCallback,
+                        cameraErrorListener,
+                        interopSessionStateCallback
+                    )
                 )
-            )
 
-        if (config.inputConfiguration != null) {
-            Api28Compat.setInputConfiguration(
-                sessionConfig,
-                Api23Compat.newInputConfiguration(
-                    config.inputConfiguration.width,
-                    config.inputConfiguration.height,
-                    config.inputConfiguration.format
+            if (config.inputConfiguration != null) {
+                Api28Compat.setInputConfiguration(
+                    sessionConfig,
+                    Api23Compat.newInputConfiguration(
+                        config.inputConfiguration.width,
+                        config.inputConfiguration.height,
+                        config.inputConfiguration.format
+                    )
                 )
-            )
-        }
-
-        val requestBuilder = cameraDevice.createCaptureRequest(config.sessionTemplateId)
-
-        // This compares and sets ONLY the session keys for this camera. Setting parameters that are
-        // not listed in availableSessionKeys can cause an unusual amount of extra latency.
-        val sessionKeyNames = cameraMetadata.sessionKeys.map { it.name }
-
-        // Iterate template parameters and CHECK BY NAME, as there have been cases where equality
-        // checks did not pass.
-        for ((key, value) in config.sessionParameters) {
-            if (key !is CaptureRequest.Key<*>) continue
-            if (sessionKeyNames.contains(key.name)) {
-                requestBuilder.writeParameter(key, value)
             }
-        }
-        Api28Compat.setSessionParameters(sessionConfig, requestBuilder.build())
-        Api28Compat.createCaptureSession(cameraDevice, sessionConfig)
-    }
 
-    override fun createCaptureRequest(template: RequestTemplate): CaptureRequest.Builder =
-        rethrowCamera2Exceptions {
+            val requestBuilder = cameraDevice.createCaptureRequest(config.sessionTemplateId)
+
+            // This compares and sets ONLY the session keys for this camera. Setting parameters that are
+            // not listed in availableSessionKeys can cause an unusual amount of extra latency.
+            val sessionKeyNames = cameraMetadata.sessionKeys.map { it.name }
+
+            // Iterate template parameters and CHECK BY NAME, as there have been cases where equality
+            // checks did not pass.
+            for ((key, value) in config.sessionParameters) {
+                if (key !is CaptureRequest.Key<*>) continue
+                if (sessionKeyNames.contains(key.name)) {
+                    requestBuilder.writeParameter(key, value)
+                }
+            }
+            Api28Compat.setSessionParameters(sessionConfig, requestBuilder.build())
+            Api28Compat.createCaptureSession(cameraDevice, sessionConfig)
+        } != null
+
+    override fun createCaptureRequest(template: RequestTemplate): CaptureRequest.Builder? =
+        catchAndReportCameraExceptions(cameraId, cameraErrorListener) {
             cameraDevice.createCaptureRequest(template.value)
         }
 
     @RequiresApi(23)
     override fun createReprocessCaptureRequest(
         inputResult: TotalCaptureResult
-    ): CaptureRequest.Builder = rethrowCamera2Exceptions {
+    ): CaptureRequest.Builder? = catchAndReportCameraExceptions(cameraId, cameraErrorListener) {
         Api23Compat.createReprocessCaptureRequest(cameraDevice, inputResult)
     }
 
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactory.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactory.kt
index 23fbb4a..254be23 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactory.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactory.kt
@@ -97,11 +97,10 @@
         surfaces: Map<StreamId, Surface>,
         captureSessionState: CaptureSessionState
     ): Map<StreamId, OutputConfigurationWrapper> {
-        try {
-            cameraDevice.createCaptureSession(
+        if (!cameraDevice.createCaptureSession(
                 surfaces.map { it.value }, captureSessionState, threads.camera2Handler
             )
-        } catch (e: Throwable) {
+        ) {
             Log.warn {
                 "Failed to create capture session from $cameraDevice for $captureSessionState!"
             }
@@ -122,9 +121,8 @@
         captureSessionState: CaptureSessionState
     ): Map<StreamId, OutputConfigurationWrapper> {
         if (graphConfig.input != null) {
-            try {
-                val outputConfig = graphConfig.input.stream.outputs.single()
-                cameraDevice.createReprocessableCaptureSession(
+            val outputConfig = graphConfig.input.stream.outputs.single()
+            if (!cameraDevice.createReprocessableCaptureSession(
                     InputConfiguration(
                         outputConfig.size.width,
                         outputConfig.size.height,
@@ -134,7 +132,7 @@
                     captureSessionState,
                     threads.camera2Handler
                 )
-            } catch (e: Throwable) {
+            ) {
                 Log.warn {
                     "Failed to create reprocessable captures session from $cameraDevice for" +
                         " $captureSessionState!"
@@ -142,11 +140,10 @@
                 captureSessionState.disconnect()
             }
         } else {
-            try {
-                cameraDevice.createCaptureSession(
+            if (!cameraDevice.createCaptureSession(
                     surfaces.map { it.value }, captureSessionState, threads.camera2Handler
                 )
-            } catch (e: Throwable) {
+            ) {
                 Log.warn {
                     "Failed to create captures session from $cameraDevice for $captureSessionState!"
                 }
@@ -165,11 +162,10 @@
         surfaces: Map<StreamId, Surface>,
         captureSessionState: CaptureSessionState
     ): Map<StreamId, OutputConfigurationWrapper> {
-        try {
-            cameraDevice.createConstrainedHighSpeedCaptureSession(
+        if (!cameraDevice.createConstrainedHighSpeedCaptureSession(
                 surfaces.map { it.value }, captureSessionState, threads.camera2Handler
             )
-        } catch (e: Throwable) {
+        ) {
             Log.warn {
                 "Failed to create ConstrainedHighSpeedCaptureSession " +
                     "from $cameraDevice for $captureSessionState!"
@@ -206,25 +202,24 @@
             return emptyMap()
         }
 
-        try {
-            if (graphConfig.input == null) {
-                cameraDevice.createCaptureSessionByOutputConfigurations(
-                    outputs.all, captureSessionState, threads.camera2Handler
-                )
-            } else {
-                val outputConfig = graphConfig.input.stream.outputs.single()
-                cameraDevice.createReprocessableCaptureSessionByConfigurations(
-                    InputConfigData(
-                        outputConfig.size.width,
-                        outputConfig.size.height,
-                        outputConfig.format.value
-                    ),
-                    outputs.all,
-                    captureSessionState,
-                    threads.camera2Handler
-                )
-            }
-        } catch (e: Throwable) {
+        val result = if (graphConfig.input == null) {
+            cameraDevice.createCaptureSessionByOutputConfigurations(
+                outputs.all, captureSessionState, threads.camera2Handler
+            )
+        } else {
+            val outputConfig = graphConfig.input.stream.outputs.single()
+            cameraDevice.createReprocessableCaptureSessionByConfigurations(
+                InputConfigData(
+                    outputConfig.size.width,
+                    outputConfig.size.height,
+                    outputConfig.format.value
+                ),
+                outputs.all,
+                captureSessionState,
+                threads.camera2Handler
+            )
+        }
+        if (!result) {
             Log.warn {
                 "Failed to create capture session from $cameraDevice for $captureSessionState!"
             }
@@ -286,9 +281,7 @@
                 graphConfig.sessionParameters
             )
 
-        try {
-            cameraDevice.createCaptureSession(sessionConfig)
-        } catch (e: Throwable) {
+        if (!cameraDevice.createCaptureSession(sessionConfig)) {
             Log.warn {
                 "Failed to create capture session from $cameraDevice for $captureSessionState!"
             }
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 2698852..1b2637b 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
@@ -18,8 +18,6 @@
 
 package androidx.camera.camera2.pipe.compat
 
-import android.hardware.camera2.CameraAccessException
-import android.hardware.camera2.CameraAccessException.CAMERA_ERROR
 import android.hardware.camera2.CameraCaptureSession
 import android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession
 import android.hardware.camera2.CaptureRequest
@@ -31,6 +29,7 @@
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.UnsafeWrapper
 import androidx.camera.camera2.pipe.core.Log
+import androidx.camera.camera2.pipe.internal.CameraErrorListener
 import kotlin.reflect.KClass
 import kotlinx.atomicfu.atomic
 
@@ -62,8 +61,7 @@
     val inputSurface: Surface?
 
     /** @see [CameraCaptureSession.abortCaptures]. */
-    @Throws(ObjectUnavailableException::class)
-    fun abortCaptures()
+    fun abortCaptures(): Boolean
 
     /**
      * @param request The settings for this exposure
@@ -73,12 +71,11 @@
      * @return An unique capture sequence id.
      * @see [CameraCaptureSession.capture].
      */
-    @Throws(ObjectUnavailableException::class)
     fun capture(
         request: CaptureRequest,
         listener: CameraCaptureSession.CaptureCallback,
         handler: Handler?
-    ): Int
+    ): Int?
 
     /**
      * @param requests A list of CaptureRequest(s) for this sequence of exposures
@@ -89,12 +86,11 @@
      * @return An unique capture sequence id.
      * @see [CameraCaptureSession.captureBurst].
      */
-    @Throws(ObjectUnavailableException::class)
     fun captureBurst(
         requests: List<CaptureRequest>,
         listener: CameraCaptureSession.CaptureCallback,
         handler: Handler?
-    ): Int
+    ): Int?
 
     /**
      * @param requests A list of settings to cycle through indefinitely.
@@ -105,12 +101,11 @@
      * @return An unique capture sequence ID.
      * @see [CameraCaptureSession.setRepeatingBurst]
      */
-    @Throws(ObjectUnavailableException::class)
     fun setRepeatingBurst(
         requests: List<CaptureRequest>,
         listener: CameraCaptureSession.CaptureCallback,
         handler: Handler?
-    ): Int
+    ): Int?
 
     /**
      * @param request The request to repeat indefinitely.
@@ -120,20 +115,17 @@
      * @return An unique capture sequence ID.
      * @see [CameraCaptureSession.setRepeatingRequest].
      */
-    @Throws(ObjectUnavailableException::class)
     fun setRepeatingRequest(
         request: CaptureRequest,
         listener: CameraCaptureSession.CaptureCallback,
         handler: Handler?
-    ): Int
+    ): Int?
 
     /** @see [CameraCaptureSession.stopRepeating]. */
-    @Throws(ObjectUnavailableException::class)
-    fun stopRepeating()
+    fun stopRepeating(): Boolean
 
     /** Forwards to CameraCaptureSession#finalizeOutputConfigurations */
-    @Throws(ObjectUnavailableException::class)
-    fun finalizeOutputConfigurations(outputConfigs: List<OutputConfigurationWrapper>)
+    fun finalizeOutputConfigurations(outputConfigs: List<OutputConfigurationWrapper>): Boolean
 
     /** @see CameraCaptureSession.StateCallback */
     interface StateCallback {
@@ -183,13 +175,14 @@
     private val device: CameraDeviceWrapper,
     private val stateCallback: CameraCaptureSessionWrapper.StateCallback,
     lastStateCallback: CameraCaptureSessionWrapper.StateCallback?,
+    private val cameraErrorListener: CameraErrorListener,
     private val interopSessionStateCallback: CameraCaptureSession.StateCallback? = null
 ) : CameraCaptureSession.StateCallback() {
     private val _lastStateCallback = atomic(lastStateCallback)
     private val captureSession = atomic<CameraCaptureSessionWrapper?>(null)
 
     override fun onConfigured(session: CameraCaptureSession) {
-        stateCallback.onConfigured(getWrapped(session))
+        stateCallback.onConfigured(getWrapped(session, cameraErrorListener))
 
         // b/249258992 - This is a workaround to ensure previous CameraCaptureSession.StateCallback
         //   instances receive some kind of "finalization" signal if onClosed is not fired by the
@@ -199,48 +192,54 @@
     }
 
     override fun onConfigureFailed(session: CameraCaptureSession) {
-        stateCallback.onConfigureFailed(getWrapped(session))
+        stateCallback.onConfigureFailed(getWrapped(session, cameraErrorListener))
         finalizeSession()
         interopSessionStateCallback?.onConfigureFailed(session)
     }
 
     override fun onReady(session: CameraCaptureSession) {
-        stateCallback.onReady(getWrapped(session))
+        stateCallback.onReady(getWrapped(session, cameraErrorListener))
         interopSessionStateCallback?.onReady(session)
     }
 
     override fun onActive(session: CameraCaptureSession) {
-        stateCallback.onActive(getWrapped(session))
+        stateCallback.onActive(getWrapped(session, cameraErrorListener))
         interopSessionStateCallback?.onActive(session)
     }
 
     override fun onClosed(session: CameraCaptureSession) {
-        stateCallback.onClosed(getWrapped(session))
+        stateCallback.onClosed(getWrapped(session, cameraErrorListener))
         finalizeSession()
         interopSessionStateCallback?.onClosed(session)
     }
 
     override fun onCaptureQueueEmpty(session: CameraCaptureSession) {
-        stateCallback.onCaptureQueueEmpty(getWrapped(session))
+        stateCallback.onCaptureQueueEmpty(getWrapped(session, cameraErrorListener))
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
             Api26CompatImpl.onCaptureQueueEmpty(session, interopSessionStateCallback)
         }
     }
 
-    private fun getWrapped(session: CameraCaptureSession): CameraCaptureSessionWrapper {
+    private fun getWrapped(
+        session: CameraCaptureSession,
+        cameraErrorListener: CameraErrorListener,
+    ): CameraCaptureSessionWrapper {
         var local = captureSession.value
         if (local != null) {
             return local
         }
 
-        local = wrapSession(session)
+        local = wrapSession(session, cameraErrorListener)
         if (captureSession.compareAndSet(null, local)) {
             return local
         }
         return captureSession.value!!
     }
 
-    private fun wrapSession(session: CameraCaptureSession): CameraCaptureSessionWrapper {
+    private fun wrapSession(
+        session: CameraCaptureSession,
+        cameraErrorListener: CameraErrorListener,
+    ): CameraCaptureSessionWrapper {
         // Starting in Android P, it's possible for the standard "createCaptureSession" method to
         // return a CameraConstrainedHighSpeedCaptureSession depending on the configuration. If
         // this happens, several methods are not allowed, the behavior is different, and interacting
@@ -248,9 +247,9 @@
         return if (Build.VERSION.SDK_INT >= 23 &&
             session is CameraConstrainedHighSpeedCaptureSession
         ) {
-            AndroidCameraConstrainedHighSpeedCaptureSession(device, session)
+            AndroidCameraConstrainedHighSpeedCaptureSession(device, session, cameraErrorListener)
         } else {
-            AndroidCameraCaptureSession(device, session)
+            AndroidCameraCaptureSession(device, session, cameraErrorListener)
         }
     }
 
@@ -281,53 +280,54 @@
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 internal open class AndroidCameraCaptureSession(
     override val device: CameraDeviceWrapper,
-    private val cameraCaptureSession: CameraCaptureSession
+    private val cameraCaptureSession: CameraCaptureSession,
+    private val cameraErrorListener: CameraErrorListener,
 ) : CameraCaptureSessionWrapper {
-    override fun abortCaptures() {
-        rethrowCamera2Exceptions { cameraCaptureSession.abortCaptures() }
-    }
+    override fun abortCaptures(): Boolean =
+        catchAndReportCameraExceptions(device.cameraId, cameraErrorListener) {
+            cameraCaptureSession.abortCaptures()
+        } != null
 
     override fun capture(
         request: CaptureRequest,
         listener: CameraCaptureSession.CaptureCallback,
         handler: Handler?
-    ): Int {
-        return rethrowCamera2Exceptions { cameraCaptureSession.capture(request, listener, handler) }
+    ): Int? = catchAndReportCameraExceptions(device.cameraId, cameraErrorListener) {
+        cameraCaptureSession.capture(
+            request,
+            listener,
+            handler
+        )
     }
 
     override fun captureBurst(
         requests: List<CaptureRequest>,
         listener: CameraCaptureSession.CaptureCallback,
         handler: Handler?
-    ): Int {
-        return rethrowCamera2Exceptions {
-            cameraCaptureSession.captureBurst(requests, listener, handler)
-        }
+    ): Int? = catchAndReportCameraExceptions(device.cameraId, cameraErrorListener) {
+        cameraCaptureSession.captureBurst(requests, listener, handler)
     }
 
     override fun setRepeatingBurst(
         requests: List<CaptureRequest>,
         listener: CameraCaptureSession.CaptureCallback,
         handler: Handler?
-    ): Int {
-        return rethrowCamera2Exceptions {
-            cameraCaptureSession.setRepeatingBurst(requests, listener, handler)
-        }
+    ): Int? = catchAndReportCameraExceptions(device.cameraId, cameraErrorListener) {
+        cameraCaptureSession.setRepeatingBurst(requests, listener, handler)
     }
 
     override fun setRepeatingRequest(
         request: CaptureRequest,
         listener: CameraCaptureSession.CaptureCallback,
         handler: Handler?
-    ): Int {
-        return rethrowCamera2Exceptions {
-            cameraCaptureSession.setRepeatingRequest(request, listener, handler)
-        }
+    ): Int? = catchAndReportCameraExceptions(device.cameraId, cameraErrorListener) {
+        cameraCaptureSession.setRepeatingRequest(request, listener, handler)
     }
 
-    override fun stopRepeating() {
-        rethrowCamera2Exceptions { cameraCaptureSession.stopRepeating() }
-    }
+    override fun stopRepeating(): Boolean =
+        catchAndReportCameraExceptions(device.cameraId, cameraErrorListener) {
+            cameraCaptureSession.stopRepeating()
+        } != null
 
     override val isReprocessable: Boolean
         get() {
@@ -349,30 +349,20 @@
         }
 
     @RequiresApi(26)
-    override fun finalizeOutputConfigurations(outputConfigs: List<OutputConfigurationWrapper>) {
+    override fun finalizeOutputConfigurations(
+        outputConfigs: List<OutputConfigurationWrapper>
+    ): Boolean {
         check(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
             "Attempting to call finalizeOutputConfigurations before O is not supported and may " +
                 "lead to to unexpected behavior if an application is expects this call to " +
                 "succeed."
         }
 
-        var exceptionToThrow: Throwable? = null
-        try {
+        return catchAndReportCameraExceptions(device.cameraId, cameraErrorListener) {
             Api26Compat.finalizeOutputConfigurations(
                 cameraCaptureSession,
                 outputConfigs.map { it.unwrapAs(OutputConfiguration::class) })
-        } catch (e: CameraAccessException) {
-            // TODO(b/266734799): There is a possibility that we might finalize output
-            //  configurations on a camera that's been disconnected. In such cases, we'll receive
-            //  CameraAccessException.CAMERA_ERROR. Catch it for now, until we properly report and
-            //  handle capture session errors.
-            if (e.reason != CAMERA_ERROR) {
-                exceptionToThrow = e
-            }
-        } catch (e: Throwable) {
-            exceptionToThrow = e
-        }
-        exceptionToThrow?.let { rethrowCamera2Exceptions { throw it } }
+        } != null
     }
 
     @Suppress("UNCHECKED_CAST")
@@ -395,8 +385,10 @@
 internal class AndroidCameraConstrainedHighSpeedCaptureSession
 internal constructor(
     device: CameraDeviceWrapper,
-    private val session: CameraConstrainedHighSpeedCaptureSession
-) : AndroidCameraCaptureSession(device, session), CameraConstrainedHighSpeedCaptureSessionWrapper {
+    private val session: CameraConstrainedHighSpeedCaptureSession,
+    private val cameraErrorListener: CameraErrorListener,
+) : AndroidCameraCaptureSession(device, session, cameraErrorListener),
+    CameraConstrainedHighSpeedCaptureSessionWrapper {
     @Throws(ObjectUnavailableException::class)
     override fun createHighSpeedRequestList(request: CaptureRequest): List<CaptureRequest> {
         return try {
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Exceptions.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Exceptions.kt
index 85cdd1c..3cf8857 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Exceptions.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Exceptions.kt
@@ -20,7 +20,9 @@
 
 import android.hardware.camera2.CameraAccessException
 import androidx.annotation.RequiresApi
-import androidx.camera.camera2.pipe.core.Log
+import androidx.camera.camera2.pipe.CameraError
+import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.internal.CameraErrorListener
 
 /**
  * Thrown when an operation cannot be executed because underlying object is closed or in an unusable
@@ -30,7 +32,11 @@
 
 /** Catch specific exceptions that are not normally thrown, log them, then rethrow. */
 @Throws(ObjectUnavailableException::class)
-internal inline fun <T> rethrowCamera2Exceptions(crossinline block: () -> T): T {
+internal inline fun <T> catchAndReportCameraExceptions(
+    cameraId: CameraId,
+    cameraErrorListener: CameraErrorListener,
+    crossinline block: () -> T
+): T? {
     // Camera2 has, at different points in time, thrown a large number of checked and/or
     // unchecked exceptions under different circumstances that are not listed in the
     // documentation. This method catches and recasts these exceptions into a common exception
@@ -49,17 +55,22 @@
     try {
         return block()
     } catch (e: Exception) {
-        throw when (e) {
+        when (e) {
             is IllegalArgumentException,
             is IllegalStateException,
             is CameraAccessException,
             is SecurityException,
-            is UnsupportedOperationException -> {
-                Log.debug(e) { "Rethrowing ${e::class.java.simpleName} from Camera2" }
-                ObjectUnavailableException(e)
+            is UnsupportedOperationException,
+            is NullPointerException -> {
+                cameraErrorListener.onCameraError(
+                    cameraId,
+                    CameraError.ERROR_GRAPH_CONFIG,
+                    willAttemptRetry = false
+                )
+                return null
             }
 
-            else -> e
+            else -> throw e
         }
     }
 }
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 151d702..f8cfa64 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
@@ -24,6 +24,7 @@
 import androidx.camera.camera2.pipe.CameraController
 import androidx.camera.camera2.pipe.CameraGraph
 import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.CameraStatusMonitor
 import androidx.camera.camera2.pipe.CaptureSequence
 import androidx.camera.camera2.pipe.CaptureSequenceProcessor
 import androidx.camera.camera2.pipe.Metadata
@@ -65,7 +66,7 @@
         }
     }
 
-    override fun tryRestart() {
+    override fun tryRestart(cameraStatus: CameraStatusMonitor.CameraStatus) {
         // This is intentionally made a no-op for now as CameraPipe external doesn't support
         // camera status monitoring and camera controller restart.
     }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt
index f4c438a..6f19a06 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt
@@ -25,7 +25,6 @@
 import androidx.camera.camera2.pipe.CameraError
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.CameraPipe
-import androidx.camera.camera2.pipe.GraphState.GraphStateError
 import androidx.camera.camera2.pipe.core.Debug
 import androidx.camera.camera2.pipe.core.DurationNs
 import androidx.camera.camera2.pipe.core.Log
@@ -35,7 +34,7 @@
 import androidx.camera.camera2.pipe.core.TimestampNs
 import androidx.camera.camera2.pipe.core.Timestamps
 import androidx.camera.camera2.pipe.core.Timestamps.formatMs
-import androidx.camera.camera2.pipe.graph.GraphListener
+import androidx.camera.camera2.pipe.internal.CameraErrorListener
 import javax.inject.Inject
 import javax.inject.Provider
 import kotlin.coroutines.resume
@@ -151,6 +150,7 @@
 constructor(
     private val cameraOpener: CameraOpener,
     private val camera2MetadataProvider: Camera2MetadataProvider,
+    private val cameraErrorListener: CameraErrorListener,
     private val timeSource: TimeSource,
     private val cameraInteropConfig: CameraPipe.CameraInteropConfig?
 ) {
@@ -167,6 +167,7 @@
                 attempts,
                 requestTimestamp,
                 timeSource,
+                cameraErrorListener,
                 cameraInteropConfig?.cameraDeviceStateCallback,
                 cameraInteropConfig?.cameraSessionStateCallback
             )
@@ -206,13 +207,13 @@
 @Inject
 constructor(
     private val cameraStateOpener: CameraStateOpener,
+    private val cameraErrorListener: CameraErrorListener,
     private val cameraAvailabilityMonitor: CameraAvailabilityMonitor,
     private val timeSource: TimeSource,
     private val devicePolicyManager: DevicePolicyManagerWrapper
 ) {
     internal suspend fun openCameraWithRetry(
         cameraId: CameraId,
-        graphListener: GraphListener
     ): OpenCameraResult {
         val requestTimestamp = Timestamps.now(timeSource)
         var attempts = 0
@@ -220,7 +221,12 @@
         while (true) {
             attempts++
 
-            val result = cameraStateOpener.tryOpenCamera(cameraId, attempts, requestTimestamp)
+            val result =
+                cameraStateOpener.tryOpenCamera(
+                    cameraId,
+                    attempts,
+                    requestTimestamp,
+                )
             with(result) {
                 if (cameraState != null) {
                     return result
@@ -250,7 +256,7 @@
                 // 1 open call to happen silently without generating an error, and notify about each
                 // error after that point.
                 if (!willRetry || attempts > 1) {
-                    graphListener.onGraphError(GraphStateError(errorCode, willRetry))
+                    cameraErrorListener.onCameraError(cameraId, errorCode, willRetry)
                 }
                 if (!willRetry) {
                     Log.error {
@@ -327,6 +333,15 @@
                 CameraError.ERROR_CAMERA_DISCONNECTED -> true
                 CameraError.ERROR_ILLEGAL_ARGUMENT_EXCEPTION -> true
                 CameraError.ERROR_SECURITY_EXCEPTION -> attempts <= 1
+                CameraError.ERROR_DO_NOT_DISTURB_ENABLED ->
+                    // The error indicates that a RuntimeException was encountered when opening the
+                    // camera while Do Not Disturb mode is on. This can happen on legacy devices on
+                    // API level 28 [1]. Retries will always fail and should not be attempted.
+                    //
+                    // [1] b/149413835 - Crash during CameraX initialization when Do Not Disturb
+                    //                   is on.
+                    false
+
                 else -> {
                     Log.error { "Unexpected CameraError: $this" }
                     false
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCamera.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCamera.kt
index b5e7a6d..04cfadb 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCamera.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCamera.kt
@@ -35,6 +35,8 @@
 import androidx.camera.camera2.pipe.core.Timestamps
 import androidx.camera.camera2.pipe.core.Timestamps.formatMs
 import androidx.camera.camera2.pipe.core.Token
+import androidx.camera.camera2.pipe.graph.GraphListener
+import androidx.camera.camera2.pipe.internal.CameraErrorListener
 import kotlin.coroutines.EmptyCoroutineContext
 import kotlinx.atomicfu.atomic
 import kotlinx.coroutines.Job
@@ -113,7 +115,10 @@
 
 internal val virtualCameraDebugIds = atomic(0)
 
-internal class VirtualCameraState(val cameraId: CameraId) : VirtualCamera {
+internal class VirtualCameraState(
+    val cameraId: CameraId,
+    val graphListener: GraphListener
+) : VirtualCamera {
     private val debugId = virtualCameraDebugIds.incrementAndGet()
     private val lock = Any()
 
@@ -208,6 +213,7 @@
     private val attemptNumber: Int,
     private val attemptTimestampNanos: TimestampNs,
     private val timeSource: TimeSource,
+    private val cameraErrorListener: CameraErrorListener,
     private val interopDeviceStateCallback: CameraDevice.StateCallback? = null,
     private val interopSessionStateCallback: CameraCaptureSession.StateCallback? = null
 ) : CameraDevice.StateCallback() {
@@ -293,7 +299,13 @@
         // while if it synchronously calls createCaptureSession.
         _state.value =
             CameraStateOpen(
-                AndroidCameraDevice(metadata, cameraDevice, cameraId, interopSessionStateCallback)
+                AndroidCameraDevice(
+                    metadata,
+                    cameraDevice,
+                    cameraId,
+                    cameraErrorListener,
+                    interopSessionStateCallback
+                )
             )
 
         // Check to see if we received close() or other events in the meantime.
@@ -393,7 +405,17 @@
                 null
             }
         if (closeInfo != null) {
+            // If the camera error is an Exception during open, the error should be reported by
+            // RetryingCameraStateOpener.
+            if (closeInfo.errorCode != null && closeInfo.reason != ClosedReason.CAMERA2_EXCEPTION) {
+                cameraErrorListener.onCameraError(
+                    cameraId,
+                    closeInfo.errorCode,
+                    willAttemptRetry = false
+                )
+            }
             _state.value = CameraStateClosing(closeInfo.errorCode)
+
             cameraDeviceWrapper.closeWithTrace()
             cameraDevice.closeWithTrace()
             _state.value = computeClosedState(closeInfo)
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCameraManager.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCameraManager.kt
index fe27798..06e4e27 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCameraManager.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCameraManager.kt
@@ -60,6 +60,7 @@
 constructor(
     private val permissions: Permissions,
     private val retryingCameraStateOpener: RetryingCameraStateOpener,
+    private val camera2ErrorProcessor: Camera2ErrorProcessor,
     private val threads: Threads
 ) {
     // TODO: Consider rewriting this as a MutableSharedFlow
@@ -75,7 +76,7 @@
         share: Boolean = false,
         graphListener: GraphListener
     ): VirtualCamera {
-        val result = VirtualCameraState(cameraId)
+        val result = VirtualCameraState(cameraId, graphListener)
         offerChecked(RequestOpen(result, share, graphListener))
         return result
     }
@@ -178,10 +179,11 @@
             }
 
             // Stage 3: Open or select an active camera device.
+            camera2ErrorProcessor.setActiveVirtualCamera(cameraIdToOpen, request.virtualCamera)
             var realCamera = activeCameras.firstOrNull { it.cameraId == cameraIdToOpen }
             if (realCamera == null) {
                 val openResult =
-                    openCameraWithRetry(cameraIdToOpen, request.graphListener, scope = this)
+                    openCameraWithRetry(cameraIdToOpen, scope = this)
                 if (openResult.activeCamera != null) {
                     realCamera = openResult.activeCamera
                     activeCameras.add(realCamera)
@@ -200,7 +202,6 @@
 
     private suspend fun openCameraWithRetry(
         cameraId: CameraId,
-        graphListener: GraphListener,
         scope: CoroutineScope
     ): OpenVirtualCameraResult {
         // TODO: Figure out how 1-time permissions work, and see if they can be reset without
@@ -208,14 +209,15 @@
         check(permissions.hasCameraPermission) { "Missing camera permissions!" }
 
         Log.debug { "Opening $cameraId with retries..." }
-        val result = retryingCameraStateOpener.openCameraWithRetry(cameraId, graphListener)
+        val result = retryingCameraStateOpener.openCameraWithRetry(cameraId)
         if (result.cameraState == null) {
             return OpenVirtualCameraResult(lastCameraError = result.errorCode)
         }
         return OpenVirtualCameraResult(
             activeCamera =
             ActiveCamera(
-                androidCameraState = result.cameraState, scope = scope, channel = requestQueue
+                androidCameraState = result.cameraState,
+                scope = scope, channel = requestQueue
             )
         )
     }
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 57d38e5..710b044 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
@@ -29,6 +29,7 @@
 import androidx.camera.camera2.pipe.compat.Camera2CameraStatusMonitor
 import androidx.camera.camera2.pipe.compat.Camera2CaptureSequenceProcessorFactory
 import androidx.camera.camera2.pipe.compat.Camera2CaptureSessionsModule
+import androidx.camera.camera2.pipe.compat.Camera2ErrorProcessor
 import androidx.camera.camera2.pipe.compat.Camera2MetadataCache
 import androidx.camera.camera2.pipe.compat.Camera2MetadataProvider
 import androidx.camera.camera2.pipe.compat.CameraAvailabilityMonitor
@@ -37,6 +38,7 @@
 import androidx.camera.camera2.pipe.core.Threads
 import androidx.camera.camera2.pipe.graph.GraphListener
 import androidx.camera.camera2.pipe.graph.StreamGraphImpl
+import androidx.camera.camera2.pipe.internal.CameraErrorListener
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
@@ -61,6 +63,11 @@
     ): Camera2MetadataProvider
 
     @Binds
+    abstract fun bindCameraErrorListener(
+        camera2ErrorProcessor: Camera2ErrorProcessor
+    ): CameraErrorListener
+
+    @Binds
     abstract fun bindCameraAvailabilityMonitor(
         camera2CameraAvailabilityMonitor: Camera2CameraAvailabilityMonitor
     ): CameraAvailabilityMonitor
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/CameraGraphSessionImpl.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/CameraGraphSessionImpl.kt
index e720845..22556f7 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/CameraGraphSessionImpl.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/CameraGraphSessionImpl.kt
@@ -143,9 +143,15 @@
         )
     }
 
-    override suspend fun unlock3A(ae: Boolean?, af: Boolean?, awb: Boolean?): Deferred<Result3A> {
+    override suspend fun unlock3A(
+        ae: Boolean?,
+        af: Boolean?,
+        awb: Boolean?,
+        frameLimit: Int,
+        timeLimitNs: Long
+    ): Deferred<Result3A> {
         check(!closed.value) { "Cannot call unlock3A on $this after close." }
-        return controller3A.unlock3A(ae, af, awb)
+        return controller3A.unlock3A(ae, af, awb, frameLimit, timeLimitNs)
     }
 
     override suspend fun lock3AForCapture(frameLimit: Int, timeLimitNs: Long): Deferred<Result3A> {
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/Controller3A.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/Controller3A.kt
index c7a5f1a..1def65c 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/Controller3A.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/Controller3A.kt
@@ -318,7 +318,9 @@
     suspend fun unlock3A(
         ae: Boolean? = null,
         af: Boolean? = null,
-        awb: Boolean? = null
+        awb: Boolean? = null,
+        frameLimit: Int = DEFAULT_FRAME_LIMIT,
+        timeLimitNs: Long? = DEFAULT_TIME_LIMIT_NS
     ): Deferred<Result3A> {
         var afSanitized = af
         if (!metadata.supportsAutoFocusTrigger) {
@@ -340,7 +342,7 @@
         // As needed unlock ae, awb and wait for ae, af and awb to converge.
         val unlocked3AExitConditions =
             createUnLocked3AExitConditions(ae == true, afSanitized == true, awb == true)
-        val listener = Result3AStateListenerImpl(unlocked3AExitConditions)
+        val listener = Result3AStateListenerImpl(unlocked3AExitConditions, frameLimit, timeLimitNs)
         graphListener3A.addListener(listener)
 
         // Update the 3A state of the camera graph and invalidate the repeating request with the
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 053e5cb..355643f 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
@@ -179,6 +179,7 @@
     }
 
     override fun onGraphModified(requestProcessor: GraphRequestProcessor) {
+        debug { "$this onGraphModified" }
         synchronized(lock) {
             if (closed) {
                 return
@@ -191,6 +192,7 @@
     }
 
     override fun onGraphError(graphStateError: GraphStateError) {
+        debug { "$this onGraphError($graphStateError)" }
         _graphState.update { graphState ->
             if (graphState is GraphStateStopping || graphState is GraphStateStopped) {
                 GraphStateStopped
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphRequestProcessor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphRequestProcessor.kt
index bde6051..81d3845 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphRequestProcessor.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphRequestProcessor.kt
@@ -179,7 +179,7 @@
                         Log.warn { "Did not submit $captureSequence, $this was closed!" }
                         return false
                     }
-                    val sequenceNumber = captureSequenceProcessor.submit(captureSequence)
+                    val sequenceNumber = captureSequenceProcessor.submit(captureSequence) ?: -1
                     captureSequence.sequenceNumber = sequenceNumber
                     sequenceNumber
                 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/CameraErrorListener.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/CameraErrorListener.kt
new file mode 100644
index 0000000..19b6018
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/CameraErrorListener.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.internal
+
+import androidx.camera.camera2.pipe.CameraError
+import androidx.camera.camera2.pipe.CameraId
+
+/**
+ * Interface intended to be used to report camera errors. It will ensure only the current
+ * [androidx.camera.camera2.pipe.graph.GraphListener] is notified of the error.
+ */
+interface CameraErrorListener {
+    fun onCameraError(
+        cameraId: CameraId,
+        cameraError: CameraError,
+        willAttemptRetry: Boolean = false
+    )
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/GraphLifecycleManager.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/GraphLifecycleManager.kt
index 1429f76..ee876bf 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/GraphLifecycleManager.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/GraphLifecycleManager.kt
@@ -87,10 +87,14 @@
             cameraBackend.cameraStatus.collect { cameraStatus ->
                 when (cameraStatus) {
                     is CameraStatus.CameraPrioritiesChanged ->
-                        tryRestartCameraController(cameraBackend)
+                        tryRestartCameraController(cameraBackend, cameraStatus)
 
                     is CameraStatus.CameraAvailable ->
-                        tryRestartCameraController(cameraBackend, cameraStatus.cameraId)
+                        tryRestartCameraController(
+                            cameraBackend,
+                            cameraStatus,
+                            cameraStatus.cameraId
+                        )
                 }
             }
         }
@@ -111,6 +115,7 @@
 
     private fun tryRestartCameraController(
         cameraBackend: CameraBackend,
+        cameraStatus: CameraStatus,
         cameraId: CameraId? = null,
     ) = synchronized(lock) {
         // Restart the last CameraController being tracked in each backend. The last
@@ -122,6 +127,6 @@
             } else {
                 true
             }
-        }?.tryRestart()
+        }?.tryRestart(cameraStatus)
     }
 }
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/AndroidCaptureSessionStateCallbackTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/AndroidCaptureSessionStateCallbackTest.kt
index 4e1e57f..6b0573a 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/AndroidCaptureSessionStateCallbackTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/AndroidCaptureSessionStateCallbackTest.kt
@@ -18,6 +18,7 @@
 
 import android.hardware.camera2.CameraCaptureSession
 import android.os.Build
+import androidx.camera.camera2.pipe.internal.CameraErrorListener
 import androidx.camera.camera2.pipe.testing.RobolectricCameraPipeTestRunner
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -35,11 +36,13 @@
     private val stateCallback: CameraCaptureSessionWrapper.StateCallback = mock()
     private val previousStateCallback: CameraCaptureSessionWrapper.StateCallback = mock()
     private val captureSession: CameraCaptureSession = mock()
+    private val cameraErrorListener: CameraErrorListener = mock()
     private val androidStateCallback =
         AndroidCaptureSessionStateCallback(
             device = camera,
             stateCallback = stateCallback,
             lastStateCallback = previousStateCallback,
+            cameraErrorListener = cameraErrorListener,
         )
 
     @Test
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequenceProcessorTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequenceProcessorTest.kt
index a5b2acf..93349db 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequenceProcessorTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequenceProcessorTest.kt
@@ -202,6 +202,7 @@
 
         val result = captureSequenceProcessor.submit(sequence!!)
 
+        assertThat(result).isNotNull()
         assertThat(result).isGreaterThan(0)
         assertThat(fakeCaptureSessionWrapper.lastCapture).hasSize(1)
         assertThat(fakeCaptureSessionWrapper.lastRepeating).isNull()
@@ -231,6 +232,7 @@
         assertThat(captureSequence).isNotNull()
 
         val result = captureSequenceProcessor.submit(captureSequence!!)
+        assertThat(result).isNotNull()
         assertThat(result).isGreaterThan(0)
     }
 
@@ -320,6 +322,7 @@
 
         val result = captureSequenceProcessor.submit(sequence!!)
 
+        assertThat(result).isNotNull()
         assertThat(result).isGreaterThan(0)
         assertThat(fakeCaptureSessionWrapper.lastCapture).hasSize(1)
         assertThat(fakeCaptureSessionWrapper.lastRepeating).isNull()
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt
index c2bcc17..475d2fb 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt
@@ -39,6 +39,7 @@
 import androidx.camera.camera2.pipe.config.ThreadConfigModule
 import androidx.camera.camera2.pipe.core.SystemTimeSource
 import androidx.camera.camera2.pipe.graph.StreamGraphImpl
+import androidx.camera.camera2.pipe.internal.CameraErrorListener
 import androidx.camera.camera2.pipe.testing.FakeCaptureSequence
 import androidx.camera.camera2.pipe.testing.FakeCaptureSequenceProcessor
 import androidx.camera.camera2.pipe.testing.FakeGraphProcessor
@@ -55,6 +56,7 @@
 import org.junit.After
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
 import org.robolectric.Shadows
 import org.robolectric.annotation.Config
 
@@ -66,6 +68,7 @@
     private val mainLooper = Shadows.shadowOf(Looper.getMainLooper())
     private val cameraId = RobolectricCameras.create()
     private val testCamera = RobolectricCameras.open(cameraId)
+    private val cameraErrorListener: CameraErrorListener = mock()
 
     @After
     fun teardown() {
@@ -106,7 +109,10 @@
         val pendingOutputs =
             sessionFactory.create(
                 AndroidCameraDevice(
-                    testCamera.metadata, testCamera.cameraDevice, testCamera.cameraId
+                    testCamera.metadata,
+                    testCamera.cameraDevice,
+                    testCamera.cameraId,
+                    cameraErrorListener
                 ),
                 mapOf(stream1.id to surface),
                 captureSessionState =
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpenerTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpenerTest.kt
index 06d89fc..6e1716c 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpenerTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpenerTest.kt
@@ -24,16 +24,15 @@
 import androidx.camera.camera2.pipe.CameraError.Companion.ERROR_CAMERA_DISCONNECTED
 import androidx.camera.camera2.pipe.CameraError.Companion.ERROR_CAMERA_IN_USE
 import androidx.camera.camera2.pipe.CameraError.Companion.ERROR_CAMERA_LIMIT_EXCEEDED
+import androidx.camera.camera2.pipe.CameraError.Companion.ERROR_DO_NOT_DISTURB_ENABLED
 import androidx.camera.camera2.pipe.CameraError.Companion.ERROR_ILLEGAL_ARGUMENT_EXCEPTION
 import androidx.camera.camera2.pipe.CameraError.Companion.ERROR_SECURITY_EXCEPTION
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.CameraMetadata
-import androidx.camera.camera2.pipe.GraphState.GraphStateError
 import androidx.camera.camera2.pipe.core.DurationNs
 import androidx.camera.camera2.pipe.core.TimestampNs
 import androidx.camera.camera2.pipe.core.Timestamps
-import androidx.camera.camera2.pipe.graph.GraphListener
-import androidx.camera.camera2.pipe.graph.GraphRequestProcessor
+import androidx.camera.camera2.pipe.internal.CameraErrorListener
 import androidx.camera.camera2.pipe.testing.FakeCameraMetadata
 import androidx.camera.camera2.pipe.testing.FakeTimeSource
 import androidx.camera.camera2.pipe.testing.RobolectricCameraPipeTestRunner
@@ -78,8 +77,28 @@
 
     private val fakeTimeSource = FakeTimeSource()
 
+    // TODO(lnishan): Consider mocking this object when Mockito works well with value classes.
+    private val fakeCameraErrorListener =
+        object : CameraErrorListener {
+            var numberOfErrorCalls = 0
+
+            override fun onCameraError(
+                cameraId: CameraId,
+                cameraError: CameraError,
+                willAttemptRetry: Boolean
+            ) {
+                numberOfErrorCalls++
+            }
+        }
+
     private val cameraStateOpener =
-        CameraStateOpener(cameraOpener, camera2MetadataProvider, fakeTimeSource, null)
+        CameraStateOpener(
+            cameraOpener,
+            camera2MetadataProvider,
+            fakeCameraErrorListener,
+            fakeTimeSource,
+            cameraInteropConfig = null,
+        )
 
     private val cameraAvailabilityMonitor =
         object : CameraAvailabilityMonitor {
@@ -97,25 +116,13 @@
 
     private val retryingCameraStateOpener =
         RetryingCameraStateOpener(
-            cameraStateOpener, cameraAvailabilityMonitor, fakeTimeSource, fakeDevicePolicyManager
+            cameraStateOpener,
+            fakeCameraErrorListener,
+            cameraAvailabilityMonitor,
+            fakeTimeSource,
+            fakeDevicePolicyManager,
         )
 
-    // TODO(lnishan): Consider mocking this object when Mockito works well with value classes.
-    private val fakeGraphListener =
-        object : GraphListener {
-            var numberOfErrorCalls = 0
-
-            override fun onGraphStarted(requestProcessor: GraphRequestProcessor) {}
-
-            override fun onGraphStopped(requestProcessor: GraphRequestProcessor) {}
-
-            override fun onGraphModified(requestProcessor: GraphRequestProcessor) {}
-
-            override fun onGraphError(graphStateError: GraphStateError) {
-                numberOfErrorCalls++
-            }
-        }
-
     @Test
     fun testShouldRetryReturnsTrueWithinTimeout() {
         val firstAttemptTimestamp = TimestampNs(0L)
@@ -123,7 +130,7 @@
 
         assertThat(
             RetryingCameraStateOpener.shouldRetry(
-                CameraError.ERROR_CAMERA_IN_USE,
+                ERROR_CAMERA_IN_USE,
                 1,
                 firstAttemptTimestamp,
                 fakeTimeSource,
@@ -140,7 +147,7 @@
 
         assertThat(
             RetryingCameraStateOpener.shouldRetry(
-                CameraError.ERROR_CAMERA_IN_USE,
+                ERROR_CAMERA_IN_USE,
                 1,
                 firstAttemptTimestamp,
                 fakeTimeSource,
@@ -174,7 +181,7 @@
 
         assertThat(
             RetryingCameraStateOpener.shouldRetry(
-                CameraError.ERROR_CAMERA_IN_USE,
+                ERROR_CAMERA_IN_USE,
                 1,
                 firstAttemptTimestamp,
                 fakeTimeSource,
@@ -186,7 +193,7 @@
         // The second retry attempt should fail if SDK version < S, and succeed otherwise.
         val secondRetry =
             RetryingCameraStateOpener.shouldRetry(
-                CameraError.ERROR_CAMERA_IN_USE, 2, firstAttemptTimestamp, fakeTimeSource, false
+                ERROR_CAMERA_IN_USE, 2, firstAttemptTimestamp, fakeTimeSource, false
             )
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
             assertThat(secondRetry).isFalse()
@@ -202,7 +209,7 @@
 
         assertThat(
             RetryingCameraStateOpener.shouldRetry(
-                CameraError.ERROR_CAMERA_LIMIT_EXCEEDED,
+                ERROR_CAMERA_LIMIT_EXCEEDED,
                 1,
                 firstAttemptTimestamp,
                 fakeTimeSource,
@@ -214,7 +221,7 @@
         // Second attempt should succeed as well.
         assertThat(
             RetryingCameraStateOpener.shouldRetry(
-                CameraError.ERROR_CAMERA_LIMIT_EXCEEDED,
+                ERROR_CAMERA_LIMIT_EXCEEDED,
                 2,
                 firstAttemptTimestamp,
                 fakeTimeSource,
@@ -231,7 +238,7 @@
 
         assertThat(
             RetryingCameraStateOpener.shouldRetry(
-                CameraError.ERROR_CAMERA_DISABLED,
+                ERROR_CAMERA_DISABLED,
                 1,
                 firstAttemptTimestamp,
                 fakeTimeSource,
@@ -243,7 +250,7 @@
         // Second attempt should fail if camera is disabled.
         assertThat(
             RetryingCameraStateOpener.shouldRetry(
-                CameraError.ERROR_CAMERA_DISABLED,
+                ERROR_CAMERA_DISABLED,
                 2,
                 firstAttemptTimestamp,
                 fakeTimeSource,
@@ -260,7 +267,7 @@
 
         assertThat(
             RetryingCameraStateOpener.shouldRetry(
-                CameraError.ERROR_CAMERA_DISABLED,
+                ERROR_CAMERA_DISABLED,
                 1,
                 firstAttemptTimestamp,
                 fakeTimeSource,
@@ -272,7 +279,7 @@
         // Second attempt should success if camera is not disabled.
         assertThat(
             RetryingCameraStateOpener.shouldRetry(
-                CameraError.ERROR_CAMERA_DISABLED,
+                ERROR_CAMERA_DISABLED,
                 2,
                 firstAttemptTimestamp,
                 fakeTimeSource,
@@ -347,7 +354,7 @@
 
         assertThat(
             RetryingCameraStateOpener.shouldRetry(
-                CameraError.ERROR_CAMERA_DISCONNECTED,
+                ERROR_CAMERA_DISCONNECTED,
                 1,
                 firstAttemptTimestamp,
                 fakeTimeSource,
@@ -359,7 +366,7 @@
         // Second attempt should succeed as well.
         assertThat(
             RetryingCameraStateOpener.shouldRetry(
-                CameraError.ERROR_CAMERA_DISCONNECTED,
+                ERROR_CAMERA_DISCONNECTED,
                 2,
                 firstAttemptTimestamp,
                 fakeTimeSource,
@@ -376,7 +383,7 @@
 
         assertThat(
             RetryingCameraStateOpener.shouldRetry(
-                CameraError.ERROR_ILLEGAL_ARGUMENT_EXCEPTION,
+                ERROR_ILLEGAL_ARGUMENT_EXCEPTION,
                 1,
                 firstAttemptTimestamp,
                 fakeTimeSource,
@@ -388,7 +395,7 @@
         // Second attempt should succeed as well.
         assertThat(
             RetryingCameraStateOpener.shouldRetry(
-                CameraError.ERROR_ILLEGAL_ARGUMENT_EXCEPTION,
+                ERROR_ILLEGAL_ARGUMENT_EXCEPTION,
                 2,
                 firstAttemptTimestamp,
                 fakeTimeSource,
@@ -405,7 +412,7 @@
 
         assertThat(
             RetryingCameraStateOpener.shouldRetry(
-                CameraError.ERROR_SECURITY_EXCEPTION,
+                ERROR_SECURITY_EXCEPTION,
                 1,
                 firstAttemptTimestamp,
                 fakeTimeSource,
@@ -417,7 +424,7 @@
         // Second attempt should fail.
         assertThat(
             RetryingCameraStateOpener.shouldRetry(
-                CameraError.ERROR_SECURITY_EXCEPTION,
+                ERROR_SECURITY_EXCEPTION,
                 2,
                 firstAttemptTimestamp,
                 fakeTimeSource,
@@ -428,19 +435,72 @@
     }
 
     @Test
+    fun testShouldNotRetryDoNotDisturbModeEnabled() {
+        val firstAttemptTimestamp = TimestampNs(0L)
+        fakeTimeSource.currentTimestamp = TimestampNs(1_000_000_000L) // 1 second
+
+        assertThat(
+            RetryingCameraStateOpener.shouldRetry(
+                ERROR_DO_NOT_DISTURB_ENABLED,
+                1,
+                firstAttemptTimestamp,
+                fakeTimeSource,
+                false
+            )
+        )
+            .isFalse()
+    }
+
+    @Test
     fun cameraStateOpenerReturnsCorrectError() = runTest {
         cameraOpener.toThrow = CameraAccessException(CameraAccessException.CAMERA_IN_USE)
-        val result = cameraStateOpener.tryOpenCamera(cameraId0, 1, Timestamps.now(fakeTimeSource))
+        val result = cameraStateOpener.tryOpenCamera(
+            cameraId0,
+            1,
+            Timestamps.now(fakeTimeSource),
+        )
 
         assertThat(result.errorCode).isEqualTo(ERROR_CAMERA_IN_USE)
     }
 
     @Test
+    fun cameraStateOpenerReturnsCorrectErrorWhenDoNotDisturbModeEnabledOnApi28() = runTest {
+        val throwable = RuntimeException("Camera is being used after Camera.release() was called")
+        throwable.stackTrace = arrayOf(
+            StackTraceElement(
+                "android.hardware.Camera",
+                "_enableShutterSound",
+                "Native Method",
+                0
+            ),
+            StackTraceElement(
+                "android.hardware.Camera",
+                "updateAppOpsPlayAudio",
+                "Camera.java",
+                1770
+            )
+        )
+        cameraOpener.toThrow = throwable
+
+        try {
+            val result = cameraStateOpener.tryOpenCamera(
+                cameraId0,
+                1,
+                Timestamps.now(fakeTimeSource),
+            )
+            assertThat(result.errorCode).isEqualTo(ERROR_DO_NOT_DISTURB_ENABLED)
+        } catch (throwable: Throwable) {
+            // Only non-28 SDK levels should throw an exception.
+            assertThat(Build.VERSION.SDK_INT).isNotEqualTo(28)
+        }
+    }
+
+    @Test
     fun retryingCameraStateOpenerRetriesCorrectlyOnCameraInUse() = runTest {
         whenever(fakeDevicePolicyManager.camerasDisabled).thenReturn(false)
         cameraOpener.toThrow = CameraAccessException(CameraAccessException.CAMERA_IN_USE)
         val result = async {
-            retryingCameraStateOpener.openCameraWithRetry(cameraId0, fakeGraphListener)
+            retryingCameraStateOpener.openCameraWithRetry(cameraId0)
         }
 
         // Advance virtual clock to move past the retry timeout.
@@ -457,7 +517,9 @@
         }
         // The first retry should be hidden. Therefore the number of onGraphError() calls should be
         // exactly the number of camera opens minus 1.
-        assertThat(fakeGraphListener.numberOfErrorCalls).isEqualTo(cameraOpener.numberOfOpens - 1)
+        assertThat(fakeCameraErrorListener.numberOfErrorCalls).isEqualTo(
+            cameraOpener.numberOfOpens - 1
+        )
     }
 
     @Test
@@ -465,7 +527,7 @@
         whenever(fakeDevicePolicyManager.camerasDisabled).thenReturn(false)
         cameraOpener.toThrow = CameraAccessException(CameraAccessException.MAX_CAMERAS_IN_USE)
         val result = async {
-            retryingCameraStateOpener.openCameraWithRetry(cameraId0, fakeGraphListener)
+            retryingCameraStateOpener.openCameraWithRetry(cameraId0)
         }
 
         // Advance virtual clock to move past the retry timeout.
@@ -478,7 +540,9 @@
         assertThat(cameraOpener.numberOfOpens).isGreaterThan(2)
         // The first retry should be hidden. Therefore the number of onGraphError() calls should be
         // exactly the number of camera opens minus 1.
-        assertThat(fakeGraphListener.numberOfErrorCalls).isEqualTo(cameraOpener.numberOfOpens - 1)
+        assertThat(fakeCameraErrorListener.numberOfErrorCalls).isEqualTo(
+            cameraOpener.numberOfOpens - 1
+        )
     }
 
     @Test
@@ -486,7 +550,7 @@
         whenever(fakeDevicePolicyManager.camerasDisabled).thenReturn(true)
         cameraOpener.toThrow = CameraAccessException(CameraAccessException.CAMERA_DISABLED)
         val result = async {
-            retryingCameraStateOpener.openCameraWithRetry(cameraId0, fakeGraphListener)
+            retryingCameraStateOpener.openCameraWithRetry(cameraId0)
         }
 
         // Advance virtual clock with just enough time for 1 camera retry (we wait 500ms before the
@@ -500,7 +564,7 @@
         assertThat(cameraOpener.numberOfOpens).isEqualTo(2)
         // The first retry should be hidden. Therefore the number of onGraphError() calls should be
         // exactly 1.
-        assertThat(fakeGraphListener.numberOfErrorCalls).isEqualTo(1)
+        assertThat(fakeCameraErrorListener.numberOfErrorCalls).isEqualTo(1)
     }
 
     @Test
@@ -508,7 +572,7 @@
         whenever(fakeDevicePolicyManager.camerasDisabled).thenReturn(false)
         cameraOpener.toThrow = CameraAccessException(CameraAccessException.CAMERA_DISABLED)
         val result = async {
-            retryingCameraStateOpener.openCameraWithRetry(cameraId0, fakeGraphListener)
+            retryingCameraStateOpener.openCameraWithRetry(cameraId0)
         }
 
         // Advance virtual clock to move past the retry timeout.
@@ -521,7 +585,9 @@
         assertThat(cameraOpener.numberOfOpens).isGreaterThan(2)
         // The first retry should be hidden. Therefore the number of onGraphError() calls should be
         // exactly the number of camera opens minus 1.
-        assertThat(fakeGraphListener.numberOfErrorCalls).isEqualTo(cameraOpener.numberOfOpens - 1)
+        assertThat(fakeCameraErrorListener.numberOfErrorCalls).isEqualTo(
+            cameraOpener.numberOfOpens - 1
+        )
     }
 
     @Test
@@ -529,7 +595,7 @@
         whenever(fakeDevicePolicyManager.camerasDisabled).thenReturn(false)
         cameraOpener.toThrow = CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED)
         val result = async {
-            retryingCameraStateOpener.openCameraWithRetry(cameraId0, fakeGraphListener)
+            retryingCameraStateOpener.openCameraWithRetry(cameraId0)
         }
 
         // Advance virtual clock to move past the retry timeout.
@@ -542,7 +608,9 @@
         assertThat(cameraOpener.numberOfOpens).isGreaterThan(2)
         // The first retry should be hidden. Therefore the number of onGraphError() calls should be
         // exactly the number of camera opens minus 1.
-        assertThat(fakeGraphListener.numberOfErrorCalls).isEqualTo(cameraOpener.numberOfOpens - 1)
+        assertThat(fakeCameraErrorListener.numberOfErrorCalls).isEqualTo(
+            cameraOpener.numberOfOpens - 1
+        )
     }
 
     @Test
@@ -550,7 +618,7 @@
         whenever(fakeDevicePolicyManager.camerasDisabled).thenReturn(false)
         cameraOpener.toThrow = IllegalArgumentException()
         val result = async {
-            retryingCameraStateOpener.openCameraWithRetry(cameraId0, fakeGraphListener)
+            retryingCameraStateOpener.openCameraWithRetry(cameraId0)
         }
 
         // Advance virtual clock to move past the retry timeout.
@@ -563,7 +631,9 @@
         assertThat(cameraOpener.numberOfOpens).isGreaterThan(2)
         // The first retry should be hidden. Therefore the number of onGraphError() calls should be
         // exactly the number of camera opens minus 1.
-        assertThat(fakeGraphListener.numberOfErrorCalls).isEqualTo(cameraOpener.numberOfOpens - 1)
+        assertThat(fakeCameraErrorListener.numberOfErrorCalls).isEqualTo(
+            cameraOpener.numberOfOpens - 1
+        )
     }
 
     @Test
@@ -571,7 +641,7 @@
         whenever(fakeDevicePolicyManager.camerasDisabled).thenReturn(false)
         cameraOpener.toThrow = SecurityException()
         val result = async {
-            retryingCameraStateOpener.openCameraWithRetry(cameraId0, fakeGraphListener)
+            retryingCameraStateOpener.openCameraWithRetry(cameraId0)
         }
 
         // Advance virtual clock with just enough time for 1 camera retry (we wait 500ms before the
@@ -585,6 +655,6 @@
         assertThat(cameraOpener.numberOfOpens).isEqualTo(2)
         // The first retry should be hidden. Therefore the number of onGraphError() calls should be
         // exactly 1.
-        assertThat(fakeGraphListener.numberOfErrorCalls).isEqualTo(1)
+        assertThat(fakeCameraErrorListener.numberOfErrorCalls).isEqualTo(1)
     }
 }
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/VirtualCameraTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/VirtualCameraTest.kt
index 01c6aac..75a220d 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/VirtualCameraTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/VirtualCameraTest.kt
@@ -20,10 +20,13 @@
 import android.os.Build
 import android.os.Looper.getMainLooper
 import androidx.camera.camera2.pipe.CameraError
+import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.core.SystemTimeSource
 import androidx.camera.camera2.pipe.core.TimeSource
 import androidx.camera.camera2.pipe.core.Timestamps
 import androidx.camera.camera2.pipe.core.Token
+import androidx.camera.camera2.pipe.graph.GraphListener
+import androidx.camera.camera2.pipe.internal.CameraErrorListener
 import androidx.camera.camera2.pipe.testing.RobolectricCameraPipeTestRunner
 import androidx.camera.camera2.pipe.testing.RobolectricCameras
 import com.google.common.truth.Truth.assertThat
@@ -39,6 +42,7 @@
 import org.junit.After
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
 import org.robolectric.Shadows.shadowOf
 import org.robolectric.annotation.Config
 
@@ -49,6 +53,8 @@
     private val mainLooper = shadowOf(getMainLooper())
     private val cameraId = RobolectricCameras.create()
     private val testCamera = RobolectricCameras.open(cameraId)
+    private val graphListener: GraphListener = mock()
+    private val cameraErrorListener: CameraErrorListener = mock()
 
     @After
     fun teardown() {
@@ -60,7 +66,7 @@
     fun virtualCameraStateCanBeDisconnected() = runTest {
         // This test asserts that the virtual camera starts in an unopened state and is changed to
         // "Closed" when disconnect is invoked on the VirtualCamera.
-        val virtualCamera = VirtualCameraState(cameraId)
+        val virtualCamera = VirtualCameraState(cameraId, graphListener)
         assertThat(virtualCamera.value).isInstanceOf(CameraStateUnopened::class.java)
 
         virtualCamera.disconnect()
@@ -84,12 +90,15 @@
         // This test asserts that when a virtual camera is connected to a flow of CameraState
         // changes that it receives those changes and can be subsequently disconnected, which stops
         // additional events from being passed to the virtual camera instance.
-        val virtualCamera = VirtualCameraState(cameraId)
+        val virtualCamera = VirtualCameraState(cameraId, graphListener)
         val cameraState =
             flowOf(
                 CameraStateOpen(
                     AndroidCameraDevice(
-                        testCamera.metadata, testCamera.cameraDevice, testCamera.cameraId
+                        testCamera.metadata,
+                        testCamera.cameraDevice,
+                        testCamera.cameraId,
+                        cameraErrorListener,
                     )
                 )
             )
@@ -116,12 +125,15 @@
     fun virtualCameraStateRespondsToClose() = runTest {
         // This tests that a listener attached to the virtualCamera.state property will receive all
         // of the events, starting from CameraStateUnopened.
-        val virtualCamera = VirtualCameraState(cameraId)
+        val virtualCamera = VirtualCameraState(cameraId, graphListener)
         val states =
             listOf(
                 CameraStateOpen(
                     AndroidCameraDevice(
-                        testCamera.metadata, testCamera.cameraDevice, testCamera.cameraId
+                        testCamera.metadata,
+                        testCamera.cameraDevice,
+                        testCamera.cameraId,
+                        cameraErrorListener,
                     )
                 ),
                 CameraStateClosing(),
@@ -159,6 +171,19 @@
     private val testCamera = RobolectricCameras.open(cameraId)
     private val timeSource: TimeSource = SystemTimeSource()
     private val now = Timestamps.now(timeSource)
+    private val cameraErrorListener = object : CameraErrorListener {
+        var lastCameraId: CameraId? = null
+        var lastCameraError: CameraError? = null
+
+        override fun onCameraError(
+            cameraId: CameraId,
+            cameraError: CameraError,
+            willAttemptRetry: Boolean
+        ) {
+            lastCameraId = cameraId
+            lastCameraError = cameraError
+        }
+    }
 
     @After
     fun teardown() {
@@ -174,7 +199,8 @@
                 testCamera.metadata,
                 attemptNumber = 1,
                 attemptTimestampNanos = now,
-                timeSource
+                timeSource,
+                cameraErrorListener
             )
 
         assertThat(listener.state.value).isInstanceOf(CameraStateUnopened.javaClass)
@@ -219,7 +245,8 @@
                 testCamera.metadata,
                 attemptNumber = 1,
                 attemptTimestampNanos = now,
-                timeSource
+                timeSource,
+                cameraErrorListener
             )
 
         listener.onDisconnected(testCamera.cameraDevice)
@@ -240,7 +267,8 @@
                 testCamera.metadata,
                 attemptNumber = 1,
                 attemptTimestampNanos = now,
-                timeSource
+                timeSource,
+                cameraErrorListener
             )
 
         listener.close()
@@ -258,7 +286,8 @@
                 testCamera.metadata,
                 attemptNumber = 1,
                 attemptTimestampNanos = now,
-                timeSource
+                timeSource,
+                cameraErrorListener
             )
 
         listener.closeWith(IllegalArgumentException("Test Exception"))
@@ -276,7 +305,8 @@
                 testCamera.metadata,
                 attemptNumber = 1,
                 attemptTimestampNanos = now,
-                timeSource
+                timeSource,
+                cameraErrorListener
             )
 
         listener.onError(testCamera.cameraDevice, CameraDevice.StateCallback.ERROR_CAMERA_SERVICE)
@@ -287,4 +317,44 @@
         assertThat(closedState.cameraErrorCode).isEqualTo(CameraError.ERROR_CAMERA_SERVICE)
         assertThat(closedState.cameraException).isNull()
     }
+
+    @Test
+    fun errorCodesAreReportedToGraphListener() {
+        val listener =
+            AndroidCameraState(
+                testCamera.cameraId,
+                testCamera.metadata,
+                attemptNumber = 1,
+                attemptTimestampNanos = now,
+                timeSource,
+                cameraErrorListener
+            )
+
+        listener.onOpened(testCamera.cameraDevice)
+        listener.onError(testCamera.cameraDevice, CameraDevice.StateCallback.ERROR_CAMERA_SERVICE)
+        mainLooper.idle()
+        assertThat(cameraErrorListener.lastCameraId).isEqualTo(testCamera.cameraId)
+        assertThat(cameraErrorListener.lastCameraError).isEqualTo(CameraError.ERROR_CAMERA_SERVICE)
+    }
+
+    @Test
+    fun errorCodesAreReportedToGraphListenerWhenCameraIsNotOpened() {
+        // Unless this is a camera open exception, all errors should be reported even if camera is
+        // not opened. The main reason is CameraAccessException.CAMERA_ERROR, where under which, we
+        // only know the nature of the error true onError(), and we should and would report that.
+        val listener =
+            AndroidCameraState(
+                testCamera.cameraId,
+                testCamera.metadata,
+                attemptNumber = 1,
+                attemptTimestampNanos = now,
+                timeSource,
+                cameraErrorListener
+            )
+
+        listener.onError(testCamera.cameraDevice, CameraDevice.StateCallback.ERROR_CAMERA_SERVICE)
+        mainLooper.idle()
+        assertThat(cameraErrorListener.lastCameraId).isEqualTo(testCamera.cameraId)
+        assertThat(cameraErrorListener.lastCameraError).isEqualTo(CameraError.ERROR_CAMERA_SERVICE)
+    }
 }
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/Controller3AUnlock3ATest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/Controller3AUnlock3ATest.kt
index 951bce4..77153c1 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/Controller3AUnlock3ATest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/graph/Controller3AUnlock3ATest.kt
@@ -32,6 +32,7 @@
 import kotlinx.coroutines.async
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runTest
 import org.junit.After
 import org.junit.Test
@@ -325,6 +326,46 @@
         assertThat(result.frameMetadata).isEqualTo(null)
     }
 
+    @Test
+    fun testUnlockNeverConverge_frameLimitedReached() = runTest {
+        // Arrange. Launch a task to repeatedly invoke AE Locked info.
+        val frameLimit = 100
+        val repeatingJob = launch {
+            var frameNumber = 101L
+            while (true) {
+                listener3A.onRequestSequenceCreated(
+                    FakeRequestMetadata(requestNumber = RequestNumber(1))
+                )
+                listener3A.onPartialCaptureResult(
+                    FakeRequestMetadata(requestNumber = RequestNumber(1)),
+                    FrameNumber(frameNumber),
+                    FakeFrameMetadata(
+                        frameNumber = FrameNumber(frameNumber++),
+                        resultMetadata = mapOf(
+                            CaptureResult.CONTROL_AE_STATE to
+                                CaptureResult.CONTROL_AE_STATE_LOCKED
+                        )
+                    )
+                )
+                delay(FRAME_RATE_MS)
+            }
+        }
+
+        // Act. Unlock AE
+        val result3ADeferred = controller3A.unlock3A(ae = true, frameLimit = frameLimit)
+        advanceTimeBy(FRAME_RATE_MS * frameLimit)
+        result3ADeferred.await()
+
+        // Assert. Result of unlock3A call should be completed with timeout result.
+        assertThat(result3ADeferred.isCompleted).isTrue()
+        val result = result3ADeferred.getCompleted()
+        assertThat(result.status).isEqualTo(Result3A.Status.FRAME_LIMIT_REACHED)
+
+        // Clean up
+        repeatingJob.cancel()
+        repeatingJob.join()
+    }
+
     companion object {
         // The time duration in milliseconds between two frame results.
         private const val FRAME_RATE_MS = 33L
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 c49f98a..c8a6aa8 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
@@ -19,6 +19,7 @@
 import android.view.Surface
 import androidx.camera.camera2.pipe.CameraController
 import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.CameraStatusMonitor
 import androidx.camera.camera2.pipe.StreamId
 
 internal class FakeCameraController : CameraController {
@@ -36,7 +37,7 @@
         started = false
     }
 
-    override fun tryRestart() {
+    override fun tryRestart(cameraStatus: CameraStatusMonitor.CameraStatus) {
         stop()
         start()
     }
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraDeviceWrapper.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraDeviceWrapper.kt
index 33c7946..76333bd 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraDeviceWrapper.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraDeviceWrapper.kt
@@ -61,12 +61,14 @@
         outputs: List<Surface>,
         stateCallback: CameraCaptureSessionWrapper.StateCallback,
         handler: Handler?
-    ) {
+    ): Boolean {
         createFakeCaptureSession(stateCallback)
+        return true
     }
 
-    override fun createCaptureSession(config: SessionConfigData) {
+    override fun createCaptureSession(config: SessionConfigData): Boolean {
         createFakeCaptureSession(config.stateCallback)
+        return true
     }
 
     override fun createReprocessableCaptureSession(
@@ -74,24 +76,27 @@
         outputs: List<Surface>,
         stateCallback: CameraCaptureSessionWrapper.StateCallback,
         handler: Handler?
-    ) {
+    ): Boolean {
         createFakeCaptureSession(stateCallback)
+        return true
     }
 
     override fun createConstrainedHighSpeedCaptureSession(
         outputs: List<Surface>,
         stateCallback: CameraCaptureSessionWrapper.StateCallback,
         handler: Handler?
-    ) {
+    ): Boolean {
         createFakeCaptureSession(stateCallback)
+        return true
     }
 
     override fun createCaptureSessionByOutputConfigurations(
         outputConfigurations: List<OutputConfigurationWrapper>,
         stateCallback: CameraCaptureSessionWrapper.StateCallback,
         handler: Handler?
-    ) {
+    ): Boolean {
         createFakeCaptureSession(stateCallback)
+        return true
     }
 
     override fun createReprocessableCaptureSessionByConfigurations(
@@ -99,8 +104,9 @@
         outputs: List<OutputConfigurationWrapper>,
         stateCallback: CameraCaptureSessionWrapper.StateCallback,
         handler: Handler?
-    ) {
+    ): Boolean {
         createFakeCaptureSession(stateCallback)
+        return true
     }
 
     override fun onDeviceClosed() {
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCaptureSessionWrapper.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCaptureSessionWrapper.kt
index 9688607e..bc4d75d 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCaptureSessionWrapper.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCaptureSessionWrapper.kt
@@ -43,8 +43,9 @@
 
     val unwrappedClasses = arrayListOf<Any>()
 
-    override fun abortCaptures() {
+    override fun abortCaptures(): Boolean {
         abortCapturesInvoked = true
+        return true
     }
 
     override fun capture(
@@ -95,11 +96,14 @@
         return lastSequenceNumber
     }
 
-    override fun stopRepeating() {
+    override fun stopRepeating(): Boolean {
         stopRepeatingInvoked = true
+        return true
     }
 
-    override fun finalizeOutputConfigurations(outputConfigs: List<OutputConfigurationWrapper>) {
+    override fun finalizeOutputConfigurations(
+        outputConfigs: List<OutputConfigurationWrapper>
+    ): Boolean {
         throw UnsupportedOperationException("finalizeOutputConfigurations is not supported")
     }
 
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplTest.java
index 2d941f0..3c0881fc 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplTest.java
@@ -16,9 +16,15 @@
 
 package androidx.camera.camera2.internal;
 
+import static androidx.camera.camera2.internal.Camera2CameraImplTest.TestUseCase.SurfaceOption;
+import static androidx.camera.camera2.internal.Camera2CameraImplTest.TestUseCase.SurfaceOption.NON_REPEATING;
+import static androidx.camera.camera2.internal.Camera2CameraImplTest.TestUseCase.SurfaceOption.REPEATING;
+import static androidx.camera.core.CameraSelector.DEFAULT_BACK_CAMERA;
 import static androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_CONCURRENT;
 import static androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_SINGLE;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.truth.Truth.assertThat;
 
 import static junit.framework.TestCase.assertTrue;
@@ -31,6 +37,9 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.internal.verification.VerificationModeFactory.times;
 
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.graphics.ImageFormat;
@@ -55,8 +64,6 @@
 import androidx.camera.core.CameraControl;
 import androidx.camera.core.CameraSelector;
 import androidx.camera.core.ImageCapture;
-import androidx.camera.core.InitializationException;
-import androidx.camera.core.ResolutionSelector;
 import androidx.camera.core.UseCase;
 import androidx.camera.core.impl.CameraCaptureCallback;
 import androidx.camera.core.impl.CameraCaptureResult;
@@ -69,11 +76,11 @@
 import androidx.camera.core.impl.SessionConfig;
 import androidx.camera.core.impl.StreamSpec;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
+import androidx.camera.core.resolutionselector.HighResolution;
+import androidx.camera.core.resolutionselector.ResolutionSelector;
 import androidx.camera.testing.CameraUtil;
 import androidx.camera.testing.HandlerUtil;
-import androidx.camera.testing.fakes.FakeCamera;
 import androidx.camera.testing.fakes.FakeCameraCoordinator;
-import androidx.camera.testing.fakes.FakeCameraInfoInternal;
 import androidx.camera.testing.fakes.FakeUseCase;
 import androidx.camera.testing.fakes.FakeUseCaseConfig;
 import androidx.camera.testing.mocks.MockObserver;
@@ -99,11 +106,10 @@
 import org.mockito.Mockito;
 
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -126,7 +132,18 @@
     private static final int DEFAULT_PAIRED_CAMERA_LENS_FACING = CameraSelector.LENS_FACING_FRONT;
     // For the purpose of this test, always say we have 1 camera available.
     private static final int DEFAULT_AVAILABLE_CAMERA_COUNT = 1;
-    private static final Set<CameraInternal.State> STABLE_STATES = new HashSet<>(Arrays.asList(
+    private static final int DEFAULT_TEMPLATE_TYPE = CameraDevice.TEMPLATE_PREVIEW;
+    private static final Map<Integer, Boolean> DEFAULT_TEMPLATE_TO_ZSL_DISABLED = new HashMap<>();
+
+    static {
+        DEFAULT_TEMPLATE_TO_ZSL_DISABLED.put(CameraDevice.TEMPLATE_PREVIEW, false);
+        DEFAULT_TEMPLATE_TO_ZSL_DISABLED.put(CameraDevice.TEMPLATE_RECORD, true);
+        DEFAULT_TEMPLATE_TO_ZSL_DISABLED.put(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG, false);
+    }
+
+    private static final SurfaceOption DEFAULT_SURFACE_OPTION = REPEATING;
+
+    private static final Set<CameraInternal.State> STABLE_STATES = new HashSet<>(asList(
             CameraInternal.State.CLOSED,
             CameraInternal.State.OPEN,
             CameraInternal.State.RELEASED));
@@ -138,9 +155,8 @@
             new CameraUtil.PreTestCameraIdList(Camera2Config.defaultConfig())
     );
 
-    private ArrayList<FakeUseCase> mFakeUseCases = new ArrayList<>();
+    private final ArrayList<FakeUseCase> mFakeUseCases = new ArrayList<>();
     private Camera2CameraImpl mCamera2CameraImpl;
-    private Camera2CameraImpl mPairedCamera2CameraImpl;
     private static HandlerThread sCameraHandlerThread;
     private static Handler sCameraHandler;
     private FakeCameraCoordinator mCameraCoordinator;
@@ -153,7 +169,7 @@
     SemaphoreReleasingCamera2Callbacks.SessionStateCallback mSessionStateCallback;
 
     @BeforeClass
-    public static void classSetup() throws InitializationException {
+    public static void classSetup() {
         sCameraHandlerThread = new HandlerThread("cameraThread");
         sCameraHandlerThread.start();
         sCameraHandler = HandlerCompat.createAsync(sCameraHandlerThread.getLooper());
@@ -190,6 +206,10 @@
 
     @After
     public void teardown() throws InterruptedException, ExecutionException {
+        for (FakeUseCase fakeUseCase : mFakeUseCases) {
+            fakeUseCase.unbindFromCamera(mCamera2CameraImpl);
+            fakeUseCase.onUnbind();
+        }
         // Need to release the camera no matter what is done, otherwise the CameraDevice is not
         // closed.
         // When the CameraDevice is not closed, then it can cause problems with interferes with
@@ -202,10 +222,6 @@
 
             mCamera2CameraImpl = null;
         }
-
-        for (FakeUseCase fakeUseCase : mFakeUseCases) {
-            fakeUseCase.onUnbind();
-        }
     }
 
     @Test
@@ -213,11 +229,11 @@
         mCamera2CameraImpl.open();
 
         UseCase useCase = createUseCase();
-        mCamera2CameraImpl.attachUseCases(Collections.singletonList(useCase));
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase));
 
         verify(mMockOnImageAvailableListener, never()).onImageAvailable(any(ImageReader.class));
 
-        mCamera2CameraImpl.detachUseCases(Collections.singletonList(useCase));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase));
         mCamera2CameraImpl.release();
     }
 
@@ -234,21 +250,21 @@
     @Test
     public void attachAndActiveUseCase() {
         UseCase useCase1 = createUseCase();
-        mCamera2CameraImpl.attachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase1));
         mCamera2CameraImpl.onUseCaseActive(useCase1);
 
         verify(mMockOnImageAvailableListener, timeout(4000).atLeastOnce())
                 .onImageAvailable(any(ImageReader.class));
 
-        mCamera2CameraImpl.detachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase1));
     }
 
     @Test
     public void detachUseCase() {
         UseCase useCase1 = createUseCase();
-        mCamera2CameraImpl.attachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase1));
 
-        mCamera2CameraImpl.detachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase1));
         mCamera2CameraImpl.onUseCaseActive(useCase1);
 
         verify(mMockOnImageAvailableListener, never()).onImageAvailable(any(ImageReader.class));
@@ -260,8 +276,8 @@
     @Test
     public void unopenedCamera() {
         UseCase useCase1 = createUseCase();
-        mCamera2CameraImpl.attachUseCases(Collections.singletonList(useCase1));
-        mCamera2CameraImpl.detachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase1));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase1));
 
         verify(mMockOnImageAvailableListener, never()).onImageAvailable(any(ImageReader.class));
     }
@@ -269,8 +285,8 @@
     @Test
     public void closedCamera() {
         UseCase useCase1 = createUseCase();
-        mCamera2CameraImpl.attachUseCases(Collections.singletonList(useCase1));
-        mCamera2CameraImpl.detachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase1));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase1));
 
         verify(mMockOnImageAvailableListener, never()).onImageAvailable(any(ImageReader.class));
     }
@@ -282,12 +298,12 @@
         mCamera2CameraImpl.release();
         mCamera2CameraImpl.open();
 
-        mCamera2CameraImpl.attachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase1));
         mCamera2CameraImpl.onUseCaseActive(useCase1);
 
         verify(mMockOnImageAvailableListener, never()).onImageAvailable(any(ImageReader.class));
 
-        mCamera2CameraImpl.detachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase1));
     }
 
     @Test
@@ -296,54 +312,54 @@
         mCamera2CameraImpl.open();
         mCamera2CameraImpl.release();
 
-        mCamera2CameraImpl.attachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase1));
         mCamera2CameraImpl.onUseCaseActive(useCase1);
 
         verify(mMockOnImageAvailableListener, never()).onImageAvailable(any(ImageReader.class));
 
-        mCamera2CameraImpl.detachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase1));
     }
 
     @Test
     public void attach_oneUseCase_isAttached() {
         UseCase useCase1 = createUseCase();
-        mCamera2CameraImpl.attachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase1));
 
         assertThat(mCamera2CameraImpl.isUseCaseAttached(useCase1)).isTrue();
 
-        mCamera2CameraImpl.detachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase1));
     }
 
     @Test
     public void attach_sameUseCases_staysAttached() {
         UseCase useCase1 = createUseCase();
-        mCamera2CameraImpl.attachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase1));
         boolean attachedAfterFirstAdd = mCamera2CameraImpl.isUseCaseAttached(useCase1);
 
-        mCamera2CameraImpl.attachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase1));
 
         assertThat(attachedAfterFirstAdd).isTrue();
         assertThat(mCamera2CameraImpl.isUseCaseAttached(useCase1)).isTrue();
 
-        mCamera2CameraImpl.detachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase1));
     }
 
     @Test
     public void attach_twoUseCases_bothBecomeAttached() {
         UseCase useCase1 = createUseCase();
         UseCase useCase2 = createUseCase();
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(useCase1, useCase2));
+        mCamera2CameraImpl.attachUseCases(asList(useCase1, useCase2));
 
         assertThat(mCamera2CameraImpl.isUseCaseAttached(useCase1)).isTrue();
         assertThat(mCamera2CameraImpl.isUseCaseAttached(useCase2)).isTrue();
 
-        mCamera2CameraImpl.detachUseCases(Arrays.asList(useCase1, useCase2));
+        mCamera2CameraImpl.detachUseCases(asList(useCase1, useCase2));
     }
 
     @Test
     public void detach_detachedUseCase_staysDetached() {
         UseCase useCase1 = createUseCase();
-        mCamera2CameraImpl.detachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase1));
 
         assertThat(mCamera2CameraImpl.isUseCaseAttached(useCase1)).isFalse();
     }
@@ -352,34 +368,34 @@
     public void detachOneAttachedUseCase_fromAttachedUseCases_onlyDetachedSingleUseCase() {
         UseCase useCase1 = createUseCase();
         UseCase useCase2 = createUseCase();
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(useCase1, useCase2));
+        mCamera2CameraImpl.attachUseCases(asList(useCase1, useCase2));
 
         boolean useCase1isAttachedAfterFirstAdd = mCamera2CameraImpl.isUseCaseAttached(useCase1);
         boolean useCase2isAttachedAfterFirstAdd = mCamera2CameraImpl.isUseCaseAttached(useCase2);
 
-        mCamera2CameraImpl.detachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase1));
 
         assertThat(useCase1isAttachedAfterFirstAdd).isTrue();
         assertThat(useCase2isAttachedAfterFirstAdd).isTrue();
         assertThat(mCamera2CameraImpl.isUseCaseAttached(useCase1)).isFalse();
         assertThat(mCamera2CameraImpl.isUseCaseAttached(useCase2)).isTrue();
 
-        mCamera2CameraImpl.detachUseCases(Collections.singletonList(useCase2));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase2));
     }
 
     @Test
     public void detachSameAttachedUseCaseTwice_onlyDetachesSameUseCase() {
         UseCase useCase1 = createUseCase();
         UseCase useCase2 = createUseCase();
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(useCase1, useCase2));
+        mCamera2CameraImpl.attachUseCases(asList(useCase1, useCase2));
 
-        mCamera2CameraImpl.detachUseCases(Collections.singletonList(useCase1));
-        mCamera2CameraImpl.detachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase1));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase1));
 
         assertThat(mCamera2CameraImpl.isUseCaseAttached(useCase1)).isFalse();
         assertThat(mCamera2CameraImpl.isUseCaseAttached(useCase2)).isTrue();
 
-        mCamera2CameraImpl.detachUseCases(Collections.singletonList(useCase2));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase2));
     }
 
     @Test
@@ -388,7 +404,7 @@
         blockHandler();
 
         UseCase useCase1 = createUseCase();
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(useCase1));
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase1));
         DeferrableSurface surface1 = useCase1.getSessionConfig().getSurfaces().get(0);
 
         unblockHandler();
@@ -404,13 +420,13 @@
 
         assertThat(surface1).isNotEqualTo(surface2);
 
-        // Old surface is decremented when CameraCaptueSession is closed by new
+        // Old surface is decremented when CameraCaptureSession is closed by new
         // CameraCaptureSession.
         assertThat(surface1.getUseCount()).isEqualTo(0);
-        // New surface is decremented when CameraCaptueSession is closed by
+        // New surface is decremented when CameraCaptureSession is closed by
         // mCamera2CameraImpl.release()
         assertThat(surface2.getUseCount()).isEqualTo(0);
-        mCamera2CameraImpl.detachUseCases(Arrays.asList(useCase1));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase1));
     }
 
     @Test
@@ -421,7 +437,7 @@
         blockHandler();
 
         UseCase useCase1 = createUseCase();
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(useCase1));
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase1));
 
         CameraCaptureCallback captureCallback = mock(CameraCaptureCallback.class);
         CaptureConfig.Builder captureConfigBuilder = new CaptureConfig.Builder();
@@ -430,12 +446,12 @@
         captureConfigBuilder.addCameraCaptureCallback(captureCallback);
 
         mCamera2CameraImpl.getCameraControlInternal().submitStillCaptureRequests(
-                Arrays.asList(captureConfigBuilder.build()),
+                singletonList(captureConfigBuilder.build()),
                 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
                 ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH);
 
         UseCase useCase2 = createUseCase();
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(useCase2));
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase2));
 
         // Unblock camera handler to make camera operation run quickly .
         // To make the single request not able to run in 1st capture session.  and verify if it can
@@ -447,7 +463,7 @@
         verify(captureCallback, timeout(3000).times(1))
                 .onCaptureCompleted(any(CameraCaptureResult.class));
 
-        mCamera2CameraImpl.detachUseCases(Arrays.asList(useCase1, useCase2));
+        mCamera2CameraImpl.detachUseCases(asList(useCase1, useCase2));
     }
 
     @Test
@@ -460,19 +476,19 @@
         UseCase useCase1 = createUseCase();
         UseCase useCase2 = createUseCase();
 
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(useCase1, useCase2));
+        mCamera2CameraImpl.attachUseCases(asList(useCase1, useCase2));
 
         CameraCaptureCallback captureCallback = mock(CameraCaptureCallback.class);
         CaptureConfig.Builder captureConfigBuilder = new CaptureConfig.Builder();
-        captureConfigBuilder.setTemplateType(CameraDevice.TEMPLATE_PREVIEW);
+        captureConfigBuilder.setTemplateType(DEFAULT_TEMPLATE_TYPE);
         captureConfigBuilder.addSurface(useCase1.getSessionConfig().getSurfaces().get(0));
         captureConfigBuilder.addCameraCaptureCallback(captureCallback);
 
         mCamera2CameraImpl.getCameraControlInternal().submitStillCaptureRequests(
-                Arrays.asList(captureConfigBuilder.build()),
+                singletonList(captureConfigBuilder.build()),
                 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
                 ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH);
-        mCamera2CameraImpl.detachUseCases(Arrays.asList(useCase1));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase1));
 
         // Unblock camera handle to make camera operation run quickly .
         // To make the single request not able to run in 1st capture session.  and verify if it can
@@ -487,7 +503,93 @@
         verify(captureCallback, times(0))
                 .onCaptureCompleted(any(CameraCaptureResult.class));
 
-        mCamera2CameraImpl.detachUseCases(Arrays.asList(useCase2));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase2));
+    }
+
+    @Test
+    public void attachRepeatingUseCase_meteringRepeatingIsNotAttached() {
+        UseCase repeating = createUseCase(REPEATING);
+
+        mCamera2CameraImpl.attachUseCases(singletonList(repeating));
+
+        assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isFalse();
+
+        mCamera2CameraImpl.detachUseCases(singletonList(repeating));
+    }
+
+    @Test
+    public void attachNonRepeatingUseCase_meteringRepeatingIsAttached() {
+        UseCase nonRepeating = createUseCase(NON_REPEATING);
+
+        mCamera2CameraImpl.attachUseCases(singletonList(nonRepeating));
+
+        assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isTrue();
+
+        mCamera2CameraImpl.detachUseCases(singletonList(nonRepeating));
+    }
+
+    @Test
+    public void attachRepeatingUseCaseLater_meteringRepeatingIsRemoved() {
+        UseCase nonRepeating = createUseCase(NON_REPEATING);
+        UseCase repeating = createUseCase(REPEATING);
+
+        mCamera2CameraImpl.attachUseCases(singletonList(nonRepeating));
+
+        assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isTrue();
+
+        mCamera2CameraImpl.attachUseCases(singletonList(repeating));
+
+        assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isFalse();
+
+        mCamera2CameraImpl.detachUseCases(asList(nonRepeating, repeating));
+    }
+
+    @Test
+    public void detachRepeatingUseCaseLater_meteringRepeatingIsAttached() {
+        UseCase repeating = createUseCase(REPEATING);
+        UseCase nonRepeating = createUseCase(NON_REPEATING);
+
+        mCamera2CameraImpl.attachUseCases(asList(repeating, nonRepeating));
+
+        assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isFalse();
+
+        mCamera2CameraImpl.detachUseCases(singletonList(repeating));
+
+        assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isTrue();
+
+        mCamera2CameraImpl.detachUseCases(singletonList(nonRepeating));
+    }
+
+    @Test
+    public void onUseCaseReset_toRepeating_meteringRepeatingIsAttached() {
+        TestUseCase useCase = createUseCase(NON_REPEATING);
+
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase));
+
+        assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isTrue();
+
+        useCase.setSurfaceOption(REPEATING);
+        useCase.notifyResetForTesting();
+
+        assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isFalse();
+
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase));
+    }
+
+    @Test
+    public void onUseCaseReset_toNonRepeating_meteringRepeatingIsAttached() {
+        TestUseCase useCase = createUseCase(REPEATING);
+
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase));
+
+        assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isFalse();
+
+        useCase.setSurfaceOption(NON_REPEATING);
+        useCase.notifyResetForTesting();
+
+        assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isTrue();
+
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase));
     }
 
     @Test
@@ -558,8 +660,10 @@
         mCameraStateRegistry.registerCamera(
                 mockCamera,
                 CameraXExecutors.directExecutor(),
-                () -> {},
-                () -> {});
+                () -> {
+                },
+                () -> {
+                });
         mCameraStateRegistry.tryOpenCamera(mockCamera);
 
         mCamera2CameraImpl.getCameraState().addObserver(CameraXExecutors.directExecutor(),
@@ -596,18 +700,18 @@
             throws InterruptedException {
         mCamera2CameraImpl.open();
         UseCase useCase1 = createUseCase();
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(useCase1));
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase1));
         mCamera2CameraImpl.onUseCaseActive(useCase1);
 
         // Wait a little bit for the camera to open.
         assertTrue(mSessionStateCallback.waitForOnConfigured(1));
 
         // Remove the useCase1 and trigger the CaptureSession#close().
-        mCamera2CameraImpl.detachUseCases(Arrays.asList(useCase1));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase1));
 
         // Create the secondary use case immediately and open it before the first use case closed.
         UseCase useCase2 = createUseCase();
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(useCase2));
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase2));
         mCamera2CameraImpl.onUseCaseActive(useCase2);
         // Wait for the secondary capture session is configured.
         assertTrue(mSessionStateCallback.waitForOnConfigured(1));
@@ -616,7 +720,7 @@
 
         mCamera2CameraImpl.getCameraState().addObserver(CameraXExecutors.directExecutor(),
                 mockObserver);
-        mCamera2CameraImpl.detachUseCases(Arrays.asList(useCase2));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase2));
         mCamera2CameraImpl.close();
 
         // Wait for the CLOSED state. If the test fail, the CameraX might in wrong internal state,
@@ -632,27 +736,24 @@
         // Create another use case to keep the camera open.
         UseCase useCaseDummy = createUseCase();
         UseCase useCase = createUseCase();
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(useCase, useCaseDummy));
+        mCamera2CameraImpl.attachUseCases(asList(useCase, useCaseDummy));
         mCamera2CameraImpl.onUseCaseActive(useCase);
 
         // Wait a little bit for the camera to open.
         assertTrue(mSessionStateCallback.waitForOnConfigured(2));
 
         // Remove the useCase and trigger the CaptureSession#close().
-        mCamera2CameraImpl.detachUseCases(Arrays.asList(useCase));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase));
         assertTrue(mSessionStateCallback.waitForOnClosed(2));
     }
 
     // Blocks the camera thread handler.
     private void blockHandler() {
-        sCameraHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mSemaphore.acquire();
-                } catch (InterruptedException e) {
-
-                }
+        sCameraHandler.post(() -> {
+            try {
+                mSemaphore.acquire();
+            } catch (InterruptedException e) {
+                // Do nothing.
             }
         });
     }
@@ -662,25 +763,43 @@
         mSemaphore.release();
     }
 
-    private UseCase createUseCase() {
-        return createUseCase(CameraDevice.TEMPLATE_PREVIEW, /* isZslDisabled = */false);
+    @NonNull
+    private TestUseCase createUseCase() {
+        return createUseCase(DEFAULT_TEMPLATE_TYPE);
     }
 
-    private UseCase createUseCase(int template, boolean isZslDisabled) {
+    @NonNull
+    private TestUseCase createUseCase(int template) {
+        return createUseCase(template, DEFAULT_SURFACE_OPTION);
+    }
+
+    @NonNull
+    private TestUseCase createUseCase(@NonNull SurfaceOption surfaceOption) {
+        return createUseCase(DEFAULT_TEMPLATE_TYPE, surfaceOption);
+    }
+
+    @NonNull
+    private TestUseCase createUseCase(int template, @NonNull SurfaceOption surfaceOption) {
+        boolean isZslDisabled = getDefaultZslDisabled(template);
         FakeUseCaseConfig.Builder configBuilder =
                 new FakeUseCaseConfig.Builder().setSessionOptionUnpacker(
                                 new Camera2SessionOptionUnpacker()).setTargetName("UseCase")
                         .setZslDisabled(isZslDisabled);
         new Camera2Interop.Extender<>(configBuilder).setSessionStateCallback(mSessionStateCallback);
-        return createUseCase(configBuilder.getUseCaseConfig(), template);
+        return createUseCase(configBuilder.getUseCaseConfig(), template, surfaceOption);
     }
 
-    private UseCase createUseCase(@NonNull FakeUseCaseConfig config, int template) {
-        CameraSelector selector =
-                new CameraSelector.Builder().requireLensFacing(
-                        CameraSelector.LENS_FACING_BACK).build();
-        TestUseCase testUseCase = new TestUseCase(template, config,
-                selector, mMockOnImageAvailableListener, mMockRepeatingCaptureCallback);
+    @NonNull
+    private TestUseCase createUseCase(@NonNull FakeUseCaseConfig config, int template,
+            @NonNull SurfaceOption surfaceOption) {
+        TestUseCase testUseCase = new TestUseCase(
+                template,
+                config,
+                mCamera2CameraImpl,
+                mMockOnImageAvailableListener,
+                mMockRepeatingCaptureCallback,
+                surfaceOption
+        );
 
         testUseCase.updateSuggestedStreamSpec(StreamSpec.builder(new Size(640, 480)).build());
         mFakeUseCases.add(testUseCase);
@@ -692,8 +811,8 @@
         TestUseCase useCase1 = spy((TestUseCase) createUseCase());
         TestUseCase useCase2 = spy((TestUseCase) createUseCase());
 
-        mCamera2CameraImpl.attachUseCases(Collections.singletonList(useCase1));
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(useCase1, useCase2));
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase1));
+        mCamera2CameraImpl.attachUseCases(asList(useCase1, useCase2));
 
         HandlerUtil.waitForLooperToIdle(sCameraHandler);
 
@@ -703,7 +822,7 @@
         verify(useCase1, times(1)).onStateAttached();
         verify(useCase2, times(1)).onStateAttached();
 
-        mCamera2CameraImpl.detachUseCases(Arrays.asList(useCase1, useCase2));
+        mCamera2CameraImpl.detachUseCases(asList(useCase1, useCase2));
     }
 
     @Test
@@ -712,9 +831,9 @@
         TestUseCase useCase2 = spy((TestUseCase) createUseCase());
         TestUseCase useCase3 = spy((TestUseCase) createUseCase());
 
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(useCase1, useCase2));
+        mCamera2CameraImpl.attachUseCases(asList(useCase1, useCase2));
 
-        mCamera2CameraImpl.detachUseCases(Arrays.asList(useCase1, useCase2, useCase3));
+        mCamera2CameraImpl.detachUseCases(asList(useCase1, useCase2, useCase3));
 
         HandlerUtil.waitForLooperToIdle(sCameraHandler);
 
@@ -729,13 +848,14 @@
     private boolean isCameraControlActive(Camera2CameraControlImpl camera2CameraControlImpl) {
         ListenableFuture<Void> listenableFuture = camera2CameraControlImpl.setZoomRatio(2.0f);
         try {
-            // setZoom() will fail immediately when Cameracontrol is not active.
+            // setZoom() will fail immediately when CameraControl is not active.
             listenableFuture.get(50, TimeUnit.MILLISECONDS);
         } catch (ExecutionException e) {
             if (e.getCause() instanceof CameraControl.OperationCanceledException) {
                 return false;
             }
         } catch (InterruptedException | TimeoutException e) {
+            // Do nothing.
         }
         return true;
     }
@@ -749,12 +869,12 @@
 
         UseCase useCase1 = createUseCase();
 
-        mCamera2CameraImpl.attachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase1));
         HandlerUtil.waitForLooperToIdle(sCameraHandler);
 
         assertThat(isCameraControlActive(camera2CameraControlImpl)).isTrue();
 
-        mCamera2CameraImpl.detachUseCases(Collections.singletonList(useCase1));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase1));
     }
 
     @Test
@@ -763,11 +883,11 @@
                 (Camera2CameraControlImpl) mCamera2CameraImpl.getCameraControlInternal();
         UseCase useCase1 = createUseCase();
 
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(useCase1));
+        mCamera2CameraImpl.attachUseCases(singletonList(useCase1));
         HandlerUtil.waitForLooperToIdle(sCameraHandler);
         assertThat(isCameraControlActive(camera2CameraControlImpl)).isTrue();
 
-        mCamera2CameraImpl.detachUseCases(Arrays.asList(useCase1));
+        mCamera2CameraImpl.detachUseCases(singletonList(useCase1));
         HandlerUtil.waitForLooperToIdle(sCameraHandler);
 
         assertThat(isCameraControlActive(camera2CameraControlImpl)).isFalse();
@@ -775,9 +895,9 @@
 
     @Test
     public void attachUseCaseWithTemplatePreview() throws InterruptedException {
-        UseCase preview = createUseCase(CameraDevice.TEMPLATE_PREVIEW, /* isZslDisabled = */false);
+        UseCase preview = createUseCase(CameraDevice.TEMPLATE_PREVIEW);
 
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(preview));
+        mCamera2CameraImpl.attachUseCases(singletonList(preview));
         mCamera2CameraImpl.onUseCaseActive(preview);
         HandlerUtil.waitForLooperToIdle(sCameraHandler);
 
@@ -792,15 +912,15 @@
         assertThat(captureResult.get(CaptureResult.CONTROL_CAPTURE_INTENT))
                 .isEqualTo(CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW);
 
-        mCamera2CameraImpl.detachUseCases(Arrays.asList(preview));
+        mCamera2CameraImpl.detachUseCases(singletonList(preview));
     }
 
     @Test
     public void attachUseCaseWithTemplateRecord() throws InterruptedException {
-        UseCase preview = createUseCase(CameraDevice.TEMPLATE_PREVIEW, /* isZslDisabled = */false);
-        UseCase record = createUseCase(CameraDevice.TEMPLATE_RECORD, /* isZslDisabled = */true);
+        UseCase preview = createUseCase(CameraDevice.TEMPLATE_PREVIEW);
+        UseCase record = createUseCase(CameraDevice.TEMPLATE_RECORD);
 
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(preview, record));
+        mCamera2CameraImpl.attachUseCases(asList(preview, record));
         mCamera2CameraImpl.onUseCaseActive(preview);
         mCamera2CameraImpl.onUseCaseActive(record);
         HandlerUtil.waitForLooperToIdle(sCameraHandler);
@@ -816,7 +936,7 @@
         assertThat(captureResult.get(CaptureResult.CONTROL_CAPTURE_INTENT))
                 .isEqualTo(CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);
 
-        mCamera2CameraImpl.detachUseCases(Arrays.asList(preview, record));
+        mCamera2CameraImpl.detachUseCases(asList(preview, record));
     }
 
     @SdkSuppress(minSdkVersion = 23)
@@ -825,14 +945,10 @@
         if (!mCamera2CameraImpl.getCameraInfo().isZslSupported()) {
             return;
         }
-        UseCase preview = createUseCase(
-                CameraDevice.TEMPLATE_PREVIEW,
-                /* isZslDisabled = */false);
-        UseCase zsl = createUseCase(
-                CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG,
-                /* isZslDisabled = */false);
+        UseCase preview = createUseCase(CameraDevice.TEMPLATE_PREVIEW);
+        UseCase zsl = createUseCase(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
 
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(preview, zsl));
+        mCamera2CameraImpl.attachUseCases(asList(preview, zsl));
         mCamera2CameraImpl.onUseCaseActive(preview);
         mCamera2CameraImpl.onUseCaseActive(zsl);
         HandlerUtil.waitForLooperToIdle(sCameraHandler);
@@ -851,7 +967,7 @@
                 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
                 .isFalse();
 
-        mCamera2CameraImpl.detachUseCases(Arrays.asList(preview, zsl));
+        mCamera2CameraImpl.detachUseCases(asList(preview, zsl));
         HandlerUtil.waitForLooperToIdle(sCameraHandler);
         assertThat(mCamera2CameraImpl.getCameraControlInternal()
                 .isZslDisabledByByUserCaseConfig()).isFalse();
@@ -863,13 +979,11 @@
         if (!mCamera2CameraImpl.getCameraInfo().isZslSupported()) {
             return;
         }
-        UseCase preview = createUseCase(CameraDevice.TEMPLATE_PREVIEW, /* isZslDisabled = */
-                false);
-        UseCase record = createUseCase(CameraDevice.TEMPLATE_RECORD, /* isZslDisabled = */true);
-        UseCase zsl = createUseCase(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG,
-                /* isZslDisabled = */false);
+        UseCase preview = createUseCase(CameraDevice.TEMPLATE_PREVIEW);
+        UseCase record = createUseCase(CameraDevice.TEMPLATE_RECORD);
+        UseCase zsl = createUseCase(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
 
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(preview, record, zsl));
+        mCamera2CameraImpl.attachUseCases(asList(preview, record, zsl));
         mCamera2CameraImpl.onUseCaseActive(preview);
         mCamera2CameraImpl.onUseCaseActive(record);
         mCamera2CameraImpl.onUseCaseActive(zsl);
@@ -889,7 +1003,7 @@
                 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
                 .isTrue();
 
-        mCamera2CameraImpl.detachUseCases(Arrays.asList(preview, record, zsl));
+        mCamera2CameraImpl.detachUseCases(asList(preview, record, zsl));
         HandlerUtil.waitForLooperToIdle(sCameraHandler);
         assertThat(
                 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
@@ -902,13 +1016,11 @@
         if (!mCamera2CameraImpl.getCameraInfo().isZslSupported()) {
             return;
         }
-        UseCase preview = createUseCase(CameraDevice.TEMPLATE_PREVIEW, /* isZslDisabled = */
-                false);
-        UseCase record = createUseCase(CameraDevice.TEMPLATE_RECORD, /* isZslDisabled = */true);
-        UseCase zsl = createUseCase(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG,
-                /* isZslDisabled = */false);
+        UseCase preview = createUseCase(CameraDevice.TEMPLATE_PREVIEW);
+        UseCase record = createUseCase(CameraDevice.TEMPLATE_RECORD);
+        UseCase zsl = createUseCase(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
 
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(preview, zsl));
+        mCamera2CameraImpl.attachUseCases(asList(preview, zsl));
         mCamera2CameraImpl.onUseCaseActive(preview);
         mCamera2CameraImpl.onUseCaseActive(zsl);
         HandlerUtil.waitForLooperToIdle(sCameraHandler);
@@ -917,7 +1029,7 @@
                 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
                 .isFalse();
 
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(record));
+        mCamera2CameraImpl.attachUseCases(singletonList(record));
         mCamera2CameraImpl.onUseCaseActive(record);
         HandlerUtil.waitForLooperToIdle(sCameraHandler);
 
@@ -925,32 +1037,32 @@
                 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
                 .isTrue();
 
-        mCamera2CameraImpl.detachUseCases(Arrays.asList(record));
+        mCamera2CameraImpl.detachUseCases(singletonList(record));
         HandlerUtil.waitForLooperToIdle(sCameraHandler);
         assertThat(
                 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
                 .isFalse();
 
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(record));
+        mCamera2CameraImpl.attachUseCases(singletonList(record));
         mCamera2CameraImpl.onUseCaseActive(record);
         HandlerUtil.waitForLooperToIdle(sCameraHandler);
         assertThat(
                 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
                 .isTrue();
 
-        mCamera2CameraImpl.detachUseCases(Arrays.asList(zsl));
+        mCamera2CameraImpl.detachUseCases(singletonList(zsl));
         HandlerUtil.waitForLooperToIdle(sCameraHandler);
         assertThat(
                 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
                 .isTrue();
 
-        mCamera2CameraImpl.detachUseCases(Arrays.asList(preview));
+        mCamera2CameraImpl.detachUseCases(singletonList(preview));
         HandlerUtil.waitForLooperToIdle(sCameraHandler);
         assertThat(
                 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
                 .isTrue();
 
-        mCamera2CameraImpl.detachUseCases(Arrays.asList(record));
+        mCamera2CameraImpl.detachUseCases(singletonList(record));
         HandlerUtil.waitForLooperToIdle(sCameraHandler);
         assertThat(
                 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig())
@@ -960,19 +1072,19 @@
     @SdkSuppress(minSdkVersion = 23)
     @Test
     public void zslDisabled_whenHighResolutionIsEnabled() throws InterruptedException {
-        UseCase zsl = createUseCase(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG,
-                /* isZslDisabled = */false);
+        UseCase zsl = createUseCase(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
 
         // Creates a test use case with high resolution enabled.
         ResolutionSelector highResolutionSelector =
-                new ResolutionSelector.Builder().setHighResolutionEnabled(true).build();
+                new ResolutionSelector.Builder().setHighResolutionEnabledFlags(
+                        HighResolution.FLAG_DEFAULT_MODE_ON).build();
         FakeUseCaseConfig.Builder configBuilder =
                 new FakeUseCaseConfig.Builder().setSessionOptionUnpacker(
                         new Camera2SessionOptionUnpacker()).setTargetName(
                         "UseCase").setResolutionSelector(highResolutionSelector);
         new Camera2Interop.Extender<>(configBuilder).setSessionStateCallback(mSessionStateCallback);
         UseCase highResolutionUseCase = createUseCase(configBuilder.getUseCaseConfig(),
-                CameraDevice.TEMPLATE_PREVIEW);
+                CameraDevice.TEMPLATE_PREVIEW, DEFAULT_SURFACE_OPTION);
 
         // Checks zsl is disabled after UseCase#onAttach() is called to merge/update config.
         assertThat(highResolutionUseCase.getCurrentConfig().isZslDisabled(false)).isTrue();
@@ -981,7 +1093,7 @@
             return;
         }
 
-        mCamera2CameraImpl.attachUseCases(Arrays.asList(zsl, highResolutionUseCase));
+        mCamera2CameraImpl.attachUseCases(asList(zsl, highResolutionUseCase));
         mCamera2CameraImpl.onUseCaseActive(zsl);
         mCamera2CameraImpl.onUseCaseActive(highResolutionUseCase);
         HandlerUtil.waitForLooperToIdle(sCameraHandler);
@@ -1000,14 +1112,14 @@
                 && pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_CONCURRENT)) {
             Camera2CameraInfoImpl pairedCamera2CameraInfo = new Camera2CameraInfoImpl(
                     mPairedCameraId, cameraManagerCompat);
-            mPairedCamera2CameraImpl = new Camera2CameraImpl(
+            Camera2CameraImpl pairedCamera2CameraImpl = new Camera2CameraImpl(
                     cameraManagerCompat, mPairedCameraId, pairedCamera2CameraInfo,
                     mCameraCoordinator,
                     mCameraStateRegistry, sCameraExecutor, sCameraHandler,
                     DisplayInfoManager.getInstance(ApplicationProvider.getApplicationContext()));
             mCameraCoordinator.addConcurrentCameraIdsAndCameraSelectors(
                     new HashMap<String, CameraSelector>() {{
-                        put(mCameraId, CameraSelector.DEFAULT_BACK_CAMERA);
+                        put(mCameraId, DEFAULT_BACK_CAMERA);
                         put(mPairedCameraId, CameraSelector.DEFAULT_FRONT_CAMERA);
                     }});
             mCameraCoordinator.setCameraOperatingMode(CAMERA_OPERATING_MODE_CONCURRENT);
@@ -1015,9 +1127,8 @@
                     CAMERA_OPERATING_MODE_SINGLE, CAMERA_OPERATING_MODE_CONCURRENT);
 
             // Act.
-            UseCase preview1 = createUseCase(CameraDevice.TEMPLATE_PREVIEW, /* isZslDisabled = */
-                    false);
-            mCamera2CameraImpl.attachUseCases(Arrays.asList(preview1));
+            UseCase preview1 = createUseCase(CameraDevice.TEMPLATE_PREVIEW);
+            mCamera2CameraImpl.attachUseCases(singletonList(preview1));
             mCamera2CameraImpl.onUseCaseActive(preview1);
             HandlerUtil.waitForLooperToIdle(sCameraHandler);
 
@@ -1027,10 +1138,9 @@
             verify(mMockRepeatingCaptureCallback, never()).onCaptureCompleted(captor.capture());
 
             // Act.
-            UseCase preview2 = createUseCase(CameraDevice.TEMPLATE_PREVIEW, /* isZslDisabled = */
-                    false);
-            mPairedCamera2CameraImpl.attachUseCases(Arrays.asList(preview2));
-            mPairedCamera2CameraImpl.onUseCaseActive(preview2);
+            UseCase preview2 = createUseCase(CameraDevice.TEMPLATE_PREVIEW);
+            pairedCamera2CameraImpl.attachUseCases(singletonList(preview2));
+            pairedCamera2CameraImpl.onUseCaseActive(preview2);
             HandlerUtil.waitForLooperToIdle(sCameraHandler);
 
             // Assert.
@@ -1041,40 +1151,18 @@
             assertThat(captureResult.get(CaptureResult.CONTROL_CAPTURE_INTENT))
                     .isEqualTo(CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW);
 
-            mCamera2CameraImpl.detachUseCases(Arrays.asList(preview1));
+            mCamera2CameraImpl.detachUseCases(singletonList(preview1));
         }
     }
 
-    private DeferrableSurface getUseCaseSurface(UseCase useCase) {
-        return useCase.getSessionConfig().getSurfaces().get(0);
-    }
-
     private void changeUseCaseSurface(UseCase useCase) {
         useCase.updateSuggestedStreamSpec(StreamSpec.builder(new Size(640, 480)).build());
     }
 
-    private void waitForCameraClose(Camera2CameraImpl camera2CameraImpl)
-            throws InterruptedException {
-        Semaphore semaphore = new Semaphore(0);
-
-        Observable.Observer<CameraInternal.State> observer =
-                new Observable.Observer<CameraInternal.State>() {
-                    @Override
-                    public void onNewData(@Nullable CameraInternal.State value) {
-                        // Ignore any transient states.
-                        if (value == CameraInternal.State.CLOSED) {
-                            semaphore.release();
-                        }
-                    }
-
-                    @Override
-                    public void onError(@NonNull Throwable t) { /* Ignore any transient errors. */ }
-                };
-
-        camera2CameraImpl.getCameraState().addObserver(CameraXExecutors.directExecutor(), observer);
-
-        // Wait until camera reaches closed state
-        semaphore.acquire();
+    private static boolean getDefaultZslDisabled(int templateType) {
+        Boolean isZslDisabled = DEFAULT_TEMPLATE_TO_ZSL_DISABLED.get(templateType);
+        checkState(isZslDisabled != null, "No default mapping from template to zsl disabled");
+        return isZslDisabled;
     }
 
     public static class TestUseCase extends FakeUseCase {
@@ -1082,33 +1170,40 @@
         HandlerThread mHandlerThread = new HandlerThread("HandlerThread");
         Handler mHandler;
         FakeUseCaseConfig mConfig;
-        private String mCameraId;
         private DeferrableSurface mDeferrableSurface;
+        private SurfaceOption mSurfaceOption;
         private final CameraCaptureCallback mRepeatingCaptureCallback;
         private final int mTemplate;
+        private SessionConfig.Builder mSessionConfigBuilder;
+
+        @SuppressWarnings("NewClassNamingConvention")
+        public enum SurfaceOption {
+            /** UseCase will not add any surface in SessionConfig. */
+            NO_SURFACE,
+            /** UseCase will add a repeating surface in SessionConfig. */
+            REPEATING,
+            /** UseCase will add a non-repeating surface in SessionConfig. */
+            NON_REPEATING,
+        }
 
         TestUseCase(
                 int template,
                 @NonNull FakeUseCaseConfig config,
-                @NonNull CameraSelector cameraSelector,
+                @NonNull CameraInternal camera,
                 @NonNull ImageReader.OnImageAvailableListener listener,
-                @NonNull CameraCaptureCallback repeatingCaptureCallback) {
+                @NonNull CameraCaptureCallback repeatingCaptureCallback,
+                @NonNull SurfaceOption surfaceOption) {
             super(config);
             // Ensure we're using the combined configuration (user config + defaults)
             mConfig = (FakeUseCaseConfig) getCurrentConfig();
             mTemplate = template;
+            mSurfaceOption = surfaceOption;
 
             mImageAvailableListener = listener;
             mRepeatingCaptureCallback = repeatingCaptureCallback;
             mHandlerThread.start();
             mHandler = new Handler(mHandlerThread.getLooper());
-            Integer lensFacing =
-                    cameraSelector.getLensFacing() == null ? CameraSelector.LENS_FACING_BACK :
-                            cameraSelector.getLensFacing();
-            mCameraId = CameraUtil.getCameraIdWithLensFacing(lensFacing);
-            bindToCamera(new FakeCamera(mCameraId, null,
-                            new FakeCameraInfoInternal(mCameraId, 0, lensFacing)),
-                    null, null);
+            bindToCamera(camera, null, null);
             updateSuggestedStreamSpec(StreamSpec.builder(new Size(640, 480)).build());
         }
 
@@ -1117,6 +1212,7 @@
             mHandlerThread.quitSafely();
             if (mDeferrableSurface != null) {
                 mDeferrableSurface.close();
+                mDeferrableSurface = null;
             }
         }
 
@@ -1130,16 +1226,46 @@
         @NonNull
         protected StreamSpec onSuggestedStreamSpecUpdated(
                 @NonNull StreamSpec suggestedStreamSpec) {
-            SessionConfig.Builder builder = SessionConfig.Builder.createFrom(mConfig,
-                    suggestedStreamSpec.getResolution());
-
-            builder.setTemplateType(mTemplate);
-            builder.addRepeatingCameraCaptureCallback(mRepeatingCaptureCallback);
-
             if (mDeferrableSurface != null) {
                 mDeferrableSurface.close();
             }
-            Size suggestedResolution = suggestedStreamSpec.getResolution();
+            mDeferrableSurface = createDeferrableSurface(suggestedStreamSpec);
+            mSessionConfigBuilder = SessionConfig.Builder.createFrom(mConfig,
+                    suggestedStreamSpec.getResolution());
+            mSessionConfigBuilder.setTemplateType(mTemplate);
+            mSessionConfigBuilder.addRepeatingCameraCaptureCallback(mRepeatingCaptureCallback);
+            updateSessionBuilderBySurfaceOption();
+            updateSessionConfig(mSessionConfigBuilder.build());
+            return suggestedStreamSpec;
+        }
+
+        public void setSurfaceOption(@NonNull SurfaceOption surfaceOption) {
+            if (mSurfaceOption != surfaceOption) {
+                mSurfaceOption = surfaceOption;
+                updateSessionBuilderBySurfaceOption();
+                updateSessionConfig(mSessionConfigBuilder.build());
+            }
+        }
+
+        private void updateSessionBuilderBySurfaceOption() {
+            checkNotNull(mDeferrableSurface);
+            mSessionConfigBuilder.clearSurfaces();
+            switch (mSurfaceOption) {
+                case NO_SURFACE:
+                    break;
+                case REPEATING:
+                    mSessionConfigBuilder.addSurface(mDeferrableSurface);
+                    break;
+                case NON_REPEATING:
+                    mSessionConfigBuilder.addNonRepeatingSurface(mDeferrableSurface);
+                    break;
+            }
+        }
+
+        @NonNull
+        private DeferrableSurface createDeferrableSurface(@NonNull StreamSpec streamSpec) {
+            Size suggestedResolution = streamSpec.getResolution();
+            //noinspection resource
             ImageReader imageReader =
                     ImageReader.newInstance(
                             suggestedResolution.getWidth(),
@@ -1148,15 +1274,12 @@
                             2);
             imageReader.setOnImageAvailableListener(mImageAvailableListener, mHandler);
             Surface surface = imageReader.getSurface();
-            mDeferrableSurface = new ImmediateSurface(surface);
-            mDeferrableSurface.getTerminationFuture().addListener(() -> {
+            DeferrableSurface deferrableSurface = new ImmediateSurface(surface);
+            deferrableSurface.getTerminationFuture().addListener(() -> {
                 surface.release();
                 imageReader.close();
             }, CameraXExecutors.directExecutor());
-            builder.addSurface(mDeferrableSurface);
-
-            updateSessionConfig(builder.build());
-            return suggestedStreamSpec;
+            return deferrableSurface;
         }
     }
 }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
index 436eab2..0ed3b9a 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
@@ -38,6 +38,7 @@
 import androidx.annotation.OptIn;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
 import androidx.camera.camera2.internal.annotation.CameraExecutor;
 import androidx.camera.camera2.internal.compat.ApiCompat;
 import androidx.camera.camera2.internal.compat.CameraAccessExceptionCompat;
@@ -650,7 +651,7 @@
         mExecutor.execute(() -> {
             debugLog("Use case " + useCaseId + " RESET");
             mUseCaseAttachState.updateUseCase(useCaseId, sessionConfig, useCaseConfig);
-
+            addOrRemoveMeteringRepeatingUseCase();
             resetCaptureSession(/*abortInFlightCaptures=*/false);
             updateCaptureSessionConfig();
 
@@ -690,6 +691,31 @@
         }
     }
 
+    @VisibleForTesting
+    boolean isMeteringRepeatingAttached() {
+        try {
+            return CallbackToFutureAdapter.<Boolean>getFuture(completer -> {
+                try {
+                    mExecutor.execute(() -> {
+                        if (mMeteringRepeatingSession == null) {
+                            completer.set(false);
+                            return;
+                        }
+                        String id = getMeteringRepeatingId(mMeteringRepeatingSession);
+                        completer.set(mUseCaseAttachState.isUseCaseAttached(id));
+                    });
+                } catch (RejectedExecutionException e) {
+                    completer.setException(new RuntimeException(
+                            "Unable to check if MeteringRepeating is attached. Camera executor "
+                                    + "shut down."));
+                }
+                return "isMeteringRepeatingAttached";
+            }).get();
+        } catch (InterruptedException | ExecutionException e) {
+            throw new RuntimeException("Unable to check if MeteringRepeating is attached.", e);
+        }
+    }
+
     /**
      * Sets the use case to be in the state where the capture session will be configured to handle
      * capture requests from the use case.
@@ -977,12 +1003,13 @@
 
     private void addMeteringRepeating() {
         if (mMeteringRepeatingSession != null) {
+            String id = getMeteringRepeatingId(mMeteringRepeatingSession);
             mUseCaseAttachState.setUseCaseAttached(
-                    mMeteringRepeatingSession.getName() + mMeteringRepeatingSession.hashCode(),
+                    id,
                     mMeteringRepeatingSession.getSessionConfig(),
                     mMeteringRepeatingSession.getUseCaseConfig());
             mUseCaseAttachState.setUseCaseActive(
-                    mMeteringRepeatingSession.getName() + mMeteringRepeatingSession.hashCode(),
+                    id,
                     mMeteringRepeatingSession.getSessionConfig(),
                     mMeteringRepeatingSession.getUseCaseConfig());
         }
@@ -1378,6 +1405,11 @@
         return useCase.getName() + useCase.hashCode();
     }
 
+    @NonNull
+    static String getMeteringRepeatingId(@NonNull MeteringRepeatingSession meteringRepeating) {
+        return meteringRepeating.getName() + meteringRepeating.hashCode();
+    }
+
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     void debugLog(@NonNull String msg) {
         debugLog(msg, null);
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java
index e893e30..d53901a 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraInfoImpl.java
@@ -40,6 +40,7 @@
 import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
 import androidx.camera.camera2.internal.compat.CameraManagerCompat;
 import androidx.camera.camera2.internal.compat.StreamConfigurationMapCompat;
+import androidx.camera.camera2.internal.compat.params.DynamicRangesCompat;
 import androidx.camera.camera2.internal.compat.quirk.CameraQuirks;
 import androidx.camera.camera2.internal.compat.quirk.DeviceQuirks;
 import androidx.camera.camera2.internal.compat.quirk.ZslDisablerQuirk;
@@ -48,6 +49,7 @@
 import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
 import androidx.camera.core.CameraSelector;
 import androidx.camera.core.CameraState;
+import androidx.camera.core.DynamicRange;
 import androidx.camera.core.ExposureState;
 import androidx.camera.core.FocusMeteringAction;
 import androidx.camera.core.Logger;
@@ -72,6 +74,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
@@ -435,6 +438,15 @@
         return size != null ? Arrays.asList(size) : Collections.emptyList();
     }
 
+    @NonNull
+    @Override
+    public Set<DynamicRange> getSupportedDynamicRanges() {
+        DynamicRangesCompat dynamicRangesCompat = DynamicRangesCompat.fromCameraCharacteristics(
+                mCameraCharacteristicsCompat);
+
+        return dynamicRangesCompat.getSupportedDynamicRanges();
+    }
+
     @Override
     public void addSessionCaptureCallback(@NonNull Executor executor,
             @NonNull CameraCaptureCallback callback) {
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java
index b4c5083..a85be9d 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java
@@ -30,6 +30,7 @@
 import androidx.camera.core.CameraUnavailableException;
 import androidx.camera.core.impl.AttachedSurfaceInfo;
 import androidx.camera.core.impl.CameraDeviceSurfaceManager;
+import androidx.camera.core.impl.CameraMode;
 import androidx.camera.core.impl.StreamSpec;
 import androidx.camera.core.impl.SurfaceConfig;
 import androidx.camera.core.impl.UseCaseConfig;
@@ -113,39 +114,9 @@
     }
 
     /**
-     * Check whether the input surface configuration list is under the capability of any combination
-     * of this object.
-     *
-     * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
-     * @param cameraId          the camera id of the camera device to be compared
-     * @param surfaceConfigList the surface configuration list to be compared
-     * @return the check result that whether it could be supported
-     * @throws IllegalStateException if not initialized
-     */
-    @Override
-    public boolean checkSupported(
-            boolean isConcurrentCameraModeOn,
-            @NonNull String cameraId, @Nullable List<SurfaceConfig> surfaceConfigList) {
-        if (surfaceConfigList == null || surfaceConfigList.isEmpty()) {
-            return true;
-        }
-
-        SupportedSurfaceCombination supportedSurfaceCombination =
-                mCameraSupportedSurfaceCombinationMap.get(cameraId);
-
-        boolean isSupported = false;
-        if (supportedSurfaceCombination != null) {
-            isSupported = supportedSurfaceCombination.checkSupported(isConcurrentCameraModeOn,
-                    surfaceConfigList);
-        }
-
-        return isSupported;
-    }
-
-    /**
      * Transform to a SurfaceConfig object with cameraId, image format and size info
      *
-     * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
+     * @param cameraMode  the working camera mode.
      * @param cameraId    the camera id of the camera device to transform the object
      * @param imageFormat the image format info for the surface configuration object
      * @param size        the size info for the surface configuration object
@@ -155,7 +126,7 @@
     @Nullable
     @Override
     public SurfaceConfig transformSurfaceConfig(
-            boolean isConcurrentCameraModeOn,
+            @CameraMode.Mode int cameraMode,
             @NonNull String cameraId,
             int imageFormat,
             @NonNull Size size) {
@@ -166,7 +137,7 @@
         if (supportedSurfaceCombination != null) {
             surfaceConfig =
                     supportedSurfaceCombination.transformSurfaceConfig(
-                            isConcurrentCameraModeOn,
+                            cameraMode,
                             imageFormat,
                             size);
         }
@@ -177,8 +148,7 @@
     /**
      * Retrieves a map of suggested stream specifications for the given list of use cases.
      *
-     * @param isConcurrentCameraModeOn          true if concurrent camera mode is on, otherwise
-     *                                          false.
+     * @param cameraMode                        the working camera mode.
      * @param cameraId                          the camera id of the camera device used by the
      *                                          use cases
      * @param existingSurfaces                  list of surfaces already configured and used by
@@ -197,7 +167,7 @@
     @NonNull
     @Override
     public Map<UseCaseConfig<?>, StreamSpec> getSuggestedStreamSpecs(
-            boolean isConcurrentCameraModeOn, @NonNull String cameraId,
+            @CameraMode.Mode int cameraMode, @NonNull String cameraId,
             @NonNull List<AttachedSurfaceInfo> existingSurfaces,
             @NonNull Map<UseCaseConfig<?>, List<Size>> newUseCaseConfigsSupportedSizeMap) {
         Preconditions.checkArgument(!newUseCaseConfigsSupportedSizeMap.isEmpty(),
@@ -212,7 +182,7 @@
         }
 
         return supportedSurfaceCombination.getSuggestedStreamSpecifications(
-                isConcurrentCameraModeOn,
+                cameraMode,
                 existingSurfaces,
                 newUseCaseConfigsSupportedSizeMap);
     }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java
index fe5cae3..66ce64d 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java
@@ -17,6 +17,7 @@
 package androidx.camera.camera2.internal;
 
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_MAX_RESOLUTION;
+import static androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_ROTATION;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_CONFIG_UNPACKER;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_DEFAULT_CAPTURE_CONFIG;
@@ -26,6 +27,7 @@
 
 import android.content.Context;
 import android.hardware.camera2.CameraDevice;
+import android.util.Size;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
@@ -37,6 +39,8 @@
 import androidx.camera.core.impl.OptionsBundle;
 import androidx.camera.core.impl.SessionConfig;
 import androidx.camera.core.impl.UseCaseConfigFactory;
+import androidx.camera.core.resolutionselector.ResolutionSelector;
+import androidx.camera.core.resolutionselector.ResolutionStrategy;
 
 /**
  * Implementation of UseCaseConfigFactory to provide the default camera2 configurations for use
@@ -90,7 +94,7 @@
                 captureBuilder.setTemplateType(
                         captureMode == ImageCapture.CAPTURE_MODE_ZERO_SHUTTER_LAG
                                 ? CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG :
-                        CameraDevice.TEMPLATE_STILL_CAPTURE);
+                                CameraDevice.TEMPLATE_STILL_CAPTURE);
                 break;
             case PREVIEW:
             case IMAGE_ANALYSIS:
@@ -109,8 +113,13 @@
                         : Camera2CaptureOptionUnpacker.INSTANCE);
 
         if (captureType == CaptureType.PREVIEW) {
-            mutableConfig.insertOption(OPTION_MAX_RESOLUTION,
-                    mDisplayInfoManager.getPreviewSize());
+            Size previewSize = mDisplayInfoManager.getPreviewSize();
+            mutableConfig.insertOption(OPTION_MAX_RESOLUTION, previewSize);
+            ResolutionStrategy resolutionStrategy = ResolutionStrategy.create(previewSize,
+                    ResolutionStrategy.FALLBACK_RULE_CLOSEST_LOWER);
+            mutableConfig.insertOption(OPTION_RESOLUTION_SELECTOR,
+                    new ResolutionSelector.Builder().setResolutionStrategy(
+                            resolutionStrategy).build());
         }
 
         int targetRotation = mDisplayInfoManager.getMaxSizeDisplay().getRotation();
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/GuaranteedConfigurationsUtil.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/GuaranteedConfigurationsUtil.java
index 8932669..c4dd892 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/GuaranteedConfigurationsUtil.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/GuaranteedConfigurationsUtil.java
@@ -401,6 +401,143 @@
     }
 
     /**
+     * Returns the at least supported stream combinations for the ultra high resolution pixel
+     * sensor mode.
+     */
+    @NonNull
+    public static List<SurfaceCombination> getUltraHighResolutionSupportedCombinationList() {
+        List<SurfaceCombination> combinationList = new ArrayList<>();
+
+        // (YUV, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (PRIV, RECORD)
+        // Covers (YUV, ULTRA_MAXIMUM) + (PRIV, PREVIEW) in the guaranteed table.
+        SurfaceCombination surfaceCombination1 = new SurfaceCombination();
+        surfaceCombination1.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.YUV, ConfigSize.ULTRA_MAXIMUM));
+        surfaceCombination1.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+        surfaceCombination1.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.RECORD));
+        combinationList.add(surfaceCombination1);
+
+        // (JPEG, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (PRIV, RECORD)
+        // Covers (JPEG, ULTRA_MAXIMUM) + (PRIV, PREVIEW) in the guaranteed table.
+        SurfaceCombination surfaceCombination2 = new SurfaceCombination();
+        surfaceCombination2.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.JPEG, ConfigSize.ULTRA_MAXIMUM));
+        surfaceCombination2.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+        surfaceCombination2.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.RECORD));
+        combinationList.add(surfaceCombination2);
+
+        // (RAW, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (PRIV, RECORD)
+        // Covers (RAW, ULTRA_MAXIMUM) + (PRIV, PREVIEW) in the guaranteed table.
+        SurfaceCombination surfaceCombination3 = new SurfaceCombination();
+        surfaceCombination3.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.RAW, ConfigSize.ULTRA_MAXIMUM));
+        surfaceCombination3.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+        surfaceCombination3.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.RECORD));
+        combinationList.add(surfaceCombination3);
+
+        // (YUV, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (JPEG, MAXIMUM)
+        SurfaceCombination surfaceCombination4 = new SurfaceCombination();
+        surfaceCombination4.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.YUV, ConfigSize.ULTRA_MAXIMUM));
+        surfaceCombination4.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+        surfaceCombination4.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM));
+        combinationList.add(surfaceCombination4);
+
+        // (JPEG, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (JPEG, MAXIMUM)
+        SurfaceCombination surfaceCombination5 = new SurfaceCombination();
+        surfaceCombination5.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.JPEG, ConfigSize.ULTRA_MAXIMUM));
+        surfaceCombination5.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+        surfaceCombination5.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM));
+        combinationList.add(surfaceCombination5);
+
+        // (RAW, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (JPEG, MAXIMUM)
+        SurfaceCombination surfaceCombination6 = new SurfaceCombination();
+        surfaceCombination6.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.RAW, ConfigSize.ULTRA_MAXIMUM));
+        surfaceCombination6.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+        surfaceCombination6.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM));
+        combinationList.add(surfaceCombination6);
+
+        // (YUV, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (YUV, MAXIMUM)
+        // Covers (YUV, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (YUV, RECORD) in the guaranteed table.
+        SurfaceCombination surfaceCombination7 = new SurfaceCombination();
+        surfaceCombination7.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.YUV, ConfigSize.ULTRA_MAXIMUM));
+        surfaceCombination7.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+        surfaceCombination7.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.YUV, ConfigSize.MAXIMUM));
+        combinationList.add(surfaceCombination7);
+
+        // (JPEG, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (YUV, MAXIMUM)
+        // Covers (JPEG, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (YUV, RECORD) in the guaranteed table.
+        SurfaceCombination surfaceCombination8 = new SurfaceCombination();
+        surfaceCombination8.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.JPEG, ConfigSize.ULTRA_MAXIMUM));
+        surfaceCombination8.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+        surfaceCombination8.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.YUV, ConfigSize.MAXIMUM));
+        combinationList.add(surfaceCombination8);
+
+        // (RAW, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (YUV, MAXIMUM)
+        // Covers (RAW, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (YUV, RECORD) in the guaranteed table.
+        SurfaceCombination surfaceCombination9 = new SurfaceCombination();
+        surfaceCombination9.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.RAW, ConfigSize.ULTRA_MAXIMUM));
+        surfaceCombination9.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+        surfaceCombination9.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.YUV, ConfigSize.MAXIMUM));
+        combinationList.add(surfaceCombination9);
+
+        // (YUV, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (RAW, MAXIMUM)
+        SurfaceCombination surfaceCombination10 = new SurfaceCombination();
+        surfaceCombination10.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.YUV, ConfigSize.ULTRA_MAXIMUM));
+        surfaceCombination10.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+        surfaceCombination10.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.RAW, ConfigSize.MAXIMUM));
+        combinationList.add(surfaceCombination10);
+
+        // (JPEG, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (RAW, MAXIMUM)
+        SurfaceCombination surfaceCombination11 = new SurfaceCombination();
+        surfaceCombination11.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.JPEG, ConfigSize.ULTRA_MAXIMUM));
+        surfaceCombination11.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+        surfaceCombination11.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.RAW, ConfigSize.MAXIMUM));
+        combinationList.add(surfaceCombination11);
+
+        // (RAW, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (RAW, MAXIMUM)
+        SurfaceCombination surfaceCombination12 = new SurfaceCombination();
+        surfaceCombination12.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.RAW, ConfigSize.ULTRA_MAXIMUM));
+        surfaceCombination12.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.PRIV, ConfigSize.PREVIEW));
+        surfaceCombination12.addSurfaceConfig(
+                SurfaceConfig.create(ConfigType.RAW, ConfigSize.MAXIMUM));
+        combinationList.add(surfaceCombination12);
+
+        return combinationList;
+    }
+
+    /**
      * Returns the at least supported stream combinations for concurrent cameras.
      */
     @NonNull
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 93b1c56..dcec315 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
@@ -21,16 +21,21 @@
 
 import static androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_1080P;
 import static androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_480P;
+import static androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_VGA;
 
 import android.content.Context;
+import android.graphics.ImageFormat;
 import android.graphics.SurfaceTexture;
 import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.media.CamcorderProfile;
 import android.media.MediaRecorder;
+import android.os.Build;
 import android.util.Range;
 import android.util.Size;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
@@ -42,6 +47,7 @@
 import androidx.camera.camera2.internal.compat.workaround.ResolutionCorrector;
 import androidx.camera.core.CameraUnavailableException;
 import androidx.camera.core.impl.AttachedSurfaceInfo;
+import androidx.camera.core.impl.CameraMode;
 import androidx.camera.core.impl.ImageFormatConstants;
 import androidx.camera.core.impl.StreamSpec;
 import androidx.camera.core.impl.SurfaceCombination;
@@ -49,6 +55,7 @@
 import androidx.camera.core.impl.SurfaceSizeDefinition;
 import androidx.camera.core.impl.UseCaseConfig;
 import androidx.camera.core.impl.utils.CompareSizesByArea;
+import androidx.camera.core.internal.utils.SizeUtil;
 import androidx.core.util.Preconditions;
 
 import java.util.ArrayList;
@@ -61,7 +68,7 @@
 /**
  * Camera device supported surface configuration combinations
  *
- * <p>{@link android.hardware.camera2.CameraDevice#createCaptureSession} defines the default
+ * <p>{@link CameraDevice#createCaptureSession} defines the default
  * guaranteed stream combinations for different hardware level devices. It defines what combination
  * of surface configuration type and size pairs can be supported for different hardware level camera
  * devices. This structure is used to store a list of surface combinations that are guaranteed to
@@ -71,7 +78,11 @@
 final class SupportedSurfaceCombination {
     private static final String TAG = "SupportedSurfaceCombination";
     private final List<SurfaceCombination> mSurfaceCombinations = new ArrayList<>();
+    private final List<SurfaceCombination> mUltraHighSurfaceCombinations = new ArrayList<>();
     private final List<SurfaceCombination> mConcurrentSurfaceCombinations = new ArrayList<>();
+
+    private Map<Integer, List<SurfaceCombination>> mCameraModeToSupportedCombinationsMap =
+            new HashMap<>();
     private final String mCameraId;
     private final CamcorderProfileHelper mCamcorderProfileHelper;
     private final CameraCharacteristicsCompat mCharacteristics;
@@ -80,6 +91,11 @@
     private final int mHardwareLevel;
     private boolean mIsRawSupported = false;
     private boolean mIsBurstCaptureSupported = false;
+    private boolean mIsConcurrentCameraModeSupported = false;
+    private boolean mIsUltraHighResolutionSensorSupported = false;
+    private final List<Integer> mSizeDefinitionFormats = new ArrayList<>(Arrays.asList(
+            ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE, ImageFormat.JPEG,
+            ImageFormat.YUV_420_888));
     @VisibleForTesting
     SurfaceSizeDefinition mSurfaceSizeDefinition;
     @NonNull
@@ -116,14 +132,34 @@
                 } else if (capability
                         == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE) {
                     mIsBurstCaptureSupported = true;
+                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && capability
+                        == CameraCharacteristics
+                        .REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR) {
+                    mIsUltraHighResolutionSensorSupported = true;
                 }
             }
         }
 
         generateSupportedCombinationList();
-        if (context.getPackageManager().hasSystemFeature(FEATURE_CAMERA_CONCURRENT)) {
+
+        if (mIsUltraHighResolutionSensorSupported) {
+            generateUltraHighSupportedCombinationList();
+        }
+
+        mIsConcurrentCameraModeSupported =
+                context.getPackageManager().hasSystemFeature(FEATURE_CAMERA_CONCURRENT);
+        if (mIsConcurrentCameraModeSupported) {
             generateConcurrentSupportedCombinationList();
         }
+
+        if (mIsRawSupported) {
+            // In CameraDevice's javadoc, RAW refers to the ImageFormat.RAW_SENSOR format. But
+            // a test in ImageCaptureTest using RAW10 to do the test. Adding the RAW10 format to
+            // make sure this is compatible with the original users.
+            mSizeDefinitionFormats.add(ImageFormat.RAW_SENSOR);
+            mSizeDefinitionFormats.add(ImageFormat.RAW10);
+        }
+
         generateSurfaceSizeDefinition();
         checkCustomization();
     }
@@ -144,18 +180,17 @@
      * Check whether the input surface configuration list is under the capability of any combination
      * of this object.
      *
-     * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
+     * @param cameraMode        the working camera mode.
      * @param surfaceConfigList the surface configuration list to be compared
      * @return the check result that whether it could be supported
      */
     boolean checkSupported(
-            boolean isConcurrentCameraModeOn,
+            @CameraMode.Mode int cameraMode,
             List<SurfaceConfig> surfaceConfigList) {
         boolean isSupported = false;
 
-        List<SurfaceCombination> targetSurfaceCombinations = isConcurrentCameraModeOn
-                ? mConcurrentSurfaceCombinations : mSurfaceCombinations;
-        for (SurfaceCombination surfaceCombination : targetSurfaceCombinations) {
+        for (SurfaceCombination surfaceCombination : getSurfaceCombinationsByCameraMode(
+                cameraMode)) {
             isSupported = surfaceCombination.isSupported(surfaceConfigList);
 
             if (isSupported) {
@@ -167,25 +202,51 @@
     }
 
     /**
+     * Returns the supported surface combinations according to the specified camera mode.
+     */
+    private List<SurfaceCombination> getSurfaceCombinationsByCameraMode(
+            @CameraMode.Mode int cameraMode) {
+        if (mCameraModeToSupportedCombinationsMap.containsKey(cameraMode)) {
+            return mCameraModeToSupportedCombinationsMap.get(cameraMode);
+        }
+
+        List<SurfaceCombination> supportedSurfaceCombinations = new ArrayList<>();
+
+        switch (cameraMode) {
+            case CameraMode.CONCURRENT_CAMERA:
+                supportedSurfaceCombinations = mConcurrentSurfaceCombinations;
+                break;
+            case CameraMode.ULTRA_HIGH_RESOLUTION_CAMERA:
+                supportedSurfaceCombinations.addAll(mUltraHighSurfaceCombinations);
+                supportedSurfaceCombinations.addAll(mSurfaceCombinations);
+                break;
+            default:
+                supportedSurfaceCombinations.addAll(mSurfaceCombinations);
+                break;
+        }
+
+        mCameraModeToSupportedCombinationsMap.put(cameraMode, supportedSurfaceCombinations);
+
+        return supportedSurfaceCombinations;
+    }
+
+    /**
      * Transform to a SurfaceConfig object with image format and size info
      *
-     * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
+     * @param cameraMode  the working camera mode.
      * @param imageFormat the image format info for the surface configuration object
      * @param size        the size info for the surface configuration object
      * @return new {@link SurfaceConfig} object
      */
     SurfaceConfig transformSurfaceConfig(
-            boolean isConcurrentCameraModeOn,
+            @CameraMode.Mode int cameraMode,
             int imageFormat,
             Size size) {
-        Size maxOutputSizeForConcurrentMode = isConcurrentCameraModeOn
-                ? getMaxOutputSizeByFormat(imageFormat) : null;
         return SurfaceConfig.transformSurfaceConfig(
-                isConcurrentCameraModeOn,
+                cameraMode,
                 imageFormat,
                 size,
-                mSurfaceSizeDefinition,
-                maxOutputSizeForConcurrentMode);
+                mSurfaceSizeDefinition);
     }
 
     static int getMaxFramerate(CameraCharacteristicsCompat characteristics, int imageFormat,
@@ -403,8 +464,7 @@
     /**
      * Finds the suggested stream specifications of the newly added UseCaseConfig.
      *
-     * @param isConcurrentCameraModeOn          true if concurrent camera mode is on, otherwise
-     *                                          false.
+     * @param cameraMode                        the working camera mode.
      * @param attachedSurfaces                  the existing surfaces.
      * @param newUseCaseConfigsSupportedSizeMap newly added UseCaseConfig to supported output
      *                                          sizes map.
@@ -416,7 +476,7 @@
      */
     @NonNull
     Map<UseCaseConfig<?>, StreamSpec> getSuggestedStreamSpecifications(
-            boolean isConcurrentCameraModeOn,
+            @CameraMode.Mode int cameraMode,
             @NonNull List<AttachedSurfaceInfo> attachedSurfaces,
             @NonNull Map<UseCaseConfig<?>, List<Size>> newUseCaseConfigsSupportedSizeMap) {
         // Refresh Preview Size based on current display configurations.
@@ -431,18 +491,15 @@
         // Use the small size (640x480) for new use cases to check whether there is any possible
         // supported combination first
         for (UseCaseConfig<?> useCaseConfig : newUseCaseConfigs) {
-            Size maxOutputSizeForConcurrentMode = isConcurrentCameraModeOn
-                    ? getMaxOutputSizeByFormat(useCaseConfig.getInputFormat()) : null;
             surfaceConfigs.add(
                     SurfaceConfig.transformSurfaceConfig(
-                            isConcurrentCameraModeOn,
+                            cameraMode,
                             useCaseConfig.getInputFormat(),
                             new Size(640, 480),
-                            mSurfaceSizeDefinition,
-                            maxOutputSizeForConcurrentMode));
+                            mSurfaceSizeDefinition));
         }
 
-        if (!checkSupported(isConcurrentCameraModeOn, surfaceConfigs)) {
+        if (!checkSupported(cameraMode, surfaceConfigs)) {
             throw new IllegalArgumentException(
                     "No supported surface combination is found for camera device - Id : "
                             + mCameraId + ".  May be attempting to bind too many use cases. "
@@ -512,15 +569,12 @@
                 UseCaseConfig<?> newUseCase =
                         newUseCaseConfigs.get(useCasesPriorityOrder.get(i));
                 // add new use case/size config to list of surfaces
-                Size maxOutputSizeForConcurrentMode = isConcurrentCameraModeOn
-                        ? getMaxOutputSizeByFormat(newUseCase.getInputFormat()) : null;
                 surfaceConfigList.add(
                         SurfaceConfig.transformSurfaceConfig(
-                                isConcurrentCameraModeOn,
+                                cameraMode,
                                 newUseCase.getInputFormat(),
                                 size,
-                                mSurfaceSizeDefinition,
-                                maxOutputSizeForConcurrentMode));
+                                mSurfaceSizeDefinition));
 
                 // get the maximum fps of the new surface and update the maximum fps of the
                 // proposed configuration
@@ -541,7 +595,7 @@
             }
 
             // only change the saved config if you get another that has a better max fps
-            if (checkSupported(isConcurrentCameraModeOn, surfaceConfigList)) {
+            if (checkSupported(cameraMode, surfaceConfigList)) {
                 // if the config is supported by the device but doesn't meet the target framerate,
                 // save the config
                 if (savedConfigMaxFps == Integer.MAX_VALUE) {
@@ -682,14 +736,13 @@
     /**
      * Get max supported output size for specific image format
      *
+     * @param map the original stream configuration map without quirks applied.
      * @param imageFormat the image format info
+     * @param highResolutionIncluded whether high resolution output sizes are included
      * @return the max supported output size for the image format
      */
-    Size getMaxOutputSizeByFormat(int imageFormat) {
-        // Needs to retrieve the output size from the original stream configuration map without
-        // quirks applied.
-        StreamConfigurationMap map =
-                mCharacteristics.getStreamConfigurationMapCompat().toStreamConfigurationMap();
+    private Size getMaxOutputSizeByFormat(StreamConfigurationMap map, int imageFormat,
+            boolean highResolutionIncluded) {
         Size[] outputSizes;
         if (imageFormat == ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE) {
             // This is a little tricky that 0x22 that is internal defined in
@@ -701,7 +754,27 @@
         } else {
             outputSizes = map.getOutputSizes(imageFormat);
         }
-        return Collections.max(Arrays.asList(outputSizes), new CompareSizesByArea());
+
+        if (outputSizes == null || outputSizes.length == 0) {
+            return null;
+        }
+
+        CompareSizesByArea compareSizesByArea = new CompareSizesByArea();
+        Size maxSize = Collections.max(Arrays.asList(outputSizes), compareSizesByArea);
+
+        // Checks high resolution output sizes
+        Size maxHighResolutionSize = SizeUtil.RESOLUTION_ZERO;
+        if (Build.VERSION.SDK_INT >= 23 && highResolutionIncluded) {
+            Size[] highResolutionOutputSizes = Api23Impl.getHighResolutionOutputSizes(map,
+                    imageFormat);
+
+            if (highResolutionOutputSizes != null && highResolutionOutputSizes.length > 0) {
+                maxHighResolutionSize = Collections.max(Arrays.asList(highResolutionOutputSizes),
+                        compareSizesByArea);
+            }
+        }
+
+        return Collections.max(Arrays.asList(maxSize, maxHighResolutionSize), compareSizesByArea);
     }
 
     private void generateSupportedCombinationList() {
@@ -713,6 +786,11 @@
                 mExtraSupportedSurfaceCombinationsContainer.get(mCameraId, mHardwareLevel));
     }
 
+    private void generateUltraHighSupportedCombinationList() {
+        mUltraHighSurfaceCombinations.addAll(
+                GuaranteedConfigurationsUtil.getUltraHighResolutionSupportedCombinationList());
+    }
+
     private void generateConcurrentSupportedCombinationList() {
         mConcurrentSurfaceCombinations.addAll(
                 GuaranteedConfigurationsUtil.getConcurrentSupportedCombinationList());
@@ -726,13 +804,81 @@
     // *********************************************************************************************
 
     private void generateSurfaceSizeDefinition() {
-        Size analysisSize = new Size(640, 480);
-        Size s720p = new Size(1280, 720);
         Size previewSize = mDisplayInfoManager.getPreviewSize();
-        Size s1440p = new Size(1920, 1440);
         Size recordSize = getRecordSize();
-        mSurfaceSizeDefinition =
-                SurfaceSizeDefinition.create(analysisSize, s720p, previewSize, s1440p, recordSize);
+        mSurfaceSizeDefinition = SurfaceSizeDefinition.create(RESOLUTION_VGA,
+                createS720pOrS1440pSizeMap(SizeUtil.RESOLUTION_720P), previewSize,
+                createS720pOrS1440pSizeMap(SizeUtil.RESOLUTION_1440P), recordSize,
+                createMaximumSizeMap(), createUltraMaximumSizeMap());
+    }
+
+    /**
+     * Creates the format to s720p or s720p size map.
+     *
+     * <p>s720p refers to the 720p (1280 x 720) or the maximum supported resolution for the
+     * particular format returned by {@link StreamConfigurationMap#getOutputSizes(int)},
+     * whichever is smaller.
+     *
+     * <p>s1440p refers to the 1440p (1920 x 1440) or the maximum supported resolution for the
+     * particular format returned by {@link StreamConfigurationMap#getOutputSizes(int)},
+     * whichever is smaller.
+     *
+     * @param targetSize the target size to create the map.
+     * @return the format to s720p or s720p size map.
+     */
+    @NonNull
+    private Map<Integer, Size> createS720pOrS1440pSizeMap(@NonNull Size targetSize) {
+        Map<Integer, Size> resultMap = new HashMap<>();
+        if (!mIsConcurrentCameraModeSupported) {
+            return resultMap;
+        }
+        CompareSizesByArea compareSizesByArea = new CompareSizesByArea();
+        StreamConfigurationMap originalMap =
+                mCharacteristics.getStreamConfigurationMapCompat().toStreamConfigurationMap();
+        for (int format : mSizeDefinitionFormats) {
+            Size maxOutputSize = getMaxOutputSizeByFormat(originalMap, format, false);
+            resultMap.put(format, maxOutputSize == null ? targetSize
+                    : Collections.min(Arrays.asList(targetSize, maxOutputSize),
+                            compareSizesByArea));
+        }
+
+        return resultMap;
+    }
+
+    @NonNull
+    private Map<Integer, Size> createMaximumSizeMap() {
+        Map<Integer, Size> resultMap = new HashMap<>();
+        StreamConfigurationMap originalMap =
+                mCharacteristics.getStreamConfigurationMapCompat().toStreamConfigurationMap();
+        for (int format : mSizeDefinitionFormats) {
+            Size maxOutputSize = getMaxOutputSizeByFormat(originalMap, format, true);
+            if (maxOutputSize != null) {
+                resultMap.put(format, maxOutputSize);
+            }
+        }
+
+        return resultMap;
+    }
+
+    @NonNull
+    private Map<Integer, Size> createUltraMaximumSizeMap() {
+        Map<Integer, Size> resultMap = new HashMap<>();
+        // Maximum resolution mode is supported since API level 31
+        if (Build.VERSION.SDK_INT < 31 || !mIsUltraHighResolutionSensorSupported) {
+            return resultMap;
+        }
+
+        StreamConfigurationMap maximumResolutionMap = mCharacteristics.get(
+                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION);
+
+        if (maximumResolutionMap == null) {
+            return resultMap;
+        }
+        for (int format : mSizeDefinitionFormats) {
+            resultMap.put(format, getMaxOutputSizeByFormat(maximumResolutionMap, format, true));
+        }
+
+        return resultMap;
     }
 
     private void refreshPreviewSize() {
@@ -743,10 +889,12 @@
             Size previewSize = mDisplayInfoManager.getPreviewSize();
             mSurfaceSizeDefinition = SurfaceSizeDefinition.create(
                     mSurfaceSizeDefinition.getAnalysisSize(),
-                    mSurfaceSizeDefinition.getS720pSize(),
+                    mSurfaceSizeDefinition.getS720pSizeMap(),
                     previewSize,
-                    mSurfaceSizeDefinition.getS1440pSize(),
-                    mSurfaceSizeDefinition.getRecordSize());
+                    mSurfaceSizeDefinition.getS1440pSizeMap(),
+                    mSurfaceSizeDefinition.getRecordSize(),
+                    mSurfaceSizeDefinition.getMaximumSizeMap(),
+                    mSurfaceSizeDefinition.getUltraMaximumSizeMap());
         }
     }
 
@@ -843,4 +991,18 @@
 
         return recordSize;
     }
+
+    @RequiresApi(23)
+    static class Api23Impl {
+        private Api23Impl() {
+            // 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/ZslControlImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlImpl.java
index bcf5423..d69d972 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
@@ -74,9 +74,6 @@
     static final int MAX_IMAGES = RING_BUFFER_CAPACITY * 3;
 
     @NonNull
-    private final Map<Integer, Size> mReprocessingInputSizeMap;
-
-    @NonNull
     private final CameraCharacteristicsCompat mCameraCharacteristicsCompat;
 
     @VisibleForTesting
@@ -104,8 +101,6 @@
                 isCapabilitySupported(mCameraCharacteristicsCompat,
                         REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
 
-        mReprocessingInputSizeMap = createReprocessingInputSizeMap(mCameraCharacteristicsCompat);
-
         mShouldZslDisabledByQuirks = DeviceQuirks.get(ZslDisablerQuirk.class) != null;
 
         mImageRingBuffer = new ZslRingBuffer(
@@ -149,6 +144,9 @@
             return;
         }
 
+        Map<Integer, Size> mReprocessingInputSizeMap =
+                createReprocessingInputSizeMap(mCameraCharacteristicsCompat);
+
         // Due to b/232268355 and feedback from pixel team that private format will have better
         // performance, we will use private only for zsl.
         if (!mIsPrivateReprocessingSupported
@@ -283,9 +281,15 @@
     @NonNull
     private Map<Integer, Size> createReprocessingInputSizeMap(
             @NonNull CameraCharacteristicsCompat cameraCharacteristicsCompat) {
-        StreamConfigurationMap map =
-                cameraCharacteristicsCompat.get(
-                        CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+        StreamConfigurationMap map = null;
+        try {
+            map = cameraCharacteristicsCompat.get(
+                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+        } catch (AssertionError e) {
+            // Some devices may throw AssertionError when retrieving the stream configuration map.
+            Logger.e(TAG, "Failed to retrieve StreamConfigurationMap, error = "
+                    + e.getMessage());
+        }
 
         if (map == null || map.getInputFormats() == null) {
             return new HashMap<>();
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CameraInfoImplTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CameraInfoImplTest.java
index 0c0a626..235acef 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CameraInfoImplTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CameraInfoImplTest.java
@@ -18,6 +18,8 @@
 
 import static android.hardware.camera2.CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES;
 
+import static androidx.camera.core.DynamicRange.SDR;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -31,6 +33,7 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.params.DynamicRangeProfiles;
 import android.os.Build;
 import android.util.Pair;
 import android.util.Range;
@@ -45,6 +48,7 @@
 import androidx.camera.camera2.internal.compat.CameraManagerCompat;
 import androidx.camera.core.CameraInfo;
 import androidx.camera.core.CameraSelector;
+import androidx.camera.core.DynamicRange;
 import androidx.camera.core.ExposureState;
 import androidx.camera.core.FocusMeteringAction;
 import androidx.camera.core.SurfaceOrientedMeteringPointFactory;
@@ -94,8 +98,10 @@
     private static final boolean CAMERA0_FLASH_INFO_BOOLEAN = true;
     private static final int CAMERA0_SUPPORTED_PRIVATE_REPROCESSING =
             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING;
+    private static final int CAMERA0_SUPPORTED_DYNAMIC_RANGE_TEN_BIT =
+            CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT;
     private static final int[] CAMERA0_SUPPORTED_CAPABILITIES = new int[] {
-            CAMERA0_SUPPORTED_PRIVATE_REPROCESSING,
+            CAMERA0_SUPPORTED_PRIVATE_REPROCESSING, CAMERA0_SUPPORTED_DYNAMIC_RANGE_TEN_BIT
     };
     private static final float[] CAMERA0_LENS_FOCAL_LENGTH = new float[]{
             3.0F,
@@ -105,13 +111,15 @@
     private static final SizeF CAMERA0_SENSOR_PHYSICAL_SIZE = new SizeF(1.5F, 1F);
     private static final Rect CAMERA0_SENSOR_ACTIVE_ARRAY_SIZE = new Rect(0, 0, 1920, 1080);
     private static final Size CAMERA0_SENSOR_PIXEL_ARRAY_SIZE = new Size(1920, 1080);
-
     private static final Range<?>[] CAMERA0_AE_FPS_RANGES = {
             new Range<>(12, 30),
             new Range<>(24, 24),
             new Range<>(30, 30),
             new Range<>(60, 60)
     };
+    private static final DynamicRangeProfiles CAMERA0_DYNAMIC_RANGE_PROFILES =
+            new DynamicRangeProfiles(new long[]{DynamicRangeProfiles.HLG10, 0, 0});
+
     private static final String CAMERA1_ID = "1";
     private static final int CAMERA1_SUPPORTED_HARDWARE_LEVEL =
             CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3;
@@ -135,6 +143,8 @@
             new Range<>(12, 30),
             new Range<>(30, 30),
     };
+    private static final DynamicRange HLG10 = new DynamicRange(DynamicRange.FORMAT_HLG,
+            DynamicRange.BIT_DEPTH_10_BIT);
 
     private CameraCharacteristicsCompat mCameraCharacteristics0;
     private CameraManagerCompat mCameraManagerCompat;
@@ -146,7 +156,7 @@
 
     @Test
     public void canCreateCameraInfo() throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         CameraInfoInternal cameraInfoInternal =
                 new Camera2CameraInfoImpl(CAMERA0_ID, mCameraManagerCompat);
@@ -156,7 +166,7 @@
 
     @Test
     public void cameraInfo_canReturnSensorOrientation() throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         CameraInfoInternal cameraInfoInternal =
                 new Camera2CameraInfoImpl(CAMERA0_ID, mCameraManagerCompat);
@@ -167,7 +177,7 @@
     @Test
     public void cameraInfo_canCalculateCorrectRelativeRotation_forBackCamera()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         CameraInfoInternal cameraInfoInternal =
                 new Camera2CameraInfoImpl(CAMERA0_ID, mCameraManagerCompat);
@@ -186,7 +196,7 @@
     @Test
     public void cameraInfo_canCalculateCorrectRelativeRotation_forFrontCamera()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         CameraInfoInternal cameraInfoInternal =
                 new Camera2CameraInfoImpl(CAMERA1_ID, mCameraManagerCompat);
@@ -204,7 +214,7 @@
 
     @Test
     public void cameraInfo_canReturnLensFacing() throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         CameraInfoInternal cameraInfoInternal =
                 new Camera2CameraInfoImpl(CAMERA0_ID, mCameraManagerCompat);
@@ -214,7 +224,7 @@
     @Test
     public void cameraInfo_canReturnHasFlashUnit_forBackCamera()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         CameraInfoInternal cameraInfoInternal =
                 new Camera2CameraInfoImpl(CAMERA0_ID, mCameraManagerCompat);
@@ -224,7 +234,7 @@
     @Test
     public void cameraInfo_canReturnHasFlashUnit_forFrontCamera()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         CameraInfoInternal cameraInfoInternal =
                 new Camera2CameraInfoImpl(CAMERA1_ID, mCameraManagerCompat);
@@ -234,7 +244,7 @@
     @Test
     public void cameraInfoWithoutCameraControl_canReturnDefaultTorchState()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         Camera2CameraInfoImpl camera2CameraInfoImpl =
                 new Camera2CameraInfoImpl(CAMERA0_ID, mCameraManagerCompat);
@@ -245,7 +255,7 @@
     @Test
     public void cameraInfoWithCameraControl_canReturnTorchState()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         when(mMockTorchControl.getTorchState()).thenReturn(new MutableLiveData<>(TorchState.ON));
         Camera2CameraInfoImpl camera2CameraInfoImpl =
@@ -257,7 +267,7 @@
     @Test
     public void torchStateLiveData_SameInstanceBeforeAndAfterCameraControlLink()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         Camera2CameraInfoImpl camera2CameraInfoImpl =
                 new Camera2CameraInfoImpl(CAMERA0_ID, mCameraManagerCompat);
@@ -278,7 +288,7 @@
     @Test
     public void cameraInfoWithCameraControl_getZoom_valueIsCorrect()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         ZoomState zoomState = ImmutableZoomState.create(3.0f, 8.0f, 1.0f, 0.2f);
         when(mMockZoomControl.getZoomState()).thenReturn(new MutableLiveData<>(zoomState));
@@ -293,7 +303,7 @@
     @Test
     public void cameraInfoWithoutCameraControl_getDetaultZoomState()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         Camera2CameraInfoImpl camera2CameraInfoImpl =
                 new Camera2CameraInfoImpl(CAMERA0_ID, mCameraManagerCompat);
@@ -304,7 +314,7 @@
     @Test
     public void zoomStateLiveData_SameInstanceBeforeAndAfterCameraControlLink()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         Camera2CameraInfoImpl camera2CameraInfoImpl =
                 new Camera2CameraInfoImpl(CAMERA0_ID, mCameraManagerCompat);
@@ -324,7 +334,7 @@
     @Test
     public void cameraInfoWithCameraControl_canReturnExposureState()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         ExposureState exposureState = new ExposureStateImpl(mCameraCharacteristics0, 2);
         when(mExposureControl.getExposureState()).thenReturn(exposureState);
@@ -339,7 +349,7 @@
     @Test
     public void cameraInfoWithoutCameraControl_canReturnDefaultExposureState()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         Camera2CameraInfoImpl camera2CameraInfoImpl =
                 new Camera2CameraInfoImpl(CAMERA0_ID, mCameraManagerCompat);
@@ -359,7 +369,7 @@
 
     @Test
     public void cameraInfo_getImplementationType_legacy() throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         final CameraInfoInternal cameraInfo =
                 new Camera2CameraInfoImpl(CAMERA0_ID, mCameraManagerCompat);
@@ -369,7 +379,7 @@
 
     @Test
     public void cameraInfo_getImplementationType_noneLegacy() throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         final CameraInfoInternal cameraInfo = new Camera2CameraInfoImpl(
                 CAMERA1_ID, mCameraManagerCompat);
@@ -380,7 +390,7 @@
     @Test
     public void addSessionCameraCaptureCallback_isCalledToCameraControl()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         final Camera2CameraInfoImpl cameraInfo = new Camera2CameraInfoImpl(
                 CAMERA1_ID, mCameraManagerCompat);
@@ -396,7 +406,7 @@
     @Test
     public void removeSessionCameraCaptureCallback_isCalledToCameraControl()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         final Camera2CameraInfoImpl cameraInfo = new Camera2CameraInfoImpl(
                 CAMERA1_ID, mCameraManagerCompat);
@@ -411,7 +421,7 @@
     @Test
     public void addSessionCameraCaptureCallbackWithoutCameraControl_attachedToCameraControlLater()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         final Camera2CameraInfoImpl cameraInfo = new Camera2CameraInfoImpl(
                 CAMERA1_ID, mCameraManagerCompat);
@@ -427,7 +437,7 @@
     @Test
     public void removeSessionCameraCaptureCallbackWithoutCameraControl_callbackIsRemoved()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         final Camera2CameraInfoImpl cameraInfo = new Camera2CameraInfoImpl(
                 CAMERA1_ID, mCameraManagerCompat);
@@ -451,7 +461,7 @@
     @Test
     public void cameraInfoWithCameraControl_canReturnIsFocusMeteringSupported()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         final Camera2CameraInfoImpl cameraInfo = new Camera2CameraInfoImpl(
                 CAMERA0_ID, mCameraManagerCompat);
@@ -473,7 +483,7 @@
     @Test
     public void canReturnCameraCharacteristicsMapWithPhysicalCameras()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         CameraCharacteristics characteristics0 = mock(CameraCharacteristics.class);
         CameraCharacteristics characteristicsPhysical2 = mock(CameraCharacteristics.class);
@@ -498,7 +508,7 @@
     @Test
     public void canReturnCameraCharacteristicsMapWithMainCamera()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         Camera2CameraInfoImpl impl = new Camera2CameraInfoImpl("0", mCameraManagerCompat);
         Map<String, CameraCharacteristics> map = impl.getCameraCharacteristicsMap();
@@ -510,7 +520,7 @@
     @Test
     public void cameraInfoWithCameraControl_canReturnIsPrivateReprocessingSupported()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         final Camera2CameraInfoImpl cameraInfo = new Camera2CameraInfoImpl(
                 CAMERA0_ID, mCameraManagerCompat);
@@ -521,7 +531,7 @@
     @Config(minSdk = 23)
     @Test
     public void isZslSupported_apiVersionMet_returnTrue() throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         final Camera2CameraInfoImpl cameraInfo = new Camera2CameraInfoImpl(
                 CAMERA0_ID, mCameraManagerCompat);
@@ -532,7 +542,7 @@
     @Config(maxSdk = 22)
     @Test
     public void isZslSupported_apiVersionNotMet_returnFalse() throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         final Camera2CameraInfoImpl cameraInfo = new Camera2CameraInfoImpl(
                 CAMERA0_ID, mCameraManagerCompat);
@@ -543,7 +553,7 @@
     @Test
     public void isZslSupported_noReprocessingCapability_returnFalse()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ false);
+        init(/* hasAvailableCapabilities = */ false);
 
         final Camera2CameraInfoImpl cameraInfo = new Camera2CameraInfoImpl(
                 CAMERA0_ID, mCameraManagerCompat);
@@ -558,7 +568,7 @@
         ReflectionHelpers.setStaticField(Build.class, "BRAND", "samsung");
         ReflectionHelpers.setStaticField(Build.class, "MODEL", "SM-F936B");
 
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         final Camera2CameraInfoImpl cameraInfo = new Camera2CameraInfoImpl(
                 CAMERA0_ID, mCameraManagerCompat);
@@ -573,7 +583,7 @@
         ReflectionHelpers.setStaticField(Build.class, "BRAND", "samsung");
         ReflectionHelpers.setStaticField(Build.class, "MODEL", "SM-G973");
 
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         final Camera2CameraInfoImpl cameraInfo = new Camera2CameraInfoImpl(
                 CAMERA0_ID, mCameraManagerCompat);
@@ -588,7 +598,7 @@
         ReflectionHelpers.setStaticField(Build.class, "BRAND", "xiaomi");
         ReflectionHelpers.setStaticField(Build.class, "MODEL", "Mi 8");
 
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         final Camera2CameraInfoImpl cameraInfo = new Camera2CameraInfoImpl(
                 CAMERA0_ID, mCameraManagerCompat);
@@ -603,7 +613,7 @@
         ReflectionHelpers.setStaticField(Build.class, "BRAND", "xiaomi");
         ReflectionHelpers.setStaticField(Build.class, "MODEL", "Mi A1");
 
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         final Camera2CameraInfoImpl cameraInfo = new Camera2CameraInfoImpl(
                 CAMERA0_ID, mCameraManagerCompat);
@@ -613,7 +623,7 @@
 
     @Test
     public void canReturnSupportedResolutions() throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ true);
+        init(/* hasAvailableCapabilities = */ true);
 
         Camera2CameraInfoImpl cameraInfo = new Camera2CameraInfoImpl(CAMERA0_ID,
                 mCameraManagerCompat);
@@ -637,7 +647,7 @@
 
     @Test
     public void cameraInfo_canReturnIntrinsicZoomRatio() throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ false);
+        init(/* hasAvailableCapabilities = */ false);
 
         final Camera2CameraInfoImpl cameraInfo = new Camera2CameraInfoImpl(CAMERA2_ID,
                 mCameraManagerCompat);
@@ -649,7 +659,7 @@
 
     @Test
     public void cameraInfo_canReturnSupportedFpsRanges() throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ false);
+        init(/* hasAvailableCapabilities = */ false);
 
         final Camera2CameraInfoImpl cameraInfo0 = new Camera2CameraInfoImpl(CAMERA0_ID,
                 mCameraManagerCompat);
@@ -666,7 +676,7 @@
     @Test
     public void cameraInfo_returnsEmptyFpsRanges_whenNotSupported()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ false);
+        init(/* hasAvailableCapabilities = */ false);
 
         final Camera2CameraInfoImpl cameraInfo1 = new Camera2CameraInfoImpl(CAMERA1_ID,
                 mCameraManagerCompat);
@@ -679,7 +689,7 @@
     @Test
     public void cameraInfo_checkDefaultCameraIntrinsicZoomRatio()
             throws CameraAccessExceptionCompat {
-        init(/* hasReprocessingCapabilities = */ false);
+        init(/* hasAvailableCapabilities = */ false);
 
         final Camera2CameraInfoImpl cameraInfo = new Camera2CameraInfoImpl(CAMERA0_ID,
                 mCameraManagerCompat);
@@ -690,6 +700,31 @@
         assertThat(resultZoomRatio).isEqualTo(1.0F);
     }
 
+    @Config(minSdk = 33)
+    @Test
+    public void apiVersionMet_canReturnSupportedDynamicRanges() throws CameraAccessExceptionCompat {
+        init(/* hasAvailableCapabilities = */ true);
+
+        final Camera2CameraInfoImpl cameraInfo = new Camera2CameraInfoImpl(
+                CAMERA0_ID, mCameraManagerCompat);
+
+        Set<DynamicRange> supportedDynamicRanges = cameraInfo.getSupportedDynamicRanges();
+        assertThat(supportedDynamicRanges).containsExactly(SDR, HLG10);
+    }
+
+    @Config(maxSdk = 32)
+    @Test
+    public void apiVersionNotMet_canReturnSupportedDynamicRanges()
+            throws CameraAccessExceptionCompat {
+        init(/* hasAvailableCapabilities = */ true);
+
+        final Camera2CameraInfoImpl cameraInfo = new Camera2CameraInfoImpl(
+                CAMERA0_ID, mCameraManagerCompat);
+
+        Set<DynamicRange> supportedDynamicRanges = cameraInfo.getSupportedDynamicRanges();
+        assertThat(supportedDynamicRanges).containsExactly(SDR);
+    }
+
     private CameraManagerCompat initCameraManagerWithPhysicalIds(
             List<Pair<String, CameraCharacteristics>> cameraIdsAndCharacteristicsList) {
         FakeCameraManagerImpl cameraManagerImpl = new FakeCameraManagerImpl();
@@ -701,8 +736,8 @@
         return CameraManagerCompat.from(cameraManagerImpl);
     }
 
-    private void init(boolean hasReprocessingCapabilities) throws CameraAccessExceptionCompat {
-        initCameras(hasReprocessingCapabilities);
+    private void init(boolean hasAvailableCapabilities) throws CameraAccessExceptionCompat {
+        initCameras(hasAvailableCapabilities);
 
         mCameraManagerCompat =
                 CameraManagerCompat.from((Context) ApplicationProvider.getApplicationContext());
@@ -720,7 +755,7 @@
         when(mMockCameraControl.getFocusMeteringControl()).thenReturn(mFocusMeteringControl);
     }
 
-    private void initCameras(boolean hasReprocessingCapabilities) {
+    private void initCameras(boolean hasAvailableCapabilities) {
         // **** Camera 0 characteristics ****//
         CameraCharacteristics characteristics0 =
                 ShadowCameraCharacteristics.newCameraCharacteristics();
@@ -771,8 +806,14 @@
                 CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
                 CAMERA0_AE_FPS_RANGES);
 
+        if (Build.VERSION.SDK_INT >= 33) {
+            shadowCharacteristics0.set(
+                    CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES,
+                    CAMERA0_DYNAMIC_RANGE_PROFILES);
+        }
+
         // Mock the request capability
-        if (hasReprocessingCapabilities) {
+        if (hasAvailableCapabilities) {
             shadowCharacteristics0.set(REQUEST_AVAILABLE_CAPABILITIES,
                     CAMERA0_SUPPORTED_CAPABILITIES);
         }
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java
index b871f2b..5d49349 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java
@@ -17,8 +17,6 @@
 package androidx.camera.camera2.internal;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -43,8 +41,8 @@
 import androidx.camera.core.CameraXConfig;
 import androidx.camera.core.InitializationException;
 import androidx.camera.core.impl.CameraDeviceSurfaceManager;
+import androidx.camera.core.impl.CameraMode;
 import androidx.camera.core.impl.ImageFormatConstants;
-import androidx.camera.core.impl.SurfaceCombination;
 import androidx.camera.core.impl.SurfaceConfig;
 import androidx.camera.core.impl.SurfaceConfig.ConfigSize;
 import androidx.camera.core.impl.SurfaceConfig.ConfigType;
@@ -69,7 +67,6 @@
 import org.robolectric.shadows.ShadowCameraCharacteristics;
 import org.robolectric.shadows.ShadowCameraManager;
 
-import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -160,210 +157,9 @@
     }
 
     @Test
-    public void checkLegacySurfaceCombinationSupportedInLegacyDevice()
-            throws Exception {
-        SupportedSurfaceCombination supportedSurfaceCombination =
-                new SupportedSurfaceCombination(
-                        mContext, LEGACY_CAMERA_ID, getCameraManagerCompat(),
-                        mMockCamcorderProfileHelper);
-
-        List<SurfaceCombination> combinationList =
-                GuaranteedConfigurationsUtil.getLegacySupportedCombinationList();
-
-        for (SurfaceCombination combination : combinationList) {
-            boolean isSupported =
-                    mSurfaceManager.checkSupported(
-                            /* isConcurrentCameraModeOn = */false,
-                            LEGACY_CAMERA_ID,
-                            combination.getSurfaceConfigList());
-            assertTrue(isSupported);
-        }
-    }
-
-    @Test
-    public void checkLimitedSurfaceCombinationNotSupportedInLegacyDevice()
-            throws Exception {
-        SupportedSurfaceCombination supportedSurfaceCombination =
-                new SupportedSurfaceCombination(
-                        mContext, LEGACY_CAMERA_ID, getCameraManagerCompat(),
-                        mMockCamcorderProfileHelper);
-
-        List<SurfaceCombination> combinationList =
-                GuaranteedConfigurationsUtil.getLimitedSupportedCombinationList();
-
-        for (SurfaceCombination combination : combinationList) {
-            boolean isSupported =
-                    mSurfaceManager.checkSupported(
-                            /* isConcurrentCameraModeOn = */false,
-                            LEGACY_CAMERA_ID, combination.getSurfaceConfigList());
-            assertFalse(isSupported);
-        }
-    }
-
-    @Test
-    public void checkFullSurfaceCombinationNotSupportedInLegacyDevice()
-            throws Exception {
-        SupportedSurfaceCombination supportedSurfaceCombination =
-                new SupportedSurfaceCombination(
-                        mContext, LEGACY_CAMERA_ID, getCameraManagerCompat(),
-                        mMockCamcorderProfileHelper);
-
-        List<SurfaceCombination> combinationList =
-                GuaranteedConfigurationsUtil.getFullSupportedCombinationList();
-
-        for (SurfaceCombination combination : combinationList) {
-            boolean isSupported =
-                    mSurfaceManager.checkSupported(
-                            /* isConcurrentCameraModeOn = */false,
-                            LEGACY_CAMERA_ID, combination.getSurfaceConfigList());
-            assertFalse(isSupported);
-        }
-    }
-
-    @Test
-    public void checkLevel3SurfaceCombinationNotSupportedInLegacyDevice()
-            throws Exception {
-        SupportedSurfaceCombination supportedSurfaceCombination =
-                new SupportedSurfaceCombination(
-                        mContext, LEGACY_CAMERA_ID, getCameraManagerCompat(),
-                        mMockCamcorderProfileHelper);
-
-        List<SurfaceCombination> combinationList =
-                GuaranteedConfigurationsUtil.getLevel3SupportedCombinationList();
-
-        for (SurfaceCombination combination : combinationList) {
-            boolean isSupported =
-                    mSurfaceManager.checkSupported(
-                            /* isConcurrentCameraModeOn = */false,
-                            LEGACY_CAMERA_ID, combination.getSurfaceConfigList());
-            assertFalse(isSupported);
-        }
-    }
-
-    @Test
-    public void checkLimitedSurfaceCombinationSupportedInLimitedDevice()
-            throws Exception {
-        SupportedSurfaceCombination supportedSurfaceCombination =
-                new SupportedSurfaceCombination(
-                        mContext, LIMITED_CAMERA_ID, getCameraManagerCompat(),
-                        mMockCamcorderProfileHelper);
-
-        List<SurfaceCombination> combinationList =
-                GuaranteedConfigurationsUtil.getLimitedSupportedCombinationList();
-
-        for (SurfaceCombination combination : combinationList) {
-            boolean isSupported =
-                    mSurfaceManager.checkSupported(
-                            /* isConcurrentCameraModeOn = */false,
-                            LIMITED_CAMERA_ID, combination.getSurfaceConfigList());
-            assertTrue(isSupported);
-        }
-    }
-
-    @Test
-    public void checkFullSurfaceCombinationNotSupportedInLimitedDevice()
-            throws Exception {
-        SupportedSurfaceCombination supportedSurfaceCombination =
-                new SupportedSurfaceCombination(
-                        mContext, LIMITED_CAMERA_ID, getCameraManagerCompat(),
-                        mMockCamcorderProfileHelper);
-
-        List<SurfaceCombination> combinationList =
-                GuaranteedConfigurationsUtil.getFullSupportedCombinationList();
-
-        for (SurfaceCombination combination : combinationList) {
-            boolean isSupported =
-                    mSurfaceManager.checkSupported(
-                            /* isConcurrentCameraModeOn = */false,
-                            LIMITED_CAMERA_ID, combination.getSurfaceConfigList());
-            assertFalse(isSupported);
-        }
-    }
-
-    @Test
-    public void checkLevel3SurfaceCombinationNotSupportedInLimitedDevice()
-            throws Exception {
-        SupportedSurfaceCombination supportedSurfaceCombination =
-                new SupportedSurfaceCombination(
-                        mContext, LIMITED_CAMERA_ID, getCameraManagerCompat(),
-                        mMockCamcorderProfileHelper);
-
-        List<SurfaceCombination> combinationList =
-                GuaranteedConfigurationsUtil.getLevel3SupportedCombinationList();
-
-        for (SurfaceCombination combination : combinationList) {
-            boolean isSupported =
-                    mSurfaceManager.checkSupported(
-                            /* isConcurrentCameraModeOn = */false,
-                            LIMITED_CAMERA_ID, combination.getSurfaceConfigList());
-            assertFalse(isSupported);
-        }
-    }
-
-    @Test
-    public void checkFullSurfaceCombinationSupportedInFullDevice()
-            throws Exception {
-        SupportedSurfaceCombination supportedSurfaceCombination =
-                new SupportedSurfaceCombination(
-                        mContext, FULL_CAMERA_ID, getCameraManagerCompat(),
-                        mMockCamcorderProfileHelper);
-
-        List<SurfaceCombination> combinationList =
-                GuaranteedConfigurationsUtil.getFullSupportedCombinationList();
-
-        for (SurfaceCombination combination : combinationList) {
-            boolean isSupported =
-                    mSurfaceManager.checkSupported(
-                            /* isConcurrentCameraModeOn = */false,
-                            FULL_CAMERA_ID, combination.getSurfaceConfigList());
-            assertTrue(isSupported);
-        }
-    }
-
-    @Test
-    public void checkLevel3SurfaceCombinationNotSupportedInFullDevice()
-            throws Exception {
-        SupportedSurfaceCombination supportedSurfaceCombination =
-                new SupportedSurfaceCombination(
-                        mContext, FULL_CAMERA_ID, getCameraManagerCompat(),
-                        mMockCamcorderProfileHelper);
-
-        List<SurfaceCombination> combinationList =
-                GuaranteedConfigurationsUtil.getLevel3SupportedCombinationList();
-
-        for (SurfaceCombination combination : combinationList) {
-            boolean isSupported =
-                    mSurfaceManager.checkSupported(
-                            /* isConcurrentCameraModeOn = */false,
-                            FULL_CAMERA_ID, combination.getSurfaceConfigList());
-            assertFalse(isSupported);
-        }
-    }
-
-    @Test
-    public void checkLevel3SurfaceCombinationSupportedInLevel3Device()
-            throws Exception {
-        SupportedSurfaceCombination supportedSurfaceCombination =
-                new SupportedSurfaceCombination(
-                        mContext, LEVEL3_CAMERA_ID, getCameraManagerCompat(),
-                        mMockCamcorderProfileHelper);
-
-        List<SurfaceCombination> combinationList =
-                GuaranteedConfigurationsUtil.getLevel3SupportedCombinationList();
-
-        for (SurfaceCombination combination : combinationList) {
-            boolean isSupported =
-                    mSurfaceManager.checkSupported(
-                            /* isConcurrentCameraModeOn = */false,
-                            LEVEL3_CAMERA_ID, combination.getSurfaceConfigList());
-            assertTrue(isSupported);
-        }
-    }
-
-    @Test
     public void transformSurfaceConfigWithYUVAnalysisSize() {
         SurfaceConfig surfaceConfig = mSurfaceManager.transformSurfaceConfig(
-                /* isConcurrentCameraModeOn = */false,
+                CameraMode.DEFAULT,
                 LEGACY_CAMERA_ID, ImageFormat.YUV_420_888, mAnalysisSize);
         SurfaceConfig expectedSurfaceConfig =
                 SurfaceConfig.create(ConfigType.YUV, ConfigSize.VGA);
@@ -373,7 +169,7 @@
     @Test
     public void transformSurfaceConfigWithYUVPreviewSize() {
         SurfaceConfig surfaceConfig = mSurfaceManager.transformSurfaceConfig(
-                /* isConcurrentCameraModeOn = */false,
+                CameraMode.DEFAULT,
                 LEGACY_CAMERA_ID, ImageFormat.YUV_420_888, mPreviewSize);
         SurfaceConfig expectedSurfaceConfig =
                 SurfaceConfig.create(ConfigType.YUV, ConfigSize.PREVIEW);
@@ -383,7 +179,7 @@
     @Test
     public void transformSurfaceConfigWithYUVRecordSize() {
         SurfaceConfig surfaceConfig = mSurfaceManager.transformSurfaceConfig(
-                /* isConcurrentCameraModeOn = */false,
+                CameraMode.DEFAULT,
                 LEGACY_CAMERA_ID, ImageFormat.YUV_420_888, mRecordSize);
         SurfaceConfig expectedSurfaceConfig =
                 SurfaceConfig.create(ConfigType.YUV, SurfaceConfig.ConfigSize.RECORD);
@@ -393,7 +189,7 @@
     @Test
     public void transformSurfaceConfigWithYUVMaximumSize() {
         SurfaceConfig surfaceConfig = mSurfaceManager.transformSurfaceConfig(
-                /* isConcurrentCameraModeOn = */false,
+                CameraMode.DEFAULT,
                 LEGACY_CAMERA_ID, ImageFormat.YUV_420_888, mMaximumSize);
         SurfaceConfig expectedSurfaceConfig =
                 SurfaceConfig.create(SurfaceConfig.ConfigType.YUV, ConfigSize.MAXIMUM);
@@ -404,7 +200,7 @@
     public void transformSurfaceConfigWithJPEGAnalysisSize() {
         SurfaceConfig surfaceConfig =
                 mSurfaceManager.transformSurfaceConfig(
-                        /* isConcurrentCameraModeOn = */false,
+                        CameraMode.DEFAULT,
                         LEGACY_CAMERA_ID, ImageFormat.JPEG, mAnalysisSize);
         SurfaceConfig expectedSurfaceConfig =
                 SurfaceConfig.create(SurfaceConfig.ConfigType.JPEG, ConfigSize.VGA);
@@ -415,7 +211,7 @@
     public void transformSurfaceConfigWithJPEGPreviewSize() {
         SurfaceConfig surfaceConfig =
                 mSurfaceManager.transformSurfaceConfig(
-                        /* isConcurrentCameraModeOn = */false,
+                        CameraMode.DEFAULT,
                         LEGACY_CAMERA_ID, ImageFormat.JPEG, mPreviewSize);
         SurfaceConfig expectedSurfaceConfig =
                 SurfaceConfig.create(ConfigType.JPEG, ConfigSize.PREVIEW);
@@ -426,7 +222,7 @@
     public void transformSurfaceConfigWithJPEGRecordSize() {
         SurfaceConfig surfaceConfig =
                 mSurfaceManager.transformSurfaceConfig(
-                        /* isConcurrentCameraModeOn = */false,
+                        CameraMode.DEFAULT,
                         LEGACY_CAMERA_ID, ImageFormat.JPEG, mRecordSize);
         SurfaceConfig expectedSurfaceConfig =
                 SurfaceConfig.create(ConfigType.JPEG, ConfigSize.RECORD);
@@ -437,7 +233,7 @@
     public void transformSurfaceConfigWithJPEGMaximumSize() {
         SurfaceConfig surfaceConfig =
                 mSurfaceManager.transformSurfaceConfig(
-                        /* isConcurrentCameraModeOn = */false,
+                        CameraMode.DEFAULT,
                         LEGACY_CAMERA_ID, ImageFormat.JPEG, mMaximumSize);
         SurfaceConfig expectedSurfaceConfig =
                 SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM);
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
index fad6f14..d1fbc51 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
@@ -42,6 +42,7 @@
 import androidx.camera.core.UseCase
 import androidx.camera.core.impl.AttachedSurfaceInfo
 import androidx.camera.core.impl.CameraDeviceSurfaceManager
+import androidx.camera.core.impl.CameraMode
 import androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE
 import androidx.camera.core.impl.StreamSpec
 import androidx.camera.core.impl.SurfaceCombination
@@ -104,6 +105,19 @@
     Size(800, 450), // 16:9
     Size(640, 480), // 4:3
 )
+private val HIGH_RESOLUTION_MAXIMUM_SIZE = Size(6000, 4500)
+private val HIGH_RESOLUTION_SUPPORTED_SIZES = arrayOf(
+    Size(6000, 4500), // 4:3
+    Size(6000, 3375), // 16:9
+)
+private val ULTRA_HIGH_MAXIMUM_SIZE = Size(8000, 6000)
+private val MAXIMUM_RESOLUTION_SUPPORTED_SIZES = arrayOf(
+    Size(7200, 5400), // 4:3
+    Size(7200, 4050), // 16:9
+)
+private val MAXIMUM_RESOLUTION_HIGH_RESOLUTION_SUPPORTED_SIZES = arrayOf(
+    Size(8000, 6000), // 4:3
+)
 
 /** Robolectric test for [SupportedSurfaceCombination] class */
 @RunWith(RobolectricTestRunner::class)
@@ -173,7 +187,7 @@
         )
         GuaranteedConfigurationsUtil.getLegacySupportedCombinationList().forEach {
             assertThat(supportedSurfaceCombination.checkSupported(
-                false, it.surfaceConfigList)).isTrue()
+                CameraMode.DEFAULT, it.surfaceConfigList)).isTrue()
         }
     }
 
@@ -184,7 +198,13 @@
             context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
         )
         GuaranteedConfigurationsUtil.getLegacySupportedCombinationList().also {
-            assertThat(isAllSubConfigListSupported(false, supportedSurfaceCombination, it)).isTrue()
+            assertThat(
+                isAllSubConfigListSupported(
+                    CameraMode.DEFAULT,
+                    supportedSurfaceCombination,
+                    it
+                )
+            ).isTrue()
         }
     }
 
@@ -196,7 +216,7 @@
         )
         GuaranteedConfigurationsUtil.getLimitedSupportedCombinationList().forEach {
             assertThat(supportedSurfaceCombination.checkSupported(
-                false, it.surfaceConfigList)).isFalse()
+                CameraMode.DEFAULT, it.surfaceConfigList)).isFalse()
         }
     }
 
@@ -208,7 +228,7 @@
         )
         GuaranteedConfigurationsUtil.getFullSupportedCombinationList().forEach {
             assertThat(supportedSurfaceCombination.checkSupported(
-                false, it.surfaceConfigList)).isFalse()
+                CameraMode.DEFAULT, it.surfaceConfigList)).isFalse()
         }
     }
 
@@ -220,7 +240,7 @@
         )
         GuaranteedConfigurationsUtil.getLevel3SupportedCombinationList().forEach {
             assertThat(supportedSurfaceCombination.checkSupported(
-                false, it.surfaceConfigList)).isFalse()
+                CameraMode.DEFAULT, it.surfaceConfigList)).isFalse()
         }
     }
 
@@ -234,7 +254,7 @@
         )
         GuaranteedConfigurationsUtil.getLimitedSupportedCombinationList().forEach {
             assertThat(supportedSurfaceCombination.checkSupported(
-                false, it.surfaceConfigList)).isTrue()
+                CameraMode.DEFAULT, it.surfaceConfigList)).isTrue()
         }
     }
 
@@ -247,7 +267,13 @@
             context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
         )
         GuaranteedConfigurationsUtil.getLimitedSupportedCombinationList().also {
-            assertThat(isAllSubConfigListSupported(false, supportedSurfaceCombination, it)).isTrue()
+            assertThat(
+                isAllSubConfigListSupported(
+                    CameraMode.DEFAULT,
+                    supportedSurfaceCombination,
+                    it
+                )
+            ).isTrue()
         }
     }
 
@@ -261,7 +287,7 @@
         )
         GuaranteedConfigurationsUtil.getFullSupportedCombinationList().forEach {
             assertThat(supportedSurfaceCombination.checkSupported(
-                false, it.surfaceConfigList)).isFalse()
+                CameraMode.DEFAULT, it.surfaceConfigList)).isFalse()
         }
     }
 
@@ -275,7 +301,7 @@
         )
         GuaranteedConfigurationsUtil.getLevel3SupportedCombinationList().forEach {
             assertThat(supportedSurfaceCombination.checkSupported(
-                false, it.surfaceConfigList)).isFalse()
+                CameraMode.DEFAULT, it.surfaceConfigList)).isFalse()
         }
     }
 
@@ -289,7 +315,7 @@
         )
         GuaranteedConfigurationsUtil.getFullSupportedCombinationList().forEach {
             assertThat(supportedSurfaceCombination.checkSupported(
-                false, it.surfaceConfigList)).isTrue()
+                CameraMode.DEFAULT, it.surfaceConfigList)).isTrue()
         }
     }
 
@@ -302,7 +328,13 @@
             context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
         )
         GuaranteedConfigurationsUtil.getFullSupportedCombinationList().also {
-            assertThat(isAllSubConfigListSupported(false, supportedSurfaceCombination, it)).isTrue()
+            assertThat(
+                isAllSubConfigListSupported(
+                    CameraMode.DEFAULT,
+                    supportedSurfaceCombination,
+                    it
+                )
+            ).isTrue()
         }
     }
 
@@ -316,7 +348,7 @@
         )
         GuaranteedConfigurationsUtil.getLevel3SupportedCombinationList().forEach {
             assertThat(supportedSurfaceCombination.checkSupported(
-                false, it.surfaceConfigList)).isFalse()
+                CameraMode.DEFAULT, it.surfaceConfigList)).isFalse()
         }
     }
 
@@ -331,7 +363,7 @@
         )
         GuaranteedConfigurationsUtil.getLimitedSupportedCombinationList().forEach {
             assertThat(supportedSurfaceCombination.checkSupported(
-                false, it.surfaceConfigList)).isTrue()
+                CameraMode.DEFAULT, it.surfaceConfigList)).isTrue()
         }
     }
 
@@ -346,7 +378,7 @@
         )
         GuaranteedConfigurationsUtil.getLegacySupportedCombinationList().forEach {
             assertThat(supportedSurfaceCombination.checkSupported(
-                false, it.surfaceConfigList)).isTrue()
+                CameraMode.DEFAULT, it.surfaceConfigList)).isTrue()
         }
     }
 
@@ -361,7 +393,7 @@
         )
         GuaranteedConfigurationsUtil.getFullSupportedCombinationList().forEach {
             assertThat(supportedSurfaceCombination.checkSupported(
-                false, it.surfaceConfigList)).isTrue()
+                CameraMode.DEFAULT, it.surfaceConfigList)).isTrue()
         }
     }
 
@@ -376,7 +408,7 @@
         )
         GuaranteedConfigurationsUtil.getRAWSupportedCombinationList().forEach {
             assertThat(supportedSurfaceCombination.checkSupported(
-                false, it.surfaceConfigList)).isTrue()
+                CameraMode.DEFAULT, it.surfaceConfigList)).isTrue()
         }
     }
 
@@ -390,7 +422,7 @@
         )
         GuaranteedConfigurationsUtil.getLevel3SupportedCombinationList().forEach {
             assertThat(supportedSurfaceCombination.checkSupported(
-                false, it.surfaceConfigList)).isTrue()
+                CameraMode.DEFAULT, it.surfaceConfigList)).isTrue()
         }
     }
 
@@ -403,7 +435,13 @@
             context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
         )
         GuaranteedConfigurationsUtil.getLevel3SupportedCombinationList().also {
-            assertThat(isAllSubConfigListSupported(false, supportedSurfaceCombination, it)).isTrue()
+            assertThat(
+                isAllSubConfigListSupported(
+                    CameraMode.DEFAULT,
+                    supportedSurfaceCombination,
+                    it
+                )
+            ).isTrue()
         }
     }
 
@@ -419,7 +457,7 @@
         )
         GuaranteedConfigurationsUtil.getConcurrentSupportedCombinationList().forEach {
             assertThat(supportedSurfaceCombination.checkSupported(
-                true, it.surfaceConfigList)).isTrue()
+                CameraMode.CONCURRENT_CAMERA, it.surfaceConfigList)).isTrue()
         }
     }
 
@@ -435,7 +473,47 @@
         )
         GuaranteedConfigurationsUtil.getConcurrentSupportedCombinationList().also {
             assertThat(isAllSubConfigListSupported(
-                true, supportedSurfaceCombination, it)).isTrue()
+                CameraMode.CONCURRENT_CAMERA, supportedSurfaceCombination, it)).isTrue()
+        }
+    }
+
+    @Test
+    @Config(minSdk = Build.VERSION_CODES.S)
+    fun checkUltraHighResolutionSurfaceCombinationSupportedInUltraHighCameraMode() {
+        setupCameraAndInitCameraX(
+            maximumResolutionSupportedSizes = MAXIMUM_RESOLUTION_SUPPORTED_SIZES,
+            maximumResolutionHighResolutionSupportedSizes =
+            MAXIMUM_RESOLUTION_HIGH_RESOLUTION_SUPPORTED_SIZES,
+            capabilities = intArrayOf(
+                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR
+            )
+        )
+        val supportedSurfaceCombination = SupportedSurfaceCombination(
+            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
+        )
+        GuaranteedConfigurationsUtil.getUltraHighResolutionSupportedCombinationList().forEach {
+            assertThat(supportedSurfaceCombination.checkSupported(
+                CameraMode.ULTRA_HIGH_RESOLUTION_CAMERA, it.surfaceConfigList)).isTrue()
+        }
+    }
+
+    @Test
+    @Config(minSdk = Build.VERSION_CODES.S)
+    fun checkUltraHighResolutionSurfaceCombinationSubListSupportedInUltraHighCameraMode() {
+        setupCameraAndInitCameraX(
+            maximumResolutionSupportedSizes = MAXIMUM_RESOLUTION_SUPPORTED_SIZES,
+            maximumResolutionHighResolutionSupportedSizes =
+            MAXIMUM_RESOLUTION_HIGH_RESOLUTION_SUPPORTED_SIZES,
+            capabilities = intArrayOf(
+                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR
+            )
+        )
+        val supportedSurfaceCombination = SupportedSurfaceCombination(
+            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
+        )
+        GuaranteedConfigurationsUtil.getUltraHighResolutionSupportedCombinationList().also {
+            assertThat(isAllSubConfigListSupported(
+                CameraMode.ULTRA_HIGH_RESOLUTION_CAMERA, supportedSurfaceCombination, it)).isTrue()
         }
     }
 
@@ -452,7 +530,7 @@
             context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            false,
+            CameraMode.DEFAULT,
             ImageFormat.YUV_420_888, RESOLUTION_VGA
         )
         val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.YUV, ConfigSize.VGA)
@@ -466,7 +544,7 @@
             context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            false,
+            CameraMode.DEFAULT,
             ImageFormat.YUV_420_888, PREVIEW_SIZE
         )
         val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.YUV, ConfigSize.PREVIEW)
@@ -480,7 +558,7 @@
             context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            false,
+            CameraMode.DEFAULT,
             ImageFormat.YUV_420_888, RECORD_SIZE
         )
         val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.YUV, ConfigSize.RECORD)
@@ -494,7 +572,7 @@
             context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            false,
+            CameraMode.DEFAULT,
             ImageFormat.YUV_420_888, MAXIMUM_SIZE
         )
         val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.YUV, ConfigSize.MAXIMUM)
@@ -508,7 +586,7 @@
             context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            false,
+            CameraMode.DEFAULT,
             ImageFormat.JPEG, RESOLUTION_VGA
         )
         val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.JPEG, ConfigSize.VGA)
@@ -522,7 +600,7 @@
             context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            false,
+            CameraMode.DEFAULT,
             ImageFormat.JPEG, PREVIEW_SIZE
         )
         val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.JPEG, ConfigSize.PREVIEW)
@@ -536,7 +614,7 @@
             context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            false,
+            CameraMode.DEFAULT,
             ImageFormat.JPEG, RECORD_SIZE
         )
         val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.JPEG, ConfigSize.RECORD)
@@ -550,7 +628,7 @@
             context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            false,
+            CameraMode.DEFAULT,
             ImageFormat.JPEG, MAXIMUM_SIZE
         )
         val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.JPEG, ConfigSize.MAXIMUM)
@@ -565,7 +643,7 @@
             context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            true,
+            CameraMode.CONCURRENT_CAMERA,
             ImageFormat.PRIVATE, RESOLUTION_720P
         )
         val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.PRIV, ConfigSize.s720p)
@@ -580,7 +658,7 @@
             context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            true,
+            CameraMode.CONCURRENT_CAMERA,
             ImageFormat.YUV_420_888, RESOLUTION_720P
         )
         val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.YUV, ConfigSize.s720p)
@@ -595,7 +673,7 @@
             context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            true,
+            CameraMode.CONCURRENT_CAMERA,
             ImageFormat.JPEG, RESOLUTION_720P
         )
         val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.JPEG, ConfigSize.s720p)
@@ -610,7 +688,7 @@
             context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            true,
+            CameraMode.CONCURRENT_CAMERA,
             ImageFormat.PRIVATE, RESOLUTION_1440P
         )
         val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.PRIV, ConfigSize.s1440p)
@@ -625,7 +703,7 @@
             context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            true,
+            CameraMode.CONCURRENT_CAMERA,
             ImageFormat.YUV_420_888, RESOLUTION_1440P
         )
         val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.YUV, ConfigSize.s1440p)
@@ -640,13 +718,47 @@
             context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
         )
         val surfaceConfig = supportedSurfaceCombination.transformSurfaceConfig(
-            true,
+            CameraMode.CONCURRENT_CAMERA,
             ImageFormat.JPEG, RESOLUTION_1440P
         )
         val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.JPEG, ConfigSize.s1440p)
         assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
     }
 
+    @Test
+    @Config(minSdk = 31)
+    fun transformSurfaceConfigWithUltraHighResolution() {
+        setupCameraAndInitCameraX(
+            maximumResolutionSupportedSizes = MAXIMUM_RESOLUTION_SUPPORTED_SIZES,
+            maximumResolutionHighResolutionSupportedSizes =
+            MAXIMUM_RESOLUTION_HIGH_RESOLUTION_SUPPORTED_SIZES,
+            capabilities = intArrayOf(
+                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR
+            )
+        )
+        val supportedSurfaceCombination = SupportedSurfaceCombination(
+            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
+        )
+        assertThat(
+            supportedSurfaceCombination.transformSurfaceConfig(
+                CameraMode.DEFAULT,
+                ImageFormat.PRIVATE, ULTRA_HIGH_MAXIMUM_SIZE
+            )
+        ).isEqualTo(SurfaceConfig.create(ConfigType.PRIV, ConfigSize.ULTRA_MAXIMUM))
+        assertThat(
+            supportedSurfaceCombination.transformSurfaceConfig(
+                CameraMode.DEFAULT,
+                ImageFormat.YUV_420_888, ULTRA_HIGH_MAXIMUM_SIZE
+            )
+        ).isEqualTo(SurfaceConfig.create(ConfigType.YUV, ConfigSize.ULTRA_MAXIMUM))
+        assertThat(
+            supportedSurfaceCombination.transformSurfaceConfig(
+                CameraMode.DEFAULT,
+                ImageFormat.JPEG, ULTRA_HIGH_MAXIMUM_SIZE
+            )
+        ).isEqualTo(SurfaceConfig.create(ConfigType.JPEG, ConfigSize.ULTRA_MAXIMUM))
+    }
+
     // //////////////////////////////////////////////////////////////////////////////////////////
     //
     // Resolution selection tests for LEGACY-level guaranteed configurations
@@ -1074,7 +1186,8 @@
         }
         getSuggestedSpecsAndVerify(
             useCaseExpectedResultMap,
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3,
+            capabilities = intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_RAW)
         )
     }
 
@@ -1095,7 +1208,8 @@
         }
         getSuggestedSpecsAndVerify(
             useCaseExpectedResultMap,
-            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+            hardwareLevel = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3,
+            capabilities = intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_RAW)
         )
     }
 
@@ -1348,7 +1462,7 @@
         val useCaseConfigToOutputSizesMap =
             getUseCaseConfigToOutputSizesMap(useCaseConfigMap.values.toList())
         val suggestedStreamSpecs = supportedSurfaceCombination.getSuggestedStreamSpecifications(
-            false,
+            CameraMode.DEFAULT,
             attachedSurfaceInfoList,
             useCaseConfigToOutputSizesMap
         )
@@ -1735,16 +1849,117 @@
     // //////////////////////////////////////////////////////////////////////////////////////////
 
     @Test
-    fun getMaximumSizeForImageFormat() {
+    fun generateCorrectSurfaceDefinition() {
+        shadowOf(context.packageManager).setSystemFeature(
+            FEATURE_CAMERA_CONCURRENT, true)
         setupCameraAndInitCameraX()
         val supportedSurfaceCombination = SupportedSurfaceCombination(
             context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
         )
-        val maximumYUVSize =
-            supportedSurfaceCombination.getMaxOutputSizeByFormat(ImageFormat.YUV_420_888)
-        assertThat(maximumYUVSize).isEqualTo(MAXIMUM_SIZE)
-        val maximumJPEGSize = supportedSurfaceCombination.getMaxOutputSizeByFormat(ImageFormat.JPEG)
-        assertThat(maximumJPEGSize).isEqualTo(MAXIMUM_SIZE)
+        val imageFormat = ImageFormat.JPEG
+        assertThat(
+            supportedSurfaceCombination.mSurfaceSizeDefinition.s720pSizeMap[imageFormat]
+        ).isEqualTo(
+            RESOLUTION_720P
+        )
+        assertThat(
+            supportedSurfaceCombination.mSurfaceSizeDefinition.previewSize
+        ).isEqualTo(
+            PREVIEW_SIZE
+        )
+        assertThat(
+            supportedSurfaceCombination.mSurfaceSizeDefinition.s1440pSizeMap[imageFormat]
+        ).isEqualTo(
+            RESOLUTION_1440P
+        )
+        assertThat(
+            supportedSurfaceCombination.mSurfaceSizeDefinition.recordSize
+        ).isEqualTo(
+            RECORD_SIZE
+        )
+        assertThat(
+            supportedSurfaceCombination.mSurfaceSizeDefinition.maximumSizeMap[imageFormat]
+        ).isEqualTo(
+            MAXIMUM_SIZE
+        )
+        assertThat(
+            supportedSurfaceCombination.mSurfaceSizeDefinition.ultraMaximumSizeMap
+        ).isEmpty()
+    }
+
+    @Test
+    fun correctS720pSize_withSmallerOutputSizes() {
+        shadowOf(context.packageManager).setSystemFeature(
+            FEATURE_CAMERA_CONCURRENT, true)
+        setupCameraAndInitCameraX(
+            supportedSizes = arrayOf(RESOLUTION_VGA)
+        )
+        val supportedSurfaceCombination = SupportedSurfaceCombination(
+            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
+        )
+        val imageFormat = ImageFormat.JPEG
+        assertThat(
+            supportedSurfaceCombination.mSurfaceSizeDefinition.s720pSizeMap[imageFormat]
+        ).isEqualTo(
+            RESOLUTION_VGA
+        )
+    }
+
+    @Test
+    fun correctS1440pSize_withSmallerOutputSizes() {
+        shadowOf(context.packageManager).setSystemFeature(
+            FEATURE_CAMERA_CONCURRENT, true)
+        setupCameraAndInitCameraX(
+            supportedSizes = arrayOf(RESOLUTION_VGA)
+        )
+        val supportedSurfaceCombination = SupportedSurfaceCombination(
+            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
+        )
+        val imageFormat = ImageFormat.JPEG
+        assertThat(
+            supportedSurfaceCombination.mSurfaceSizeDefinition.s1440pSizeMap[imageFormat]
+        ).isEqualTo(
+            RESOLUTION_VGA
+        )
+    }
+
+    @Test
+    @Config(minSdk = 23)
+    fun correctMaximumSize_withHighResolutionOutputSizes() {
+        setupCameraAndInitCameraX(
+            supportedHighResolutionSizes = HIGH_RESOLUTION_SUPPORTED_SIZES
+        )
+        val supportedSurfaceCombination = SupportedSurfaceCombination(
+            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
+        )
+        val imageFormat = ImageFormat.JPEG
+        assertThat(
+            supportedSurfaceCombination.mSurfaceSizeDefinition.maximumSizeMap[imageFormat]
+        ).isEqualTo(
+            HIGH_RESOLUTION_MAXIMUM_SIZE
+        )
+    }
+
+    @Test
+    @Config(minSdk = 32)
+    fun correctUltraMaximumSize_withMaximumResolutionMap() {
+        setupCameraAndInitCameraX(
+            maximumResolutionSupportedSizes = MAXIMUM_RESOLUTION_SUPPORTED_SIZES,
+            maximumResolutionHighResolutionSupportedSizes =
+            MAXIMUM_RESOLUTION_HIGH_RESOLUTION_SUPPORTED_SIZES,
+            capabilities = intArrayOf(
+                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR
+            )
+        )
+        val supportedSurfaceCombination = SupportedSurfaceCombination(
+            context, DEFAULT_CAMERA_ID, cameraManagerCompat!!, mockCamcorderProfileHelper
+        )
+        val imageFormat = ImageFormat.JPEG
+        assertThat(
+            supportedSurfaceCombination.mSurfaceSizeDefinition.ultraMaximumSizeMap[imageFormat]
+        ).isEqualTo(
+            ULTRA_HIGH_MAXIMUM_SIZE
+        )
     }
 
     @Test
@@ -1798,6 +2013,12 @@
      * [LANDSCAPE_PIXEL_ARRAY_SIZE].
      * @param supportedSizes the supported sizes of the camera. Default value is
      * [DEFAULT_SUPPORTED_SIZES].
+     * @param supportedHighResolutionSizes the high resolution supported sizes of the camera.
+     * Default value is null.
+     * @param maximumResolutionSupportedSizes the maximum resolution mode supported sizes of the
+     * camera. Default value is null.
+     * @param maximumResolutionHighResolutionSupportedSizes the maximum resolution mode high
+     * resolution supported sizes of the camera. Default value is null.
      * @param capabilities the capabilities of the camera. Default value is null.
      */
     private fun setupCameraAndInitCameraX(
@@ -1807,6 +2028,8 @@
         pixelArraySize: Size = LANDSCAPE_PIXEL_ARRAY_SIZE,
         supportedSizes: Array<Size> = DEFAULT_SUPPORTED_SIZES,
         supportedHighResolutionSizes: Array<Size>? = null,
+        maximumResolutionSupportedSizes: Array<Size>? = null,
+        maximumResolutionHighResolutionSupportedSizes: Array<Size>? = null,
         capabilities: IntArray? = null
     ) {
         setupCamera(
@@ -1816,6 +2039,8 @@
             pixelArraySize,
             supportedSizes,
             supportedHighResolutionSizes,
+            maximumResolutionSupportedSizes,
+            maximumResolutionHighResolutionSupportedSizes,
             capabilities
         )
 
@@ -1857,6 +2082,12 @@
      * [LANDSCAPE_PIXEL_ARRAY_SIZE].
      * @param supportedSizes the supported sizes of the camera. Default value is
      * [DEFAULT_SUPPORTED_SIZES].
+     * @param supportedHighResolutionSizes the high resolution supported sizes of the camera.
+     * Default value is null.
+     * @param maximumResolutionSupportedSizes the maximum resolution mode supported sizes of the
+     * camera. Default value is null.
+     * @param maximumResolutionHighResolutionSupportedSizes the maximum resolution mode high
+     * resolution supported sizes of the camera. Default value is null.
      * @param capabilities the capabilities of the camera. Default value is null.
      */
     fun setupCamera(
@@ -1866,6 +2097,8 @@
         pixelArraySize: Size = LANDSCAPE_PIXEL_ARRAY_SIZE,
         supportedSizes: Array<Size> = DEFAULT_SUPPORTED_SIZES,
         supportedHighResolutionSizes: Array<Size>? = null,
+        maximumResolutionSupportedSizes: Array<Size>? = null,
+        maximumResolutionHighResolutionSupportedSizes: Array<Size>? = null,
         capabilities: IntArray? = null
     ) {
         val mockMap = Mockito.mock(StreamConfigurationMap::class.java).also {
@@ -1943,6 +2176,21 @@
             }
         }
 
+        val maximumResolutionMap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
+            (maximumResolutionSupportedSizes != null ||
+                maximumResolutionHighResolutionSupportedSizes != null)) {
+            Mockito.mock(StreamConfigurationMap::class.java).also {
+                Mockito.`when`(it.getOutputSizes(ArgumentMatchers.anyInt()))
+                    .thenReturn(maximumResolutionSupportedSizes)
+                Mockito.`when`(it.getOutputSizes(SurfaceTexture::class.java))
+                    .thenReturn(maximumResolutionSupportedSizes)
+                Mockito.`when`(it.getHighResolutionOutputSizes(ArgumentMatchers.anyInt()))
+                    .thenReturn(maximumResolutionHighResolutionSupportedSizes)
+            }
+        } else {
+            null
+        }
+
         val deviceFPSRanges: Array<Range<Int>?> = arrayOf(
             Range(10, 22),
             Range(22, 22),
@@ -1962,6 +2210,15 @@
             set(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP, mockMap)
             set(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, deviceFPSRanges)
 
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+                maximumResolutionMap?.let {
+                    set(
+                        CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION,
+                        maximumResolutionMap
+                    )
+                }
+            }
+
             capabilities?.let {
                 set(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES, it)
             }
@@ -2002,7 +2259,7 @@
     }
 
     private fun isAllSubConfigListSupported(
-        isConcurrentCameraModeOn: Boolean,
+        cameraMode: Int = CameraMode.DEFAULT,
         supportedSurfaceCombination: SupportedSurfaceCombination,
         combinationList: List<SurfaceCombination>
     ): Boolean {
@@ -2018,7 +2275,7 @@
                     removeAt(index)
                 }
                 if (!supportedSurfaceCombination.checkSupported(
-                        isConcurrentCameraModeOn, subConfigurationList
+                        cameraMode, subConfigurationList
                     )
                 ) {
                     return false
diff --git a/camera/camera-core/api/current.txt b/camera/camera-core/api/current.txt
index 4614cf8..1f0d922 100644
--- a/camera/camera-core/api/current.txt
+++ b/camera/camera-core/api/current.txt
@@ -359,6 +359,12 @@
     method public static float getDefaultPointSize();
   }
 
+  @RequiresApi(21) public class MirrorMode {
+    field public static final int MIRROR_MODE_OFF = 0; // 0x0
+    field public static final int MIRROR_MODE_ON = 1; // 0x1
+    field public static final int MIRROR_MODE_ON_FRONT_ONLY = 2; // 0x2
+  }
+
   @RequiresApi(21) public final class Preview extends androidx.camera.core.UseCase {
     method public androidx.camera.core.ResolutionInfo? getResolutionInfo();
     method public int getTargetRotation();
@@ -395,7 +401,7 @@
     ctor public SurfaceOrientedMeteringPointFactory(float, float, androidx.camera.core.UseCase);
   }
 
-  public interface SurfaceOutput {
+  public interface SurfaceOutput extends java.io.Closeable {
     method public void close();
     method public android.util.Size getSize();
     method public android.view.Surface getSurface(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.core.SurfaceOutput.Event!>);
@@ -404,7 +410,6 @@
   }
 
   @com.google.auto.value.AutoValue public abstract static class SurfaceOutput.Event {
-    ctor public SurfaceOutput.Event();
     method public abstract int getEventCode();
     method public abstract androidx.camera.core.SurfaceOutput getSurfaceOutput();
     field public static final int EVENT_REQUEST_CLOSE = 0; // 0x0
diff --git a/camera/camera-core/api/public_plus_experimental_current.txt b/camera/camera-core/api/public_plus_experimental_current.txt
index fc38de4..38b65a3 100644
--- a/camera/camera-core/api/public_plus_experimental_current.txt
+++ b/camera/camera-core/api/public_plus_experimental_current.txt
@@ -376,6 +376,12 @@
     method public static float getDefaultPointSize();
   }
 
+  @RequiresApi(21) public class MirrorMode {
+    field public static final int MIRROR_MODE_OFF = 0; // 0x0
+    field public static final int MIRROR_MODE_ON = 1; // 0x1
+    field public static final int MIRROR_MODE_ON_FRONT_ONLY = 2; // 0x2
+  }
+
   @RequiresApi(21) public final class Preview extends androidx.camera.core.UseCase {
     method public androidx.camera.core.ResolutionInfo? getResolutionInfo();
     method public int getTargetRotation();
@@ -412,7 +418,7 @@
     ctor public SurfaceOrientedMeteringPointFactory(float, float, androidx.camera.core.UseCase);
   }
 
-  public interface SurfaceOutput {
+  public interface SurfaceOutput extends java.io.Closeable {
     method public void close();
     method public android.util.Size getSize();
     method public android.view.Surface getSurface(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.core.SurfaceOutput.Event!>);
@@ -421,7 +427,6 @@
   }
 
   @com.google.auto.value.AutoValue public abstract static class SurfaceOutput.Event {
-    ctor public SurfaceOutput.Event();
     method public abstract int getEventCode();
     method public abstract androidx.camera.core.SurfaceOutput getSurfaceOutput();
     field public static final int EVENT_REQUEST_CLOSE = 0; // 0x0
diff --git a/camera/camera-core/api/restricted_current.txt b/camera/camera-core/api/restricted_current.txt
index 4614cf8..1f0d922 100644
--- a/camera/camera-core/api/restricted_current.txt
+++ b/camera/camera-core/api/restricted_current.txt
@@ -359,6 +359,12 @@
     method public static float getDefaultPointSize();
   }
 
+  @RequiresApi(21) public class MirrorMode {
+    field public static final int MIRROR_MODE_OFF = 0; // 0x0
+    field public static final int MIRROR_MODE_ON = 1; // 0x1
+    field public static final int MIRROR_MODE_ON_FRONT_ONLY = 2; // 0x2
+  }
+
   @RequiresApi(21) public final class Preview extends androidx.camera.core.UseCase {
     method public androidx.camera.core.ResolutionInfo? getResolutionInfo();
     method public int getTargetRotation();
@@ -395,7 +401,7 @@
     ctor public SurfaceOrientedMeteringPointFactory(float, float, androidx.camera.core.UseCase);
   }
 
-  public interface SurfaceOutput {
+  public interface SurfaceOutput extends java.io.Closeable {
     method public void close();
     method public android.util.Size getSize();
     method public android.view.Surface getSurface(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.core.SurfaceOutput.Event!>);
@@ -404,7 +410,6 @@
   }
 
   @com.google.auto.value.AutoValue public abstract static class SurfaceOutput.Event {
-    ctor public SurfaceOutput.Event();
     method public abstract int getEventCode();
     method public abstract androidx.camera.core.SurfaceOutput getSurfaceOutput();
     field public static final int EVENT_REQUEST_CLOSE = 0; // 0x0
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.kt
index 287d3d4a..71f4651 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.kt
@@ -22,9 +22,9 @@
 import android.util.Size
 import android.view.Surface
 import androidx.camera.core.CameraSelector.LENS_FACING_FRONT
-import androidx.camera.core.MirrorMode.MIRROR_MODE_ON
-import androidx.camera.core.MirrorMode.MIRROR_MODE_FRONT_ON
 import androidx.camera.core.MirrorMode.MIRROR_MODE_OFF
+import androidx.camera.core.MirrorMode.MIRROR_MODE_ON
+import androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY
 import androidx.camera.core.concurrent.CameraCoordinator
 import androidx.camera.core.impl.Config
 import androidx.camera.core.impl.ImageOutputConfig
@@ -286,8 +286,8 @@
     }
 
     @Test
-    fun setMirrorModeFrontOn_isMirroringRequiredDependsOnCamera() {
-        val fakeUseCase = createFakeUseCase(mirrorMode = MIRROR_MODE_FRONT_ON)
+    fun setMirrorModeOnFrontOnly_isMirroringRequiredDependsOnCamera() {
+        val fakeUseCase = createFakeUseCase(mirrorMode = MIRROR_MODE_ON_FRONT_ONLY)
         assertThat(fakeUseCase.isMirroringRequired(fakeCamera)).isFalse()
         assertThat(fakeUseCase.isMirroringRequired(fakeFrontCamera)).isTrue()
     }
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt
index 72bc72c9..c6c231e 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/imagecapture/ProcessingNodeDeviceTest.kt
@@ -148,7 +148,7 @@
             takePictureCallback,
             Futures.immediateFuture(null)
         )
-        val input = ProcessingNode.InputPacket.of(processingRequest, imageIn)
+        val input = ProcessingNode.InputPacket.of(processingRequest, imageIn, false)
         // Act and return.
         nodeIn.edge.accept(input)
         return if (outputFileOptions == null) {
@@ -184,7 +184,7 @@
             CameraCaptureResultImageInfo(CAMERA_CAPTURE_RESULT),
             createJpegBytes(WIDTH, HEIGHT)
         )
-        val input = ProcessingNode.InputPacket.of(processingRequest, imageIn)
+        val input = ProcessingNode.InputPacket.of(processingRequest, imageIn, false)
         // Act and return.
         nodeIn.edge.accept(input)
         val filePath = takePictureCallback.getOnDiskResult().savedUri!!.path!!
@@ -223,7 +223,7 @@
             createJpegBytes(WIDTH, HEIGHT)
         )
         // Act.
-        val input = ProcessingNode.InputPacket.of(processingRequest, imageIn)
+        val input = ProcessingNode.InputPacket.of(processingRequest, imageIn, false)
         // Act and return.
         nodeIn.edge.accept(input)
         // Assert: the output image is identical to the input.
@@ -257,7 +257,7 @@
             takePictureCallback,
             Futures.immediateFuture(null)
         )
-        val input = ProcessingNode.InputPacket.of(processingRequest, imageIn)
+        val input = ProcessingNode.InputPacket.of(processingRequest, imageIn, false)
 
         // Act: send input to the edge and wait for the saved URI
         nodeIn.edge.accept(input)
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
index 59a07ee4..573a0d7 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
@@ -23,6 +23,7 @@
 import static androidx.camera.core.impl.ImageAnalysisConfig.OPTION_OUTPUT_IMAGE_FORMAT;
 import static androidx.camera.core.impl.ImageAnalysisConfig.OPTION_OUTPUT_IMAGE_ROTATION_ENABLED;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_CUSTOM_ORDERED_RESOLUTIONS;
+import static androidx.camera.core.impl.ImageOutputConfig.OPTION_DEFAULT_RESOLUTION;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_MAX_RESOLUTION;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_SUPPORTED_RESOLUTIONS;
@@ -82,6 +83,10 @@
 import androidx.camera.core.internal.TargetConfig;
 import androidx.camera.core.internal.ThreadConfig;
 import androidx.camera.core.internal.compat.quirk.OnePixelShiftQuirk;
+import androidx.camera.core.internal.utils.SizeUtil;
+import androidx.camera.core.resolutionselector.AspectRatioStrategy;
+import androidx.camera.core.resolutionselector.ResolutionSelector;
+import androidx.camera.core.resolutionselector.ResolutionStrategy;
 import androidx.core.util.Preconditions;
 import androidx.lifecycle.LifecycleOwner;
 
@@ -265,35 +270,38 @@
                     ? mSubscribedAnalyzer.getDefaultTargetResolution() : null;
         }
 
-        if (analyzerResolution != null) {
-            if (!builder.getMutableConfig().containsOption(OPTION_RESOLUTION_SELECTOR)) {
-                int targetRotation = builder.getMutableConfig().retrieveOption(
-                        OPTION_TARGET_ROTATION, Surface.ROTATION_0);
-                // analyzerResolution is a size in the sensor coordinate system, but the legacy
-                // target resolution setting is in the view coordinate system. Flips the
-                // analyzerResolution according to the sensor rotation degrees.
-                if (cameraInfo.getSensorRotationDegrees(targetRotation) % 180 == 90) {
-                    analyzerResolution = new Size(/* width= */ analyzerResolution.getHeight(),
-                            /* height= */ analyzerResolution.getWidth());
-                }
+        if (analyzerResolution == null) {
+            return builder.getUseCaseConfig();
+        }
 
-                if (!builder.getUseCaseConfig().containsOption(OPTION_TARGET_RESOLUTION)) {
-                    builder.getMutableConfig().insertOption(OPTION_TARGET_RESOLUTION,
-                            analyzerResolution);
-                }
-            } else {
-                // Merges analyzerResolution or default resolution to ResolutionSelector.
-                ResolutionSelector resolutionSelector =
-                        builder.getMutableConfig().retrieveOption(OPTION_RESOLUTION_SELECTOR);
+        int targetRotation = builder.getMutableConfig().retrieveOption(
+                OPTION_TARGET_ROTATION, Surface.ROTATION_0);
+        // analyzerResolution is a size in the sensor coordinate system, but the legacy
+        // target resolution setting is in the view coordinate system. Flips the
+        // analyzerResolution according to the sensor rotation degrees.
+        if (cameraInfo.getSensorRotationDegrees(targetRotation) % 180 == 90) {
+            analyzerResolution = new Size(/* width= */ analyzerResolution.getHeight(),
+                    /* height= */ analyzerResolution.getWidth());
+        }
 
-                if (resolutionSelector.getPreferredResolution() == null) {
-                    ResolutionSelector.Builder resolutionSelectorBuilder =
-                            ResolutionSelector.Builder.fromSelector(resolutionSelector);
-                    resolutionSelectorBuilder.setPreferredResolution(analyzerResolution);
-                    builder.getMutableConfig().insertOption(OPTION_RESOLUTION_SELECTOR,
-                            resolutionSelectorBuilder.build());
-                }
-            }
+        // Merges the analyzerResolution as legacy target resolution setting so that it can take
+        // effect when running the legacy resolution selection logic flow.
+        if (!builder.getUseCaseConfig().containsOption(OPTION_TARGET_RESOLUTION)) {
+            builder.getMutableConfig().insertOption(OPTION_TARGET_RESOLUTION,
+                    analyzerResolution);
+        }
+
+        // Merges the analyzerResolution to ResolutionSelector.
+        ResolutionSelector resolutionSelector =
+                builder.getMutableConfig().retrieveOption(OPTION_RESOLUTION_SELECTOR, null);
+        if (resolutionSelector != null && resolutionSelector.getResolutionStrategy() == null) {
+            ResolutionSelector.Builder resolutionSelectorBuilder =
+                    ResolutionSelector.Builder.fromResolutionSelector(resolutionSelector);
+            resolutionSelectorBuilder.setResolutionStrategy(
+                    ResolutionStrategy.create(analyzerResolution,
+                            ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER));
+            builder.getMutableConfig().insertOption(OPTION_RESOLUTION_SELECTOR,
+                    resolutionSelectorBuilder.build());
         }
 
         return builder.getUseCaseConfig();
@@ -721,6 +729,18 @@
         return super.getResolutionInfo();
     }
 
+    /**
+     * Returns the resolution selector setting.
+     *
+     * <p>This setting is set when constructing an ImageCapture using
+     * {@link Builder#setResolutionSelector(ResolutionSelector)}.
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public ResolutionSelector getResolutionSelector() {
+        return ((ImageOutputConfig) getCurrentConfig()).getResolutionSelector(null);
+    }
+
     @Override
     @NonNull
     public String toString() {
@@ -985,13 +1005,21 @@
         private static final int DEFAULT_SURFACE_OCCUPANCY_PRIORITY = 1;
         private static final int DEFAULT_ASPECT_RATIO = AspectRatio.RATIO_4_3;
 
+        private static final ResolutionSelector DEFAULT_RESOLUTION_SELECTOR =
+                new ResolutionSelector.Builder().setAspectRatioStrategy(
+                        AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY).setResolutionStrategy(
+                        ResolutionStrategy.create(SizeUtil.RESOLUTION_VGA,
+                                ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER))
+                        .build();
+
         private static final ImageAnalysisConfig DEFAULT_CONFIG;
 
         static {
             Builder builder = new Builder()
                     .setDefaultResolution(DEFAULT_TARGET_RESOLUTION)
                     .setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY)
-                    .setTargetAspectRatio(DEFAULT_ASPECT_RATIO);
+                    .setTargetAspectRatio(DEFAULT_ASPECT_RATIO)
+                    .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR);
 
             DEFAULT_CONFIG = builder.getUseCaseConfig();
         }
@@ -1383,7 +1411,7 @@
         @RestrictTo(Scope.LIBRARY_GROUP)
         @Override
         public Builder setDefaultResolution(@NonNull Size resolution) {
-            getMutableConfig().insertOption(ImageOutputConfig.OPTION_DEFAULT_RESOLUTION,
+            getMutableConfig().insertOption(OPTION_DEFAULT_RESOLUTION,
                     resolution);
             return this;
         }
@@ -1415,14 +1443,14 @@
         /**
          * Sets the resolution selector to select the preferred supported resolution.
          *
-         * <p>ImageAnalysis has a default minimal bounding size as 640x480. The input
-         * {@link ResolutionSelector}'s' preferred resolution can override the minimal bounding
-         * size to find the best resolution.
+         * <p>ImageAnalysis has a default {@link ResolutionStrategy} with bound size as 640x480
+         * and fallback rule of {@link ResolutionStrategy#FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER}.
+         * Applications can override this default strategy with a different resolution strategy.
          *
-         * <p>When using the {@code camera-camera2} CameraX implementation, which resolution will
-         * be finally selected will depend on the camera device's hardware level, capabilities
-         * and the bound use cases combination. The device hardware level and capabilities
-         * information can be retrieved via the interop class
+         * <p>When using the {@code camera-camera2} CameraX implementation, which resolution is
+         * finally selected depends on the camera device's hardware level, capabilities and the
+         * bound use cases combination. The device hardware level and capabilities information
+         * can be retrieved via the interop class
          * {@link androidx.camera.camera2.interop.Camera2CameraInfo#getCameraCharacteristic(android.hardware.camera2.CameraCharacteristics.Key)}
          * with
          * {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} and
@@ -1431,13 +1459,14 @@
          * <p>A {@code LIMITED-level} above device can support a {@code RECORD} size resolution
          * for {@link ImageAnalysis} when it is bound together with {@link Preview} and
          * {@link ImageCapture}. The trade-off is the selected resolution for the
-         * {@link ImageCapture} will also be restricted by the {@code RECORD} size. To
-         * successfully select a {@code RECORD} size resolution for {@link ImageAnalysis}, a
-         * {@code RECORD} size preferred resolution should be set on both {@link ImageCapture} and
-         * {@link ImageAnalysis}. This indicates that the application clearly understand the
-         * trade-off and prefer the {@link ImageAnalysis} to have a larger resolution rather than
-         * the {@link ImageCapture} to have a {@code MAXIMUM} size resolution. For the
-         * definitions of {@code RECORD}, {@code MAXIMUM} sizes and more details see the
+         * {@link ImageCapture} is also restricted by the {@code RECORD} size. To successfully
+         * select a {@code RECORD} size resolution for {@link ImageAnalysis}, a
+         * {@link ResolutionStrategy} of selecting {@code RECORD} size resolution should be set
+         * on both {@link ImageCapture} and {@link ImageAnalysis}. This indicates that the
+         * application clearly understand the trade-off and prefer the {@link ImageAnalysis} to
+         * have a larger resolution rather than the {@link ImageCapture} to have a {@code MAXIMUM
+         * } size resolution. For the definitions of {@code RECORD}, {@code MAXIMUM} sizes and
+         * more details see the
          * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#regular-capture">Regular capture</a>
          * section in {@link android.hardware.camera2.CameraDevice}'s. The {@code RECORD} size
          * refers to the camera device's maximum supported recording resolution, as determined by
@@ -1447,11 +1476,13 @@
          *
          * <p>The existing {@link #setTargetResolution(Size)} and
          * {@link #setTargetAspectRatio(int)} APIs are deprecated and are not compatible with
-         * {@link ResolutionSelector}. Calling any of these APIs together with
-         * {@link ResolutionSelector} will throw an {@link IllegalArgumentException} while
-         * {@link #build()} is called to create the {@link ImageAnalysis} instance.
+         * {@link #setResolutionSelector(ResolutionSelector)}. Calling either of these APIs
+         * together with {@link #setResolutionSelector(ResolutionSelector)} will result in an
+         * {@link IllegalArgumentException} being thrown when you attempt to build the
+         * {@link ImageAnalysis} instance.
          *
-         **/
+         * @return The current Builder.
+         */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @Override
         @NonNull
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
index ebae469..0c7ebbf 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
@@ -119,6 +119,9 @@
 import androidx.camera.core.internal.compat.quirk.SoftwareJpegEncodingPreferredQuirk;
 import androidx.camera.core.internal.compat.workaround.ExifRotationAvailability;
 import androidx.camera.core.internal.utils.ImageUtil;
+import androidx.camera.core.resolutionselector.AspectRatioStrategy;
+import androidx.camera.core.resolutionselector.ResolutionSelector;
+import androidx.camera.core.resolutionselector.ResolutionStrategy;
 import androidx.concurrent.futures.CallbackToFutureAdapter;
 import androidx.core.util.Preconditions;
 import androidx.lifecycle.LifecycleOwner;
@@ -244,20 +247,17 @@
 
     /**
      * When flash is required for taking a picture, a normal one shot flash will be used.
-     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     public static final int FLASH_TYPE_ONE_SHOT_FLASH = 0;
     /**
      * When flash is required for taking a picture, torch will be used as flash.
-     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     public static final int FLASH_TYPE_USE_TORCH_AS_FLASH = 1;
 
     /**
      * Provides a static configuration with implementation-agnostic options.
-     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     public static final Defaults DEFAULT_CONFIG = new Defaults();
@@ -503,7 +503,6 @@
 
     /**
      * {@inheritDoc}
-     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
@@ -524,7 +523,6 @@
 
     /**
      * {@inheritDoc}
-     *
      */
     @NonNull
     @RestrictTo(Scope.LIBRARY_GROUP)
@@ -535,7 +533,6 @@
 
     /**
      * {@inheritDoc}
-     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @NonNull
@@ -608,7 +605,6 @@
 
     /**
      * Configures flash mode to CameraControlInternal once it is ready.
-     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
@@ -902,7 +898,6 @@
 
     /**
      * {@inheritDoc}
-     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @Nullable
@@ -933,6 +928,18 @@
     }
 
     /**
+     * Returns the resolution selector setting.
+     *
+     * <p>This setting is set when constructing an ImageCapture using
+     * {@link Builder#setResolutionSelector(ResolutionSelector)}.
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public ResolutionSelector getResolutionSelector() {
+        return ((ImageOutputConfig) getCurrentConfig()).getResolutionSelector(null);
+    }
+
+    /**
      * Captures a new still image for in memory access.
      *
      * <p>The callback will be called only once for every invocation of this method. The listener
@@ -1085,7 +1092,6 @@
 
     /**
      * {@inheritDoc}
-     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @UiThread
@@ -1552,7 +1558,6 @@
 
     /**
      * {@inheritDoc}
-     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
@@ -1564,7 +1569,6 @@
 
     /**
      * {@inheritDoc}
-     *
      */
     @Override
     @RestrictTo(Scope.LIBRARY_GROUP)
@@ -1584,7 +1588,6 @@
 
     /**
      * {@inheritDoc}
-     *
      */
     @NonNull
     @Override
@@ -1724,7 +1727,8 @@
         Size resolution = streamSpec.getResolution();
 
         checkState(mImagePipeline == null);
-        mImagePipeline = new ImagePipeline(config, resolution, getEffect());
+        mImagePipeline = new ImagePipeline(config, resolution, getEffect(),
+                !requireNonNull(getCamera()).getHasTransform());
 
         if (mTakePictureManager == null) {
             // mTakePictureManager is reused when the Surface is reset.
@@ -1892,7 +1896,6 @@
      *
      * <p>This is a parameter sent to the error callback functions set in listeners such as {@link
      * ImageCapture.OnImageSavedCallback#onError(ImageCaptureException)}.
-     *
      */
     @IntDef({ERROR_UNKNOWN, ERROR_FILE_IO, ERROR_CAPTURE_FAILED, ERROR_CAMERA_CLOSED,
             ERROR_INVALID_CAMERA})
@@ -1904,7 +1907,6 @@
     /**
      * Capture mode options for ImageCapture. A picture will always be taken regardless of
      * mode, and the mode will be used on devices that support it.
-     *
      */
     @IntDef({CAPTURE_MODE_MAXIMIZE_QUALITY, CAPTURE_MODE_MINIMIZE_LATENCY,
             CAPTURE_MODE_ZERO_SHUTTER_LAG})
@@ -1925,7 +1927,6 @@
      * will remain enabled during photo capture regardless of flash mode setting. When
      * the torch is disabled, flash will function as specified by
      * {@link #setFlashMode(int)}.
-     *
      */
     @IntDef({FLASH_MODE_UNKNOWN, FLASH_MODE_AUTO, FLASH_MODE_ON, FLASH_MODE_OFF})
     @Retention(RetentionPolicy.SOURCE)
@@ -1935,7 +1936,6 @@
 
     /**
      * The flash type options when flash is required for taking a picture.
-     *
      */
     @IntDef({FLASH_TYPE_ONE_SHOT_FLASH, FLASH_TYPE_USE_TORCH_AS_FLASH})
     @Retention(RetentionPolicy.SOURCE)
@@ -2009,7 +2009,6 @@
      *
      * <p>These values may be overridden by the implementation. They only provide a minimum set of
      * defaults that are implementation independent.
-     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     public static final class Defaults
@@ -2017,11 +2016,18 @@
         private static final int DEFAULT_SURFACE_OCCUPANCY_PRIORITY = 4;
         private static final int DEFAULT_ASPECT_RATIO = AspectRatio.RATIO_4_3;
 
+        private static final ResolutionSelector DEFAULT_RESOLUTION_SELECTOR =
+                new ResolutionSelector.Builder().setAspectRatioStrategy(
+                        AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY).setResolutionStrategy(
+                        ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY).build();
+
         private static final ImageCaptureConfig DEFAULT_CONFIG;
 
         static {
-            Builder builder = new Builder().setSurfaceOccupancyPriority(
-                    DEFAULT_SURFACE_OCCUPANCY_PRIORITY).setTargetAspectRatio(DEFAULT_ASPECT_RATIO);
+            Builder builder = new Builder()
+                    .setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY)
+                    .setTargetAspectRatio(DEFAULT_ASPECT_RATIO)
+                    .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR);
 
             DEFAULT_CONFIG = builder.getUseCaseConfig();
         }
@@ -2070,6 +2076,7 @@
         }
 
         /**
+         *
          */
         @Nullable
         @RestrictTo(Scope.LIBRARY_GROUP)
@@ -2078,6 +2085,7 @@
         }
 
         /**
+         *
          */
         @Nullable
         @RestrictTo(Scope.LIBRARY_GROUP)
@@ -2086,6 +2094,7 @@
         }
 
         /**
+         *
          */
         @Nullable
         @RestrictTo(Scope.LIBRARY_GROUP)
@@ -2094,6 +2103,7 @@
         }
 
         /**
+         *
          */
         @Nullable
         @RestrictTo(Scope.LIBRARY_GROUP)
@@ -2102,6 +2112,7 @@
         }
 
         /**
+         *
          */
         @Nullable
         @RestrictTo(Scope.LIBRARY_GROUP)
@@ -2113,7 +2124,6 @@
          * Exposed internally so that CameraController can overwrite the flip horizontal flag for
          * front camera. External core API users shouldn't need this because they are the ones who
          * created the {@link Metadata}.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
@@ -2232,6 +2242,7 @@
         private final Uri mSavedUri;
 
         /**
+         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         public OutputFileResults(@Nullable Uri savedUri) {
@@ -2294,7 +2305,6 @@
          *
          * <p> CameraController's default behavior is mirroring the picture when front camera is
          * used. This method is used to check if reverseHorizontal is set explicitly by the app.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         public boolean isReversedHorizontalSet() {
@@ -2554,7 +2564,6 @@
 
         /**
          * {@inheritDoc}
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @Override
@@ -2565,7 +2574,6 @@
 
         /**
          * {@inheritDoc}
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
@@ -2802,7 +2810,6 @@
 
         /**
          * setMirrorMode is not supported on ImageCapture.
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
@@ -2885,21 +2892,20 @@
         /**
          * Sets the resolution selector to select the preferred supported resolution.
          *
-         * <p>If no resolution selector is set, the largest available resolution will be selected
-         * to use. Usually, users will intend to get the largest still image that the camera
-         * device can support. Unlike {@link Builder#setTargetResolution(Size)},
-         * {@link #setCropAspectRatio(Rational)} won't be automatically called to set the
-         * corresponding value and crop the output image when a target resolution is set. Use
-         * {@link ViewPort} instead if the output images need to be cropped in a specific
-         * aspect ratio.
+         * <p>The default resolution strategy for ImageCapture is
+         * {@link ResolutionStrategy#HIGHEST_AVAILABLE_STRATEGY}, which will select the largest
+         * available resolution to use. Applications can override this default strategy with a
+         * different resolution strategy.
          *
          * <p>The existing {@link #setTargetResolution(Size)} and
          * {@link #setTargetAspectRatio(int)} APIs are deprecated and are not compatible with
-         * {@link ResolutionSelector}. Calling any of these APIs together with
-         * {@link ResolutionSelector} will throw an {@link IllegalArgumentException} while
-         * {@link #build()} is called to create the {@link ImageCapture} instance.
+         * {@link #setResolutionSelector(ResolutionSelector)}. Calling either of these APIs
+         * together with {@link #setResolutionSelector(ResolutionSelector)} will result in an
+         * {@link IllegalArgumentException} being thrown when you attempt to build the
+         * {@link ImageCapture} instance.
          *
-         **/
+         * @return The current Builder.
+         */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @Override
         @NonNull
@@ -3051,7 +3057,6 @@
 
         /**
          * {@inheritDoc}
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
@@ -3063,7 +3068,6 @@
 
         /**
          * {@inheritDoc}
-         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/MirrorMode.java b/camera/camera-core/src/main/java/androidx/camera/core/MirrorMode.java
index 72e2efa..49af138 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/MirrorMode.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/MirrorMode.java
@@ -23,13 +23,12 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-// TODO: to public API
 /**
  * The mirror mode.
  *
+ * <p>Constants describing image mirroring transforms.
  */
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public class MirrorMode {
     /** No mirror effect will be applied. */
     public static final int MIRROR_MODE_OFF = 0;
@@ -38,17 +37,17 @@
     public static final int MIRROR_MODE_ON = 1;
 
     /**
-     * The mirroring effect is applied only when the lens facing of the associated camera is
+     * The mirror effect is applied only when the lens facing of the associated camera is
      * {@link CameraSelector#LENS_FACING_FRONT}.
      */
-    public static final int MIRROR_MODE_FRONT_ON = 2;
+    public static final int MIRROR_MODE_ON_FRONT_ONLY = 2;
 
     private MirrorMode() {
     }
 
     /**
      */
-    @IntDef({MIRROR_MODE_OFF, MIRROR_MODE_ON, MIRROR_MODE_FRONT_ON})
+    @IntDef({MIRROR_MODE_OFF, MIRROR_MODE_ON, MIRROR_MODE_ON_FRONT_ONLY})
     @Retention(RetentionPolicy.SOURCE)
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public @interface Mirror {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
index 862dce9..1164a47 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
@@ -17,7 +17,7 @@
 package androidx.camera.core;
 
 import static androidx.camera.core.CameraEffect.PREVIEW;
-import static androidx.camera.core.MirrorMode.MIRROR_MODE_FRONT_ON;
+import static androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY;
 import static androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE;
 import static androidx.camera.core.impl.ImageInputConfig.OPTION_INPUT_FORMAT;
 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_APP_TARGET_ROTATION;
@@ -89,6 +89,9 @@
 import androidx.camera.core.processing.Node;
 import androidx.camera.core.processing.SurfaceEdge;
 import androidx.camera.core.processing.SurfaceProcessorNode;
+import androidx.camera.core.resolutionselector.AspectRatioStrategy;
+import androidx.camera.core.resolutionselector.ResolutionSelector;
+import androidx.camera.core.resolutionselector.ResolutionStrategy;
 import androidx.core.util.Consumer;
 import androidx.lifecycle.LifecycleOwner;
 
@@ -534,6 +537,18 @@
         return super.getResolutionInfo();
     }
 
+    /**
+     * Returns the resolution selector setting.
+     *
+     * <p>This setting is set when constructing an ImageCapture using
+     * {@link Builder#setResolutionSelector(ResolutionSelector)}.
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public ResolutionSelector getResolutionSelector() {
+        return ((ImageOutputConfig) getCurrentConfig()).getResolutionSelector(null);
+    }
+
     @NonNull
     @Override
     public String toString() {
@@ -573,20 +588,6 @@
         builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT,
                 INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE);
 
-        // Merges Preview's default max resolution setting when resolution selector is used
-        ResolutionSelector resolutionSelector =
-                builder.getMutableConfig().retrieveOption(OPTION_RESOLUTION_SELECTOR, null);
-        if (resolutionSelector != null && resolutionSelector.getMaxResolution() == null) {
-            Size maxResolution = builder.getMutableConfig().retrieveOption(OPTION_MAX_RESOLUTION);
-            if (maxResolution != null) {
-                ResolutionSelector.Builder resolutionSelectorBuilder =
-                        ResolutionSelector.Builder.fromSelector(resolutionSelector);
-                resolutionSelectorBuilder.setMaxResolution(maxResolution);
-                builder.getMutableConfig().insertOption(OPTION_RESOLUTION_SELECTOR,
-                        resolutionSelectorBuilder.build());
-            }
-        }
-
         return builder.getUseCaseConfig();
     }
 
@@ -736,13 +737,20 @@
     public static final class Defaults implements ConfigProvider<PreviewConfig> {
         private static final int DEFAULT_SURFACE_OCCUPANCY_PRIORITY = 2;
         private static final int DEFAULT_ASPECT_RATIO = AspectRatio.RATIO_4_3;
-        private static final int DEFAULT_MIRROR_MODE = MIRROR_MODE_FRONT_ON;
+        private static final int DEFAULT_MIRROR_MODE = MIRROR_MODE_ON_FRONT_ONLY;
+
+        private static final ResolutionSelector DEFAULT_RESOLUTION_SELECTOR =
+                new ResolutionSelector.Builder().setAspectRatioStrategy(
+                        AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY).setResolutionStrategy(
+                        ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY).build();
 
         private static final PreviewConfig DEFAULT_CONFIG;
 
         static {
-            Builder builder = new Builder().setSurfaceOccupancyPriority(
-                    DEFAULT_SURFACE_OCCUPANCY_PRIORITY).setTargetAspectRatio(DEFAULT_ASPECT_RATIO);
+            Builder builder = new Builder()
+                    .setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY)
+                    .setTargetAspectRatio(DEFAULT_ASPECT_RATIO)
+                    .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR);
             DEFAULT_CONFIG = builder.getUseCaseConfig();
         }
 
@@ -1061,21 +1069,23 @@
          * size match to the device's screen resolution, or to 1080p (1920x1080), whichever is
          * smaller. See the
          * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#regular-capture">Regular capture</a>
-         * section in {@link android.hardware.camera2.CameraDevice}'. If the
-         * {@link ResolutionSelector} contains the max resolution setting larger than the {@code
-         * PREVIEW} size, a size larger than the device's screen resolution or 1080p can be
-         * selected to use for {@link Preview}.
+         * section in {@link android.hardware.camera2.CameraDevice}'. {@link Preview} has a
+         * default {@link ResolutionStrategy} with the {@code PREVIEW} bound size and
+         * {@link ResolutionStrategy#FALLBACK_RULE_CLOSEST_LOWER} to achieve this. Applications
+         * can override this default strategy with a different resolution strategy.
          *
          * <p>Note that due to compatibility reasons, CameraX may select a resolution that is
          * larger than the default screen resolution on certain devices.
          *
          * <p>The existing {@link #setTargetResolution(Size)} and
          * {@link #setTargetAspectRatio(int)} APIs are deprecated and are not compatible with
-         * {@link ResolutionSelector}. Calling any of these APIs together with
-         * {@link ResolutionSelector} will throw an {@link IllegalArgumentException} while
-         * {@link #build()} is called to create the {@link Preview} instance.
+         * {@link #setResolutionSelector(ResolutionSelector)}. Calling either of these APIs
+         * together with {@link #setResolutionSelector(ResolutionSelector)} will result in an
+         * {@link IllegalArgumentException} being thrown when you attempt to build the
+         * {@link Preview} instance.
          *
-         **/
+         * @return The current Builder.
+         */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @Override
         @NonNull
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ResolutionSelector.java b/camera/camera-core/src/main/java/androidx/camera/core/ResolutionSelector.java
deleted file mode 100644
index 9d18df4..0000000
--- a/camera/camera-core/src/main/java/androidx/camera/core/ResolutionSelector.java
+++ /dev/null
@@ -1,381 +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.camera.core;
-
-import android.util.Size;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-import androidx.annotation.RestrictTo;
-import androidx.camera.core.impl.SizeCoordinate;
-
-/**
- * A set of requirements and priorities used to select a resolution for the use case.
- *
- * <p>The resolution selection mechanism is determined by the following three steps:
- * <ol>
- *     <li> Collect supported output sizes to the candidate resolution list
- *     <li> Determine the selecting priority of the candidate resolution list by the preference
- *     settings
- *     <li> Consider all the resolution selector settings of bound use cases to find the best
- *     resolution for each use case
- * </ol>
- *
- * <p>For the first step, all supported resolution output sizes are put into the candidate
- * resolution list as the base in the beginning.
- *
- * <p>ResolutionSelector provides the following two functions for applications to adjust the
- * conditions of the candidate resolutions.
- * <ul>
- *     <li> {@link Builder#setMaxResolution(Size)}
- *     <li> {@link Builder#setHighResolutionEnabled(boolean)}
- * </ul>
- *
- * <p>For the second step, ResolutionSelector provides the following three functions for
- * applications to determine which resolution has higher priority to be selected.
- * <ul>
- *     <li> {@link Builder#setPreferredResolution(Size)}
- *     <li> {@link Builder#setPreferredResolutionByViewSize(Size)}
- *     <li> {@link Builder#setPreferredAspectRatio(int)}
- * </ul>
- *
- * <p>The resolution that exactly matches the preferred resolution is selected in first priority.
- * If the resolution can't be found, CameraX falls back to use the sizes of the preferred aspect
- * ratio. In this case, the preferred resolution is treated as the minimal bounding size to find
- * the best resolution.
- *
- * <p>Different types of use cases might have their own additional conditions. Please see the use
- * case config builders’ {@code setResolutionSelector()} function to know the condition details
- * for each type of use case.
- *
- * <p>For the third step, CameraX selects the final resolution for the use case based on the
- * camera device's hardware level, capabilities and the bound use case combination. Applications
- * can check which resolution is finally selected by using the use case's {@code
- * getResolutionInfo()} function.
- *
- */
-@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class ResolutionSelector {
-    @Nullable
-    private final Size mPreferredResolution;
-
-    private final SizeCoordinate mSizeCoordinate;
-
-    private final int mPreferredAspectRatio;
-
-    @Nullable
-    private final Size mMaxResolution;
-
-    private final boolean mIsHighResolutionEnabled;
-
-    ResolutionSelector(int preferredAspectRatio,
-            @Nullable Size preferredResolution,
-            @NonNull SizeCoordinate sizeCoordinate,
-            @Nullable Size maxResolution,
-            boolean isHighResolutionEnabled) {
-        mPreferredAspectRatio = preferredAspectRatio;
-        mPreferredResolution = preferredResolution;
-        mSizeCoordinate = sizeCoordinate;
-        mMaxResolution = maxResolution;
-        mIsHighResolutionEnabled = isHighResolutionEnabled;
-    }
-
-    /**
-     * Retrieves the preferred aspect ratio in the ResolutionSelector.
-     *
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @AspectRatio.Ratio
-    public int getPreferredAspectRatio() {
-        return mPreferredAspectRatio;
-    }
-
-    /**
-     * Retrieves the preferred resolution in the ResolutionSelector.
-     *
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @Nullable
-    public Size getPreferredResolution() {
-        return mPreferredResolution;
-    }
-
-    /**
-     * Retrieves the size coordinate of the preferred resolution in the ResolutionSelector.
-     *
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @NonNull
-    public SizeCoordinate getSizeCoordinate() {
-        return mSizeCoordinate;
-    }
-
-    /**
-     * Returns the max resolution in the ResolutionSelector.
-     *
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @Nullable
-    public Size getMaxResolution() {
-        return mMaxResolution;
-    }
-
-    /**
-     * Returns {@code true} if high resolutions are allowed to be selected, otherwise {@code false}.
-     *
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public boolean isHighResolutionEnabled() {
-        return mIsHighResolutionEnabled;
-    }
-
-    /**
-     * Builder for a {@link ResolutionSelector}.
-     */
-    public static final class Builder {
-        @AspectRatio.Ratio
-        private int mPreferredAspectRatio = AspectRatio.RATIO_4_3;
-        @Nullable
-        private Size mPreferredResolution = null;
-        @NonNull
-        private SizeCoordinate mSizeCoordinate = SizeCoordinate.CAMERA_SENSOR;
-        @Nullable
-        private Size mMaxResolution = null;
-        private boolean mIsHighResolutionEnabled = false;
-
-        /**
-         * Creates a new Builder object.
-         */
-        public Builder() {
-        }
-
-        private Builder(@NonNull ResolutionSelector selector) {
-            mPreferredAspectRatio = selector.getPreferredAspectRatio();
-            mPreferredResolution = selector.getPreferredResolution();
-            mSizeCoordinate = selector.getSizeCoordinate();
-            mMaxResolution = selector.getMaxResolution();
-            mIsHighResolutionEnabled = selector.isHighResolutionEnabled();
-        }
-
-        /**
-         * Generates a Builder from another {@link ResolutionSelector} object.
-         *
-         * @param selector an existing {@link ResolutionSelector}.
-         * @return the new Builder.
-         */
-        @NonNull
-        public static Builder fromSelector(@NonNull ResolutionSelector selector) {
-            return new Builder(selector);
-        }
-
-        /**
-         * Sets the preferred aspect ratio that the output images are expected to have.
-         *
-         * <p>The aspect ratio is the ratio of width to height in the camera sensor's natural
-         * orientation. If set, CameraX finds the sizes that match the aspect ratio with priority
-         * . Among the sizes that match the aspect ratio, the larger the size, the higher the
-         * priority.
-         *
-         * <p>If CameraX can't find any available sizes that match the preferred aspect ratio,
-         * CameraX falls back to select the sizes with the nearest aspect ratio that can contain
-         * the full field of view of the sizes with preferred aspect ratio.
-         *
-         * <p>If preferred aspect ratio is not set, the default aspect ratio is
-         * {@link AspectRatio#RATIO_4_3}, which usually has largest field of view because most
-         * camera sensor are {@code 4:3}.
-         *
-         * <p>This API is useful for apps that want to capture images matching the {@code 16:9}
-         * display aspect ratio. Apps can set preferred aspect ratio as
-         * {@link AspectRatio#RATIO_16_9} to achieve this.
-         *
-         * <p>The actual aspect ratio of the output may differ from the specified preferred
-         * aspect ratio value. Application code should check the resulting output's resolution.
-         *
-         * @param preferredAspectRatio the aspect ratio you prefer to use.
-         * @return the current Builder.
-         */
-        @NonNull
-        public Builder setPreferredAspectRatio(@AspectRatio.Ratio int preferredAspectRatio) {
-            mPreferredAspectRatio = preferredAspectRatio;
-            return this;
-        }
-
-        /**
-         * Sets the preferred resolution you expect to select. The resolution is expressed in the
-         * camera sensor's natural orientation (landscape), which means you can set the size
-         * retrieved from
-         * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes} directly.
-         *
-         * <p>Once the preferred resolution is set, CameraX finds exactly matched size first
-         * regardless of the preferred aspect ratio. This API is useful for apps that want to
-         * select an exact size retrieved from
-         * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes}.
-         *
-         * <p>If CameraX can't find the size that matches the preferred resolution, it attempts
-         * to establish a minimal bound for the given resolution. The actual resolution is the
-         * closest available resolution that is not smaller than the preferred resolution.
-         * However, if no resolution exists that is equal to or larger than the preferred
-         * resolution, the nearest available resolution smaller than the preferred resolution is
-         * chosen.
-         *
-         * <p>When the preferred resolution is used as a minimal bound, CameraX also considers
-         * the preferred aspect ratio to find the sizes that either match it or are close to it.
-         * Using preferred resolution as the minimal bound is useful for apps that want to shrink
-         * the size for the surface. For example, for apps that just show the camera preview in a
-         * small view, apps can specify a size smaller than display size. CameraX can effectively
-         * select a smaller size for better efficiency.
-         *
-         * <p>If both {@link Builder#setPreferredResolution(Size)} and
-         * {@link Builder#setPreferredResolutionByViewSize(Size)} are invoked, which one set
-         * later overrides the one set before.
-         *
-         * @param preferredResolution the preferred resolution expressed in the orientation of
-         *                            the device camera sensor coordinate to choose the preferred
-         *                            resolution from supported output sizes list.
-         * @return the current Builder.
-         */
-        @NonNull
-        public Builder setPreferredResolution(@NonNull Size preferredResolution) {
-            mPreferredResolution = preferredResolution;
-            mSizeCoordinate = SizeCoordinate.CAMERA_SENSOR;
-            return this;
-        }
-
-        /**
-         * Sets the preferred resolution you expect to select. The resolution is expressed in the
-         * Android {@link View} coordinate system.
-         *
-         * <p>For phone devices, the sensor coordinate orientation usually has 90 degrees
-         * difference from the phone device display’s natural orientation. Depending on the
-         * display rotation value when the use case is bound, CameraX transforms the input
-         * resolution into the camera sensor's natural orientation to find the best suitable
-         * resolution.
-         *
-         * <p>Once the preferred resolution is set, CameraX finds the size that exactly matches
-         * the preferred resolution first regardless of the preferred aspect ratio.
-         *
-         * <p>If CameraX can't find the size that matches the preferred resolution, it attempts
-         * to establish a minimal bound for the given resolution. The actual resolution is the
-         * closest available resolution that is not smaller than the preferred resolution.
-         * However, if no resolution exists that is equal to or larger than the preferred
-         * resolution, the nearest available resolution smaller than the preferred resolution is
-         * chosen.
-         *
-         * <p>When the preferred resolution is used as a minimal bound, CameraX also considers
-         * the preferred aspect ratio to find the sizes that either match it or are close to it.
-         * Using Android {@link View} size as preferred resolution is useful for apps that want
-         * to shrink the size for the surface. For example, for apps that just show the camera
-         * preview in a small view, apps can specify the small size of Android {@link View}.
-         * CameraX can effectively select a smaller size for better efficiency.
-         *
-         * <p>If both {@link Builder#setPreferredResolution(Size)} and
-         * {@link Builder#setPreferredResolutionByViewSize(Size)} are invoked, the later setting
-         * overrides the former one.
-         *
-         * @param preferredResolutionByViewSize the preferred resolution expressed in the
-         *                                      orientation of the app layout's Android
-         *                                      {@link View} to choose the preferred resolution
-         *                                      from supported output sizes list.
-         * @return the current Builder.
-         */
-        @NonNull
-        public Builder setPreferredResolutionByViewSize(
-                @NonNull Size preferredResolutionByViewSize) {
-            mPreferredResolution = preferredResolutionByViewSize;
-            mSizeCoordinate = SizeCoordinate.ANDROID_VIEW;
-            return this;
-        }
-
-        /**
-         * Sets the max resolution condition for the use case.
-         *
-         * <p>The max resolution prevents the use case to select the sizes which either width or
-         * height exceeds the specified resolution.
-         *
-         * <p>The resolution should be expressed in the camera sensor's natural orientation
-         * (landscape).
-         *
-         * <p>For example, if applications want to select a resolution smaller than a specific
-         * resolution to have better performance, a {@link ResolutionSelector} which sets this
-         * specific resolution as the max resolution can be used. Or, if applications want to
-         * select a larger resolution for a {@link Preview} which has the default max resolution
-         * of the small one of device's screen size and 1080p (1920x1080), use a
-         * {@link ResolutionSelector} with max resolution.
-         *
-         * @param resolution the max resolution limitation to choose from supported output sizes
-         *                   list.
-         * @return the current Builder.
-         */
-        @NonNull
-        public Builder setMaxResolution(@NonNull Size resolution) {
-            mMaxResolution = resolution;
-            return this;
-        }
-
-        /**
-         * Sets whether high resolutions are allowed to be selected for the use cases.
-         *
-         * <p>Calling this function allows the use case to select the high resolution output
-         * sizes if it is supported for the camera device.
-         *
-         * <p>When high resolution is enabled, if an {@link ImageCapture} with
-         * {@link ImageCapture#CAPTURE_MODE_ZERO_SHUTTER_LAG} mode is bound, the
-         * {@link ImageCapture#CAPTURE_MODE_ZERO_SHUTTER_LAG} mode is forced disabled.
-         *
-         * <p>When using the {@code camera-extensions} to enable an extension mode, even if high
-         * resolution is enabled, the supported high resolution output sizes are still excluded
-         * from the candidate resolution list.
-         *
-         * <p>When using the {@code camera-camera2} CameraX implementation, the supported
-         * high resolutions are retrieved from
-         * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighResolutionOutputSizes(int)}.
-         * Be noticed that the high resolution sizes might cause the entire capture session to
-         * not meet the 20 fps frame rate. Even if only an ImageCapture use case selects a high
-         * resolution, it might still impact the FPS of the Preview, ImageAnalysis or
-         * VideoCapture use cases which are bound together. This function only takes effect on
-         * devices with
-         * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE}
-         * capability. For devices without
-         * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE}
-         * capability, all resolutions can be retrieved from
-         * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes(int)},
-         * but it is not guaranteed to meet >= 20 fps for any resolution in the list.
-         *
-         * @param enabled {@code true} to allow to select high resolution for the use case.
-         * @return the current Builder.
-         */
-        @NonNull
-        public Builder setHighResolutionEnabled(boolean enabled) {
-            mIsHighResolutionEnabled = enabled;
-            return this;
-        }
-
-        /**
-         * Builds the {@link ResolutionSelector}.
-         *
-         * @return the {@link ResolutionSelector} built with the specified resolution settings.
-         */
-        @NonNull
-        public ResolutionSelector build() {
-            return new ResolutionSelector(mPreferredAspectRatio, mPreferredResolution,
-                    mSizeCoordinate, mMaxResolution, mIsHighResolutionEnabled);
-        }
-    }
-}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/SurfaceOutput.java b/camera/camera-core/src/main/java/androidx/camera/core/SurfaceOutput.java
index be20322..0cde5ad 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/SurfaceOutput.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/SurfaceOutput.java
@@ -16,6 +16,8 @@
 
 package androidx.camera.core;
 
+import static androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE;
+
 import android.graphics.SurfaceTexture;
 import android.util.Size;
 import android.view.Surface;
@@ -27,6 +29,7 @@
 
 import com.google.auto.value.AutoValue;
 
+import java.io.Closeable;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.concurrent.Executor;
@@ -39,7 +42,7 @@
  *
  * @see SurfaceProcessor#onOutputSurface(SurfaceOutput)
  */
-public interface SurfaceOutput {
+public interface SurfaceOutput extends Closeable {
 
     /**
      * Gets the {@link Surface} for drawing processed frames.
@@ -67,11 +70,12 @@
 
     /**
      * This field indicates the format of the {@link Surface}.
-     *
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @CameraEffect.Formats
-    int getFormat();
+    default int getFormat() {
+        return INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE;
+    }
 
     /**
      * Gets the size of the {@link Surface}.
@@ -87,16 +91,10 @@
      * {@link Surface}. Writing to the {@link Surface} after calling this method might cause
      * errors.
      */
+    @Override
     void close();
 
     /**
-     * Asks the {@link SurfaceProcessor} implementation to stopping writing to the {@link Surface}.
-     *
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    void requestClose();
-
-    /**
      * Applies an additional 4x4 transformation on the original matrix.
      *
      * <p>When the input {@link Surface} of {@link SurfaceProcessor} is backed by a
@@ -158,9 +156,11 @@
     @AutoValue
     abstract class Event {
 
+        Event() {
+        }
+
         /**
          * Possible event codes.
-         *
          */
         @IntDef({EVENT_REQUEST_CLOSE})
         @Retention(RetentionPolicy.SOURCE)
@@ -195,7 +195,6 @@
 
         /**
          * Creates a {@link Event} for sending to the implementation.
-         *
          */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         @NonNull
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java b/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
index 362e463..0aa20f0 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
@@ -16,9 +16,13 @@
 
 package androidx.camera.core;
 
-import static androidx.camera.core.MirrorMode.MIRROR_MODE_FRONT_ON;
 import static androidx.camera.core.MirrorMode.MIRROR_MODE_OFF;
 import static androidx.camera.core.MirrorMode.MIRROR_MODE_ON;
+import static androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY;
+import static androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR;
+import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO;
+import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_RESOLUTION;
+import static androidx.camera.core.impl.StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED;
 import static androidx.camera.core.impl.utils.TransformUtils.within360;
 import static androidx.camera.core.processing.TargetUtils.isSuperset;
 import static androidx.core.util.Preconditions.checkArgument;
@@ -27,6 +31,7 @@
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.media.ImageReader;
+import android.util.Range;
 import android.util.Size;
 import android.view.Surface;
 
@@ -209,6 +214,16 @@
             mergedConfig = MutableOptionsBundle.create();
         }
 
+        // Removes the default resolution selector setting to go for the legacy resolution
+        // selection logic flow if applications call the legacy setTargetAspectRatio and
+        // setTargetResolution APIs to do the setting.
+        if (mUseCaseConfig.containsOption(OPTION_TARGET_ASPECT_RATIO)
+                || mUseCaseConfig.containsOption(OPTION_TARGET_RESOLUTION)) {
+            if (mergedConfig.containsOption(OPTION_RESOLUTION_SELECTOR)) {
+                mergedConfig.removeOption(OPTION_RESOLUTION_SELECTOR);
+            }
+        }
+
         // If any options need special handling, this is the place to do it. For now we'll just copy
         // over all options.
         for (Option<?> opt : mUseCaseConfig.listOptions()) {
@@ -247,7 +262,8 @@
         // Forces disable ZSL when high resolution is enabled.
         if (mergedConfig.containsOption(ImageOutputConfig.OPTION_RESOLUTION_SELECTOR)
                 && mergedConfig.retrieveOption(
-                ImageOutputConfig.OPTION_RESOLUTION_SELECTOR).isHighResolutionEnabled()) {
+                ImageOutputConfig.OPTION_RESOLUTION_SELECTOR).getHighResolutionEnabledFlags()
+                != 0) {
             mergedConfig.insertOption(UseCaseConfig.OPTION_ZSL_DISABLED, true);
         }
 
@@ -339,6 +355,17 @@
     }
 
     /**
+     * Returns the target frame rate range for the associated VideoCapture use case.
+     *
+     * @return The target frame rate.
+     */
+    @NonNull
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    protected Range<Integer> getTargetFramerateInternal() {
+        return mCurrentConfig.getTargetFramerate(FRAME_RATE_RANGE_UNSPECIFIED);
+    }
+
+    /**
      * Returns the mirror mode.
      *
      * <p>If mirror mode is not set, defaults to {@link MirrorMode#MIRROR_MODE_OFF}.
@@ -362,7 +389,7 @@
                 return false;
             case MIRROR_MODE_ON:
                 return true;
-            case MIRROR_MODE_FRONT_ON:
+            case MIRROR_MODE_ON_FRONT_ONLY:
                 return camera.isFrontFacing();
             default:
                 throw new AssertionError("Unknown mirrorMode: " + mirrorMode);
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/CaptureNode.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/CaptureNode.java
index 3cdb6fe..1f59ef1 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/CaptureNode.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/CaptureNode.java
@@ -120,7 +120,7 @@
         inputEdge.getRequestEdge().setListener(this::onRequestAvailable);
         inputEdge.getErrorEdge().setListener(this::sendCaptureError);
 
-        mOutputEdge = Out.of(inputEdge.getFormat());
+        mOutputEdge = Out.of(inputEdge.getFormat(), inputEdge.isVirtualCamera());
         return mOutputEdge;
     }
 
@@ -257,6 +257,11 @@
         abstract int getFormat();
 
         /**
+         * Whether the pipeline is connected to a virtual camera.
+         */
+        abstract boolean isVirtualCamera();
+
+        /**
          * Edge that accepts {@link ProcessingRequest}.
          */
         @NonNull
@@ -297,8 +302,9 @@
         }
 
         @NonNull
-        static In of(Size size, int format) {
-            return new AutoValue_CaptureNode_In(size, format, new Edge<>(), new Edge<>());
+        static In of(Size size, int format, boolean isVirtualCamera) {
+            return new AutoValue_CaptureNode_In(size, format, isVirtualCamera,
+                    new Edge<>(), new Edge<>());
         }
     }
 
@@ -325,8 +331,14 @@
          */
         abstract int getFormat();
 
-        static Out of(int format) {
-            return new AutoValue_CaptureNode_Out(new Edge<>(), new Edge<>(), format);
+        /**
+         * Whether the pipeline is connected to a virtual camera.
+         */
+        abstract boolean isVirtualCamera();
+
+        static Out of(int format, boolean isVirtualCamera) {
+            return new AutoValue_CaptureNode_Out(new Edge<>(), new Edge<>(), format,
+                    isVirtualCamera);
         }
     }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ImagePipeline.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ImagePipeline.java
index fc8a4af..6c28158 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ImagePipeline.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ImagePipeline.java
@@ -88,14 +88,16 @@
     public ImagePipeline(
             @NonNull ImageCaptureConfig useCaseConfig,
             @NonNull Size cameraSurfaceSize) {
-        this(useCaseConfig, cameraSurfaceSize, /*cameraEffect=*/ null);
+        this(useCaseConfig, cameraSurfaceSize, /*cameraEffect=*/ null,
+                /*isVirtualCamera=*/ false);
     }
 
     @MainThread
     public ImagePipeline(
             @NonNull ImageCaptureConfig useCaseConfig,
             @NonNull Size cameraSurfaceSize,
-            @Nullable CameraEffect cameraEffect) {
+            @Nullable CameraEffect cameraEffect,
+            boolean isVirtualCamera) {
         checkMainThread();
         mUseCaseConfig = useCaseConfig;
         mCaptureConfig = CaptureConfig.Builder.createFrom(useCaseConfig).build();
@@ -108,7 +110,8 @@
                 cameraEffect != null ? new InternalImageProcessor(cameraEffect) : null);
 
         // Connect nodes
-        mPipelineIn = CaptureNode.In.of(cameraSurfaceSize, mUseCaseConfig.getInputFormat());
+        mPipelineIn = CaptureNode.In.of(cameraSurfaceSize, mUseCaseConfig.getInputFormat(),
+                isVirtualCamera);
         CaptureNode.Out captureOut = mCaptureNode.transform(mPipelineIn);
         ProcessingNode.In processingIn = mBundlingNode.transform(captureOut);
         mProcessingNode.transform(processingIn);
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingInput2Packet.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingInput2Packet.java
index 4109418..318f126 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingInput2Packet.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingInput2Packet.java
@@ -74,7 +74,9 @@
                 throw new ImageCaptureException(ERROR_FILE_IO, "Failed to extract EXIF data.", e);
             }
         }
-        if (EXIF_ROTATION_AVAILABILITY.shouldUseExifOrientation(image)) {
+        if (EXIF_ROTATION_AVAILABILITY.shouldUseExifOrientation(image)
+                && !inputPacket.isVirtualCamera()) {
+            // Virtual camera doesn't respect the CaptureRequest rotation degrees.
             checkNotNull(exif, "JPEG image must have exif.");
             return createPacketWithHalRotation(request, exif, image);
         }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java
index b4e15fc..f53cae3 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/ProcessingNode.java
@@ -225,9 +225,15 @@
         @NonNull
         abstract ImageProxy getImageProxy();
 
+        /**
+         * Whether the pipeline is connected to a virtual camera.
+         */
+        abstract boolean isVirtualCamera();
+
         static InputPacket of(@NonNull ProcessingRequest processingRequest,
-                @NonNull ImageProxy imageProxy) {
-            return new AutoValue_ProcessingNode_InputPacket(processingRequest, imageProxy);
+                @NonNull ImageProxy imageProxy, boolean isVirtualCamera) {
+            return new AutoValue_ProcessingNode_InputPacket(processingRequest, imageProxy,
+                    isVirtualCamera);
         }
     }
 
@@ -253,8 +259,8 @@
     }
 
     @VisibleForTesting
-    void injectJpegBytes2CroppedBitmapForTesting(
-            @NonNull Operation<Packet<byte[]>, Packet<Bitmap>> operation) {
-        mJpegBytes2CroppedBitmap = operation;
+    void injectProcessingInput2Packet(
+            @NonNull Operation<InputPacket, Packet<ImageProxy>> input2Packet) {
+        mInput2Packet = input2Packet;
     }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/SingleBundlingNode.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/SingleBundlingNode.java
index 3cb95720..1af04d6 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/SingleBundlingNode.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/SingleBundlingNode.java
@@ -43,10 +43,12 @@
 
     ProcessingRequest mPendingRequest;
     private ProcessingNode.In mOutputEdge;
+    private boolean mIsVirtualCamera;
 
     @NonNull
     @Override
     public ProcessingNode.In transform(@NonNull CaptureNode.Out captureNodeOut) {
+        mIsVirtualCamera = captureNodeOut.isVirtualCamera();
         // Listen to input edges.
         captureNodeOut.getImageEdge().setListener(this::matchImageWithRequest);
         captureNodeOut.getRequestEdge().setListener(this::trackIncomingRequest);
@@ -95,7 +97,7 @@
         checkState(stageId == mPendingRequest.getStageIds().get(0));
 
         mOutputEdge.getEdge().accept(
-                ProcessingNode.InputPacket.of(mPendingRequest, imageProxy));
+                ProcessingNode.InputPacket.of(mPendingRequest, imageProxy, mIsVirtualCamera));
         mPendingRequest = null;
     }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraDeviceSurfaceManager.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraDeviceSurfaceManager.java
index 48bd32c..b0c0a2a 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraDeviceSurfaceManager.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraDeviceSurfaceManager.java
@@ -55,23 +55,9 @@
     }
 
     /**
-     * Check whether the input surface configuration list is under the capability of any combination
-     * of this object.
-     *
-     * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
-     * @param cameraId          the camera id of the camera device to be compared
-     * @param surfaceConfigList the surface configuration list to be compared
-     * @return the check result that whether it could be supported
-     */
-    boolean checkSupported(
-            boolean isConcurrentCameraModeOn,
-            @NonNull String cameraId,
-            @Nullable List<SurfaceConfig> surfaceConfigList);
-
-    /**
      * Transform to a SurfaceConfig object with cameraId, image format and size info
      *
-     * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
+     * @param cameraMode  the working camera mode.
      * @param cameraId    the camera id of the camera device to transform the object
      * @param imageFormat the image format info for the surface configuration object
      * @param size        the size info for the surface configuration object
@@ -79,7 +65,7 @@
      */
     @Nullable
     SurfaceConfig transformSurfaceConfig(
-            boolean isConcurrentCameraModeOn,
+            @CameraMode.Mode int cameraMode,
             @NonNull String cameraId,
             int imageFormat,
             @NonNull Size size);
@@ -87,8 +73,7 @@
     /**
      * Retrieves a map of suggested stream specifications for the given list of use cases.
      *
-     * @param isConcurrentCameraModeOn          true if concurrent camera mode is on, otherwise
-     *                                          false.
+     * @param cameraMode                        the working camera mode.
      * @param cameraId                          the camera id of the camera device used by the
      *                                          use cases
      * @param existingSurfaces                  list of surfaces already configured and used by
@@ -106,7 +91,7 @@
      */
     @NonNull
     Map<UseCaseConfig<?>, StreamSpec> getSuggestedStreamSpecs(
-            boolean isConcurrentCameraModeOn,
+            @CameraMode.Mode int cameraMode,
             @NonNull String cameraId,
             @NonNull List<AttachedSurfaceInfo> existingSurfaces,
             @NonNull Map<UseCaseConfig<?>, List<Size>> newUseCaseConfigsSupportedSizeMap);
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInfoInternal.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInfoInternal.java
index f7ec5a0..84beaf0 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInfoInternal.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInfoInternal.java
@@ -25,10 +25,12 @@
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.CameraInfo;
 import androidx.camera.core.CameraSelector;
+import androidx.camera.core.DynamicRange;
 import androidx.core.util.Preconditions;
 
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
@@ -103,6 +105,14 @@
     @NonNull
     List<Size> getSupportedHighResolutions(int format);
 
+    /**
+     * Returns the supported dynamic ranges of this camera.
+     *
+     * @return a set of supported dynamic range, or an empty set if no dynamic range is supported.
+     */
+    @NonNull
+    Set<DynamicRange> getSupportedDynamicRanges();
+
     /** {@inheritDoc} */
     @NonNull
     @Override
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraMode.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraMode.java
new file mode 100644
index 0000000..322f141
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraMode.java
@@ -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.camera.core.impl;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.RequiresApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The available camera modes.
+ *
+ * <p>Camera devices might work in different camera modes:
+ * <ul>
+ *   <li> Default mode
+ *   <li> Concurrent mode
+ *   <li> Maximum resolution sensor pixel mode
+ *   <li> HDR 10-bit mode
+ * </ul>
+ *
+ * <p>The surface combination that is used depends on the camera mode. The defined constants are
+ * used to identify which supported surface combination list should be used.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public final class CameraMode {
+    /**
+     * The camera is in the default mode.
+     */
+    public static final int DEFAULT = 0;
+    /**
+     * The camera is running in the concurrent camera mode.
+     */
+    public static final int CONCURRENT_CAMERA = 1;
+    /**
+     * The camera is running in the ultra high resolution camera mode.
+     */
+    public static final int ULTRA_HIGH_RESOLUTION_CAMERA = 2;
+
+    @IntDef({DEFAULT, CONCURRENT_CAMERA, ULTRA_HIGH_RESOLUTION_CAMERA})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Mode {
+    }
+
+    private CameraMode() {
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/Config.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/Config.java
index 2d3ae77..67493a2 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/Config.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/Config.java
@@ -19,9 +19,12 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
+import androidx.camera.core.impl.utils.ResolutionSelectorUtil;
+import androidx.camera.core.resolutionselector.ResolutionSelector;
 
 import com.google.auto.value.AutoValue;
 
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -321,11 +324,23 @@
             // just copy over all options.
             for (Config.Option<?> opt : extendedConfig.listOptions()) {
                 @SuppressWarnings("unchecked") // Options/values are being copied directly
-                        Config.Option<Object> objectOpt = (Config.Option<Object>) opt;
+                Config.Option<Object> objectOpt = (Config.Option<Object>) opt;
 
-                mergedConfig.insertOption(objectOpt,
-                        extendedConfig.getOptionPriority(opt),
-                        extendedConfig.retrieveOption(objectOpt));
+                // ResolutionSelector needs special handling to merge the underlying settings.
+                if (Objects.equals(objectOpt, ImageOutputConfig.OPTION_RESOLUTION_SELECTOR)) {
+                    ResolutionSelector resolutionSelectorToOverride =
+                            (ResolutionSelector) extendedConfig.retrieveOption(objectOpt);
+                    ResolutionSelector baseResolutionSelector =
+                            (ResolutionSelector) baseConfig.retrieveOption(objectOpt);
+                    mergedConfig.insertOption(objectOpt,
+                            extendedConfig.getOptionPriority(opt),
+                            ResolutionSelectorUtil.overrideResolutionSelectors(
+                                    baseResolutionSelector, resolutionSelectorToOverride));
+                } else {
+                    mergedConfig.insertOption(objectOpt,
+                            extendedConfig.getOptionPriority(opt),
+                            extendedConfig.retrieveOption(objectOpt));
+                }
             }
         }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/EncoderProfilesProxy.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/EncoderProfilesProxy.java
index f37cb38..d8152fa 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/EncoderProfilesProxy.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/EncoderProfilesProxy.java
@@ -85,6 +85,9 @@
         /** Constant representing bit depth 8. */
         public static final int BIT_DEPTH_8 = 8;
 
+        /** Constant representing bit depth 10. */
+        public static final int BIT_DEPTH_10 = 10;
+
         @Retention(RetentionPolicy.SOURCE)
         @IntDef({H263, H264, HEVC, VP8, MPEG_4_SP, VP9, DOLBY_VISION, AV1,
                 MediaRecorder.VideoEncoder.DEFAULT})
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageOutputConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageOutputConfig.java
index a2168c3..9cef56c 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageOutputConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/ImageOutputConfig.java
@@ -29,7 +29,7 @@
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.AspectRatio;
 import androidx.camera.core.MirrorMode;
-import androidx.camera.core.ResolutionSelector;
+import androidx.camera.core.resolutionselector.ResolutionSelector;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -414,7 +414,7 @@
          * Sets the mirror mode of the intended target for images from this configuration.
          *
          * <p>Valid values include: {@link MirrorMode#MIRROR_MODE_OFF},
-         * {@link MirrorMode#MIRROR_MODE_ON} and {@link MirrorMode#MIRROR_MODE_FRONT_ON}.
+         * {@link MirrorMode#MIRROR_MODE_ON} and {@link MirrorMode#MIRROR_MODE_ON_FRONT_ONLY}.
          *
          * @param mirrorMode The mirror mode of the intended target.
          * @return The current Builder.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/SizeCoordinate.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/SizeCoordinate.java
deleted file mode 100644
index b1cfbf1..0000000
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/SizeCoordinate.java
+++ /dev/null
@@ -1,32 +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.camera.core.impl;
-
-/**
- * The size coordinate system enum.
- */
-public enum SizeCoordinate {
-    /**
-     * Size is expressed in the camera sensor's natural orientation (landscape).
-     */
-    CAMERA_SENSOR,
-
-    /**
-     * Size is expressed in the Android View's orientation.
-     */
-    ANDROID_VIEW
-}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/StreamSpec.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/StreamSpec.java
index 932e71a..17c29ae 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/StreamSpec.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/StreamSpec.java
@@ -21,6 +21,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
+import androidx.camera.core.DynamicRange;
 
 import com.google.auto.value.AutoValue;
 
@@ -45,6 +46,13 @@
     public abstract Size getResolution();
 
     /**
+     * Returns the {@link DynamicRange} for the stream associated with this stream specification.
+     * @return the dynamic range for the stream.
+     */
+    @NonNull
+    public abstract DynamicRange getDynamicRange();
+
+    /**
      * Returns the expected frame rate range for the stream associated with this stream
      * specification.
      * @return the expected frame rate range for the stream.
@@ -57,7 +65,8 @@
     public static Builder builder(@NonNull Size resolution) {
         return new AutoValue_StreamSpec.Builder()
                 .setResolution(resolution)
-                .setExpectedFrameRateRange(FRAME_RATE_RANGE_UNSPECIFIED);
+                .setExpectedFrameRateRange(FRAME_RATE_RANGE_UNSPECIFIED)
+                .setDynamicRange(DynamicRange.SDR);
     }
 
     /** Returns a builder pre-populated with the current specification. */
@@ -76,6 +85,14 @@
         public abstract Builder setResolution(@NonNull Size resolution);
 
         /**
+         * Sets the dynamic range.
+         *
+         * <p>If not set, the default dynamic range is {@link DynamicRange#SDR}.
+         */
+        @NonNull
+        public abstract Builder setDynamicRange(@NonNull DynamicRange dynamicRange);
+
+        /**
          * Sets the expected frame rate range.
          *
          * <p>If not set, the default expected frame rate range is
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/SurfaceConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/SurfaceConfig.java
index 5263af2..1f98de1 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/SurfaceConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/SurfaceConfig.java
@@ -18,12 +18,10 @@
 
 import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCaptureSession.StateCallback;
-import android.hardware.camera2.params.StreamConfigurationMap;
 import android.os.Handler;
 import android.util.Size;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.internal.utils.SizeUtil;
 
@@ -105,21 +103,18 @@
     /**
      * Transform to a SurfaceConfig object with image format and size info
      *
-     * @param isConcurrentCameraModeOn true if concurrent camera mode is on, otherwise false.
+     * @param cameraMode            the working camera mode.
      * @param imageFormat           the image format info for the surface configuration object
      * @param size                  the size info for the surface configuration object
      * @param surfaceSizeDefinition the surface definition for the surface configuration object
-     * @param maxOutputSize         the maximum supported resolution for the particular format
-     *                              returned by {@link StreamConfigurationMap#getOutputSizes(int)}
      * @return new {@link SurfaceConfig} object
      */
     @NonNull
     public static SurfaceConfig transformSurfaceConfig(
-            boolean isConcurrentCameraModeOn,
+            @CameraMode.Mode int cameraMode,
             int imageFormat,
             @NonNull Size size,
-            @NonNull SurfaceSizeDefinition surfaceSizeDefinition,
-            @Nullable Size maxOutputSize) {
+            @NonNull SurfaceSizeDefinition surfaceSizeDefinition) {
         ConfigType configType =
                 SurfaceConfig.getConfigType(imageFormat);
         ConfigSize configSize = ConfigSize.NOT_SUPPORT;
@@ -127,26 +122,28 @@
         // Compare with surface size definition to determine the surface configuration size
         int sizeArea = SizeUtil.getArea(size);
 
-        if (isConcurrentCameraModeOn) {
-            int maxOutputSizeArea = maxOutputSize != null ? SizeUtil.getArea(maxOutputSize) : 0;
-            if (sizeArea <= Math.min(maxOutputSizeArea,
-                    SizeUtil.getArea(surfaceSizeDefinition.getS720pSize()))) {
+        if (cameraMode == CameraMode.CONCURRENT_CAMERA) {
+            if (sizeArea <= SizeUtil.getArea(surfaceSizeDefinition.getS720pSize(imageFormat))) {
                 configSize = ConfigSize.s720p;
-            } else if (sizeArea <= Math.min(maxOutputSizeArea,
-                    SizeUtil.getArea(surfaceSizeDefinition.getS1440pSize()))) {
+            } else if (sizeArea <= SizeUtil.getArea(surfaceSizeDefinition.getS1440pSize(
+                    imageFormat))) {
                 configSize = ConfigSize.s1440p;
             }
         } else {
             if (sizeArea <= SizeUtil.getArea(surfaceSizeDefinition.getAnalysisSize())) {
                 configSize = ConfigSize.VGA;
-            } else if (sizeArea
-                    <= SizeUtil.getArea(surfaceSizeDefinition.getPreviewSize())) {
+            } else if (sizeArea <= SizeUtil.getArea(surfaceSizeDefinition.getPreviewSize())) {
                 configSize = ConfigSize.PREVIEW;
-            } else if (sizeArea
-                    <= SizeUtil.getArea(surfaceSizeDefinition.getRecordSize())) {
+            } else if (sizeArea <= SizeUtil.getArea(surfaceSizeDefinition.getRecordSize())) {
                 configSize = ConfigSize.RECORD;
-            } else {
+            } else if (sizeArea <= SizeUtil.getArea(
+                    surfaceSizeDefinition.getMaximumSize(imageFormat))) {
                 configSize = ConfigSize.MAXIMUM;
+            } else {
+                Size ultraMaximumSize = surfaceSizeDefinition.getUltraMaximumSize(imageFormat);
+                if (ultraMaximumSize != null && sizeArea <= SizeUtil.getArea(ultraMaximumSize)) {
+                    configSize = ConfigSize.ULTRA_MAXIMUM;
+                }
             }
         }
 
@@ -179,29 +176,36 @@
          * s720p refers to the best size match to the device's screen resolution, or to 720p
          * (1280x720), whichever is smaller.
          */
-        s720p(5),
+        s720p(1),
         /**
          * PREVIEW refers to the best size match to the device's screen resolution, or to 1080p
          * (1920x1080), whichever is smaller.
          */
-        PREVIEW(1),
+        PREVIEW(2),
         /**
          * s1440p refers to the best size match to the device's screen resolution, or to 1440p
          * (1920x1440), whichever is smaller.
          */
-        s1440p(6),
+        s1440p(3),
         /**
          * RECORD refers to the camera device's maximum supported recording resolution, as
          * determined by CamcorderProfile.
          */
-        RECORD(2),
+        RECORD(4),
         /**
-         * MAXIMUM refers to the camera device's maximum output resolution for that format or target
-         * from StreamConfigurationMap.getOutputSizes(int)
+         * MAXIMUM refers to the camera device's maximum output resolution for that format or
+         * target from StreamConfigurationMap.getOutputSizes() or getHighResolutionOutputSizes()
+         * in the default sensor pixel mode.
          */
-        MAXIMUM(3),
+        MAXIMUM(5),
+        /**
+         * ULTRA_MAXIMUM refers to the camera device's maximum output resolution for that format or
+         * target from StreamConfigurationMap.getOutputSizes() or getHighResolutionOutputSizes()
+         * in the maximum resolution sensor pixel mode.
+         */
+        ULTRA_MAXIMUM(6),
         /** NOT_SUPPORT is for the size larger than MAXIMUM */
-        NOT_SUPPORT(4);
+        NOT_SUPPORT(7);
 
         final int mId;
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/SurfaceSizeDefinition.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/SurfaceSizeDefinition.java
index 48d3878..6b62897 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/SurfaceSizeDefinition.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/SurfaceSizeDefinition.java
@@ -20,10 +20,13 @@
 import android.util.Size;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 
 import com.google.auto.value.AutoValue;
 
+import java.util.Map;
+
 /**
  * Camera device surface size definition
  *
@@ -42,54 +45,109 @@
     /**
      * Create a SurfaceSizeDefinition object with input analysis, preview, record and maximum sizes
      *
-     * @param analysisSize   Default ANALYSIS size is * 640x480.
-     * @param s720p          s720p refers to the 720p (1280 x 720) or the maximum supported
-     *                       resolution for the particular format returned by
-     *                       {@link StreamConfigurationMap#getOutputSizes(int)}, whichever is
-     *                       smaller.
-     * @param previewSize    PREVIEW refers to the best size match to the device's screen
-     *                       resolution,
-     *                       or to 1080p * (1920x1080), whichever is smaller.
-     * @param s1440p         s1440p refers to the 1440p (1920 x 1440) or the maximum supported
-     *                       resolution for the particular format returned by
-     *                       {@link StreamConfigurationMap#getOutputSizes(int)}, whichever is
-     *                       smaller.
-     * @param recordSize     RECORD refers to the camera device's maximum supported * recording
-     *                       resolution, as determined by CamcorderProfile.
+     * @param analysisSize        Default ANALYSIS size is * 640x480.
+     * @param s720pSizeMap        The format to size map of an s720p size stream. s720p refers to
+     *                            the 720p (1280 x 720) or the maximum supported resolution for the
+     *                            particular format returned by
+     *                            {@link StreamConfigurationMap#getOutputSizes(int)}, whichever is
+     *                            smaller.
+     * @param previewSize         PREVIEW refers to the best size match to the device's screen
+     *                            resolution, or to 1080p * (1920x1080), whichever is smaller.
+     * @param s1440pSizeMap       The format to size map of an s1440p size stream. s1440p refers
+     *                            to the 1440p (1920 x 1440) or the maximum supported resolution
+     *                            for the particular format returned by
+     *                            {@link StreamConfigurationMap#getOutputSizes(int)}, whichever is
+     *                            smaller.
+     * @param recordSize          RECORD refers to the camera device's maximum supported * recording
+     *                            resolution, as determined by CamcorderProfile.
+     * @param maximumSizeMap      The format to size map of an MAXIMUM size stream. MAXIMUM
+     *                            refers to the camera device's maximum output resolution in the
+     *                            default sensor pixel mode.
+     * @param ultraMaximumSizeMap The format to size map of an ULTRA_MAXIMUM size stream.
+     *                            ULTRA_MAXIMUM refers to the camera device's maximum output
+     *                            resolution in the maximum resolution sensor pixel mode.
      * @return new {@link SurfaceSizeDefinition} object
      */
     @NonNull
     public static SurfaceSizeDefinition create(
             @NonNull Size analysisSize,
-            @NonNull Size s720p,
+            @NonNull Map<Integer, Size> s720pSizeMap,
             @NonNull Size previewSize,
-            @NonNull Size s1440p,
-            @NonNull Size recordSize) {
+            @NonNull Map<Integer, Size> s1440pSizeMap,
+            @NonNull Size recordSize,
+            @NonNull Map<Integer, Size> maximumSizeMap,
+            @NonNull Map<Integer, Size> ultraMaximumSizeMap) {
         return new AutoValue_SurfaceSizeDefinition(
                 analysisSize,
-                s720p,
+                s720pSizeMap,
                 previewSize,
-                s1440p,
-                recordSize);
+                s1440pSizeMap,
+                recordSize,
+                maximumSizeMap,
+                ultraMaximumSizeMap);
     }
 
     /** Returns the size of an ANALYSIS stream. */
     @NonNull
     public abstract Size getAnalysisSize();
 
-    /** Returns the size of an s720p stream. */
+    /** Returns the format to size map of an s720p stream. */
     @NonNull
-    public abstract Size getS720pSize();
+    public abstract Map<Integer, Size> getS720pSizeMap();
 
     /** Returns the size of a PREVIEW stream. */
     @NonNull
     public abstract Size getPreviewSize();
 
-    /** Returns the size of an s1440p stream. */
+    /** Returns the format to size map of an s1440p stream. */
     @NonNull
-    public abstract Size getS1440pSize();
+    public abstract Map<Integer, Size> getS1440pSizeMap();
 
     /** Returns the size of a RECORD stream*/
     @NonNull
     public abstract Size getRecordSize();
+
+    /** Returns the format to size map of an MAXIMUM stream. */
+    @NonNull
+    public abstract Map<Integer, Size> getMaximumSizeMap();
+
+    /** Returns the format to size map of an ULTRA_MAXIMUM stream. */
+    @NonNull
+    public abstract Map<Integer, Size> getUltraMaximumSizeMap();
+
+    /**
+     * Returns the s720p size for the specified format, or {@code null} null if there is no data
+     * for the format.
+     */
+    @NonNull
+    public Size getS720pSize(int format) {
+        return getS720pSizeMap().get(format);
+    }
+
+    /**
+     * Returns the s1440p size for the specified format, or {@code null} null if there is no data
+     * for the format.
+     */
+    @NonNull
+    public Size getS1440pSize(int format) {
+        return getS1440pSizeMap().get(format);
+    }
+
+    /**
+     * Returns the MAXIMUM size for the specified format, or {@code null} null if there is no
+     * data for the format.
+     */
+    @NonNull
+    public Size getMaximumSize(int format) {
+        return getMaximumSizeMap().get(format);
+    }
+
+    /**
+     * Returns the ULTRA_MAXIMUM size for the specified format, or {@code null} if the device
+     * doesn't support maximum resolution sensor pixel mode.
+     */
+    @Nullable
+    public Size getUltraMaximumSize(int format) {
+        return getUltraMaximumSizeMap().get(format);
+    }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ResolutionSelectorUtil.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ResolutionSelectorUtil.java
new file mode 100644
index 0000000..da7aaa3
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/ResolutionSelectorUtil.java
@@ -0,0 +1,73 @@
+/*
+ * 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.core.impl.utils;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.resolutionselector.ResolutionSelector;
+
+/**
+ * Utility class for resolution selector related operations.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class ResolutionSelectorUtil {
+    private ResolutionSelectorUtil() {
+    }
+
+    /**
+     * Merges two resolution selectors.
+     *
+     * @param baseResolutionSelector       the base resolution selector.
+     * @param resolutionSelectorToOverride the resolution selector that the inside settings can
+     *                                     override the corresponding setting in the base
+     *                                     resolution selector.
+     * @return {@code null} if both the input resolution selectors are null. Otherwise, returns
+     * the merged resolution selector.
+     */
+    @Nullable
+    public static ResolutionSelector overrideResolutionSelectors(
+            @Nullable ResolutionSelector baseResolutionSelector,
+            @Nullable ResolutionSelector resolutionSelectorToOverride) {
+        if (resolutionSelectorToOverride == null) {
+            return baseResolutionSelector;
+        } else if (baseResolutionSelector == null) {
+            return resolutionSelectorToOverride;
+        }
+
+        ResolutionSelector.Builder builder =
+                ResolutionSelector.Builder.fromResolutionSelector(baseResolutionSelector);
+
+        if (resolutionSelectorToOverride.getAspectRatioStrategy() != null) {
+            builder.setAspectRatioStrategy(resolutionSelectorToOverride.getAspectRatioStrategy());
+        }
+
+        if (resolutionSelectorToOverride.getResolutionStrategy() != null) {
+            builder.setResolutionStrategy(resolutionSelectorToOverride.getResolutionStrategy());
+        }
+
+        if (resolutionSelectorToOverride.getResolutionFilter() != null) {
+            builder.setResolutionFilter(resolutionSelectorToOverride.getResolutionFilter());
+        }
+
+        if (resolutionSelectorToOverride.getHighResolutionEnabledFlags() != 0) {
+            builder.setHighResolutionEnabledFlags(
+                    resolutionSelectorToOverride.getHighResolutionEnabledFlags());
+        }
+
+        return builder.build();
+    }
+}
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 3556349..5112f97 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
@@ -18,6 +18,7 @@
 
 import static androidx.camera.core.CameraEffect.PREVIEW;
 import static androidx.camera.core.CameraEffect.VIDEO_CAPTURE;
+import static androidx.camera.core.impl.utils.TransformUtils.rectToSize;
 import static androidx.camera.core.processing.TargetUtils.getNumberOfTargets;
 import static androidx.core.util.Preconditions.checkArgument;
 import static androidx.core.util.Preconditions.checkState;
@@ -55,6 +56,7 @@
 import androidx.camera.core.impl.CameraDeviceSurfaceManager;
 import androidx.camera.core.impl.CameraInfoInternal;
 import androidx.camera.core.impl.CameraInternal;
+import androidx.camera.core.impl.CameraMode;
 import androidx.camera.core.impl.Config;
 import androidx.camera.core.impl.StreamSpec;
 import androidx.camera.core.impl.SurfaceConfig;
@@ -281,8 +283,7 @@
             Map<UseCase, StreamSpec> suggestedStreamSpecMap;
             try {
                 suggestedStreamSpecMap = calculateSuggestedStreamSpecs(
-                        mCameraCoordinator.getCameraOperatingMode()
-                                == CameraCoordinator.CAMERA_OPERATING_MODE_CONCURRENT,
+                        getCameraMode(),
                         mCameraInternal.getCameraInfoInternal(), cameraUseCasesToAttach,
                         cameraUseCasesToKeep, configs);
                 // TODO(b/265704882): enable stream sharing for LEVEL_3 and high preview
@@ -343,6 +344,20 @@
         }
     }
 
+    private @CameraMode.Mode int getCameraMode() {
+        synchronized (mLock) {
+            if (mCameraCoordinator.getCameraOperatingMode()
+                    == CameraCoordinator.CAMERA_OPERATING_MODE_CONCURRENT) {
+                return CameraMode.CONCURRENT_CAMERA;
+            }
+        }
+
+        // TODO(b/271199876): return ULTRA_HIGH_RESOLUTION_CAMERA when it can be enabled via
+        //  Camera2Interop
+
+        return CameraMode.DEFAULT;
+    }
+
     private boolean hasNoExtension() {
         synchronized (mLock) {
             return mCameraConfig == CameraConfigs.emptyConfig();
@@ -527,7 +542,7 @@
     }
 
     private Map<UseCase, StreamSpec> calculateSuggestedStreamSpecs(
-            boolean isConcurrentCameraModeOn,
+            @CameraMode.Mode int cameraMode,
             @NonNull CameraInfoInternal cameraInfoInternal,
             @NonNull Collection<UseCase> newUseCases,
             @NonNull Collection<UseCase> currentUseCases,
@@ -540,7 +555,7 @@
         for (UseCase useCase : currentUseCases) {
             SurfaceConfig surfaceConfig =
                     mCameraDeviceSurfaceManager.transformSurfaceConfig(
-                            isConcurrentCameraModeOn,
+                            cameraMode,
                             cameraId,
                             useCase.getImageFormat(),
                             useCase.getAttachedSurfaceResolution());
@@ -554,8 +569,17 @@
         if (!newUseCases.isEmpty()) {
             Map<UseCaseConfig<?>, UseCase> configToUseCaseMap = new HashMap<>();
             Map<UseCaseConfig<?>, List<Size>> configToSupportedSizesMap = new HashMap<>();
+            Rect sensorRect;
+            try {
+                sensorRect = ((CameraControlInternal) getCameraControl()).getSensorRect();
+            } catch (NullPointerException e) {
+                // TODO(b/274531208): Remove the unnecessary SENSOR_INFO_ACTIVE_ARRAY_SIZE NPE
+                //  check related code only which is used for robolectric tests
+                sensorRect = null;
+            }
             SupportedOutputSizesSorter supportedOutputSizesSorter = new SupportedOutputSizesSorter(
-                    (CameraInfoInternal) getCameraInfo());
+                    (CameraInfoInternal) getCameraInfo(),
+                    sensorRect != null ? rectToSize(sensorRect) : null);
             for (UseCase useCase : newUseCases) {
                 ConfigPair configPair = configPairMap.get(useCase);
                 // Combine with default configuration.
@@ -571,7 +595,7 @@
             // Get suggested stream specifications and update the use case session configuration
             Map<UseCaseConfig<?>, StreamSpec> useCaseConfigStreamSpecMap =
                     mCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
-                            isConcurrentCameraModeOn,
+                            cameraMode,
                             cameraId, existingSurfaces,
                             configToSupportedSizesMap);
 
@@ -804,8 +828,7 @@
                 Map<UseCase, ConfigPair> configs = getConfigs(Arrays.asList(useCases),
                         mCameraConfig.getUseCaseConfigFactory(), mUseCaseConfigFactory);
                 calculateSuggestedStreamSpecs(
-                        mCameraCoordinator.getCameraOperatingMode()
-                                == CameraCoordinator.CAMERA_OPERATING_MODE_CONCURRENT,
+                        getCameraMode(),
                         mCameraInternal.getCameraInfoInternal(),
                         Arrays.asList(useCases), emptyList(), configs);
             } catch (IllegalArgumentException e) {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorter.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorter.java
index 3072d63..2a84e43 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorter.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorter.java
@@ -26,42 +26,62 @@
 import android.util.Pair;
 import android.util.Rational;
 import android.util.Size;
+import android.view.Surface;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.AspectRatio;
+import androidx.camera.core.CameraSelector;
 import androidx.camera.core.Logger;
-import androidx.camera.core.ResolutionSelector;
 import androidx.camera.core.impl.CameraInfoInternal;
 import androidx.camera.core.impl.ImageOutputConfig;
 import androidx.camera.core.impl.UseCaseConfig;
 import androidx.camera.core.impl.utils.AspectRatioUtil;
+import androidx.camera.core.impl.utils.CameraOrientationUtil;
 import androidx.camera.core.impl.utils.CompareSizesByArea;
-import androidx.camera.core.internal.utils.SizeUtil;
-import androidx.core.util.Preconditions;
+import androidx.camera.core.resolutionselector.AspectRatioStrategy;
+import androidx.camera.core.resolutionselector.ResolutionFilter;
+import androidx.camera.core.resolutionselector.ResolutionSelector;
+import androidx.camera.core.resolutionselector.ResolutionStrategy;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
 /**
- * A class used to sort the supported output sizes according to the use case configs
+ * The supported output sizes collector to help collect the available resolution candidate list
+ * according to the use case config and the following settings in {@link ResolutionSelector}:
+ *
+ * <ul>
+ *   <li>Aspect ratio strategy
+ *   <li>Resolution strategy
+ *   <li>Custom resolution filter
+ *   <li>High resolution enabled flags
+ * </ul>
  */
 @RequiresApi(21)
 class SupportedOutputSizesSorter {
     private static final String TAG = "SupportedOutputSizesCollector";
     private final CameraInfoInternal mCameraInfoInternal;
+    private final int mSensorOrientation;
+    private final int mLensFacing;
     private final Rational mFullFovRatio;
     private final boolean mIsSensorLandscapeResolution;
     private final SupportedOutputSizesSorterLegacy mSupportedOutputSizesSorterLegacy;
 
-    SupportedOutputSizesSorter(@NonNull CameraInfoInternal cameraInfoInternal) {
+    SupportedOutputSizesSorter(@NonNull CameraInfoInternal cameraInfoInternal,
+            @Nullable Size activeArraySize) {
         mCameraInfoInternal = cameraInfoInternal;
-        mFullFovRatio = calculateFullFovRatio(mCameraInfoInternal);
+        mSensorOrientation = mCameraInfoInternal.getSensorRotationDegrees();
+        mLensFacing = mCameraInfoInternal.getLensFacing();
+        mFullFovRatio = activeArraySize != null ? calculateFullFovRatioFromActiveArraySize(
+                activeArraySize) : calculateFullFovRatioFromSupportedOutputSizes(
+                mCameraInfoInternal);
         // Determines the sensor resolution orientation info by the full FOV ratio.
         mIsSensorLandscapeResolution = mFullFovRatio != null ? mFullFovRatio.getNumerator()
                 >= mFullFovRatio.getDenominator() : true;
@@ -70,6 +90,14 @@
     }
 
     /**
+     * Calculates the full FOV ratio by the active array size.
+     */
+    @NonNull
+    private Rational calculateFullFovRatioFromActiveArraySize(@NonNull Size activeArraySize) {
+        return new Rational(activeArraySize.getWidth(), activeArraySize.getHeight());
+    }
+
+    /**
      * Calculates the full FOV ratio by the output sizes retrieved from CameraInfoInternal.
      *
      * <p>For most devices, the full FOV ratio should match the aspect ratio of the max supported
@@ -77,7 +105,8 @@
      * test to fail if it is not set in the test environment.
      */
     @Nullable
-    private Rational calculateFullFovRatio(@NonNull CameraInfoInternal cameraInfoInternal) {
+    private Rational calculateFullFovRatioFromSupportedOutputSizes(
+            @NonNull CameraInfoInternal cameraInfoInternal) {
         List<Size> jpegOutputSizes = cameraInfoInternal.getSupportedResolutions(ImageFormat.JPEG);
         if (jpegOutputSizes.isEmpty()) {
             return null;
@@ -86,6 +115,13 @@
         return new Rational(maxSize.getWidth(), maxSize.getHeight());
     }
 
+    /**
+     * Returns the sorted output sizes according to the use case config.
+     *
+     * <p>If ResolutionSelector is specified in the use case config, the output sizes will be
+     * sorted according to the ResolutionSelector setting and logic. Otherwise, the output sizes
+     * will be sorted according to the legacy resolution API settings and logic.
+     */
     @NonNull
     List<Size> getSortedSupportedOutputSizes(@NonNull UseCaseConfig<?> useCaseConfig) {
         ImageOutputConfig imageOutputConfig = (ImageOutputConfig) useCaseConfig;
@@ -96,50 +132,16 @@
             return customOrderedResolutions;
         }
 
-        // Retrieves the resolution candidate list according to the use case config if
-        List<Size> resolutionCandidateList = getResolutionCandidateList(useCaseConfig);
-
         ResolutionSelector resolutionSelector = imageOutputConfig.getResolutionSelector(null);
 
         if (resolutionSelector == null) {
             return mSupportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
-                    resolutionCandidateList, useCaseConfig);
+                    getResolutionCandidateList(useCaseConfig), useCaseConfig);
         } else {
-            Size miniBoundingSize = resolutionSelector.getPreferredResolution();
-            if (miniBoundingSize == null) {
-                miniBoundingSize = imageOutputConfig.getDefaultResolution(null);
-            }
-            return sortSupportedOutputSizesByResolutionSelector(resolutionCandidateList,
-                    resolutionSelector, miniBoundingSize);
+            return sortSupportedOutputSizesByResolutionSelector(useCaseConfig);
         }
     }
 
-    @NonNull
-    private List<Size> getResolutionCandidateList(@NonNull UseCaseConfig<?> useCaseConfig) {
-        int imageFormat = useCaseConfig.getInputFormat();
-        ImageOutputConfig imageOutputConfig = (ImageOutputConfig) useCaseConfig;
-        // Tries to get the custom supported resolutions list if it is set
-        List<Size> resolutionCandidateList = getCustomizedSupportedResolutionsFromConfig(
-                imageFormat, imageOutputConfig);
-
-        // Tries to get the supported output sizes from the CameraInfoInternal if both custom
-        // ordered and supported resolutions lists are not set.
-        if (resolutionCandidateList == null) {
-            resolutionCandidateList = mCameraInfoInternal.getSupportedResolutions(imageFormat);
-        }
-
-        // Appends high resolution output sizes if high resolution is enabled by ResolutionSelector
-        if (imageOutputConfig.getResolutionSelector(null) != null
-                && imageOutputConfig.getResolutionSelector().isHighResolutionEnabled()) {
-            List<Size> allSizesList = new ArrayList<>();
-            allSizesList.addAll(resolutionCandidateList);
-            allSizesList.addAll(mCameraInfoInternal.getSupportedHighResolutions(imageFormat));
-            return allSizesList;
-        }
-
-        return resolutionCandidateList;
-    }
-
     /**
      * Retrieves the customized supported resolutions from the use case config.
      *
@@ -171,148 +173,56 @@
     }
 
     /**
-     * Sorts the resolution candidate list by the following steps:
+     * Sorts the resolution candidate list according to the ResolutionSelector API logic.
      *
-     * 1. Filters out the candidate list according to the max resolution.
-     * 2. Sorts the candidate list according to ResolutionSelector strategies.
+     * <ol>
+     *   <li>Collects the output sizes
+     *     <ul>
+     *       <li>Applies the high resolution settings
+     *     </ul>
+     *   <li>Applies the aspect ratio strategy
+     *     <ul>
+     *       <li>Applies the aspect ratio strategy fallback rule
+     *     </ul>
+     *   <li>Applies the resolution strategy
+     *     <ul>
+     *       <li>Applies the resolution strategy fallback rule
+     *     </ul>
+     *   <li>Applies the resolution filter
+     * </ol>
+     *
+     * @return a size list which has been filtered and sorted by the specified resolution
+     * selector settings.
+     * @throws IllegalArgumentException if the specified resolution filter returns any size which
+     *                                  is not included in the provided supported size list.
      */
     @NonNull
     private List<Size> sortSupportedOutputSizesByResolutionSelector(
-            @NonNull List<Size> resolutionCandidateList,
-            @NonNull ResolutionSelector resolutionSelector,
-            @Nullable Size miniBoundingSize) {
-        if (resolutionCandidateList.isEmpty()) {
-            return resolutionCandidateList;
+            @NonNull UseCaseConfig<?> useCaseConfig) {
+        ResolutionSelector resolutionSelector =
+                ((ImageOutputConfig) useCaseConfig).getResolutionSelector();
+
+        // Retrieves the normal supported output sizes.
+        List<Size> resolutionCandidateList = getResolutionCandidateList(useCaseConfig);
+
+        // Applies the high resolution settings onto the resolution candidate list.
+        if (!useCaseConfig.isHigResolutionDisabled(false)) {
+            resolutionCandidateList = applyHighResolutionSettings(resolutionCandidateList,
+                    resolutionSelector, useCaseConfig.getInputFormat());
         }
 
-        List<Size> descendingSizeList = new ArrayList<>(resolutionCandidateList);
+        // Applies the aspect ratio strategy onto the resolution candidate list.
+        LinkedHashMap<Rational, List<Size>> aspectRatioSizeListMap =
+                applyAspectRatioStrategy(resolutionCandidateList,
+                        resolutionSelector.getAspectRatioStrategy());
 
-        // Sort the result sizes. The Comparator result must be reversed to have a descending
-        // order result.
-        Collections.sort(descendingSizeList, new CompareSizesByArea(true));
+        // Applies the resolution strategy onto the resolution candidate list.
+        applyResolutionStrategy(aspectRatioSizeListMap, resolutionSelector.getResolutionStrategy());
 
-        // 1. Filters out the candidate list according to the min size bound and max resolution.
-        List<Size> filteredSizeList = filterOutResolutionCandidateListByMaxResolutionSetting(
-                descendingSizeList, resolutionSelector);
-
-        // 2. Sorts the candidate list according to the rules of new Resolution API.
-        return sortResolutionCandidateListByTargetAspectRatioAndResolutionSettings(
-                filteredSizeList, resolutionSelector, miniBoundingSize);
-
-    }
-
-    /**
-     * Filters out the resolution candidate list by the max resolution setting.
-     *
-     * The input size list should have been sorted in descending order.
-     */
-    private static List<Size> filterOutResolutionCandidateListByMaxResolutionSetting(
-            @NonNull List<Size> resolutionCandidateList,
-            @NonNull ResolutionSelector resolutionSelector) {
-        // Retrieves the max resolution setting. When ResolutionSelector is used, all resolution
-        // selection logic should depend on ResolutionSelector's settings.
-        Size maxResolution = resolutionSelector.getMaxResolution();
-
-        if (maxResolution == null) {
-            return resolutionCandidateList;
-        }
-
-        // Filter out the resolution candidate list by the max resolution. Sizes that any edge
-        // exceeds the max resolution will be filtered out.
+        // Collects all sizes from the sorted aspect ratio size groups into the final sorted list.
         List<Size> resultList = new ArrayList<>();
-        for (Size outputSize : resolutionCandidateList) {
-            if (!SizeUtil.isLongerInAnyEdge(outputSize, maxResolution)) {
-                resultList.add(outputSize);
-            }
-        }
-
-        if (resultList.isEmpty()) {
-            throw new IllegalArgumentException(
-                    "Resolution candidate list is empty after filtering out by the settings!");
-        }
-
-        return resultList;
-    }
-
-    /**
-     * Sorts the resolution candidate list according to the new ResolutionSelector API logic.
-     *
-     * The list will be sorted by the following order:
-     * 1. size of preferred resolution
-     * 2. a resolution with preferred aspect ratio, is not smaller than, and is closest to the
-     * preferred resolution.
-     * 3. resolutions with preferred aspect ratio and is smaller than the preferred resolution
-     * size in descending order of resolution area size.
-     * 4. Other sizes sorted by CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace and
-     * area size.
-     */
-    @NonNull
-    private List<Size> sortResolutionCandidateListByTargetAspectRatioAndResolutionSettings(
-            @NonNull List<Size> resolutionCandidateList,
-            @NonNull ResolutionSelector resolutionSelector,
-            @Nullable Size miniBoundingSize) {
-        Rational aspectRatio = getTargetAspectRatioRationalValue(
-                resolutionSelector.getPreferredAspectRatio(), mIsSensorLandscapeResolution);
-        Preconditions.checkNotNull(aspectRatio, "ResolutionSelector should also have aspect ratio"
-                + " value.");
-
-        Size targetSize = resolutionSelector.getPreferredResolution();
-        List<Size> resultList = sortResolutionCandidateListByTargetAspectRatioAndSize(
-                resolutionCandidateList, aspectRatio, miniBoundingSize);
-
-        // Moves the target size to the first position if it exists in the resolution candidate
-        // list.
-        if (resultList.contains(targetSize)) {
-            resultList.remove(targetSize);
-            resultList.add(0, targetSize);
-        }
-
-        return resultList;
-    }
-
-    /**
-     * Sorts the resolution candidate list according to the target aspect ratio and size settings.
-     *
-     * 1. The resolution candidate list will be grouped by aspect ratio.
-     * 2. Moves the smallest size larger than the mini bounding size to the first position for each
-     * aspect ratio sizes group.
-     * 3. The aspect ratios of groups will be sorted against to the target aspect ratio setting by
-     * CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace.
-     * 4. Concatenate all sizes as the result list
-     */
-    @NonNull
-    private List<Size> sortResolutionCandidateListByTargetAspectRatioAndSize(
-            @NonNull List<Size> resolutionCandidateList, @NonNull Rational aspectRatio,
-            @Nullable Size miniBoundingSize) {
-        // Rearrange the supported size to put the ones with the same aspect ratio in the front
-        // of the list and put others in the end from large to small. Some low end devices may
-        // not able to get an supported resolution that match the preferred aspect ratio.
-
-        // Group output sizes by aspect ratio.
-        Map<Rational, List<Size>> aspectRatioSizeListMap =
-                groupSizesByAspectRatio(resolutionCandidateList);
-
-        // If the target resolution is set, use it to remove unnecessary larger sizes.
-        if (miniBoundingSize != null) {
-            // Sorts sizes from each aspect ratio size list
-            for (Rational key : aspectRatioSizeListMap.keySet()) {
-                List<Size> sortedResult = sortSupportedSizesByMiniBoundingSize(
-                        aspectRatioSizeListMap.get(key), miniBoundingSize);
-                aspectRatioSizeListMap.put(key, sortedResult);
-            }
-        }
-
-        // Sort the aspect ratio key set by the target aspect ratio.
-        List<Rational> aspectRatios = new ArrayList<>(aspectRatioSizeListMap.keySet());
-        Collections.sort(aspectRatios,
-                new AspectRatioUtil.CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace(
-                        aspectRatio, mFullFovRatio));
-
-        List<Size> resultList = new ArrayList<>();
-
-        // Put available sizes into final result list by aspect ratio distance to target ratio.
-        for (Rational rational : aspectRatios) {
-            for (Size size : aspectRatioSizeListMap.get(rational)) {
+        for (List<Size> sortedSizes : aspectRatioSizeListMap.values()) {
+            for (Size size : sortedSizes) {
                 // A size may exist in multiple groups in mod16 condition. Keep only one in
                 // the final list.
                 if (!resultList.contains(size)) {
@@ -321,7 +231,322 @@
             }
         }
 
-        return resultList;
+        // Applies the resolution filter onto the resolution candidate list.
+        return applyResolutionFilter(resultList, resolutionSelector.getResolutionFilter(),
+                ((ImageOutputConfig) useCaseConfig).getTargetRotation(Surface.ROTATION_0));
+    }
+
+    /**
+     * Returns the normal supported output sizes.
+     *
+     * <p>When using camera-camera2 implementation, the output sizes are retrieved via
+     * StreamConfigurationMap#getOutputSizes().
+     *
+     * @return the resolution candidate list sorted in descending order.
+     */
+    @NonNull
+    private List<Size> getResolutionCandidateList(@NonNull UseCaseConfig<?> useCaseConfig) {
+        int imageFormat = useCaseConfig.getInputFormat();
+        ImageOutputConfig imageOutputConfig = (ImageOutputConfig) useCaseConfig;
+        // Tries to get the custom supported resolutions list if it is set
+        List<Size> resolutionCandidateList = getCustomizedSupportedResolutionsFromConfig(
+                imageFormat, imageOutputConfig);
+
+        // Tries to get the supported output sizes from the CameraInfoInternal if both custom
+        // ordered and supported resolutions lists are not set.
+        if (resolutionCandidateList == null) {
+            resolutionCandidateList = mCameraInfoInternal.getSupportedResolutions(imageFormat);
+        }
+
+        Collections.sort(resolutionCandidateList, new CompareSizesByArea(true));
+
+        return resolutionCandidateList;
+    }
+
+    /**
+     * Appends the high resolution supported output sizes according to the high resolution settings.
+     *
+     * <p>When using camera-camera2 implementation, the output sizes are retrieved via
+     * StreamConfigurationMap#getHighResolutionOutputSizes().
+     *
+     * @param resolutionCandidateList the supported size list which contains only normal output
+     *                                sizes.
+     * @param resolutionSelector      the specified resolution selector.
+     * @param imageFormat             the desired image format for the target use case.
+     * @return the resolution candidate list including the high resolution output sizes sorted in
+     * descending order.
+     */
+    @NonNull
+    private List<Size> applyHighResolutionSettings(@NonNull List<Size> resolutionCandidateList,
+            @NonNull ResolutionSelector resolutionSelector, int imageFormat) {
+        // Appends high resolution output sizes if high resolution is enabled by ResolutionSelector
+        if (resolutionSelector.getHighResolutionEnabledFlags() != 0) {
+            List<Size> allSizesList = new ArrayList<>();
+            allSizesList.addAll(resolutionCandidateList);
+            allSizesList.addAll(mCameraInfoInternal.getSupportedHighResolutions(imageFormat));
+            Collections.sort(allSizesList, new CompareSizesByArea(true));
+            return allSizesList;
+        }
+
+        return resolutionCandidateList;
+    }
+
+    /**
+     * Applies the aspect ratio strategy onto the input resolution candidate list.
+     *
+     * @param resolutionCandidateList the supported sizes list which has been sorted in
+     *                                descending order.
+     * @param aspectRatioStrategy     the specified aspect ratio strategy.
+     * @return an aspect ratio to size list linked hash map which the aspect ratio fallback rule
+     * is applied and is sorted against the preferred aspect ratio.
+     */
+    @NonNull
+    private LinkedHashMap<Rational, List<Size>> applyAspectRatioStrategy(
+            @NonNull List<Size> resolutionCandidateList,
+            @NonNull AspectRatioStrategy aspectRatioStrategy) {
+        // Group output sizes by aspect ratio.
+        Map<Rational, List<Size>> aspectRatioSizeListMap =
+                groupSizesByAspectRatio(resolutionCandidateList);
+
+        // Applies the aspect ratio fallback rule
+        return applyAspectRatioStrategyFallbackRule(aspectRatioSizeListMap, aspectRatioStrategy);
+    }
+
+    /**
+     * Applies the aspect ratio strategy fallback rule to the aspect ratio to size list map.
+     *
+     * @param sizeGroupsMap       the aspect ratio to size list map. The size list should have been
+     *                            sorted in descending order.
+     * @param aspectRatioStrategy the specified aspect ratio strategy.
+     * @return an aspect ratio to size list linked hash map which the aspect ratio fallback rule
+     * is applied and is sorted against the preferred aspect ratio.
+     */
+    private LinkedHashMap<Rational, List<Size>> applyAspectRatioStrategyFallbackRule(
+            @NonNull Map<Rational, List<Size>> sizeGroupsMap,
+            @NonNull AspectRatioStrategy aspectRatioStrategy) {
+        Rational aspectRatio = getTargetAspectRatioRationalValue(
+                aspectRatioStrategy.getPreferredAspectRatio(), mIsSensorLandscapeResolution);
+
+        // Remove items of all other aspect ratios if the fallback rule is AspectRatioStrategy
+        // .FALLBACK_RULE_NONE
+        if (aspectRatioStrategy.getFallbackRule() == AspectRatioStrategy.FALLBACK_RULE_NONE) {
+            Rational preferredAspectRatio = getTargetAspectRatioRationalValue(
+                    aspectRatioStrategy.getPreferredAspectRatio(), mIsSensorLandscapeResolution);
+            for (Rational ratio : new ArrayList<>(sizeGroupsMap.keySet())) {
+                if (!ratio.equals(preferredAspectRatio)) {
+                    sizeGroupsMap.remove(ratio);
+                }
+            }
+        }
+
+        // Sorts the aspect ratio key set by the preferred aspect ratio.
+        List<Rational> aspectRatios = new ArrayList<>(sizeGroupsMap.keySet());
+        Collections.sort(aspectRatios,
+                new AspectRatioUtil.CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace(
+                        aspectRatio, mFullFovRatio));
+
+        // Stores the size groups into LinkedHashMap to keep the order
+        LinkedHashMap<Rational, List<Size>> sortedAspectRatioSizeListMap = new LinkedHashMap<>();
+        for (Rational ratio : aspectRatios) {
+            sortedAspectRatioSizeListMap.put(ratio, sizeGroupsMap.get(ratio));
+        }
+
+        return sortedAspectRatioSizeListMap;
+    }
+
+    /**
+     * Applies the resolution strategy onto the aspect ratio to size list linked hash map.
+     *
+     * <p>The resolution fallback rule is applied to filter out and sort the sizes in the
+     * underlying size list.
+     *
+     * @param sortedAspectRatioSizeListMap the aspect ratio to size list linked hash map. The
+     *                                     entries order should not be changed.
+     * @param resolutionStrategy           the resolution strategy to sort the candidate
+     *                                     resolutions.
+     */
+    private static void applyResolutionStrategy(
+            @NonNull LinkedHashMap<Rational, List<Size>> sortedAspectRatioSizeListMap,
+            @Nullable ResolutionStrategy resolutionStrategy) {
+        if (resolutionStrategy == null) {
+            return;
+        }
+
+        // Applies the resolution strategy with the specified fallback rule
+        for (Rational key : sortedAspectRatioSizeListMap.keySet()) {
+            applyResolutionStrategyFallbackRule(sortedAspectRatioSizeListMap.get(key),
+                    resolutionStrategy);
+        }
+    }
+
+    /**
+     * Applies the resolution strategy fallback rule to the size list.
+     *
+     * @param supportedSizesList the supported sizes list which has been sorted in descending order.
+     * @param resolutionStrategy the resolution strategy to sort the candidate resolutions.
+     */
+    private static void applyResolutionStrategyFallbackRule(
+            @NonNull List<Size> supportedSizesList,
+            @NonNull ResolutionStrategy resolutionStrategy) {
+        if (supportedSizesList.isEmpty()) {
+            return;
+        }
+        Integer fallbackRule = resolutionStrategy.getFallbackRule();
+
+        if (fallbackRule == null) {
+            // Do nothing for HIGHEST_AVAILABLE_STRATEGY case.
+            return;
+        }
+
+        Size boundSize = resolutionStrategy.getBoundSize();
+
+        switch (fallbackRule) {
+            case ResolutionStrategy.FALLBACK_RULE_NONE:
+                sortSupportedSizesByFallbackRuleNone(supportedSizesList, boundSize);
+                break;
+            case ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER:
+                sortSupportedSizesByFallbackRuleClosestHigherThenLower(supportedSizesList,
+                        boundSize, true);
+                break;
+            case ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER:
+                sortSupportedSizesByFallbackRuleClosestHigherThenLower(supportedSizesList,
+                        boundSize, false);
+                break;
+            case ResolutionStrategy.FALLBACK_RULE_CLOSEST_LOWER_THEN_HIGHER:
+                sortSupportedSizesByFallbackRuleClosestLowerThenHigher(supportedSizesList,
+                        boundSize, true);
+                break;
+            case ResolutionStrategy.FALLBACK_RULE_CLOSEST_LOWER:
+                sortSupportedSizesByFallbackRuleClosestLowerThenHigher(supportedSizesList,
+                        boundSize, false);
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Applies the resolution filtered to the sorted output size list.
+     *
+     * @param sizeList         the supported size list which has been filtered and sorted by the
+     *                         specified aspect ratio, resolution strategies.
+     * @param resolutionFilter the specified resolution filter.
+     * @param targetRotation   the use case target rotation info
+     * @return the result size list applied the specified resolution filter.
+     * @throws IllegalArgumentException if the specified resolution filter returns any size which
+     *                                  is not included in the provided supported size list.
+     */
+    @NonNull
+    private List<Size> applyResolutionFilter(@NonNull List<Size> sizeList,
+            @Nullable ResolutionFilter resolutionFilter,
+            @ImageOutputConfig.RotationValue int targetRotation) {
+        if (resolutionFilter == null) {
+            return sizeList;
+        }
+
+        // Invokes ResolutionFilter#filter() to filter/sort and return the result if it is
+        // specified.
+        int destRotationDegrees = CameraOrientationUtil.surfaceRotationToDegrees(
+                targetRotation);
+        int rotationDegrees =
+                CameraOrientationUtil.getRelativeImageRotation(destRotationDegrees,
+                        mSensorOrientation,
+                        mLensFacing == CameraSelector.LENS_FACING_BACK);
+        List<Size> filteredResultList = resolutionFilter.filter(new ArrayList<>(sizeList),
+                rotationDegrees);
+        if (sizeList.containsAll(filteredResultList)) {
+            return filteredResultList;
+        } else {
+            throw new IllegalArgumentException("The returned sizes list of the resolution "
+                    + "filter must be a subset of the provided sizes list.");
+        }
+    }
+
+    /**
+     * Sorts the size list for {@link ResolutionStrategy#FALLBACK_RULE_NONE}.
+     *
+     * @param supportedSizesList the supported sizes list which has been sorted in descending order.
+     * @param boundSize          the resolution strategy bound size.
+     */
+    private static void sortSupportedSizesByFallbackRuleNone(
+            @NonNull List<Size> supportedSizesList, @NonNull Size boundSize) {
+        boolean containsBoundSize = supportedSizesList.contains(boundSize);
+        supportedSizesList.clear();
+        if (containsBoundSize) {
+            supportedSizesList.add(boundSize);
+        }
+    }
+
+    /**
+     * Sorts the size list for {@link ResolutionStrategy#FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER}
+     * or {@link ResolutionStrategy#FALLBACK_RULE_CLOSEST_HIGHER}.
+     *
+     * @param supportedSizesList the supported sizes list which has been sorted in descending order.
+     * @param boundSize          the resolution strategy bound size.
+     * @param keepLowerSizes     keeps the sizes lower than the bound size in the result list if
+     *                           this is {@code true}.
+     */
+    static void sortSupportedSizesByFallbackRuleClosestHigherThenLower(
+            @NonNull List<Size> supportedSizesList, @NonNull Size boundSize,
+            boolean keepLowerSizes) {
+        List<Size> lowerSizes = new ArrayList<>();
+
+        for (int i = supportedSizesList.size() - 1; i >= 0; i--) {
+            Size outputSize = supportedSizesList.get(i);
+            if (outputSize.getWidth() < boundSize.getWidth()
+                    || outputSize.getHeight() < boundSize.getHeight()) {
+                // The supportedSizesList is in descending order. Checking and put the
+                // bounding-below size at position 0 so that the largest smaller resolution
+                // will be put in the first position finally.
+                lowerSizes.add(0, outputSize);
+            } else {
+                break;
+            }
+        }
+        // Removes the lower sizes from the list
+        supportedSizesList.removeAll(lowerSizes);
+        // Reverses the list so that the smallest larger resolution will be put in the first
+        // position.
+        Collections.reverse(supportedSizesList);
+        if (keepLowerSizes) {
+            // Appends the lower sizes to the tail
+            supportedSizesList.addAll(lowerSizes);
+        }
+    }
+
+    /**
+     * Sorts the size list for {@link ResolutionStrategy#FALLBACK_RULE_CLOSEST_LOWER_THEN_HIGHER}
+     * or {@link ResolutionStrategy#FALLBACK_RULE_CLOSEST_LOWER}.
+     *
+     * @param supportedSizesList the supported sizes list which has been sorted in descending order.
+     * @param boundSize          the resolution strategy bound size.
+     * @param keepHigherSizes    keeps the sizes higher than the bound size in the result list if
+     *                           this is {@code true}.
+     */
+    private static void sortSupportedSizesByFallbackRuleClosestLowerThenHigher(
+            @NonNull List<Size> supportedSizesList, @NonNull Size boundSize,
+            boolean keepHigherSizes) {
+        List<Size> higherSizes = new ArrayList<>();
+
+        for (int i = 0; i < supportedSizesList.size(); i++) {
+            Size outputSize = supportedSizesList.get(i);
+            if (outputSize.getWidth() > boundSize.getWidth()
+                    || outputSize.getHeight() > boundSize.getHeight()) {
+                // The supportedSizesList is in descending order. Checking and put the
+                // bounding-above size at position 0 so that the smallest larger resolution
+                // will be put in the first position finally.
+                higherSizes.add(0, outputSize);
+            } else {
+                // Breaks the for-loop to keep the equal-to or lower sizes in the list.
+                break;
+            }
+        }
+        // Removes the higher sizes from the list
+        supportedSizesList.removeAll(higherSizes);
+        if (keepHigherSizes) {
+            // Appends the higher sizes to the tail
+            supportedSizesList.addAll(higherSizes);
+        }
     }
 
     /**
@@ -391,42 +616,8 @@
     }
 
     /**
-     * Removes unnecessary sizes by target size.
-     *
-     * <p>If the target resolution is set, a size that is equal to or closest to the target
-     * resolution will be selected. If the list includes more than one size equal to or larger
-     * than the target resolution, only one closest size needs to be kept. The other larger sizes
-     * can be removed so that they won't be selected to use.
-     *
-     * @param supportedSizesList The list should have been sorted in descending order.
-     * @param miniBoundingSize   The target size used to remove unnecessary sizes.
+     * Groups the input sizes into an aspect ratio to size list map.
      */
-    static List<Size> sortSupportedSizesByMiniBoundingSize(@NonNull List<Size> supportedSizesList,
-            @NonNull Size miniBoundingSize) {
-        if (supportedSizesList.isEmpty()) {
-            return supportedSizesList;
-        }
-
-        List<Size> resultList = new ArrayList<>();
-
-        // Get the index of the item that is equal to or closest to the target size.
-        for (int i = 0; i < supportedSizesList.size(); i++) {
-            Size outputSize = supportedSizesList.get(i);
-            if (outputSize.getWidth() >= miniBoundingSize.getWidth()
-                    && outputSize.getHeight() >= miniBoundingSize.getHeight()) {
-                // The supportedSizesList is in descending order. Checking and put the
-                // mini-bounding-above size at position 0 so that the smallest larger resolution
-                // will be put in the first position finally.
-                resultList.add(0, outputSize);
-            } else {
-                // Appends the remaining smaller sizes in descending order.
-                resultList.add(outputSize);
-            }
-        }
-
-        return resultList;
-    }
-
     static Map<Rational, List<Size>> groupSizesByAspectRatio(@NonNull List<Size> sizes) {
         Map<Rational, List<Size>> aspectRatioSizeListMap = new HashMap<>();
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorterLegacy.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorterLegacy.java
index 2329794..7483407 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorterLegacy.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorterLegacy.java
@@ -19,7 +19,7 @@
 import static androidx.camera.core.impl.utils.AspectRatioUtil.hasMatchingAspectRatio;
 import static androidx.camera.core.internal.SupportedOutputSizesSorter.getResolutionListGroupingAspectRatioKeys;
 import static androidx.camera.core.internal.SupportedOutputSizesSorter.groupSizesByAspectRatio;
-import static androidx.camera.core.internal.SupportedOutputSizesSorter.sortSupportedSizesByMiniBoundingSize;
+import static androidx.camera.core.internal.SupportedOutputSizesSorter.sortSupportedSizesByFallbackRuleClosestHigherThenLower;
 import static androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_VGA;
 import static androidx.camera.core.internal.utils.SizeUtil.RESOLUTION_ZERO;
 import static androidx.camera.core.internal.utils.SizeUtil.getArea;
@@ -141,7 +141,8 @@
 
             // If the target resolution is set, use it to sort the sizes list.
             if (targetSize != null) {
-                resultSizeList = sortSupportedSizesByMiniBoundingSize(resultSizeList, targetSize);
+                sortSupportedSizesByFallbackRuleClosestHigherThenLower(resultSizeList, targetSize,
+                        true);
             }
         } else {
             // Rearrange the supported size to put the ones with the same aspect ratio in the front
@@ -151,13 +152,11 @@
             // Group output sizes by aspect ratio.
             aspectRatioSizeListMap = groupSizesByAspectRatio(filteredSizeList);
 
-            // If the target resolution is set, use it to remove unnecessary larger sizes.
+            // If the target resolution is set, sort the sizes against it.
             if (targetSize != null) {
-                // Remove unnecessary larger sizes from each aspect ratio size list
                 for (Rational key : aspectRatioSizeListMap.keySet()) {
-                    List<Size> sortedResult = sortSupportedSizesByMiniBoundingSize(
-                            aspectRatioSizeListMap.get(key), targetSize);
-                    aspectRatioSizeListMap.put(key, sortedResult);
+                    sortSupportedSizesByFallbackRuleClosestHigherThenLower(
+                            aspectRatioSizeListMap.get(key), targetSize, true);
                 }
             }
 
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 d144363..40b9b28 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
@@ -29,6 +29,7 @@
 import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
 import androidx.annotation.WorkerThread;
+import androidx.camera.core.Logger;
 import androidx.camera.core.SurfaceOutput;
 import androidx.camera.core.SurfaceProcessor;
 import androidx.camera.core.SurfaceRequest;
@@ -42,6 +43,7 @@
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -53,13 +55,14 @@
 @RequiresApi(21)
 public class DefaultSurfaceProcessor implements SurfaceProcessorInternal,
         SurfaceTexture.OnFrameAvailableListener {
+    private static final String TAG = "DefaultSurfaceProcessor";
     private final OpenGlRenderer mGlRenderer;
     @VisibleForTesting
     final HandlerThread mGlThread;
     private final Executor mGlExecutor;
     @VisibleForTesting
     final Handler mGlHandler;
-    private final AtomicBoolean mIsReleased = new AtomicBoolean(false);
+    private final AtomicBoolean mIsReleaseRequested = new AtomicBoolean(false);
     private final float[] mTextureMatrix = new float[16];
     private final float[] mSurfaceOutputMatrix = new float[16];
     // Map of current set of available outputs. Only access this on GL thread.
@@ -68,6 +71,8 @@
 
     // Only access this on GL thread.
     private int mInputSurfaceCount = 0;
+    // Only access this on GL thread.
+    private boolean mIsReleased = false;
 
     /** Constructs {@link DefaultSurfaceProcessor} with default shaders. */
     DefaultSurfaceProcessor() {
@@ -99,11 +104,11 @@
      */
     @Override
     public void onInputSurface(@NonNull SurfaceRequest surfaceRequest) {
-        if (mIsReleased.get()) {
+        if (mIsReleaseRequested.get()) {
             surfaceRequest.willNotProvideSurface();
             return;
         }
-        mGlExecutor.execute(() -> {
+        executeSafely(() -> {
             mInputSurfaceCount++;
             SurfaceTexture surfaceTexture = new SurfaceTexture(mGlRenderer.getTextureName());
             surfaceTexture.setDefaultBufferSize(surfaceRequest.getResolution().getWidth(),
@@ -117,7 +122,7 @@
                 checkReadyToRelease();
             });
             surfaceTexture.setOnFrameAvailableListener(this, mGlHandler);
-        });
+        }, surfaceRequest::willNotProvideSurface);
     }
 
     /**
@@ -125,11 +130,11 @@
      */
     @Override
     public void onOutputSurface(@NonNull SurfaceOutput surfaceOutput) {
-        if (mIsReleased.get()) {
+        if (mIsReleaseRequested.get()) {
             surfaceOutput.close();
             return;
         }
-        mGlExecutor.execute(() -> {
+        executeSafely(() -> {
             Surface surface = surfaceOutput.getSurface(mGlExecutor, event -> {
                 surfaceOutput.close();
                 Surface removedSurface = mOutputSurfaces.remove(surfaceOutput);
@@ -139,7 +144,7 @@
             });
             mGlRenderer.registerOutputSurface(surface);
             mOutputSurfaces.put(surfaceOutput, surface);
-        });
+        }, surfaceOutput::close);
     }
 
     /**
@@ -147,10 +152,13 @@
      */
     @Override
     public void release() {
-        if (mIsReleased.getAndSet(true)) {
+        if (mIsReleaseRequested.getAndSet(true)) {
             return;
         }
-        mGlExecutor.execute(this::checkReadyToRelease);
+        executeSafely(() -> {
+            mIsReleased = true;
+            checkReadyToRelease();
+        });
     }
 
     /**
@@ -158,7 +166,7 @@
      */
     @Override
     public void onFrameAvailable(@NonNull SurfaceTexture surfaceTexture) {
-        if (mIsReleased.get()) {
+        if (mIsReleaseRequested.get()) {
             // Ignore frame update if released.
             return;
         }
@@ -183,7 +191,7 @@
 
     @WorkerThread
     private void checkReadyToRelease() {
-        if (mIsReleased.get() && mInputSurfaceCount == 0) {
+        if (mIsReleased && mInputSurfaceCount == 0) {
             // Once release is called, we can stop sending frame to output surfaces.
             for (SurfaceOutput surfaceOutput : mOutputSurfaces.keySet()) {
                 surfaceOutput.close();
@@ -196,7 +204,7 @@
 
     private void initGlRenderer(@NonNull ShaderProvider shaderProvider) {
         ListenableFuture<Void> initFuture = CallbackToFutureAdapter.getFuture(completer -> {
-            mGlExecutor.execute(() -> {
+            executeSafely(() -> {
                 try {
                     mGlRenderer.init(shaderProvider);
                     completer.set(null);
@@ -220,6 +228,27 @@
         }
     }
 
+    private void executeSafely(@NonNull Runnable runnable) {
+        executeSafely(runnable, () -> {
+            // Do nothing.
+        });
+    }
+
+    private void executeSafely(@NonNull Runnable runnable, @NonNull Runnable onFailure) {
+        try {
+            mGlExecutor.execute(() -> {
+                if (mIsReleased) {
+                    onFailure.run();
+                } else {
+                    runnable.run();
+                }
+            });
+        } catch (RejectedExecutionException e) {
+            Logger.w(TAG, "Unable to executor runnable", e);
+            onFailure.run();
+        }
+    }
+
     /**
      * Factory class that produces {@link DefaultSurfaceProcessor}.
      *
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceEdge.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceEdge.java
index e71e058..77a1cc3 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceEdge.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceEdge.java
@@ -315,7 +315,7 @@
     @NonNull
     public ListenableFuture<SurfaceOutput> createSurfaceOutputFuture(@NonNull Size inputSize,
             @CameraEffect.Formats int format, @NonNull Rect cropRect, int rotationDegrees,
-            boolean mirroring, @NonNull CameraInternal cameraInternal) {
+            boolean mirroring, @Nullable CameraInternal cameraInternal) {
         checkMainThread();
         checkNotClosed();
         checkAndSetHasConsumer();
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceOutputImpl.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceOutputImpl.java
index 177090e..64a682b 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceOutputImpl.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceOutputImpl.java
@@ -20,6 +20,7 @@
 import static androidx.camera.core.impl.utils.TransformUtils.getRectToRect;
 import static androidx.camera.core.impl.utils.TransformUtils.rotateSize;
 import static androidx.camera.core.impl.utils.TransformUtils.sizeToRectF;
+import static androidx.core.util.Preconditions.checkState;
 
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -38,6 +39,7 @@
 import androidx.camera.core.CameraEffect;
 import androidx.camera.core.Logger;
 import androidx.camera.core.SurfaceOutput;
+import androidx.camera.core.SurfaceProcessor;
 import androidx.camera.core.impl.CameraInternal;
 import androidx.concurrent.futures.CallbackToFutureAdapter;
 import androidx.core.util.Consumer;
@@ -76,7 +78,7 @@
     private final float[] mAdditionalTransform = new float[16];
     // The inverted value of SurfaceTexture#getTransformMatrix()
     @NonNull
-    private final float[] mInvertedCameraTransform = new float[16];
+    private final float[] mInvertedTextureTransform = new float[16];
     @GuardedBy("mLock")
     @Nullable
     private Consumer<Event> mEventListener;
@@ -91,6 +93,8 @@
     @NonNull
     private final ListenableFuture<Void> mCloseFuture;
     private CallbackToFutureAdapter.Completer<Void> mCloseFutureCompleter;
+    @Nullable
+    private CameraInternal mCameraInternal;
 
     SurfaceOutputImpl(
             @NonNull Surface surface,
@@ -101,7 +105,7 @@
             @NonNull Rect inputCropRect,
             int rotationDegree,
             boolean mirroring,
-            @NonNull CameraInternal cameraInternal) {
+            @Nullable CameraInternal cameraInternal) {
         mSurface = surface;
         mTargets = targets;
         mFormat = format;
@@ -110,7 +114,8 @@
         mInputCropRect = new Rect(inputCropRect);
         mMirroring = mirroring;
         mRotationDegrees = rotationDegree;
-        calculateAdditionalTransform(cameraInternal);
+        mCameraInternal = cameraInternal;
+        calculateAdditionalTransform();
         mCloseFuture = CallbackToFutureAdapter.getFuture(
                 completer -> {
                     mCloseFutureCompleter = completer;
@@ -138,9 +143,8 @@
     }
 
     /**
-     * @inheritDoc
+     * Asks the {@link SurfaceProcessor} implementation to stopping writing to the {@link Surface}.
      */
-    @Override
     public void requestClose() {
         AtomicReference<Consumer<Event>> eventListenerRef = new AtomicReference<>();
         Executor executor = null;
@@ -211,6 +215,11 @@
         return mMirroring;
     }
 
+    @VisibleForTesting
+    public CameraInternal getCamera() {
+        return mCameraInternal;
+    }
+
     /**
      * This method can be invoked by the processor implementation on any thread.
      *
@@ -229,7 +238,6 @@
 
     /**
      * Returns the close state.
-     *
      */
     @RestrictTo(RestrictTo.Scope.TESTS)
     public boolean isClosed() {
@@ -258,22 +266,22 @@
     /**
      * Calculates the additional GL transform and saves it to {@link #mAdditionalTransform}.
      *
-     * <p>The effect implementation needs to apply this value on top of camera transform obtained
+     * <p>The effect implementation needs to apply this value on top of texture transform obtained
      * from {@link SurfaceTexture#getTransformMatrix}.
      *
-     * <p>The overall transformation (A * B) is a concatenation of 2 values: A) the camera/GL
+     * <p>The overall transformation (A * B) is a concatenation of 2 values: A) the texture
      * transform (value of SurfaceTexture#getTransformMatrix), and B) CameraX's additional
      * transform based on user config such as the ViewPort API and UseCase#targetRotation. To
      * calculate B, we do it in 3 steps:
      * <ol>
      * <li>1. Calculate A * B by using CameraX transformation value such as crop rect, relative
-     * rotation, and mirroring. These info already contain the camera transform(A) in it.
-     * <li>2. Calculate A^-1 by predicating the camera transform(A) based on camera
+     * rotation, and mirroring. It already contains the texture transform(A).
+     * <li>2. Calculate A^-1 by predicating the texture transform(A) based on camera
      * characteristics then inverting it.
      * <li>3. Calculate B by multiplying A^-1 * A * B.
      * </ol>
      */
-    private void calculateAdditionalTransform(@NonNull CameraInternal cameraInternal) {
+    private void calculateAdditionalTransform() {
         Matrix.setIdentityM(mAdditionalTransform, 0);
 
         // Step 1, calculate the overall transformation(A * B) with the following steps:
@@ -313,47 +321,48 @@
         Matrix.translateM(mAdditionalTransform, 0, offsetX, offsetY, 0f);
         Matrix.scaleM(mAdditionalTransform, 0, scaleX, scaleY, 1f);
 
-        // Step 2: calculate the inverted camera/GL transform: A^-1
-        calculateInvertedCameraTransform(cameraInternal);
+        // Step 2: calculate the inverted texture transform: A^-1
+        calculateInvertedTextureTransform();
 
         // Step 3: calculate the additional transform: B = A^-1 * A * B
-        Matrix.multiplyMM(mAdditionalTransform, 0, mInvertedCameraTransform, 0,
+        Matrix.multiplyMM(mAdditionalTransform, 0, mInvertedTextureTransform, 0,
                 mAdditionalTransform, 0);
     }
 
     /**
-     * Calculates the inverted camera/GL transform and saves it to
-     * {@link #mInvertedCameraTransform}.
+     * Calculates the inverted texture transform and saves it to
+     * {@link #mInvertedTextureTransform}.
      *
      * <p>This method predicts the value of {@link SurfaceTexture#getTransformMatrix} based on
-     * camera characteristics then invert it. The result is used to remove the camera transform
+     * camera characteristics then invert it. The result is used to remove the texture transform
      * from overall transformation.
      */
-    private void calculateInvertedCameraTransform(@NonNull CameraInternal cameraInternal) {
-        Matrix.setIdentityM(mInvertedCameraTransform, 0);
+    private void calculateInvertedTextureTransform() {
+        Matrix.setIdentityM(mInvertedTextureTransform, 0);
 
         // Flip for GL. SurfaceTexture#getTransformMatrix always contains this flipping regardless
         // of whether it has the camera transform.
-        Matrix.translateM(mInvertedCameraTransform, 0, 0f, 1f, 0f);
-        Matrix.scaleM(mInvertedCameraTransform, 0, 1f, -1f, 1f);
+        Matrix.translateM(mInvertedTextureTransform, 0, 0f, 1f, 0f);
+        Matrix.scaleM(mInvertedTextureTransform, 0, 1f, -1f, 1f);
 
-        // Applies the transform from CameraInfo if the camera has transformation.
-        if (cameraInternal.getHasTransform()) {
+        // Applies the camera sensor orientation if the input surface contains camera transform.
+        if (mCameraInternal != null) {
+            checkState(mCameraInternal.getHasTransform(), "Camera has no transform.");
 
             // Rotation
-            preRotate(mInvertedCameraTransform,
-                    cameraInternal.getCameraInfo().getSensorRotationDegrees(),
+            preRotate(mInvertedTextureTransform,
+                    mCameraInternal.getCameraInfo().getSensorRotationDegrees(),
                     0.5f,
                     0.5f);
 
             // Mirroring
-            if (cameraInternal.isFrontFacing()) {
-                Matrix.translateM(mInvertedCameraTransform, 0, 1, 0f, 0f);
-                Matrix.scaleM(mInvertedCameraTransform, 0, -1, 1f, 1f);
+            if (mCameraInternal.isFrontFacing()) {
+                Matrix.translateM(mInvertedTextureTransform, 0, 1, 0f, 0f);
+                Matrix.scaleM(mInvertedTextureTransform, 0, -1, 1f, 1f);
             }
         }
 
         // Invert the matrix so it can be used to "undo" the SurfaceTexture#getTransformMatrix.
-        Matrix.invertM(mInvertedCameraTransform, 0, mInvertedCameraTransform, 0);
+        Matrix.invertM(mInvertedTextureTransform, 0, mInvertedTextureTransform, 0);
     }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorNode.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorNode.java
index 66ca7ab4..040006d 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorNode.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorNode.java
@@ -200,7 +200,7 @@
                 output.getKey().getCropRect(),
                 output.getKey().getRotationDegrees(),
                 output.getKey().getMirroring(),
-                mCameraInternal);
+                input.hasCameraTransform() ? mCameraInternal : null);
         Futures.addCallback(future, new FutureCallback<SurfaceOutput>() {
             @Override
             public void onSuccess(@Nullable SurfaceOutput output) {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/resolutionselector/AspectRatioStrategy.java b/camera/camera-core/src/main/java/androidx/camera/core/resolutionselector/AspectRatioStrategy.java
new file mode 100644
index 0000000..bf36dfde
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/resolutionselector/AspectRatioStrategy.java
@@ -0,0 +1,132 @@
+/*
+ * 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.core.resolutionselector;
+
+import static androidx.camera.core.AspectRatio.RATIO_4_3;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.camera.core.AspectRatio;
+import androidx.camera.core.CameraSelector;
+import androidx.camera.core.UseCase;
+import androidx.lifecycle.LifecycleOwner;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The aspect ratio strategy defines the sequence of aspect ratios that are used to select the
+ * best size for a particular image.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public final class AspectRatioStrategy {
+    /**
+     * CameraX doesn't fall back to select sizes of any other aspect ratio when this fallback
+     * rule is used.
+     */
+    public static final int FALLBACK_RULE_NONE = 0;
+    /**
+     * CameraX automatically chooses the next best aspect ratio which contains the closest field
+     * of view (FOV) of the camera sensor, from the remaining options.
+     */
+    public static final int FALLBACK_RULE_AUTO = 1;
+
+    /**
+     * Defines the available fallback rules for AspectRatioStrategy.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({FALLBACK_RULE_NONE,
+            FALLBACK_RULE_AUTO
+    })
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public @interface AspectRatioFallbackRule {
+    }
+
+    /**
+     * The pre-defined default aspect ratio strategy that selects sizes with
+     * {@link AspectRatio#RATIO_4_3} in priority. Then, selects sizes with other aspect ratios
+     * according to which aspect ratio can contain the closest FOV of the camera sensor.
+     *
+     * <p>Please see the
+     * <a href="https://source.android.com/docs/core/camera/camera3_crop_reprocess">Output streams,
+     * Cropping, and Zoom</a> introduction to know more about the camera FOV.
+     */
+    @NonNull
+    public static final AspectRatioStrategy RATIO_4_3_FALLBACK_AUTO_STRATEGY =
+            AspectRatioStrategy.create(RATIO_4_3, FALLBACK_RULE_AUTO);
+
+    @AspectRatio.Ratio
+    private final int mPreferredAspectRatio;
+    @AspectRatioFallbackRule
+    private final int mFallbackRule;
+
+    private AspectRatioStrategy(@AspectRatio.Ratio int preferredAspectRatio,
+            @AspectRatioFallbackRule int fallbackRule) {
+        mPreferredAspectRatio = preferredAspectRatio;
+        mFallbackRule = fallbackRule;
+    }
+
+    /**
+     * Returns the specified preferred aspect ratio.
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @AspectRatio.Ratio
+    public int getPreferredAspectRatio() {
+        return mPreferredAspectRatio;
+    }
+
+    /**
+     * Returns the specified fallback rule for choosing the aspect ratio when the preferred aspect
+     * ratio is not available.
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @AspectRatioFallbackRule
+    public int getFallbackRule() {
+        return mFallbackRule;
+    }
+
+    /**
+     * Creates a new AspectRatioStrategy instance, configured with the specified preferred aspect
+     * ratio and fallback rule.
+     *
+     * <p>OEMs might make the width or height of the supported output sizes be mod 16 aligned for
+     * performance reasons. This means that the device might support 1920x1088 instead of
+     * 1920x1080, even though a 16:9 aspect ratio size is 1920x1080. CameraX can select these mod
+     * 16 aligned sizes when applications specify the preferred aspect ratio as
+     * {@link AspectRatio#RATIO_16_9}.
+     *
+     * <p>Some devices may have issues using sizes of the preferred aspect ratios. CameraX
+     * recommends that applications use the {@link #FALLBACK_RULE_AUTO} setting to avoid no
+     * resolution being available, as an {@link IllegalArgumentException} may be thrown when
+     * calling
+     * {@link androidx.camera.lifecycle.ProcessCameraProvider#bindToLifecycle(LifecycleOwner, CameraSelector, UseCase...)}
+     * to bind {@link UseCase}s with the AspectRatioStrategy specified in the
+     * {@link ResolutionSelector}.
+     *
+     * @param preferredAspectRatio the preferred aspect ratio to select first.
+     * @param fallbackRule the rule to follow when the preferred aspect ratio is not available.
+     */
+    @NonNull
+    public static AspectRatioStrategy create(
+            @AspectRatio.Ratio int preferredAspectRatio,
+            @AspectRatioFallbackRule int fallbackRule) {
+        return new AspectRatioStrategy(preferredAspectRatio, fallbackRule);
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/resolutionselector/HighResolution.java b/camera/camera-core/src/main/java/androidx/camera/core/resolutionselector/HighResolution.java
new file mode 100644
index 0000000..f9c0941
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/resolutionselector/HighResolution.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core.resolutionselector;
+
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+
+/**
+ * The flags that are available in high resolution.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public final class HighResolution {
+    /**
+     * This flag enables high resolution in the default sensor pixel mode.
+     *
+     * <p>When using the <code>camera-camera2</code> CameraX implementation, please see
+     * {@link android.hardware.camera2.CaptureRequest#SENSOR_PIXEL_MODE} to know more information
+     * about the default and maximum resolution sensor pixel mode.
+     *
+     * <p>When this high resolution flag is set, the high resolution is retrieved via the
+     * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighResolutionOutputSizes(int)}
+     * from the stream configuration map obtained with the
+     * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}
+     * camera characteristics.
+     *
+     * <p>Since Android S, some devices might support a maximum resolution sensor pixel mode,
+     * which allows them to capture additional ultra high resolutions retrieved from
+     * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION}
+     * . Enabling high resolution with this flag does not allow applications to select those
+     * ultra high resolutions.
+     */
+    public static final int FLAG_DEFAULT_MODE_ON = 0x1;
+
+    private HighResolution() {
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/resolutionselector/ResolutionFilter.java b/camera/camera-core/src/main/java/androidx/camera/core/resolutionselector/ResolutionFilter.java
new file mode 100644
index 0000000..0756560
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/resolutionselector/ResolutionFilter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core.resolutionselector;
+
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.camera.core.AspectRatio;
+import androidx.camera.core.ImageCapture;
+import androidx.camera.core.UseCase;
+
+import java.util.List;
+
+/**
+ * Applications can filter out unsuitable sizes and sort the resolution list in the preferred
+ * order by implementing the resolution filter interface. The preferred order is the order in
+ * which the resolutions should be tried first.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public interface ResolutionFilter {
+    /**
+     * Removes unsuitable sizes and sorts the resolution list in the preferred order.
+     *
+     * <p>OEMs might make the width or height of the supported output sizes be mod 16 aligned for
+     * performance reasons. This means that the device might support 1920x1088 instead of
+     * 1920x1080, even though a 16:9 aspect ratio size is 1920x1080. Therefore, the input
+     * supported sizes list also contains these aspect ratio sizes when applications specify
+     * an {@link AspectRatioStrategy} with {@link AspectRatio#RATIO_16_9} and then also specify a
+     * ResolutionFilter to apply their own selection logic.
+     *
+     * @param supportedSizes  the supported output sizes which have been filtered and sorted
+     *                        according to the other resolution selector settings.
+     * @param rotationDegrees the rotation degrees to rotate the image to the desired
+     *                        orientation, matching the {@link UseCase}’s target rotation setting
+     *                        . For example, the target rotation set via
+     *                        {@link ImageCapture.Builder#setTargetRotation(int)} or
+     *                        {@link ImageCapture#setTargetRotation(int)}.
+     * @return the desired ordered sizes list for resolution selection. The returned list should
+     * only include sizes in the provided input supported sizes list.
+     */
+    @NonNull
+    List<Size> filter(@NonNull List<Size> supportedSizes, int rotationDegrees);
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/resolutionselector/ResolutionSelector.java b/camera/camera-core/src/main/java/androidx/camera/core/resolutionselector/ResolutionSelector.java
new file mode 100644
index 0000000..006cb53
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/resolutionselector/ResolutionSelector.java
@@ -0,0 +1,227 @@
+/*
+ * 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.core.resolutionselector;
+
+import static androidx.camera.core.resolutionselector.AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.camera.core.UseCase;
+
+/**
+ * A set of requirements and priorities used to select a resolution for the {@link UseCase}.
+ *
+ * <p>The resolution selection mechanism is determined by the following three steps:
+ * <ol>
+ *     <li> Collect the supported output sizes and add them to the candidate resolution list.
+ *     <li> Filter and sort the candidate resolution list according to the preference settings.
+ *     <li> Consider all the resolution selector settings of bound {@link UseCase}s to find the
+ *     resolution that best suits each {@link UseCase}.
+ * </ol>
+ *
+ * <p>For the first step, all supported resolution output sizes are added to the candidate
+ * resolution list as the starting point.
+ *
+ * <p>ResolutionSelector provides the following function for applications to adjust the candidate
+ * resolution settings.
+ * <ul>
+ *     <li> {@link Builder#setHighResolutionEnabledFlags(int)}
+ * </ul>
+ *
+ * <p>For the second step, ResolutionSelector provides the following three functions for
+ * applications to determine which resolution should be selected with higher priority.
+ * <ul>
+ *     <li> {@link Builder#setAspectRatioStrategy(AspectRatioStrategy)}
+ *     <li> {@link Builder#setResolutionStrategy(ResolutionStrategy)}
+ *     <li> {@link Builder#setResolutionFilter(ResolutionFilter)}
+ * </ul>
+ *
+ * <p>CameraX sorts the collected sizes according to the specified aspect ratio and resolution
+ * strategies. The aspect ratio strategy has precedence over the resolution strategy for sorting
+ * the resolution candidate list. If applications specify a custom resolution filter, CameraX
+ * passes the resulting sizes list, sorted by the specified aspect ratio and resolution
+ * strategies, to the resolution filter to get the final desired list.
+ *
+ * <p>Different types of {@link UseCase}s might have their own default settings. You can see the
+ * {@link UseCase} builders’ {@code setResolutionSelector()} function to know the details for each
+ * type of {@link UseCase}.
+ *
+ * <p>In the third step, CameraX selects the final resolution for the {@link UseCase} based on the
+ * camera device's hardware level, capabilities, and the bound {@link UseCase} combination.
+ * Applications can check which resolution is finally selected by using the {@link UseCase}'s
+ * {@code getResolutionInfo()} function.
+ *
+ * <p>Note that a ResolutionSelector with more restricted settings may result in that no
+ * resolution can be selected to use. Applications will receive {@link IllegalArgumentException}
+ * when binding the {@link UseCase}s with such kind of ResolutionSelector. Applications can
+ * specify the {@link AspectRatioStrategy} and {@link ResolutionStrategy} with proper fallback
+ * rules to avoid the {@link IllegalArgumentException} or try-catch it and show a proper message
+ * to the end users.
+ *
+ * <p>When creating a ResolutionSelector instance, the
+ * {@link AspectRatioStrategy#RATIO_4_3_FALLBACK_AUTO_STRATEGY} will be the default
+ * {@link AspectRatioStrategy} if it is not set. However, if neither the
+ * {@link ResolutionStrategy} nor the high resolution enabled flags are set, there will be no
+ * default value specified.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public final class ResolutionSelector {
+    @Nullable
+    private final AspectRatioStrategy mAspectRatioStrategy;
+    @Nullable
+    private final ResolutionStrategy mResolutionStrategy;
+    @Nullable
+    private final ResolutionFilter mResolutionFilter;
+    private final int mHighResolutionEnabledFlags;
+
+    ResolutionSelector(
+            @Nullable AspectRatioStrategy aspectRatioStrategy,
+            @Nullable ResolutionStrategy resolutionStrategy,
+            @Nullable ResolutionFilter resolutionFilter,
+            int highResolutionEnabledFlags) {
+        mAspectRatioStrategy = aspectRatioStrategy;
+        mResolutionStrategy = resolutionStrategy;
+        mResolutionFilter = resolutionFilter;
+        mHighResolutionEnabledFlags = highResolutionEnabledFlags;
+    }
+
+    /**
+     * Returns the specified {@link AspectRatioStrategy}, or null if not specified.
+     */
+    @Nullable
+    public AspectRatioStrategy getAspectRatioStrategy() {
+        return mAspectRatioStrategy;
+    }
+
+    /**
+     * Returns the specified {@link ResolutionStrategy}, or null if not specified.
+     */
+    @Nullable
+    public ResolutionStrategy getResolutionStrategy() {
+        return mResolutionStrategy;
+    }
+
+    /**
+     * Returns the specified {@link ResolutionFilter} implementation, or null if not specified.
+     */
+    @Nullable
+    public ResolutionFilter getResolutionFilter() {
+        return mResolutionFilter;
+    }
+
+    /**
+     * Returns the specified high resolution enabled flags.
+     */
+    public int getHighResolutionEnabledFlags() {
+        return mHighResolutionEnabledFlags;
+    }
+
+    /**
+     * Builder for a {@link ResolutionSelector}.
+     */
+    public static final class Builder {
+        @Nullable
+        private AspectRatioStrategy mAspectRatioStrategy = RATIO_4_3_FALLBACK_AUTO_STRATEGY;
+        @Nullable
+        private ResolutionStrategy mResolutionStrategy = null;
+        @Nullable
+        private ResolutionFilter mResolutionFilter = null;
+        private int mHighResolutionEnabledFlags = 0;
+
+        /**
+         * Creates a Builder instance.
+         */
+        public Builder() {
+        }
+
+        private Builder(@NonNull ResolutionSelector resolutionSelector) {
+            mAspectRatioStrategy = resolutionSelector.getAspectRatioStrategy();
+            mResolutionStrategy = resolutionSelector.getResolutionStrategy();
+            mResolutionFilter = resolutionSelector.getResolutionFilter();
+            mHighResolutionEnabledFlags = resolutionSelector.getHighResolutionEnabledFlags();
+        }
+
+        /**
+         * Creates a Builder from an existing resolution selector.
+         */
+        @NonNull
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        public static Builder fromResolutionSelector(
+                @NonNull ResolutionSelector resolutionSelector) {
+            return new Builder(resolutionSelector);
+        }
+
+        /**
+         * Sets the aspect ratio selection strategy for the {@link UseCase}. The aspect ratio
+         * selection strategy determines how the {@link UseCase} will choose the aspect ratio of
+         * the captured image.
+         */
+        @NonNull
+        public Builder setAspectRatioStrategy(@NonNull AspectRatioStrategy aspectRatioStrategy) {
+            mAspectRatioStrategy = aspectRatioStrategy;
+            return this;
+        }
+
+        /**
+         * Sets the resolution selection strategy for the {@link UseCase}. The resolution selection
+         * strategy determines how the {@link UseCase} will choose the resolution of the captured
+         * image.
+         */
+        @NonNull
+        public Builder setResolutionStrategy(@NonNull ResolutionStrategy resolutionStrategy) {
+            mResolutionStrategy = resolutionStrategy;
+            return this;
+        }
+
+        /**
+         * Sets the resolution filter to output the final desired sizes list. The resolution
+         * filter will filter out unsuitable sizes and sort the resolution list in the preferred
+         * order. The preferred order is the order in which the resolutions should be tried first.
+         */
+        @NonNull
+        public Builder setResolutionFilter(@NonNull ResolutionFilter resolutionFilter) {
+            mResolutionFilter = resolutionFilter;
+            return this;
+        }
+
+        /**
+         * Sets high resolutions enabled flags to allow the application to select high
+         * resolutions for the {@link UseCase}s. This will enable the application to choose high
+         * resolutions for the captured image, which may result in better quality images.
+         *
+         * <p>Now, only {@link HighResolution#FLAG_DEFAULT_MODE_ON} is allowed for this function.
+         */
+        @NonNull
+        public Builder setHighResolutionEnabledFlags(int flags) {
+            mHighResolutionEnabledFlags = flags;
+            return this;
+        }
+
+        /**
+         * Builds the resolution selector. This will create a resolution selector that can be
+         * used to select the desired resolution for the captured image.
+         */
+        @NonNull
+        public ResolutionSelector build() {
+            return new ResolutionSelector(mAspectRatioStrategy, mResolutionStrategy,
+                    mResolutionFilter, mHighResolutionEnabledFlags);
+        }
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/resolutionselector/ResolutionStrategy.java b/camera/camera-core/src/main/java/androidx/camera/core/resolutionselector/ResolutionStrategy.java
new file mode 100644
index 0000000..b0282e1
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/resolutionselector/ResolutionStrategy.java
@@ -0,0 +1,172 @@
+/*
+ * 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.core.resolutionselector;
+
+import android.util.Size;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.camera.core.CameraSelector;
+import androidx.camera.core.UseCase;
+import androidx.lifecycle.LifecycleOwner;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The resolution strategy defines the resolution selection sequence to select the best size.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public final class ResolutionStrategy {
+    /**
+     * A resolution strategy chooses the highest available resolution. This strategy does not
+     * have a bound size or fallback rule. When using this strategy, CameraX selects the
+     * available resolutions to use in descending order, starting with the highest quality
+     * resolution available.
+     */
+    @NonNull
+    public static final ResolutionStrategy HIGHEST_AVAILABLE_STRATEGY = new ResolutionStrategy();
+
+    /**
+     * CameraX doesn't select an alternate size when the specified bound size is unavailable.
+     */
+    public static final int FALLBACK_RULE_NONE = 0;
+    /**
+     * When the specified bound size is unavailable, CameraX falls back to select the closest
+     * higher resolution size. If CameraX still cannot find any available resolution, it will
+     * fallback to select other lower resolutions.
+     */
+    public static final int FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER = 1;
+    /**
+     * When the specified bound size is unavailable, CameraX falls back to the closest higher
+     * resolution size.
+     */
+    public static final int FALLBACK_RULE_CLOSEST_HIGHER = 2;
+    /**
+     * When the specified bound size is unavailable, CameraX falls back to select the closest
+     * lower resolution size. If CameraX still cannot find any available resolution, it will
+     * fallback to select other higher resolutions.
+     */
+    public static final int FALLBACK_RULE_CLOSEST_LOWER_THEN_HIGHER = 3;
+    /**
+     * When the specified bound size is unavailable, CameraX falls back to the closest lower
+     * resolution size.
+     */
+    public static final int FALLBACK_RULE_CLOSEST_LOWER = 4;
+
+    /**
+     * Defines the available fallback rules for ResolutionStrategy.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({FALLBACK_RULE_NONE,
+            FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER,
+            FALLBACK_RULE_CLOSEST_HIGHER,
+            FALLBACK_RULE_CLOSEST_LOWER_THEN_HIGHER,
+            FALLBACK_RULE_CLOSEST_LOWER
+    })
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public @interface ResolutionFallbackRule {
+    }
+
+    private boolean mIsHighestAvailableStrategy = false;
+    @Nullable
+    private Size mBoundSize = null;
+    @Nullable
+    private Integer mFallbackRule = null;
+
+    /**
+     * Creates a default ResolutionStrategy instance to select the highest available resolution.
+     */
+    private ResolutionStrategy() {
+        mIsHighestAvailableStrategy = true;
+    }
+
+    /**
+     * Creates a ResolutionStrategy instance to select resolution according to the specified bound
+     * size and fallback rule.
+     */
+    private ResolutionStrategy(@NonNull Size boundSize,
+            @NonNull Integer fallbackRule) {
+        mBoundSize = boundSize;
+        mFallbackRule = fallbackRule;
+    }
+
+    /**
+     * Returns {@code true} if the instance is a highest available resolution strategy.
+     * Otherwise, returns {@code false}.
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public boolean isHighestAvailableStrategy() {
+        return mIsHighestAvailableStrategy;
+    }
+
+    /**
+     * Returns the specified bound size.
+     *
+     * @return the specified bound size or {@code null} if this is instance of
+     * {@link #HIGHEST_AVAILABLE_STRATEGY}.
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @Nullable
+    public Size getBoundSize() {
+        return mBoundSize;
+    }
+
+    /**
+     * Returns the fallback rule for choosing an alternate size when the specified bound size is
+     * unavailable.
+     */
+    @Nullable
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public Integer getFallbackRule() {
+        return mFallbackRule;
+    }
+
+    /**
+     * Creates a new ResolutionStrategy instance, configured with the specified bound size and
+     * fallback rule.
+     *
+     * <p>If the resolution candidate list contains the bound size and the bound size can fulfill
+     * all resolution selector settings, CameraX can also select the specified bound size as the
+     * result for the {@link UseCase}.
+     *
+     * <p>Some devices may have issues using sizes of the preferred aspect ratios. CameraX
+     * recommends that applications use the following fallback rule setting to avoid no
+     * resolution being available, as an {@link IllegalArgumentException} may be thrown when
+     * calling
+     * {@link androidx.camera.lifecycle.ProcessCameraProvider#bindToLifecycle(LifecycleOwner, CameraSelector, UseCase...)}
+     * to bind {@link UseCase}s with the ResolutionStrategy specified in the
+     * {@link ResolutionSelector}.
+     * <ul>
+     *     <li> {@link #FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER}
+     *     <li> {@link #FALLBACK_RULE_CLOSEST_LOWER_THEN_HIGHER}
+     * </ul>
+     *
+     * @param boundSize the bound size to select the best resolution with the fallback rule.
+     * @param fallbackRule the rule to follow when the specified bound size is not available.
+     */
+    @NonNull
+    public static ResolutionStrategy create(
+            @NonNull Size boundSize,
+            @ResolutionFallbackRule int fallbackRule) {
+        return new ResolutionStrategy(boundSize, fallbackRule);
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharing.java b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharing.java
index 5eabff9..8f36c1d 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharing.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharing.java
@@ -52,6 +52,8 @@
 import androidx.camera.core.processing.SurfaceEdge;
 import androidx.camera.core.processing.SurfaceProcessorNode;
 
+import com.google.common.util.concurrent.ListenableFuture;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -97,12 +99,12 @@
     public StreamSharing(@NonNull CameraInternal parentCamera,
             @NonNull Set<UseCase> children,
             @NonNull UseCaseConfigFactory useCaseConfigFactory) {
-        this(new VirtualCamera(parentCamera, children, useCaseConfigFactory));
-    }
-
-    StreamSharing(@NonNull VirtualCamera virtualCamera) {
         super(DEFAULT_CONFIG);
-        mVirtualCamera = virtualCamera;
+        mVirtualCamera = new VirtualCamera(parentCamera, children, useCaseConfigFactory,
+                () -> {
+                    // TODO: Ask the DefaultSurfaceProcessor to take a snapshot.
+                    throw new UnsupportedOperationException();
+                });
     }
 
     @Nullable
@@ -295,6 +297,18 @@
         return new Rect(0, 0, surfaceResolution.getWidth(), surfaceResolution.getHeight());
     }
 
+    /**
+     * Interface for controlling the {@link StreamSharing}.
+     */
+    interface Control {
+
+        /**
+         * Takes a snapshot of the current stream and write it to the children with JPEG Surface.
+         */
+        @NonNull
+        ListenableFuture<Void> jpegSnapshot();
+    }
+
     @VisibleForTesting
     @Nullable
     SurfaceEdge getCameraEdge() {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCamera.java b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCamera.java
index b652892..64bb811 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCamera.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCamera.java
@@ -87,6 +87,8 @@
     // The callback that receives the parent camera's metadata.
     @NonNull
     private final CameraCaptureCallback mParentMetadataCallback = createCameraCaptureCallback();
+    @NonNull
+    private final VirtualCameraControl mVirtualCameraControl;
 
     /**
      * @param parentCamera         the parent {@link CameraInternal} instance. For example, the
@@ -96,10 +98,13 @@
      */
     VirtualCamera(@NonNull CameraInternal parentCamera,
             @NonNull Set<UseCase> children,
-            @NonNull UseCaseConfigFactory useCaseConfigFactory) {
+            @NonNull UseCaseConfigFactory useCaseConfigFactory,
+            @NonNull StreamSharing.Control streamSharingControl) {
         mParentCamera = parentCamera;
         mUseCaseConfigFactory = useCaseConfigFactory;
         mChildren = children;
+        mVirtualCameraControl = new VirtualCameraControl(parentCamera.getCameraControlInternal(),
+                streamSharingControl);
         // Set children state to inactive by default.
         for (UseCase child : children) {
             mChildrenActiveState.put(child, false);
@@ -279,7 +284,7 @@
     @NonNull
     @Override
     public CameraControlInternal getCameraControlInternal() {
-        return mParentCamera.getCameraControlInternal();
+        return mVirtualCameraControl;
     }
 
     @NonNull
@@ -357,8 +362,9 @@
             @NonNull CameraCaptureResult cameraCaptureResult,
             @NonNull SessionConfig sessionConfig) {
         for (CameraCaptureCallback callback : sessionConfig.getRepeatingCameraCaptureCallbacks()) {
-            callback.onCaptureCompleted(new VirtualCameraCaptureResult(cameraCaptureResult,
-                    sessionConfig.getRepeatingCaptureConfig().getTagBundle()));
+            callback.onCaptureCompleted(new VirtualCameraCaptureResult(
+                    sessionConfig.getRepeatingCaptureConfig().getTagBundle(),
+                    cameraCaptureResult));
         }
     }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCameraCaptureResult.java b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCameraCaptureResult.java
index 3e96bc0..7f8a7dd 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCameraCaptureResult.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCameraCaptureResult.java
@@ -19,6 +19,7 @@
 import android.os.Build;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.impl.CameraCaptureMetaData;
 import androidx.camera.core.impl.CameraCaptureResult;
@@ -31,20 +32,45 @@
 @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
 public class VirtualCameraCaptureResult implements CameraCaptureResult {
 
-    @NonNull
+    private static final long INVALID_TIMESTAMP = -1;
+
+    @Nullable
     private final CameraCaptureResult mBaseCameraCaptureResult;
     @NonNull
     private final TagBundle mTagBundle;
+    private final long mTimestamp;
 
     /**
+     * Creates an instance based on another {@link CameraCaptureResult}.
+     *
      * @param baseCameraCaptureResult Most of the fields return the value of the base instance.
      * @param tagBundle               the overridden value for the {@link #getTagBundle()} field.
      */
-    VirtualCameraCaptureResult(
-            @NonNull CameraCaptureResult baseCameraCaptureResult,
-            @NonNull TagBundle tagBundle) {
+    public VirtualCameraCaptureResult(
+            @NonNull TagBundle tagBundle,
+            @Nullable CameraCaptureResult baseCameraCaptureResult) {
+        this(baseCameraCaptureResult, tagBundle, INVALID_TIMESTAMP);
+    }
+
+    /**
+     * Creates empty instance with timestamp overridden.
+     *
+     * @param tagBundle the overridden value for the {@link #getTagBundle()} field.
+     * @param timestamp the overridden value for the {@link #getTimestamp()} field.
+     */
+    public VirtualCameraCaptureResult(
+            @NonNull TagBundle tagBundle,
+            long timestamp) {
+        this(/*baseCameraCaptureResult*/null, tagBundle, timestamp);
+    }
+
+    private VirtualCameraCaptureResult(
+            @Nullable CameraCaptureResult baseCameraCaptureResult,
+            @NonNull TagBundle tagBundle,
+            long timestamp) {
         mBaseCameraCaptureResult = baseCameraCaptureResult;
         mTagBundle = tagBundle;
+        mTimestamp = timestamp;
     }
 
     @NonNull
@@ -57,35 +83,45 @@
     @NonNull
     @Override
     public CameraCaptureMetaData.AfMode getAfMode() {
-        return mBaseCameraCaptureResult.getAfMode();
+        return mBaseCameraCaptureResult != null ? mBaseCameraCaptureResult.getAfMode() :
+                CameraCaptureMetaData.AfMode.UNKNOWN;
     }
 
     @NonNull
     @Override
     public CameraCaptureMetaData.AfState getAfState() {
-        return mBaseCameraCaptureResult.getAfState();
+        return mBaseCameraCaptureResult != null ? mBaseCameraCaptureResult.getAfState() :
+                CameraCaptureMetaData.AfState.UNKNOWN;
     }
 
     @NonNull
     @Override
     public CameraCaptureMetaData.AeState getAeState() {
-        return mBaseCameraCaptureResult.getAeState();
+        return mBaseCameraCaptureResult != null ? mBaseCameraCaptureResult.getAeState() :
+                CameraCaptureMetaData.AeState.UNKNOWN;
     }
 
     @NonNull
     @Override
     public CameraCaptureMetaData.AwbState getAwbState() {
-        return mBaseCameraCaptureResult.getAwbState();
+        return mBaseCameraCaptureResult != null ? mBaseCameraCaptureResult.getAwbState() :
+                CameraCaptureMetaData.AwbState.UNKNOWN;
     }
 
     @NonNull
     @Override
     public CameraCaptureMetaData.FlashState getFlashState() {
-        return mBaseCameraCaptureResult.getFlashState();
+        return mBaseCameraCaptureResult != null ? mBaseCameraCaptureResult.getFlashState() :
+                CameraCaptureMetaData.FlashState.UNKNOWN;
     }
 
     @Override
     public long getTimestamp() {
-        return mBaseCameraCaptureResult.getTimestamp();
+        if (mBaseCameraCaptureResult != null) {
+            return mBaseCameraCaptureResult.getTimestamp();
+        } else if (mTimestamp != INVALID_TIMESTAMP) {
+            return mTimestamp;
+        }
+        throw new IllegalStateException("No timestamp is available.");
     }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCameraControl.java b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCameraControl.java
new file mode 100644
index 0000000..887e1e5
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/VirtualCameraControl.java
@@ -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.camera.core.streamsharing;
+
+import static androidx.core.util.Preconditions.checkArgument;
+
+import static java.util.Collections.singletonList;
+
+import android.graphics.Rect;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.FocusMeteringAction;
+import androidx.camera.core.FocusMeteringResult;
+import androidx.camera.core.ImageCapture;
+import androidx.camera.core.impl.CameraControlInternal;
+import androidx.camera.core.impl.CaptureConfig;
+import androidx.camera.core.impl.Config;
+import androidx.camera.core.impl.SessionConfig;
+import androidx.camera.core.impl.utils.futures.Futures;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.List;
+
+/**
+ * A {@link CameraControlInternal} that is used to control the virtual camera.
+ */
+@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+public class VirtualCameraControl implements CameraControlInternal {
+
+    private final CameraControlInternal mParent;
+    private final StreamSharing.Control mStreamSharingControl;
+
+    VirtualCameraControl(@NonNull CameraControlInternal parent,
+            @NonNull StreamSharing.Control streamSharingControl) {
+        mParent = parent;
+        mStreamSharingControl = streamSharingControl;
+    }
+
+    @NonNull
+    @Override
+    public ListenableFuture<Void> enableTorch(boolean torch) {
+        return mParent.enableTorch(torch);
+    }
+
+    @NonNull
+    @Override
+    public ListenableFuture<FocusMeteringResult> startFocusAndMetering(
+            @NonNull FocusMeteringAction action) {
+        return mParent.startFocusAndMetering(action);
+    }
+
+    @NonNull
+    @Override
+    public ListenableFuture<Void> cancelFocusAndMetering() {
+        return mParent.cancelFocusAndMetering();
+    }
+
+    @NonNull
+    @Override
+    public ListenableFuture<Void> setZoomRatio(float ratio) {
+        return mParent.setZoomRatio(ratio);
+    }
+
+    @NonNull
+    @Override
+    public ListenableFuture<Void> setLinearZoom(float linearZoom) {
+        return mParent.setLinearZoom(linearZoom);
+    }
+
+    @NonNull
+    @Override
+    public ListenableFuture<Integer> setExposureCompensationIndex(int value) {
+        return mParent.setExposureCompensationIndex(value);
+    }
+
+    @Override
+    public int getFlashMode() {
+        return mParent.getFlashMode();
+    }
+
+    @Override
+    public void setFlashMode(int flashMode) {
+        mParent.setFlashMode(flashMode);
+    }
+
+    @Override
+    public void addZslConfig(@NonNull SessionConfig.Builder sessionConfigBuilder) {
+        mParent.addZslConfig(sessionConfigBuilder);
+    }
+
+    @Override
+    public void setZslDisabledByUserCaseConfig(boolean disabled) {
+        mParent.setZslDisabledByUserCaseConfig(disabled);
+    }
+
+    @Override
+    public boolean isZslDisabledByByUserCaseConfig() {
+        return mParent.isZslDisabledByByUserCaseConfig();
+    }
+
+    @NonNull
+    @Override
+    public ListenableFuture<List<Void>> submitStillCaptureRequests(
+            @NonNull List<CaptureConfig> captureConfigs,
+            @ImageCapture.CaptureMode int captureMode,
+            @ImageCapture.FlashType int flashType) {
+        checkArgument(captureConfigs.size() == 1, "Only support one capture config.");
+        return Futures.allAsList(singletonList(mStreamSharingControl.jpegSnapshot()));
+    }
+
+    @NonNull
+    @Override
+    public SessionConfig getSessionConfig() {
+        return mParent.getSessionConfig();
+    }
+
+    @NonNull
+    @Override
+    public Rect getSensorRect() {
+        return mParent.getSensorRect();
+    }
+
+    @Override
+    public void addInteropConfig(@NonNull Config config) {
+        mParent.addInteropConfig(config);
+    }
+
+    @Override
+    public void clearInteropConfig() {
+        mParent.clearInteropConfig();
+    }
+
+    @NonNull
+    @Override
+    public Config getInteropConfig() {
+        return mParent.getInteropConfig();
+    }
+}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
index 30de277..a6289c1 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
@@ -16,8 +16,8 @@
 
 package androidx.camera.core;
 
-import static androidx.camera.core.MirrorMode.MIRROR_MODE_FRONT_ON;
 import static androidx.camera.core.MirrorMode.MIRROR_MODE_OFF;
+import static androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -42,6 +42,9 @@
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.core.internal.CameraUseCaseAdapter;
 import androidx.camera.core.internal.utils.SizeUtil;
+import androidx.camera.core.resolutionselector.AspectRatioStrategy;
+import androidx.camera.core.resolutionselector.ResolutionSelector;
+import androidx.camera.core.resolutionselector.ResolutionStrategy;
 import androidx.camera.testing.CameraUtil;
 import androidx.camera.testing.CameraXUtil;
 import androidx.camera.testing.fakes.FakeAppConfig;
@@ -184,7 +187,7 @@
 
     @Test(expected = UnsupportedOperationException.class)
     public void setMirrorMode_throwException() {
-        new ImageAnalysis.Builder().setMirrorMode(MIRROR_MODE_FRONT_ON);
+        new ImageAnalysis.Builder().setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY);
     }
 
     @Test
@@ -197,7 +200,7 @@
     public void setAnalyzerWithResolution_doesNotOverridesUseCaseResolution_resolutionSelector() {
         ImageAnalysisConfig config = getMergedImageAnalysisConfig(APP_RESOLUTION,
                 ANALYZER_RESOLUTION, -1, true);
-        assertThat(config.getResolutionSelector().getPreferredResolution()).isEqualTo(
+        assertThat(config.getResolutionSelector().getResolutionStrategy().getBoundSize()).isEqualTo(
                 APP_RESOLUTION);
     }
 
@@ -212,8 +215,8 @@
     public void setAnalyzerWithResolution_usedAsDefaultUseCaseResolution_resolutionSelector() {
         ImageAnalysisConfig config = getMergedImageAnalysisConfig(null,
                 ANALYZER_RESOLUTION, -1, true);
-        assertThat(config.getResolutionSelector().getPreferredResolution()).isEqualTo(
-                ANALYZER_RESOLUTION);
+        assertThat(config.getResolutionSelector().getResolutionStrategy().getBoundSize()).isEqualTo(
+                FLIPPED_ANALYZER_RESOLUTION);
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -232,11 +235,15 @@
 
         // Sets preferred resolution by ResolutionSelector or legacy API
         if (useResolutionSelector) {
-            ResolutionSelector.Builder resolutionSelectorBuilder = new ResolutionSelector.Builder();
+            ResolutionSelector.Builder selectorBuilder = new ResolutionSelector.Builder();
             if (appResolution != null) {
-                resolutionSelectorBuilder.setPreferredResolution(appResolution);
+                selectorBuilder.setResolutionStrategy(ResolutionStrategy.create(appResolution,
+                        ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER));
+            } else {
+                selectorBuilder.setAspectRatioStrategy(
+                        AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY);
             }
-            builder.setResolutionSelector(resolutionSelectorBuilder.build());
+            builder.setResolutionSelector(selectorBuilder.build());
         } else {
             if (appResolution != null) {
                 builder.setTargetResolution(appResolution);
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
index de6014c..65f6131 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
@@ -33,7 +33,7 @@
 import androidx.camera.core.ImageCapture.ImageCaptureRequest
 import androidx.camera.core.ImageCapture.ImageCaptureRequestProcessor
 import androidx.camera.core.ImageCapture.ImageCaptureRequestProcessor.ImageCaptor
-import androidx.camera.core.MirrorMode.MIRROR_MODE_FRONT_ON
+import androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY
 import androidx.camera.core.MirrorMode.MIRROR_MODE_OFF
 import androidx.camera.core.impl.CameraConfig
 import androidx.camera.core.impl.CameraFactory
@@ -48,6 +48,7 @@
 import androidx.camera.core.impl.utils.futures.Futures
 import androidx.camera.core.internal.CameraUseCaseAdapter
 import androidx.camera.core.internal.utils.SizeUtil
+import androidx.camera.core.resolutionselector.ResolutionSelector
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.CameraXUtil
 import androidx.camera.testing.fakes.FakeAppConfig
@@ -192,7 +193,7 @@
 
     @Test(expected = UnsupportedOperationException::class)
     fun setMirrorMode_throwException() {
-        ImageCapture.Builder().setMirrorMode(MIRROR_MODE_FRONT_ON)
+        ImageCapture.Builder().setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY)
     }
 
     @Test
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt
index 6ef3e65..b6933d2 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt
@@ -30,7 +30,7 @@
 import androidx.camera.core.CameraEffect.PREVIEW
 import androidx.camera.core.CameraEffect.VIDEO_CAPTURE
 import androidx.camera.core.CameraSelector.LENS_FACING_FRONT
-import androidx.camera.core.MirrorMode.MIRROR_MODE_FRONT_ON
+import androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY
 import androidx.camera.core.SurfaceRequest.TransformationInfo
 import androidx.camera.core.impl.CameraFactory
 import androidx.camera.core.impl.CameraThreadConfig
@@ -44,6 +44,7 @@
 import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor
 import androidx.camera.core.internal.CameraUseCaseAdapter
 import androidx.camera.core.internal.utils.SizeUtil
+import androidx.camera.core.resolutionselector.ResolutionSelector
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.CameraXUtil
 import androidx.camera.testing.fakes.FakeAppConfig
@@ -202,14 +203,14 @@
     }
 
     @Test
-    fun defaultMirrorModeIsFrontOn() {
+    fun defaultMirrorModeIsOnFrontOnly() {
         val preview = Preview.Builder().build()
-        assertThat(preview.mirrorModeInternal).isEqualTo(MIRROR_MODE_FRONT_ON)
+        assertThat(preview.mirrorModeInternal).isEqualTo(MIRROR_MODE_ON_FRONT_ONLY)
     }
 
     @Test(expected = UnsupportedOperationException::class)
     fun setMirrorMode_throwException() {
-        Preview.Builder().setMirrorMode(MIRROR_MODE_FRONT_ON)
+        Preview.Builder().setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY)
     }
 
     @Test
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/CaptureNodeTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/CaptureNodeTest.kt
index c904b5d..acc1781 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/CaptureNodeTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/CaptureNodeTest.kt
@@ -52,7 +52,7 @@
 
     @Before
     fun setUp() {
-        captureNodeIn = CaptureNode.In.of(Size(10, 10), ImageFormat.JPEG)
+        captureNodeIn = CaptureNode.In.of(Size(10, 10), ImageFormat.JPEG, false)
         captureNodeOut = captureNode.transform(captureNodeIn)
         captureNodeOut.imageEdge.setListener {
             imagePropagated.add(it)
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt
index 9d65a9e0..eeb5846 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ImagePipelineTest.kt
@@ -17,6 +17,7 @@
 package androidx.camera.core.imagecapture
 
 import android.graphics.ImageFormat
+import android.graphics.Matrix
 import android.graphics.Rect
 import android.hardware.camera2.CameraDevice
 import android.os.Build
@@ -49,8 +50,10 @@
 import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor
 import androidx.camera.core.impl.utils.futures.Futures
 import androidx.camera.core.internal.IoConfig.OPTION_IO_EXECUTOR
+import androidx.camera.core.processing.Packet
 import androidx.camera.testing.TestImageUtil.createJpegBytes
 import androidx.camera.testing.TestImageUtil.createJpegFakeImageProxy
+import androidx.camera.testing.fakes.FakeCameraCaptureResult
 import androidx.camera.testing.fakes.FakeImageInfo
 import androidx.camera.testing.fakes.FakeImageReaderProxy
 import androidx.camera.testing.fakes.GrayscaleImageEffect
@@ -111,12 +114,44 @@
             ImagePipeline(
                 imageCaptureConfig,
                 SIZE,
-                GrayscaleImageEffect()
+                GrayscaleImageEffect(),
+                false
             ).processingNode.mImageProcessor
         ).isNotNull()
     }
 
     @Test
+    fun createPipelineWithVirtualCamera_plumbedToProcessingInput2PacketOperation() {
+        // Arrange: create a pipeline with a virtual camera.
+        val pipeline = ImagePipeline(
+            imageCaptureConfig,
+            SIZE,
+            GrayscaleImageEffect(),
+            true
+        )
+        // Listen to the input to packet operation.
+        var isVirtualCamera = false
+        pipeline.processingNode.injectProcessingInput2Packet {
+            isVirtualCamera = it.isVirtualCamera
+            return@injectProcessingInput2Packet Packet.of(
+                it.imageProxy,
+                null,
+                it.imageProxy.cropRect,
+                it.imageProxy.format,
+                Matrix(),
+                FakeCameraCaptureResult()
+            )
+        }
+
+        // Act: send in-memory request.
+        sendInMemoryRequest(pipeline)
+
+        // Assert: the input packet is marked as from a virtual camera.
+        assertThat(isVirtualCamera).isTrue()
+        pipeline.close()
+    }
+
+    @Test
     fun createRequests_verifyCameraRequest() {
         // Arrange.
         val captureInput = imagePipeline.captureNode.inputEdge
@@ -264,6 +299,17 @@
 
     @Test
     fun sendInMemoryRequest_receivesImageProxy() {
+        // Arrange & act.
+        val image = sendInMemoryRequest(imagePipeline)
+
+        // Assert: the image is received by TakePictureCallback.
+        assertThat(CALLBACK.inMemoryResult!!.planes).isEqualTo(image.planes)
+    }
+
+    /**
+     * Creates a ImageProxy and sends it to the pipeline.
+     */
+    private fun sendInMemoryRequest(pipeline: ImagePipeline): ImageProxy {
         // Arrange.
         val processingRequest = imagePipeline.createRequests(
             IN_MEMORY_REQUEST, CALLBACK, Futures.immediateFuture(null)
@@ -276,12 +322,11 @@
         val image = createJpegFakeImageProxy(imageInfo, jpegBytes)
 
         // Act: send processing request and the image.
-        imagePipeline.submitProcessingRequest(processingRequest)
-        imagePipeline.captureNode.onImageProxyAvailable(image)
+        pipeline.submitProcessingRequest(processingRequest)
+        pipeline.captureNode.onImageProxyAvailable(image)
         shadowOf(getMainLooper()).idle()
 
-        // Assert: the image is received by TakePictureCallback.
-        assertThat(CALLBACK.inMemoryResult!!.planes).isEqualTo(image.planes)
+        return image
     }
 
     @Test
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingInput2PacketTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingInput2PacketTest.kt
index 4d024c5..096fea8 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingInput2PacketTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingInput2PacketTest.kt
@@ -65,7 +65,7 @@
             HEIGHT
         )
         val processingRequest = createProcessingRequest()
-        val input = ProcessingNode.InputPacket.of(processingRequest, image)
+        val input = ProcessingNode.InputPacket.of(processingRequest, image, false)
 
         // Act.
         val output = operation.apply(input)
@@ -89,7 +89,7 @@
         }
         val image = createJpegFakeImageProxy(jpegBytes)
         val processingRequest = createProcessingRequest()
-        val input = ProcessingNode.InputPacket.of(processingRequest, image)
+        val input = ProcessingNode.InputPacket.of(processingRequest, image, false)
 
         // Act.
         val output = operation.apply(input)
@@ -121,7 +121,7 @@
             FakeTakePictureCallback(),
             Futures.immediateFuture(null)
         )
-        val input = ProcessingNode.InputPacket.of(processingRequest, image)
+        val input = ProcessingNode.InputPacket.of(processingRequest, image, false)
 
         // Act.
         val output = operation.apply(input)
@@ -146,7 +146,25 @@
         injectRotationOptionQuirk()
         val image = createJpegFakeImageProxy(createJpegBytes(WIDTH, HEIGHT))
         val processingRequest = createProcessingRequest()
-        val input = ProcessingNode.InputPacket.of(processingRequest, image)
+        val input = ProcessingNode.InputPacket.of(processingRequest, image, false)
+
+        // Act.
+        val output = operation.apply(input)
+
+        // Assert: the metadata are based on Packet only.
+        assertThat(output.cropRect).isEqualTo(CROP_RECT)
+        assertThat(output.rotationDegrees).isEqualTo(ROTATION_DEGREES)
+        assertThat(output.format).isEqualTo(ImageFormat.JPEG)
+        assertThat(output.size).isEqualTo(Size(WIDTH, HEIGHT))
+        assertThat(output.sensorToBufferTransform).isEqualTo(SENSOR_TO_BUFFER)
+    }
+
+    @Test
+    fun isVirtualCamera_outputIgnoresExifRotation() {
+        // Arrange: create input
+        val image = createJpegFakeImageProxy(createJpegBytes(WIDTH, HEIGHT))
+        val processingRequest = createProcessingRequest()
+        val input = ProcessingNode.InputPacket.of(processingRequest, image, true)
 
         // Act.
         val output = operation.apply(input)
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt
index b16a6b2..0234bed8 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/ProcessingNodeTest.kt
@@ -81,7 +81,7 @@
         // Act: process the request.
         val jpegBytes = createJpegBytes(WIDTH, HEIGHT)
         val image = createJpegFakeImageProxy(jpegBytes)
-        processingNodeIn.edge.accept(ProcessingNode.InputPacket.of(request, image))
+        processingNodeIn.edge.accept(ProcessingNode.InputPacket.of(request, image, false))
         shadowOf(getMainLooper()).idle()
 
         // Assert: the image is not saved.
@@ -94,7 +94,7 @@
         val takePictureCallback = FakeTakePictureCallback()
         val image = FakeImageProxy(FakeImageInfo())
         val processingRequest = createProcessingRequest(takePictureCallback)
-        val input = ProcessingNode.InputPacket.of(processingRequest, image)
+        val input = ProcessingNode.InputPacket.of(processingRequest, image, false)
 
         // Act: send input to the edge and wait for callback
         processingNodeIn.edge.accept(input)
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/SingleBundlingNodeTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/SingleBundlingNodeTest.kt
index 56db6a2b..e04e4c4 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/SingleBundlingNodeTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/SingleBundlingNodeTest.kt
@@ -44,7 +44,7 @@
 
     @Before
     fun setUp() {
-        captureNodeOut = CaptureNode.Out.of(ImageFormat.JPEG)
+        captureNodeOut = CaptureNode.Out.of(ImageFormat.JPEG, false)
         matchingNodeOut = node.transform(captureNodeOut)
         matchingNodeOut.edge.setListener {
             packetPropagated.add(it)
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/impl/AttachedSurfaceInfoTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/impl/AttachedSurfaceInfoTest.kt
index 65495b9..8d36389 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/impl/AttachedSurfaceInfoTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/impl/AttachedSurfaceInfoTest.kt
@@ -16,32 +16,40 @@
 package androidx.camera.core.impl
 
 import android.graphics.ImageFormat
+import android.os.Build
 import android.util.Range
 import android.util.Size
 import com.google.common.truth.Truth
 import org.junit.Before
 import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
 
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 class AttachedSurfaceInfoTest {
-    private var mAttachedSurfaceInfo: AttachedSurfaceInfo? = null
-    private val mSurfaceConfig = SurfaceConfig.create(
+    private var attachedSurfaceInfo: AttachedSurfaceInfo? = null
+    private val surfaceConfig = SurfaceConfig.create(
         SurfaceConfig.ConfigType.JPEG,
         SurfaceConfig.ConfigSize.PREVIEW
     )
-    private val mImageFormat = ImageFormat.JPEG
-    private val mSize = Size(1920, 1080)
-    private val mTargetFramerate = Range(10, 20)
+    private val imageFormat = ImageFormat.JPEG
+    private val size = Size(1920, 1080)
+    private val targetFramerate = Range(10, 20)
     @Before
     fun setup() {
-        mAttachedSurfaceInfo = AttachedSurfaceInfo.create(
-            mSurfaceConfig, mImageFormat, mSize,
-            mTargetFramerate
+        attachedSurfaceInfo = AttachedSurfaceInfo.create(
+            surfaceConfig, imageFormat, size,
+            targetFramerate
         )
     }
 
     @Test
     fun canGetSurfaceConfig() {
-        Truth.assertThat(mAttachedSurfaceInfo!!.surfaceConfig).isEqualTo(
+        Truth.assertThat(attachedSurfaceInfo!!.surfaceConfig).isEqualTo(
             SurfaceConfig.create(
                 SurfaceConfig.ConfigType.JPEG, SurfaceConfig.ConfigSize.PREVIEW
             )
@@ -50,24 +58,24 @@
 
     @Test
     fun canGetImageFormat() {
-        Truth.assertThat(mAttachedSurfaceInfo!!.imageFormat).isEqualTo(ImageFormat.JPEG)
+        Truth.assertThat(attachedSurfaceInfo!!.imageFormat).isEqualTo(ImageFormat.JPEG)
     }
 
     @Test
     fun canGetSize() {
-        Truth.assertThat(mAttachedSurfaceInfo!!.size).isEqualTo(mSize)
+        Truth.assertThat(attachedSurfaceInfo!!.size).isEqualTo(size)
     }
 
     @Test
     fun canGetTargetFrameRate() {
-        Truth.assertThat(mAttachedSurfaceInfo!!.targetFrameRate).isEqualTo(mTargetFramerate)
+        Truth.assertThat(attachedSurfaceInfo!!.targetFrameRate).isEqualTo(targetFramerate)
     }
 
     @Test
     fun nullGetTargetFrameRateReturnsNull() {
         val attachedSurfaceInfo2 = AttachedSurfaceInfo.create(
-            mSurfaceConfig,
-            mImageFormat, mSize, null
+            surfaceConfig,
+            imageFormat, size, null
         )
         Truth.assertThat(attachedSurfaceInfo2.targetFrameRate).isNull()
     }
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/impl/StreamSpecTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/impl/StreamSpecTest.kt
index 6142527..fee7f3e 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/impl/StreamSpecTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/impl/StreamSpecTest.kt
@@ -19,6 +19,7 @@
 import android.os.Build
 import android.util.Range
 import android.util.Size
+import androidx.camera.core.DynamicRange
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -38,6 +39,20 @@
         assertThat(streamSpec.resolution).isEqualTo(TEST_RESOLUTION)
     }
 
+    fun defaultDynamicRangeIsSdr() {
+        val streamSpec = StreamSpec.builder(TEST_RESOLUTION).build()
+
+        assertThat(streamSpec.dynamicRange).isEqualTo(DynamicRange.SDR)
+    }
+
+    @Test
+    fun canRetrieveDynamicRange() {
+        val dynamicRange = DynamicRange(DynamicRange.FORMAT_HLG, DynamicRange.BIT_DEPTH_10_BIT)
+        val streamSpec = StreamSpec.builder(TEST_RESOLUTION).setDynamicRange(dynamicRange).build()
+
+        assertThat(streamSpec.dynamicRange).isEqualTo(dynamicRange)
+    }
+
     @Test
     fun defaultExpectedFrameRateRangeIsUnspecified() {
         val streamSpec = StreamSpec.builder(TEST_RESOLUTION).build()
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterTest.kt
index c18ffdc..96211ee 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterTest.kt
@@ -20,15 +20,31 @@
 import android.os.Build
 import android.util.Pair
 import android.util.Size
+import androidx.camera.core.AspectRatio
+import androidx.camera.core.impl.UseCaseConfig
 import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType
+import androidx.camera.core.impl.utils.AspectRatioUtil
+import androidx.camera.core.resolutionselector.AspectRatioStrategy
+import androidx.camera.core.resolutionselector.HighResolution
+import androidx.camera.core.resolutionselector.ResolutionFilter
+import androidx.camera.core.resolutionselector.ResolutionSelector
+import androidx.camera.core.resolutionselector.ResolutionStrategy
+import androidx.camera.core.resolutionselector.ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER
+import androidx.camera.core.resolutionselector.ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER
+import androidx.camera.core.resolutionselector.ResolutionStrategy.FALLBACK_RULE_CLOSEST_LOWER
+import androidx.camera.core.resolutionselector.ResolutionStrategy.FALLBACK_RULE_CLOSEST_LOWER_THEN_HIGHER
 import androidx.camera.testing.fakes.FakeCameraInfoInternal
 import androidx.camera.testing.fakes.FakeUseCaseConfig
 import com.google.common.truth.Truth.assertThat
+import java.util.Collections
+import org.junit.Assert.assertThrows
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
 import org.robolectric.annotation.internal.DoNotInstrument
 
+private const val TARGET_ASPECT_RATIO_NONE = -99
 private val DEFAULT_SUPPORTED_SIZES = listOf(
     Size(4032, 3024), // 4:3
     Size(3840, 2160), // 16:9
@@ -36,6 +52,7 @@
     Size(1920, 1080), // 16:9
     Size(1280, 960), // 4:3
     Size(1280, 720), // 16:9
+    Size(960, 960), // 1:1
     Size(960, 544), // a mod16 version of resolution with 16:9 aspect ratio.
     Size(800, 450), // 16:9
     Size(640, 480), // 4:3
@@ -43,14 +60,42 @@
     Size(320, 180), // 16:9
     Size(256, 144) // 16:9
 )
+private val HIGH_RESOLUTION_SUPPORTED_SIZES = listOf(
+    Size(8000, 6000),
+    Size(8000, 4500),
+)
+private val CUSTOM_SUPPORTED_SIZES = listOf(
+    Size(1920, 1080),
+    Size(720, 480),
+    Size(640, 480)
+)
+private val PORTRAIT_SUPPORTED_SIZES = listOf(
+    Size(1440, 1920),
+    Size(1080, 1920),
+    Size(1080, 1440),
+    Size(960, 1280),
+    Size(720, 1280),
+    Size(960, 540),
+    Size(480, 640),
+    Size(640, 480),
+    Size(360, 480)
+)
+private val LANDSCAPE_ACTIVE_ARRAY_SIZE = Size(4032, 3024)
+private val PORTRAIT_ACTIVE_ARRAY_SIZE = Size(1440, 1920)
 
 /**
  * Unit tests for [SupportedOutputSizesSorter].
  */
 @RunWith(RobolectricTestRunner::class)
 @DoNotInstrument
-@org.robolectric.annotation.Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 class SupportedOutputSizesSorterTest {
+    private val cameraInfoInternal = FakeCameraInfoInternal().apply {
+        setSupportedResolutions(ImageFormat.JPEG, DEFAULT_SUPPORTED_SIZES)
+        setSupportedHighResolutions(ImageFormat.JPEG, HIGH_RESOLUTION_SUPPORTED_SIZES)
+    }
+    private val supportedOutputSizesSorter =
+        SupportedOutputSizesSorter(cameraInfoInternal, LANDSCAPE_ACTIVE_ARRAY_SIZE)
 
     @Test
     fun canSelectCustomOrderedResolutions() {
@@ -59,30 +104,19 @@
         val cameraInfoInternal = FakeCameraInfoInternal().apply {
             setSupportedResolutions(imageFormat, DEFAULT_SUPPORTED_SIZES)
         }
-        val supportedOutputSizesSorter = SupportedOutputSizesSorter(cameraInfoInternal)
+        val supportedOutputSizesSorter =
+            SupportedOutputSizesSorter(cameraInfoInternal, LANDSCAPE_ACTIVE_ARRAY_SIZE)
         // Sets up the custom ordered resolutions
         val useCaseConfig =
             FakeUseCaseConfig.Builder(CaptureType.IMAGE_CAPTURE, imageFormat).apply {
-                setCustomOrderedResolutions(
-                    listOf(
-                        Size(1920, 1080),
-                        Size(720, 480),
-                        Size(640, 480)
-                    )
-                )
+                setCustomOrderedResolutions(CUSTOM_SUPPORTED_SIZES)
             }.useCaseConfig
 
         // Act
         val sortedResult = supportedOutputSizesSorter.getSortedSupportedOutputSizes(useCaseConfig)
 
         // Assert
-        assertThat(sortedResult).containsExactlyElementsIn(
-            listOf(
-                Size(1920, 1080),
-                Size(720, 480),
-                Size(640, 480)
-            )
-        ).inOrder()
+        assertThat(sortedResult).containsExactlyElementsIn(CUSTOM_SUPPORTED_SIZES).inOrder()
     }
 
     @Test
@@ -92,20 +126,13 @@
         val cameraInfoInternal = FakeCameraInfoInternal().apply {
             setSupportedResolutions(imageFormat, DEFAULT_SUPPORTED_SIZES)
         }
-        val supportedOutputSizesSorter = SupportedOutputSizesSorter(cameraInfoInternal)
+        val supportedOutputSizesSorter =
+            SupportedOutputSizesSorter(cameraInfoInternal, LANDSCAPE_ACTIVE_ARRAY_SIZE)
         // Sets up the custom supported resolutions
         val useCaseConfig =
             FakeUseCaseConfig.Builder(CaptureType.IMAGE_CAPTURE, imageFormat).apply {
                 setSupportedResolutions(
-                    listOf(
-                        Pair.create(
-                            imageFormat, arrayOf(
-                                Size(1920, 1080),
-                                Size(720, 480),
-                                Size(640, 480)
-                            )
-                        )
-                    )
+                    listOf(Pair.create(imageFormat, CUSTOM_SUPPORTED_SIZES.toTypedArray()))
                 )
             }.useCaseConfig
 
@@ -113,12 +140,461 @@
         val sortedResult = supportedOutputSizesSorter.getSortedSupportedOutputSizes(useCaseConfig)
 
         // Assert
-        assertThat(sortedResult).containsExactlyElementsIn(
-            listOf(
-                Size(1920, 1080),
-                Size(720, 480),
-                Size(640, 480)
-            )
-        ).inOrder()
+        assertThat(sortedResult).containsExactlyElementsIn(CUSTOM_SUPPORTED_SIZES).inOrder()
     }
-}
\ No newline at end of file
+
+    @Test
+    fun getSupportedOutputSizes_aspectRatio4x3_fallbackRuleNone() {
+        verifySupportedOutputSizesWithResolutionSelectorSettings(
+            preferredAspectRatio = AspectRatio.RATIO_4_3,
+            aspectRatioFallbackRule = AspectRatioStrategy.FALLBACK_RULE_NONE,
+            expectedList = listOf(
+                // Only returns preferred AspectRatio matched items, sorted by area size.
+                Size(4032, 3024),
+                Size(1920, 1440),
+                Size(1280, 960),
+                Size(640, 480),
+                Size(320, 240),
+            )
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_aspectRatio4x3_fallbackRuleAuto() {
+        verifySupportedOutputSizesWithResolutionSelectorSettings(
+            preferredAspectRatio = AspectRatio.RATIO_4_3,
+            aspectRatioFallbackRule = AspectRatioStrategy.FALLBACK_RULE_AUTO,
+            expectedList = listOf(
+                // Matched preferred AspectRatio items, sorted by area size.
+                Size(4032, 3024), // 4:3
+                Size(1920, 1440),
+                Size(1280, 960),
+                Size(640, 480),
+                Size(320, 240),
+                // Mismatched preferred AspectRatio items, sorted by FOV and area size.
+                Size(960, 960), // 1:1
+                Size(3840, 2160), // 16:9
+                Size(1920, 1080),
+                Size(1280, 720),
+                Size(960, 544),
+                Size(800, 450),
+                Size(320, 180),
+                Size(256, 144)
+            )
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_aspectRatio16x9_fallbackRuleNone() {
+        verifySupportedOutputSizesWithResolutionSelectorSettings(
+            preferredAspectRatio = AspectRatio.RATIO_16_9,
+            aspectRatioFallbackRule = AspectRatioStrategy.FALLBACK_RULE_NONE,
+            expectedList = listOf(
+                // Only returns preferred AspectRatio matched items, sorted by area size.
+                Size(3840, 2160),
+                Size(1920, 1080),
+                Size(1280, 720),
+                Size(960, 544),
+                Size(800, 450),
+                Size(320, 180),
+                Size(256, 144),
+            )
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_aspectRatio16x9_fallbackRuleAuto() {
+        verifySupportedOutputSizesWithResolutionSelectorSettings(
+            preferredAspectRatio = AspectRatio.RATIO_16_9,
+            aspectRatioFallbackRule = AspectRatioStrategy.FALLBACK_RULE_AUTO,
+            expectedList = listOf(
+                // Matched preferred AspectRatio items, sorted by area size.
+                Size(3840, 2160), // 16:9
+                Size(1920, 1080),
+                Size(1280, 720),
+                Size(960, 544),
+                Size(800, 450),
+                Size(320, 180),
+                Size(256, 144),
+                // Mismatched preferred AspectRatio items, sorted by FOV and area size.
+                Size(4032, 3024), // 4:3
+                Size(1920, 1440),
+                Size(1280, 960),
+                Size(640, 480),
+                Size(320, 240),
+                Size(960, 960), // 1:1
+            )
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_boundSize1920x1080_withResolutionFallbackRuleNone() {
+        verifySupportedOutputSizesWithResolutionSelectorSettings(
+            boundSize = Size(1280, 960),
+            resolutionFallbackRule = ResolutionStrategy.FALLBACK_RULE_NONE,
+            // Only returns preferred AspectRatio matched item.
+            expectedList = listOf(Size(1280, 960))
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_boundSize1920x1080_withBothAspectRatioResolutionFallbackRuleNone() {
+        verifySupportedOutputSizesWithResolutionSelectorSettings(
+            preferredAspectRatio = AspectRatio.RATIO_4_3,
+            aspectRatioFallbackRule = AspectRatioStrategy.FALLBACK_RULE_NONE,
+            boundSize = Size(1920, 1080),
+            resolutionFallbackRule = ResolutionStrategy.FALLBACK_RULE_NONE,
+            // No size is returned since only 1920x1080 is allowed but it is not a 4:3 size
+            expectedList = Collections.emptyList()
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_withHighestAvailableResolutionStrategy() {
+        // Bound size will be ignored when fallback rule is HIGHEST_AVAILABLE
+        verifySupportedOutputSizesWithResolutionSelectorSettings(
+            resolutionStrategy = ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY,
+            // The 4:3 default aspect ratio will make sizes of 4/3 have the highest priority
+            expectedList = listOf(
+                // Matched default preferred AspectRatio items, sorted by area size.
+                Size(4032, 3024),
+                Size(1920, 1440),
+                Size(1280, 960),
+                Size(640, 480),
+                Size(320, 240),
+                // Mismatched default preferred AspectRatio items, sorted by FOV and area size.
+                Size(960, 960), // 1:1
+                Size(3840, 2160), // 16:9
+                Size(1920, 1080),
+                Size(1280, 720),
+                Size(960, 544),
+                Size(800, 450),
+                Size(320, 180),
+                Size(256, 144),
+            )
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_boundSize1920x1080_withClosestHigherThenLowerRule() {
+        verifySupportedOutputSizesWithResolutionSelectorSettings(
+            boundSize = Size(1920, 1080),
+            resolutionFallbackRule = FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER,
+            // The 4:3 default aspect ratio will make sizes of 4/3 have the highest priority
+            expectedList = listOf(
+                // Matched default preferred AspectRatio items, sorted by area size.
+                Size(1920, 1440), // 4:3 smallest larger size has highest priority
+                Size(4032, 3024), // the remaining 4:3 larger sizes
+                Size(1280, 960), // the remaining 4:3 smaller sizes
+                Size(640, 480),
+                Size(320, 240),
+                // Mismatched default preferred AspectRatio items, sorted by FOV and area size.
+                Size(960, 960), // 1:1
+                Size(1920, 1080), // 16:9 smallest larger size
+                Size(3840, 2160), // the remaining 16:9 larger sizes
+                Size(1280, 720), // the remaining 16:9 smaller sizes
+                Size(960, 544),
+                Size(800, 450),
+                Size(320, 180),
+                Size(256, 144),
+            )
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_boundSize1920x1080_withClosestHigherRule() {
+        verifySupportedOutputSizesWithResolutionSelectorSettings(
+            boundSize = Size(1920, 1080),
+            resolutionFallbackRule = FALLBACK_RULE_CLOSEST_HIGHER,
+            // The 4:3 default aspect ratio will make sizes of 4/3 have the highest priority
+            expectedList = listOf(
+                // Matched default preferred AspectRatio items, sorted by area size.
+                Size(1920, 1440), // 4:3 smallest larger size has highest priority
+                Size(4032, 3024), // the remaining 4:3 larger sizes
+                // Mismatched default preferred AspectRatio items, sorted by FOV and area size.
+                Size(1920, 1080), // 16:9 smallest larger size
+                Size(3840, 2160), // the remaining 16:9 larger sizes
+            )
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_boundSize1920x1080_withClosestLowerThenHigherRule() {
+        verifySupportedOutputSizesWithResolutionSelectorSettings(
+            boundSize = Size(1920, 1080),
+            resolutionFallbackRule = FALLBACK_RULE_CLOSEST_LOWER_THEN_HIGHER,
+            // The 4:3 default aspect ratio will make sizes of 4/3 have the highest priority
+            expectedList = listOf(
+                // Matched default preferred AspectRatio items, sorted by area size.
+                Size(1280, 960), // 4:3 largest smaller size has highest priority
+                Size(640, 480), // the remaining 4:3 smaller sizes
+                Size(320, 240),
+                Size(1920, 1440), // the remaining 4:3 larger sizes
+                Size(4032, 3024),
+                // Mismatched default preferred AspectRatio items, sorted by FOV and area size.
+                Size(960, 960), // 1:1
+                Size(1920, 1080), // 16:9 largest smaller size
+                Size(1280, 720), // the remaining 16:9 smaller sizes
+                Size(960, 544),
+                Size(800, 450),
+                Size(320, 180),
+                Size(256, 144),
+                Size(3840, 2160), // the remaining 16:9 larger sizes
+            )
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_boundSize1920x1080_withClosestLowerRule() {
+        verifySupportedOutputSizesWithResolutionSelectorSettings(
+            boundSize = Size(1920, 1080),
+            resolutionFallbackRule = FALLBACK_RULE_CLOSEST_LOWER,
+            // The 4:3 default aspect ratio will make sizes of 4/3 have the highest priority
+            expectedList = listOf(
+                // Matched default preferred AspectRatio items, sorted by area size.
+                Size(1280, 960), // 4:3 largest smaller size has highest priority
+                Size(640, 480), // the remaining 4:3 smaller sizes
+                Size(320, 240),
+                // Mismatched default preferred AspectRatio items, sorted by FOV and area size.
+                Size(960, 960), // 1:1
+                Size(1920, 1080), // 16:9 largest smaller size
+                Size(1280, 720), // the remaining 16:9 smaller sizes
+                Size(960, 544),
+                Size(800, 450),
+                Size(320, 180),
+                Size(256, 144),
+            )
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizesWithPortraitPixelArraySize_aspectRatio16x9() {
+        val cameraInfoInternal = FakeCameraInfoInternal().apply {
+            setSupportedResolutions(ImageFormat.JPEG, PORTRAIT_SUPPORTED_SIZES)
+        }
+        val supportedOutputSizesSorter =
+            SupportedOutputSizesSorter(cameraInfoInternal, PORTRAIT_ACTIVE_ARRAY_SIZE)
+        verifySupportedOutputSizesWithResolutionSelectorSettings(
+            outputSizesSorter = supportedOutputSizesSorter,
+            preferredAspectRatio = AspectRatio.RATIO_16_9,
+            expectedList = listOf(
+                // Matched preferred AspectRatio items, sorted by area size.
+                Size(1080, 1920),
+                Size(720, 1280),
+                // Mismatched preferred AspectRatio items, sorted by area size.
+                Size(1440, 1920),
+                Size(1080, 1440),
+                Size(960, 1280),
+                Size(480, 640),
+                Size(360, 480),
+                Size(640, 480),
+                Size(960, 540)
+            )
+        )
+    }
+
+    @Config(minSdk = Build.VERSION_CODES.M)
+    @Test
+    fun getSupportedOutputSizes_whenHighResolutionIsEnabled_aspectRatio16x9() {
+        verifySupportedOutputSizesWithResolutionSelectorSettings(
+            preferredAspectRatio = AspectRatio.RATIO_16_9,
+            highResolutionEnabledFlags = HighResolution.FLAG_DEFAULT_MODE_ON,
+            expectedList = listOf(
+                // Matched preferred AspectRatio items, sorted by area size.
+                Size(8000, 4500), // 16:9 high resolution size
+                Size(3840, 2160),
+                Size(1920, 1080),
+                Size(1280, 720),
+                Size(960, 544),
+                Size(800, 450),
+                Size(320, 180),
+                Size(256, 144),
+                // Mismatched preferred AspectRatio items, sorted by area size.
+                Size(8000, 6000), // 4:3 high resolution size
+                Size(4032, 3024),
+                Size(1920, 1440),
+                Size(1280, 960),
+                Size(640, 480),
+                Size(320, 240),
+                Size(960, 960), // 1:1
+            )
+        )
+    }
+
+    @Config(minSdk = Build.VERSION_CODES.M)
+    @Test
+    fun highResolutionCanNotBeSelected_whenHighResolutionForceDisabled() {
+        verifySupportedOutputSizesWithResolutionSelectorSettings(
+            preferredAspectRatio = AspectRatio.RATIO_16_9,
+            highResolutionEnabledFlags = HighResolution.FLAG_DEFAULT_MODE_ON,
+            highResolutionForceDisabled = true,
+            expectedList = listOf(
+                // Matched preferred AspectRatio items, sorted by area size.
+                Size(3840, 2160),
+                Size(1920, 1080),
+                Size(1280, 720),
+                Size(960, 544),
+                Size(800, 450),
+                Size(320, 180),
+                Size(256, 144),
+                // Mismatched preferred AspectRatio items, sorted by area size.
+                Size(4032, 3024), // 4:3
+                Size(1920, 1440),
+                Size(1280, 960),
+                Size(640, 480),
+                Size(320, 240),
+                Size(960, 960), // 1:1
+            )
+        )
+    }
+
+    @Test
+    fun checkAllSupportedSizesCanBeSelected() {
+        // For 4:3 and 16:9 sizes, sets each of supported size as bound size and also calculates
+        // its ratio to set as preferred aspect ratio, so that the size can be put in the first
+        // priority position. For sizes of other aspect ratio, creates a ResolutionFilter to select
+        // it.
+        DEFAULT_SUPPORTED_SIZES.forEach { supportedSize ->
+            var resolutionFilter: ResolutionFilter? = null
+            val preferredAspectRatio =
+                if (AspectRatioUtil.hasMatchingAspectRatio(
+                        supportedSize,
+                        AspectRatioUtil.ASPECT_RATIO_4_3
+                    )
+                ) {
+                    AspectRatio.RATIO_4_3
+                } else if (AspectRatioUtil.hasMatchingAspectRatio(
+                        supportedSize,
+                        AspectRatioUtil.ASPECT_RATIO_16_9
+                    )
+                ) {
+                    AspectRatio.RATIO_16_9
+                } else {
+                    // Sizes of special aspect ratios as 1:1 or 18:9 need to implement
+                    // ResolutionFilter to select them.
+                    resolutionFilter = ResolutionFilter { supportedSizes, _ ->
+                        supportedSizes.remove(supportedSize)
+                        supportedSizes.add(0, supportedSize)
+                        supportedSizes
+                    }
+                    TARGET_ASPECT_RATIO_NONE
+                }
+
+            val useCaseConfig = createUseCaseConfig(
+                preferredAspectRatio = preferredAspectRatio,
+                boundSize = supportedSize,
+                resolutionFilter = resolutionFilter
+            )
+
+            val resultList = supportedOutputSizesSorter.getSortedSupportedOutputSizes(useCaseConfig)
+            assertThat(resultList[0]).isEqualTo(supportedSize)
+        }
+    }
+
+    @Test
+    fun getSupportedOutputSizes_withResolutionFilter() {
+        val filteredSizesList = arrayListOf(
+            Size(1280, 720),
+            Size(4032, 3024),
+            Size(960, 960)
+        )
+        val resolutionFilter = ResolutionFilter { _, _ -> filteredSizesList }
+        verifySupportedOutputSizesWithResolutionSelectorSettings(
+            resolutionFilter = resolutionFilter,
+            expectedList = filteredSizesList
+        )
+    }
+
+    @Test
+    fun getSupportedOutputSizes_resolutionFilterReturnsAdditionalItem() {
+        val resolutionFilter = ResolutionFilter { supportedSizes, _ ->
+            supportedSizes.add(Size(1599, 899)) // Adds an additional size item
+            supportedSizes
+        }
+        // Throws exception when additional items is added in the returned list
+        assertThrows(IllegalArgumentException::class.java) {
+            verifySupportedOutputSizesWithResolutionSelectorSettings(
+                resolutionFilter = resolutionFilter
+            )
+        }
+    }
+
+    private fun verifySupportedOutputSizesWithResolutionSelectorSettings(
+        outputSizesSorter: SupportedOutputSizesSorter = supportedOutputSizesSorter,
+        captureType: CaptureType = CaptureType.IMAGE_CAPTURE,
+        preferredAspectRatio: Int = TARGET_ASPECT_RATIO_NONE,
+        aspectRatioFallbackRule: Int = AspectRatioStrategy.FALLBACK_RULE_AUTO,
+        resolutionStrategy: ResolutionStrategy? = null,
+        boundSize: Size? = null,
+        resolutionFallbackRule: Int = FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER,
+        resolutionFilter: ResolutionFilter? = null,
+        highResolutionEnabledFlags: Int = 0,
+        highResolutionForceDisabled: Boolean = false,
+        expectedList: List<Size> = Collections.emptyList(),
+    ) {
+        val useCaseConfig = createUseCaseConfig(
+            captureType,
+            preferredAspectRatio,
+            aspectRatioFallbackRule,
+            resolutionStrategy,
+            boundSize,
+            resolutionFallbackRule,
+            resolutionFilter,
+            highResolutionEnabledFlags,
+            highResolutionForceDisabled
+        )
+        val resultList = outputSizesSorter.getSortedSupportedOutputSizes(useCaseConfig)
+        assertThat(resultList).containsExactlyElementsIn(expectedList).inOrder()
+    }
+
+    private fun createUseCaseConfig(
+        captureType: CaptureType = CaptureType.IMAGE_CAPTURE,
+        preferredAspectRatio: Int = TARGET_ASPECT_RATIO_NONE,
+        aspectRatioFallbackRule: Int = AspectRatioStrategy.FALLBACK_RULE_AUTO,
+        resolutionStrategy: ResolutionStrategy? = null,
+        boundSize: Size? = null,
+        resolutionFallbackRule: Int = FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER,
+        resolutionFilter: ResolutionFilter? = null,
+        highResolutionEnabledFlags: Int = 0,
+        highResolutionForceDisabled: Boolean = false,
+    ): UseCaseConfig<*> {
+        val useCaseConfigBuilder = FakeUseCaseConfig.Builder(captureType, ImageFormat.JPEG)
+        val resolutionSelectorBuilder = ResolutionSelector.Builder()
+
+        // Creates aspect ratio strategy and sets to resolution selector
+        val aspectRatioStrategy = if (preferredAspectRatio != TARGET_ASPECT_RATIO_NONE) {
+            AspectRatioStrategy.create(preferredAspectRatio, aspectRatioFallbackRule)
+        } else {
+            null
+        }
+
+        aspectRatioStrategy?.let { resolutionSelectorBuilder.setAspectRatioStrategy(it) }
+
+        // Creates resolution strategy and sets to resolution selector
+        if (resolutionStrategy != null) {
+            resolutionSelectorBuilder.setResolutionStrategy(resolutionStrategy)
+        } else {
+            boundSize?.let {
+                resolutionSelectorBuilder.setResolutionStrategy(
+                    ResolutionStrategy.create(
+                        boundSize,
+                        resolutionFallbackRule
+                    )
+                )
+            }
+        }
+
+        // Sets the resolution filter to resolution selector
+        resolutionFilter?.let { resolutionSelectorBuilder.setResolutionFilter(it) }
+        // Sets the high resolution enabled flags to resolution selector
+        resolutionSelectorBuilder.setHighResolutionEnabledFlags(highResolutionEnabledFlags)
+
+        // Sets the custom resolution selector to use case config
+        useCaseConfigBuilder.setResolutionSelector(resolutionSelectorBuilder.build())
+
+        // Sets the high resolution force disabled setting
+        useCaseConfigBuilder.setHighResolutionDisabled(highResolutionForceDisabled)
+
+        return useCaseConfigBuilder.useCaseConfig
+    }
+}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/processing/SurfaceOutputImplTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/processing/SurfaceOutputImplTest.kt
index eb809d7..bc76e238 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/processing/SurfaceOutputImplTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/processing/SurfaceOutputImplTest.kt
@@ -125,9 +125,7 @@
     @Test
     fun updateMatrixWithoutCameraTransform_noCameraTransform() {
         // Arrange.
-        val cameraInfo = FakeCameraInfoInternal(180, LENS_FACING_FRONT)
-        val camera = FakeCamera(null, cameraInfo).apply { hasTransform = false }
-        val surfaceOut = createFakeSurfaceOutputImpl(camera)
+        val surfaceOut = createFakeSurfaceOutputImpl(null)
         val input = FloatArray(16).also {
             Matrix.setIdentityM(it, 0)
         }
@@ -179,7 +177,7 @@
         assertThat(hasRequestedClose).isFalse()
     }
 
-    private fun createFakeSurfaceOutputImpl(camera: FakeCamera = FakeCamera()) = SurfaceOutputImpl(
+    private fun createFakeSurfaceOutputImpl(camera: FakeCamera? = FakeCamera()) = SurfaceOutputImpl(
         fakeSurface,
         TARGET,
         INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE,
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/processing/SurfaceProcessorNodeTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/processing/SurfaceProcessorNodeTest.kt
index 1107797..f6287b4 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/processing/SurfaceProcessorNodeTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/processing/SurfaceProcessorNodeTest.kt
@@ -116,6 +116,36 @@
     }
 
     @Test
+    fun inputHasNoCameraTransform_surfaceOutputReceivesNullCamera() {
+        // Arrange: configure node to produce JPEG output.
+        createSurfaceProcessorNode()
+        createInputEdge(
+            inputEdge = SurfaceEdge(
+                PREVIEW,
+                INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE,
+                StreamSpec.builder(INPUT_SIZE).build(),
+                Matrix.IDENTITY_MATRIX,
+                false,
+                PREVIEW_CROP_RECT,
+                0,
+                false
+            )
+        )
+
+        // Act.
+        val nodeOutput = node.transform(nodeInput)
+        provideSurfaces(nodeOutput)
+        shadowOf(getMainLooper()).idle()
+
+        val previewSurfaceOutput =
+            surfaceProcessorInternal.surfaceOutputs[PREVIEW]!! as SurfaceOutputImpl
+        assertThat(previewSurfaceOutput.camera).isNull()
+        val videoSurfaceOutput =
+            surfaceProcessorInternal.surfaceOutputs[VIDEO_CAPTURE]!! as SurfaceOutputImpl
+        assertThat(videoSurfaceOutput.camera).isNull()
+    }
+
+    @Test
     fun configureJpegOutput_returnsJpegFormat() {
         // Arrange: configure node to produce JPEG output.
         createSurfaceProcessorNode()
@@ -288,6 +318,7 @@
         assertThat(previewTransformInfo.rotationDegrees).isEqualTo(0)
         assertThat(previewSurfaceOutput.inputSize).isEqualTo(INPUT_SIZE)
         assertThat(previewSurfaceOutput.mirroring).isFalse()
+        assertThat(previewSurfaceOutput.camera).isNotNull()
 
         val videoSurfaceOutput =
             surfaceProcessorInternal.surfaceOutputs[VIDEO_CAPTURE]!! as SurfaceOutputImpl
@@ -298,6 +329,7 @@
         assertThat(videoTransformInfo.rotationDegrees).isEqualTo(270)
         assertThat(videoSurfaceOutput.inputSize).isEqualTo(INPUT_SIZE)
         assertThat(videoSurfaceOutput.mirroring).isTrue()
+        assertThat(videoSurfaceOutput.camera).isNotNull()
     }
 
     @Test
@@ -421,9 +453,8 @@
         inputRotationDegrees: Int = INPUT_ROTATION_DEGREES,
         mirroring: Boolean = MIRRORING,
         videoOutputSize: Size = VIDEO_SIZE,
-        frameRateRange: Range<Int> = StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED
-    ) {
-        val inputEdge = SurfaceEdge(
+        frameRateRange: Range<Int> = StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED,
+        inputEdge: SurfaceEdge = SurfaceEdge(
             previewTarget,
             INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE,
             StreamSpec.builder(previewSize).setExpectedFrameRateRange(frameRateRange).build(),
@@ -432,7 +463,8 @@
             previewCropRect,
             inputRotationDegrees,
             mirroring,
-        )
+        ),
+    ) {
         videoOutConfig = OutConfig.of(
             VIDEO_CAPTURE,
             INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE,
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/VirtualCameraCaptureResultTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/VirtualCameraCaptureResultTest.kt
new file mode 100644
index 0000000..ff0e72e
--- /dev/null
+++ b/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/VirtualCameraCaptureResultTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core.streamsharing
+
+import android.os.Build
+import android.util.Pair
+import androidx.camera.core.impl.CameraCaptureMetaData
+import androidx.camera.core.impl.TagBundle
+import androidx.camera.testing.fakes.FakeCameraCaptureResult
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+
+/**
+ * Unit tests for [VirtualCameraCaptureResult].
+ */
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class VirtualCameraCaptureResultTest {
+
+    companion object {
+        private const val KEY = "key"
+        private const val VALUE = "value"
+        private val TAG_BUNDLE = TagBundle.create(Pair(KEY, VALUE))
+    }
+
+    @Test
+    fun metadataWithTimestamp_overrideTimestamp() {
+        // Act.
+        val result = VirtualCameraCaptureResult(TAG_BUNDLE, 1L)
+        // Assert.
+        assertThat(result.timestamp).isEqualTo(1L)
+        assertThat(result.tagBundle).isEqualTo(TAG_BUNDLE)
+        assertThat(result.aeState).isEqualTo(CameraCaptureMetaData.AeState.UNKNOWN)
+        assertThat(result.afState).isEqualTo(CameraCaptureMetaData.AfState.UNKNOWN)
+        assertThat(result.awbState).isEqualTo(CameraCaptureMetaData.AwbState.UNKNOWN)
+        assertThat(result.flashState).isEqualTo(CameraCaptureMetaData.FlashState.UNKNOWN)
+        assertThat(result.afMode).isEqualTo(CameraCaptureMetaData.AfMode.UNKNOWN)
+    }
+
+    @Test
+    fun metadataWithBaseValue_returnBaseValue() {
+        // Arrange.
+        val baseCameraCaptureResult = FakeCameraCaptureResult().apply {
+            timestamp = 2L
+            aeState = CameraCaptureMetaData.AeState.CONVERGED
+            afState = CameraCaptureMetaData.AfState.LOCKED_FOCUSED
+            awbState = CameraCaptureMetaData.AwbState.CONVERGED
+            flashState = CameraCaptureMetaData.FlashState.FIRED
+            afMode = CameraCaptureMetaData.AfMode.ON_CONTINUOUS_AUTO
+        }
+        // Act.
+        val result = VirtualCameraCaptureResult(TAG_BUNDLE, baseCameraCaptureResult)
+        // Assert.
+        assertThat(result.timestamp).isEqualTo(baseCameraCaptureResult.timestamp)
+        assertThat(result.tagBundle).isEqualTo(TAG_BUNDLE)
+        assertThat(result.aeState).isEqualTo(baseCameraCaptureResult.aeState)
+        assertThat(result.afState).isEqualTo(baseCameraCaptureResult.afState)
+        assertThat(result.awbState).isEqualTo(baseCameraCaptureResult.awbState)
+        assertThat(result.flashState).isEqualTo(baseCameraCaptureResult.flashState)
+        assertThat(result.afMode).isEqualTo(baseCameraCaptureResult.afMode)
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/VirtualCameraTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/VirtualCameraTest.kt
index 8f06cf2..fa7318c 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/VirtualCameraTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/streamsharing/VirtualCameraTest.kt
@@ -20,14 +20,20 @@
 import android.graphics.Matrix
 import android.graphics.Rect
 import android.os.Build
+import android.os.Looper.getMainLooper
 import android.util.Size
 import androidx.camera.core.CameraEffect.PREVIEW
+import androidx.camera.core.ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY
+import androidx.camera.core.ImageCapture.FLASH_MODE_AUTO
 import androidx.camera.core.MirrorMode.MIRROR_MODE_ON
 import androidx.camera.core.UseCase
+import androidx.camera.core.impl.CameraControlInternal
+import androidx.camera.core.impl.CaptureConfig
 import androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE
 import androidx.camera.core.impl.SessionConfig
 import androidx.camera.core.impl.SessionConfig.defaultEmptySessionConfig
 import androidx.camera.core.impl.StreamSpec
+import androidx.camera.core.impl.utils.futures.Futures
 import androidx.camera.core.processing.SurfaceEdge
 import androidx.camera.testing.fakes.FakeCamera
 import androidx.camera.testing.fakes.FakeDeferrableSurface
@@ -40,6 +46,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.robolectric.RobolectricTestRunner
+import org.robolectric.Shadows.shadowOf
 import org.robolectric.annotation.Config
 import org.robolectric.annotation.internal.DoNotInstrument
 
@@ -73,10 +80,16 @@
     )
     private val useCaseConfigFactory = FakeUseCaseConfigFactory()
     private lateinit var virtualCamera: VirtualCamera
+    private var snapshotTriggered = false
 
     @Before
     fun setUp() {
-        virtualCamera = VirtualCamera(parentCamera, setOf(child1, child2), useCaseConfigFactory)
+        virtualCamera = VirtualCamera(
+            parentCamera, setOf(child1, child2), useCaseConfigFactory
+        ) {
+            snapshotTriggered = true
+            Futures.immediateFuture(null)
+        }
     }
 
     @After
@@ -87,6 +100,24 @@
     }
 
     @Test
+    fun submitStillCaptureRequests_triggersSnapshot() {
+        // Arrange.
+        virtualCamera.bindChildren()
+
+        // Act: submit a still capture request from a child.
+        val cameraControl = child1.camera!!.cameraControl as CameraControlInternal
+        cameraControl.submitStillCaptureRequests(
+            listOf(CaptureConfig.Builder().build()),
+            CAPTURE_MODE_MINIMIZE_LATENCY,
+            FLASH_MODE_AUTO
+        )
+        shadowOf(getMainLooper()).idle()
+
+        // The StreamSharing.Control is called to take a snapshot.
+        assertThat(snapshotTriggered).isTrue()
+    }
+
+    @Test
     fun setUseCaseActiveAndInactive_surfaceConnectsAndDisconnects() {
         // Arrange.
         virtualCamera.bindChildren()
@@ -151,7 +182,6 @@
     fun virtualCameraInheritsParentProperties() {
         assertThat(virtualCamera.cameraState).isEqualTo(parentCamera.cameraState)
         assertThat(virtualCamera.cameraInfo).isEqualTo(parentCamera.cameraInfo)
-        assertThat(virtualCamera.cameraControl).isEqualTo(parentCamera.cameraControl)
     }
 
     @Test
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
index 514ca3b..4eba012 100755
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
@@ -56,7 +56,7 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
-    @Nullable
+    @NonNull
     @Override
     public List<CaptureStageImpl> getCaptureStages() {
         throw new RuntimeException("Stub, replace with implementation.");
@@ -105,6 +105,13 @@
 
     @Nullable
     @Override
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(
+            @NonNull Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Nullable
+    @Override
     public Range<Long> getEstimatedCaptureLatencyRange(@Nullable Size captureOutputSize) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
@@ -121,4 +128,24 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    @Nullable
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
index 85b63df..f406575 100755
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
@@ -102,4 +102,9 @@
     public List<Pair<Integer, Size[]>> getSupportedResolutions() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
index c547f772..2863256 100755
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
@@ -56,7 +56,7 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
-    @Nullable
+    @NonNull
     @Override
     public List<CaptureStageImpl> getCaptureStages() {
         throw new RuntimeException("Stub, replace with implementation.");
@@ -103,6 +103,12 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
+    @Override
+    @Nullable
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(@NonNull Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
     @Nullable
     @Override
     public Range<Long> getEstimatedCaptureLatencyRange(@Nullable Size captureOutputSize) {
@@ -120,4 +126,25 @@
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    @Nullable
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
index da5748c..b892c07 100755
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
@@ -102,4 +102,9 @@
     public List<Pair<Integer, Size[]>> getSupportedResolutions() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
index 74d50b1..c794e12 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
@@ -56,7 +56,7 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
-    @Nullable
+    @NonNull
     @Override
     public List<CaptureStageImpl> getCaptureStages() {
         throw new RuntimeException("Stub, replace with implementation.");
@@ -103,6 +103,12 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
+    @Override
+    @Nullable
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(@NonNull Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
     @Nullable
     @Override
     public Range<Long> getEstimatedCaptureLatencyRange(@Nullable Size captureOutputSize) {
@@ -121,4 +127,24 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Nullable
+    @Override
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
index 6083103..e6b69d6 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
@@ -100,4 +100,9 @@
     public List<Pair<Integer, Size[]>> getSupportedResolutions() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java
index 3eee146..2e27560 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java
@@ -21,8 +21,12 @@
 import android.hardware.camera2.TotalCaptureResult;
 import android.media.Image;
 import android.util.Pair;
+import android.util.Size;
 import android.view.Surface;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import java.util.Map;
 import java.util.concurrent.Executor;
 
@@ -43,7 +47,30 @@
      *                process. The {@link Image} that are contained within the map will become
      *                invalid after this method completes, so no references to them should be kept.
      */
-    void process(Map<Integer, Pair<Image, TotalCaptureResult>> results);
+    void process(@NonNull Map<Integer, Pair<Image, TotalCaptureResult>> results);
+
+    /**
+     * Informs the CaptureProcessorImpl where it should write the postview output to.
+     * This will only be invoked once if a valid postview surface was set.
+     *
+     * @param surface A valid {@link ImageFormat#YUV_420_888} {@link Surface}
+     *                that the CaptureProcessorImpl should write data into.
+     * @since 1.4
+     */
+    void onPostviewOutputSurface(@NonNull Surface surface);
+
+    /**
+     * Invoked when the Camera Framework changes the configured output resolution for
+     * still capture and postview.
+     *
+     * <p>After this call, {@link CaptureProcessorImpl} should expect any {@link Image} received as
+     * input for still capture and postview to be at the specified resolutions.
+     *
+     * @param size for the surface for still capture.
+     * @param postviewSize for the surface for postview.
+     * @since 1.4
+     */
+    void onResolutionUpdate(@NonNull Size size, @NonNull Size postviewSize);
 
     /**
      * Process a set images captured that were requested.
@@ -61,6 +88,32 @@
      *                       run on any arbitrary executor.
      * @since 1.3
      */
-    void process(Map<Integer, Pair<Image, TotalCaptureResult>> results,
-            ProcessResultImpl resultCallback, Executor executor);
+    void process(@NonNull Map<Integer, Pair<Image, TotalCaptureResult>> results,
+            @NonNull ProcessResultImpl resultCallback, @Nullable Executor executor);
+
+    /**
+     * Process a set images captured that were requested for both postview and
+     * still capture.
+     *
+     * <p> This processing method will be called if a postview was requested, therefore the
+     * processed postview should be written to the
+     * {@link Surface} received by {@link #onPostviewOutputSurface(Surface, int)}.
+     * The final result of the processing step should be written to the {@link Surface} that was
+     * received by {@link #onOutputSurface(Surface, int)}. Since postview should be available
+     * before the capture, it should be processed and written to the surface before
+     * the final capture is processed.
+     *
+     * @param results             The map of {@link ImageFormat#YUV_420_888} format images and
+     *                            metadata to process. The {@link Image} that are contained within
+     *                            the map will become invalid after this method completes, so no
+     *                            references to them should be kept.
+     * @param resultCallback      Capture result callback to be called once the capture result
+     *                            values of the processed image are ready.
+     * @param executor            The executor to run the callback on. If null then the callback
+     *                            will run on any arbitrary executor.
+     * @throws RuntimeException   if postview feature is not supported
+     * @since 1.4
+     */
+    void processWithPostview(@NonNull Map<Integer, Pair<Image, TotalCaptureResult>> results,
+            @NonNull ProcessResultImpl resultCallback, @Nullable Executor executor);
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ExtenderStateListener.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ExtenderStateListener.java
index a74a880..2d974541 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ExtenderStateListener.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ExtenderStateListener.java
@@ -17,7 +17,6 @@
 package androidx.camera.extensions.impl;
 
 import android.content.Context;
-import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
@@ -55,7 +54,7 @@
 
     /**
      * This will be invoked before creating a
-     * {@link CameraCaptureSession}. The {@link CaptureRequest}
+     * {@link android.hardware.camera2.CameraCaptureSession}. The {@link CaptureRequest}
      * parameters returned via {@link CaptureStageImpl} will be passed to the camera device as
      * part of the capture session initialization via
      * {@link SessionConfiguration#setSessionParameters(CaptureRequest)} which only supported from
@@ -67,7 +66,7 @@
     CaptureStageImpl onPresetSession();
 
     /**
-     * This will be invoked once after the {@link CameraCaptureSession}
+     * This will be invoked once after the {@link android.hardware.camera2.CameraCaptureSession}
      * has been created. The {@link CaptureRequest} parameters returned via
      * {@link CaptureStageImpl} will be used to generate a single request to the current
      * configured {@link CameraDevice}. The generated request will be submitted to camera before
@@ -79,7 +78,7 @@
     CaptureStageImpl onEnableSession();
 
     /**
-     * This will be invoked before the {@link CameraCaptureSession} is
+     * This will be invoked before the {@link android.hardware.camera2.CameraCaptureSession} is
      * closed. The {@link CaptureRequest} parameters returned via {@link CaptureStageImpl} will
      * be used to generate a single request to the currently configured {@link CameraDevice}. The
      * generated request will be submitted to camera before the CameraCaptureSession is closed.
@@ -88,4 +87,21 @@
      */
     @Nullable
     CaptureStageImpl onDisableSession();
+
+    /**
+     * This will be invoked before the {@link android.hardware.camera2.CameraCaptureSession} is
+     * initialized and must return a valid camera session type
+     * {@link android.hardware.camera2.params.SessionConfiguration#getSessionType}
+     * to be used to configure camera capture session. Both the preview and the image capture
+     * extender must return the same session type value for a specific extension type. If there
+     * is inconsistency between the session type values from preview and image extenders, then
+     * the session configuration will fail.
+     *
+     * @return Camera capture session type. Regular and vendor specific types are supported but
+     * not high speed values. The extension can return -1 in which case the camera capture session
+     * will be configured to use the default regular type.
+     *
+     * @since 1.4
+     */
+    int onSessionType();
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java
index e20ea0d..7b6da32 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java
@@ -52,7 +52,6 @@
      * @return the version that vendor supported in this device. The MAJOR.MINOR.PATCH format
      * should be used.
      */
-    @SuppressWarnings("unused")
     @NonNull
     public String checkApiVersion(@NonNull String version) {
         throw new RuntimeException("Stub, replace with implementation.");
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
index 507f214..50c9dec 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
@@ -56,7 +56,7 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
-    @Nullable
+    @NonNull
     @Override
     public List<CaptureStageImpl> getCaptureStages() {
         throw new RuntimeException("Stub, replace with implementation.");
@@ -103,6 +103,12 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
+    @Override
+    @Nullable
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(@NonNull Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
     @Nullable
     @Override
     public Range<Long> getEstimatedCaptureLatencyRange(@Nullable Size captureOutputSize) {
@@ -120,4 +126,25 @@
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Nullable
+    @Override
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
index 83f1a34..0fce6d35 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
@@ -102,4 +102,9 @@
     public List<Pair<Integer, Size[]>> getSupportedResolutions() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
index 10b4513..f009be8 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
@@ -20,7 +20,6 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.params.StreamConfigurationMap;
 import android.util.Pair;
 import android.util.Range;
 import android.util.Size;
@@ -64,7 +63,7 @@
     CaptureProcessorImpl getCaptureProcessor();
 
     /** The set of captures that are needed to create an image with the effect. */
-    @Nullable
+    @NonNull
     List<CaptureStageImpl> getCaptureStages();
 
     /**
@@ -79,18 +78,34 @@
      * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned.
      *
      * <p>The returned resolutions should be subset of the supported sizes retrieved from
-     * {@link StreamConfigurationMap} for the camera device. If the
+     * {@link android.hardware.camera2.params.StreamConfigurationMap} for the camera device. If the
      * returned list is not null, it will be used to find the best resolutions combination for
      * the bound use cases.
      *
      * @return the customized supported resolutions, or null to support all sizes retrieved from
-     * {@link StreamConfigurationMap}.
+     *         {@link android.hardware.camera2.params.StreamConfigurationMap}.
      * @since 1.1
      */
     @Nullable
     List<Pair<Integer, Size[]>> getSupportedResolutions();
 
     /**
+     * Returns the customized supported postview resolutions for a still capture using
+     * its size.
+     *
+     * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned.
+     *
+     * <p>The returned resolutions should be subset of the supported sizes retrieved from
+     * {@link android.hardware.camera2.params.StreamConfigurationMap} for the camera device.
+     *
+     * @return the customized supported resolutions, or null to support all sizes retrieved from
+     *         {@link android.hardware.camera2.params.StreamConfigurationMap}.
+     * @since 1.4
+     */
+    @Nullable
+    List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(@NonNull Size captureSize);
+
+    /**
      * Returns the estimated capture latency range in milliseconds for the target capture
      * resolution.
      *
@@ -168,4 +183,45 @@
      */
     @NonNull
     List<CaptureResult.Key> getAvailableCaptureResultKeys();
+
+    /**
+     * Advertise support for {@link ProcessResultImpl#onCaptureProcessProgressed}.
+     *
+     * @return {@code true} in case the process progress callback is supported and is expected to
+     * be triggered, {@code false} otherwise.
+     * @since 1.4
+     */
+    boolean isCaptureProcessProgressAvailable();
+
+    /**
+     * Returns the dynamically calculated capture latency pair in milliseconds.
+     *
+     * <p>In contrast to {@link #getEstimatedCaptureLatencyRange} this method is guaranteed to be
+     * called after the camera capture session is initialized and camera preview is enabled.
+     * The measurement is expected to take in to account dynamic parameters such as the current
+     * scene, the state of 3A algorithms, the state of internal HW modules and return a more
+     * accurate assessment of the still capture latency.</p>
+     *
+     * @return pair that includes the estimated input frame/frames camera capture latency as the
+     * first field and the estimated post-processing latency {@link CaptureProcessorImpl#process}
+     * as the second pair field. Both first and second fields will be in milliseconds. The total
+     * still capture latency will be the sum of both the first and second values.
+     * The pair is expected to be null if the dynamic latency estimation is not supported.
+     * If clients have not configured a still capture output, then this method can also return a
+     * null pair.
+     * @since 1.4
+     */
+    @Nullable
+    Pair<Long, Long> getRealtimeCaptureLatency();
+
+    /**
+     * Indicates whether the extension supports the postview for still capture feature.
+     * If the extension is using HAL processing, false should be returned since the
+     * postview feature is not currently supported for this case.
+     *
+     * @return {@code true} in case postview for still capture is supported
+     * {@code false} otherwise.
+     * @since 1.4
+     */
+    boolean isPostviewAvailable();
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
index 05a195e..a3a648d 100755
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
@@ -56,7 +56,7 @@
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
-    @Nullable
+    @NonNull
     @Override
     public List<CaptureStageImpl> getCaptureStages() {
         throw new RuntimeException("Stub, replace with implementation.");
@@ -105,6 +105,12 @@
 
     @Nullable
     @Override
+    public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(@NonNull Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Nullable
+    @Override
     public Range<Long> getEstimatedCaptureLatencyRange(@Nullable Size captureOutputSize) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
@@ -120,4 +126,25 @@
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Nullable
+    @Override
+    public Pair<Long, Long> getRealtimeCaptureLatency() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
index eeb254b..4dc7256 100755
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
@@ -102,4 +102,9 @@
     public List<Pair<Integer, Size[]>> getSupportedResolutions() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public int onSessionType() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
index b320813..d921151 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
@@ -18,7 +18,6 @@
 
 import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.TotalCaptureResult;
 import android.util.Pair;
 import android.util.Size;
@@ -69,7 +68,7 @@
      * The set of parameters required to produce the effect on the preview stream.
      *
      * <p> This will be the initial set of parameters used for the preview
-     * {@link CaptureRequest}. If the {@link ProcessorType} is defined as
+     * {@link android.hardware.camera2.CaptureRequest}. If the {@link ProcessorType} is defined as
      * {@link ProcessorType#PROCESSOR_TYPE_REQUEST_UPDATE_ONLY} then this will be updated when
      * the {@link RequestUpdateProcessorImpl#process(TotalCaptureResult)} from {@link
      * #getProcessor()} has been called, this should be updated to reflect the new {@link
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewImageProcessorImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewImageProcessorImpl.java
index f203eba..c4bf2c8 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewImageProcessorImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewImageProcessorImpl.java
@@ -16,11 +16,13 @@
 
 package androidx.camera.extensions.impl;
 
-import android.annotation.SuppressLint;
 import android.graphics.ImageFormat;
 import android.hardware.camera2.TotalCaptureResult;
 import android.media.Image;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import java.util.concurrent.Executor;
 
 /**
@@ -29,7 +31,6 @@
  *
  * @since 1.0
  */
-@SuppressLint("UnknownNullness")
 public interface PreviewImageProcessorImpl extends ProcessorImpl {
     /**
      * Processes the requested image capture.
@@ -41,7 +42,7 @@
      *               invalid after the method completes so no reference to it should be kept.
      * @param result The metadata associated with the image to process.
      */
-    void process(Image image, TotalCaptureResult result);
+    void process(@NonNull Image image, @NonNull TotalCaptureResult result);
 
     /**
      * Processes the requested image capture.
@@ -59,6 +60,7 @@
      *                       run on any arbitrary executor.
      * @since 1.3
      */
-    void process(Image image, TotalCaptureResult result, ProcessResultImpl resultCallback,
-            Executor executor);
+    void process(@NonNull Image image, @NonNull TotalCaptureResult result,
+            @NonNull ProcessResultImpl resultCallback,
+            @Nullable Executor executor);
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ProcessResultImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ProcessResultImpl.java
index d0e3605..318c60e 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ProcessResultImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ProcessResultImpl.java
@@ -16,18 +16,17 @@
 
 package androidx.camera.extensions.impl;
 
-import android.annotation.SuppressLint;
 import android.hardware.camera2.CaptureResult;
 import android.util.Pair;
 
+import androidx.annotation.NonNull;
+
 import java.util.List;
 
 /**
  * Allows clients to receive information about the capture result values of processed frames.
  *
- * @since 1.3
  */
-@SuppressLint("UnknownNullness")
 public interface ProcessResultImpl {
     /**
      * Capture result callback that needs to be called when the process capture results are
@@ -40,6 +39,23 @@
      *                             must also be passed as part of this callback. Both Camera2 and
      *                             CameraX guarantee that those two settings and results are always
      *                             supported and applied by the corresponding framework.
+     * @since 1.3
      */
-    void onCaptureCompleted(long shutterTimestamp, List<Pair<CaptureResult.Key, Object>> result);
+    void onCaptureCompleted(long shutterTimestamp,
+            @NonNull List<Pair<CaptureResult.Key, Object>> result);
+
+    /**
+     * Capture progress callback that needs to be called when the process capture is
+     * ongoing and includes the estimated progress of the processing.
+     *
+     * <p>Extensions must ensure that they always call this callback with monotonically increasing
+     * values.</p>
+     *
+     * <p>Extensions are allowed to trigger this callback multiple times but at the minimum the
+     * callback is expected to be called once when processing is done with value 100.</p>
+     *
+     * @param progress             Value between 0 and 100.
+     * @since 1.4
+     */
+    void onCaptureProcessProgressed(int progress);
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java
index 465bfe8..db3491e 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java
@@ -16,13 +16,14 @@
 
 package androidx.camera.extensions.impl.advanced;
 
-import android.annotation.SuppressLint;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.util.Range;
 import android.util.Size;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.camera.extensions.impl.ExtensionVersionImpl;
 
 import java.util.List;
@@ -50,7 +51,6 @@
  *
  * @since 1.2
  */
-@SuppressLint("UnknownNullness")
 public interface AdvancedExtenderImpl {
 
     /**
@@ -64,8 +64,8 @@
      *                           physical camera ids and their CameraCharacteristics.
      * @return true if the extension is supported, otherwise false
      */
-    boolean isExtensionAvailable(String cameraId,
-            Map<String, CameraCharacteristics> characteristicsMap);
+    boolean isExtensionAvailable(@NonNull String cameraId,
+            @NonNull Map<String, CameraCharacteristics> characteristicsMap);
 
     /**
      * Initializes the extender to be used with the specified camera.
@@ -80,7 +80,8 @@
      *                           If the camera is logical camera, it will also contain associated
      *                           physical camera ids and their CameraCharacteristics.
      */
-    void init(String cameraId, Map<String, CameraCharacteristics> characteristicsMap);
+    void init(@NonNull String cameraId,
+            @NonNull Map<String, CameraCharacteristics> characteristicsMap);
 
     /**
      * Returns the estimated capture latency range in milliseconds for the
@@ -97,8 +98,9 @@
      * @return the range of estimated minimal and maximal capture latency in milliseconds.
      * Returns null if no capture latency info can be provided.
      */
-    Range<Long> getEstimatedCaptureLatencyRange(String cameraId,
-            Size captureOutputSize, int imageFormat);
+    @Nullable
+    Range<Long> getEstimatedCaptureLatencyRange(@NonNull String cameraId,
+            @Nullable Size captureOutputSize, int imageFormat);
 
     /**
      * Returns supported output format/size map for preview. The format could be PRIVATE or
@@ -111,7 +113,8 @@
      * the HAL. Alternatively OEM can configure a intermediate YUV surface of the same size and
      * writes the output to the preview output surface.
      */
-    Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(String cameraId);
+    @NonNull
+    Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(@NonNull String cameraId);
 
     /**
      * Returns supported output format/size map for image capture. OEM is required to support
@@ -121,7 +124,20 @@
      * format/size could be either added in CameraCaptureSession with HAL processing OR it
      * configures intermediate surfaces(YUV/RAW..) and writes the output to the output surface.
      */
-    Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(String cameraId);
+    @NonNull
+    Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(@NonNull String cameraId);
+
+    /**
+     * Returns supported output format/size map for postview image. OEM is required to support
+     * both JPEG and YUV_420_888 format output.
+     *
+     * <p>The surface created with this supported format/size could configure
+     * intermediate surfaces(YUV/RAW..) and write the output to the output surface.</p>
+     *
+     * @since 1.4
+     */
+    @NonNull
+    Map<Integer, List<Size>> getSupportedPostviewResolutions(@NonNull Size captureSize);
 
     /**
      * Returns supported output sizes for Image Analysis (YUV_420_888 format).
@@ -130,12 +146,14 @@
      * output surfaces. If imageAnalysis YUV surface is not supported, OEM should return null or
      * empty list.
      */
-    List<Size> getSupportedYuvAnalysisResolutions(String cameraId);
+    @Nullable
+    List<Size> getSupportedYuvAnalysisResolutions(@NonNull String cameraId);
 
     /**
      * Returns a processor for activating extension sessions. It implements all the interactions
      * required for starting a extension and cleanup.
      */
+    @NonNull
     SessionProcessorImpl createSessionProcessor();
 
     /**
@@ -169,6 +187,7 @@
      * are not supported.
      * @since 1.3
      */
+    @NonNull
     List<CaptureRequest.Key> getAvailableCaptureRequestKeys();
 
     /**
@@ -184,5 +203,26 @@
      * an empty list if capture results are not supported.
      * @since 1.3
      */
+    @NonNull
     List<CaptureResult.Key> getAvailableCaptureResultKeys();
+
+    /**
+     * Advertise support for {@link SessionProcessorImpl#onCaptureProcessProgressed}.
+     *
+     * @return {@code true} in case the process progress callback is supported and is expected to
+     * be triggered, {@code false} otherwise.
+     * @since 1.4
+     */
+    boolean isCaptureProcessProgressAvailable();
+
+    /**
+     * Indicates whether the extension supports the postview for still capture feature.
+     * If the extension is using HAL processing, false should be returned since the
+     * postview feature is not currently supported for this case.
+     *
+     * @return {@code true} in case postview for still capture is supported
+     * {@code false} otherwise.
+     * @since 1.4
+     */
+    boolean isPostviewAvailable();
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java
index 0d3bd4a..9f567ba 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java
@@ -16,13 +16,15 @@
 
 package androidx.camera.extensions.impl.advanced;
 
-import android.annotation.SuppressLint;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.util.Range;
 import android.util.Size;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import java.util.List;
 import java.util.Map;
 
@@ -33,59 +35,82 @@
  *
  * @since 1.2
  */
-@SuppressLint("UnknownNullness")
 public class AutoAdvancedExtenderImpl implements AdvancedExtenderImpl {
     public AutoAdvancedExtenderImpl() {
     }
 
     @Override
-    public boolean isExtensionAvailable(String cameraId,
-            Map<String, CameraCharacteristics> characteristicsMap) {
+    public boolean isExtensionAvailable(@NonNull String cameraId,
+            @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
-    public void init(String cameraId,
-            Map<String, CameraCharacteristics> characteristicsMap) {
+    public void init(@NonNull String cameraId,
+            @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @Nullable
     public Range<Long> getEstimatedCaptureLatencyRange(
-            String cameraId, Size size, int imageFormat) {
+            @NonNull String cameraId, @Nullable Size size, int imageFormat) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(
-            String cameraId) {
+            @NonNull String cameraId) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(
-            String cameraId) {
+            @NonNull String cameraId) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
+    public Map<Integer, List<Size>> getSupportedPostviewResolutions(
+            @NonNull Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    @Nullable
     public List<Size> getSupportedYuvAnalysisResolutions(
-            String cameraId) {
+            @NonNull String cameraId) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public SessionProcessorImpl createSessionProcessor() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
-}
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java
index 1dec326..40bbb93 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java
@@ -16,13 +16,15 @@
 
 package androidx.camera.extensions.impl.advanced;
 
-import android.annotation.SuppressLint;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.util.Range;
 import android.util.Size;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import java.util.List;
 import java.util.Map;
 
@@ -33,59 +35,82 @@
  *
  * @since 1.2
  */
-@SuppressLint("UnknownNullness")
 public class BeautyAdvancedExtenderImpl implements AdvancedExtenderImpl {
     public BeautyAdvancedExtenderImpl() {
     }
 
     @Override
-    public boolean isExtensionAvailable(String cameraId,
-            Map<String, CameraCharacteristics> characteristicsMap) {
+    public boolean isExtensionAvailable(@NonNull String cameraId,
+            @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
-    public void init(String cameraId,
-            Map<String, CameraCharacteristics> characteristicsMap) {
+    public void init(@NonNull String cameraId,
+            @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @Nullable
     public Range<Long> getEstimatedCaptureLatencyRange(
-            String cameraId, Size size, int imageFormat) {
+            @NonNull String cameraId, @Nullable Size size, int imageFormat) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(
-            String cameraId) {
+            @NonNull String cameraId) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(
-            String cameraId) {
+            @NonNull String cameraId) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
+    public Map<Integer, List<Size>> getSupportedPostviewResolutions(
+            @NonNull Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    @Nullable
     public List<Size> getSupportedYuvAnalysisResolutions(
-            String cameraId) {
+            @NonNull String cameraId) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public SessionProcessorImpl createSessionProcessor() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java
index bc41b4e..4093211 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java
@@ -16,13 +16,15 @@
 
 package androidx.camera.extensions.impl.advanced;
 
-import android.annotation.SuppressLint;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.util.Range;
 import android.util.Size;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import java.util.List;
 import java.util.Map;
 
@@ -33,59 +35,82 @@
  *
  * @since 1.2
  */
-@SuppressLint("UnknownNullness")
 public class BokehAdvancedExtenderImpl implements AdvancedExtenderImpl {
     public BokehAdvancedExtenderImpl() {
     }
 
     @Override
-    public boolean isExtensionAvailable(String cameraId,
-            Map<String, CameraCharacteristics> characteristicsMap) {
+    public boolean isExtensionAvailable(@NonNull String cameraId,
+            @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
-    public void init(String cameraId,
-            Map<String, CameraCharacteristics> characteristicsMap) {
+    public void init(@NonNull String cameraId,
+            @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @Nullable
     public Range<Long> getEstimatedCaptureLatencyRange(
-            String cameraId, Size size, int imageFormat) {
+            @NonNull String cameraId, @Nullable Size size, int imageFormat) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(
-            String cameraId) {
+            @NonNull String cameraId) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(
-            String cameraId) {
+            @NonNull String cameraId) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
-    public List<Size> getSupportedYuvAnalysisResolutions(
-            String cameraId) {
+    @NonNull
+    public Map<Integer, List<Size>> getSupportedPostviewResolutions(
+            @NonNull Size captureSize) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @Nullable
+    public List<Size> getSupportedYuvAnalysisResolutions(@NonNull String cameraId) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    @NonNull
     public SessionProcessorImpl createSessionProcessor() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    @NonNull
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImpl.java
index 61b01ef..e20ac87 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImpl.java
@@ -16,7 +16,7 @@
 
 package androidx.camera.extensions.impl.advanced;
 
-import android.annotation.SuppressLint;
+import androidx.annotation.Nullable;
 
 import java.util.List;
 
@@ -26,7 +26,6 @@
  *
  * @since 1.2
  */
-@SuppressLint("UnknownNullness")
 public interface Camera2OutputConfigImpl {
     /**
      * Gets thd id of this output config. The id can be used to identify the stream in vendor
@@ -43,11 +42,13 @@
     /**
      * Gets the physical camera id. Returns null if not specified.
      */
+    @Nullable
     String getPhysicalCameraId();
 
     /**
      * If non-null, enable surface sharing and add the surface constructed by the return
      * Camera2OutputConfig.
      */
+    @Nullable
     List<Camera2OutputConfigImpl> getSurfaceSharingOutputConfigs();
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java
index da7fd3a..8640874 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java
@@ -16,11 +16,13 @@
 
 package androidx.camera.extensions.impl.advanced;
 
-import android.annotation.SuppressLint;
 import android.hardware.camera2.params.OutputConfiguration;
 import android.util.Size;
 import android.view.Surface;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -28,7 +30,6 @@
 /**
  * A builder implementation to help OEM build the {@link Camera2OutputConfigImpl} instance.
  */
-@SuppressLint("UnknownNullness")
 public class Camera2OutputConfigImplBuilder {
     static AtomicInteger sLastId = new AtomicInteger(0);
     private OutputConfigImplImpl mOutputConfig;
@@ -37,7 +38,7 @@
     private String mPhysicalCameraId;
     private List<Camera2OutputConfigImpl> mSurfaceSharingConfigs;
 
-    private Camera2OutputConfigImplBuilder(OutputConfigImplImpl outputConfig) {
+    private Camera2OutputConfigImplBuilder(@NonNull OutputConfigImplImpl outputConfig) {
         mOutputConfig = outputConfig;
     }
 
@@ -49,8 +50,9 @@
      * Creates a {@link Camera2OutputConfigImpl} that represents a {@link android.media.ImageReader}
      * with the given parameters.
      */
+    @NonNull
     public static Camera2OutputConfigImplBuilder newImageReaderConfig(
-            Size size, int imageFormat, int maxImages) {
+            @NonNull Size size, int imageFormat, int maxImages) {
         return new Camera2OutputConfigImplBuilder(
             new ImageReaderOutputConfigImplImpl(size, imageFormat, maxImages));
     }
@@ -59,6 +61,7 @@
      * Creates a {@link Camera2OutputConfigImpl} that represents a MultiResolutionImageReader with
      * the given parameters.
      */
+    @NonNull
     public static Camera2OutputConfigImplBuilder newMultiResolutionImageReaderConfig(
             int imageFormat, int maxImages) {
         return new Camera2OutputConfigImplBuilder(
@@ -68,15 +71,17 @@
     /**
      * Creates a {@link Camera2OutputConfigImpl} that contains the Surface directly.
      */
-    public static Camera2OutputConfigImplBuilder newSurfaceConfig(Surface surface) {
+    @NonNull
+    public static Camera2OutputConfigImplBuilder newSurfaceConfig(@NonNull Surface surface) {
         return new Camera2OutputConfigImplBuilder(new SurfaceOutputConfigImplImpl(surface));
     }
 
     /**
      * Adds a {@link Camera2SessionConfigImpl} to be shared with current config.
      */
+    @NonNull
     public Camera2OutputConfigImplBuilder addSurfaceSharingOutputConfig(
-            Camera2OutputConfigImpl camera2OutputConfig) {
+            @NonNull Camera2OutputConfigImpl camera2OutputConfig) {
         if (mSurfaceSharingConfigs == null) {
             mSurfaceSharingConfigs = new ArrayList<>();
         }
@@ -88,7 +93,8 @@
     /**
      * Sets a physical camera id.
      */
-    public Camera2OutputConfigImplBuilder setPhysicalCameraId(String physicalCameraId) {
+    @NonNull
+    public Camera2OutputConfigImplBuilder setPhysicalCameraId(@Nullable String physicalCameraId) {
         mPhysicalCameraId = physicalCameraId;
         return this;
     }
@@ -96,6 +102,7 @@
     /**
      * Sets surface group id.
      */
+    @NonNull
     public Camera2OutputConfigImplBuilder setSurfaceGroupId(int surfaceGroupId) {
         mSurfaceGroupId = surfaceGroupId;
         return this;
@@ -104,6 +111,7 @@
     /**
      * Sets Output Config id (Optional: Atomic Integer will be used if this function is not called)
      */
+    @NonNull
     public Camera2OutputConfigImplBuilder setOutputConfigId(int outputConfigId) {
         mOutputConfigId = outputConfigId;
         return this;
@@ -112,6 +120,7 @@
     /**
      * Build a {@link Camera2OutputConfigImpl} instance.
      */
+    @NonNull
     public Camera2OutputConfigImpl build() {
         // Sets an output config id otherwise an output config id will be generated
         if (mOutputConfigId == -1) {
@@ -149,11 +158,13 @@
         }
 
         @Override
+        @Nullable
         public String getPhysicalCameraId() {
             return mPhysicalCameraId;
         }
 
         @Override
+        @Nullable
         public List<Camera2OutputConfigImpl> getSurfaceSharingOutputConfigs() {
             return mSurfaceSharingConfigs;
         }
@@ -166,25 +177,26 @@
             mSurfaceGroup = surfaceGroup;
         }
 
-        public void setPhysicalCameraId(String physicalCameraId) {
+        public void setPhysicalCameraId(@Nullable String physicalCameraId) {
             mPhysicalCameraId = physicalCameraId;
         }
 
         public void setSurfaceSharingConfigs(
-                List<Camera2OutputConfigImpl> surfaceSharingConfigs) {
+                @Nullable List<Camera2OutputConfigImpl> surfaceSharingConfigs) {
             mSurfaceSharingConfigs = surfaceSharingConfigs;
         }
     }
 
     private static class SurfaceOutputConfigImplImpl extends OutputConfigImplImpl
             implements SurfaceOutputConfigImpl {
-        private Surface mSurface;
+        private final Surface mSurface;
 
-        SurfaceOutputConfigImplImpl(Surface surface) {
+        SurfaceOutputConfigImplImpl(@NonNull Surface surface) {
             mSurface = surface;
         }
 
         @Override
+        @NonNull
         public Surface getSurface() {
             return mSurface;
         }
@@ -192,17 +204,18 @@
 
     private static class ImageReaderOutputConfigImplImpl extends OutputConfigImplImpl
             implements ImageReaderOutputConfigImpl {
-        private Size mSize;
-        private int mImageFormat;
-        private int mMaxImages;
+        private final Size mSize;
+        private final int mImageFormat;
+        private final int mMaxImages;
 
-        ImageReaderOutputConfigImplImpl(Size size, int imageFormat, int maxImages) {
+        ImageReaderOutputConfigImplImpl(@NonNull Size size, int imageFormat, int maxImages) {
             mSize = size;
             mImageFormat = imageFormat;
             mMaxImages = maxImages;
         }
 
         @Override
+        @NonNull
         public Size getSize() {
             return mSize;
         }
@@ -220,8 +233,8 @@
 
     private static class MultiResolutionImageReaderOutputConfigImplImpl extends OutputConfigImplImpl
             implements MultiResolutionImageReaderOutputConfigImpl {
-        private int mImageFormat;
-        private int mMaxImages;
+        private final int mImageFormat;
+        private final int mMaxImages;
 
         MultiResolutionImageReaderOutputConfigImplImpl(int imageFormat, int maxImages) {
             mImageFormat = imageFormat;
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java
index b470063..e0559fb 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java
@@ -16,9 +16,10 @@
 
 package androidx.camera.extensions.impl.advanced;
 
-import android.annotation.SuppressLint;
 import android.hardware.camera2.CaptureRequest;
 
+import androidx.annotation.NonNull;
+
 import java.util.List;
 import java.util.Map;
 
@@ -27,17 +28,18 @@
  *
  * @since 1.2
  */
-@SuppressLint("UnknownNullness")
 public interface Camera2SessionConfigImpl {
     /**
      * Returns all the {@link Camera2OutputConfigImpl}s that will be used to create
      * {@link android.hardware.camera2.params.OutputConfiguration}.
      */
+    @NonNull
     List<Camera2OutputConfigImpl> getOutputConfigs();
 
     /**
      * Gets all the parameters to create the session parameters with.
      */
+    @NonNull
     Map<CaptureRequest.Key<?>, Object> getSessionParameters();
 
     /**
@@ -45,4 +47,17 @@
      * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters}.
      */
     int getSessionTemplateId();
-}
+
+
+    /**
+     * Retrieves the session type to be used when initializing the
+     * {@link android.hardware.camera2.CameraCaptureSession}.
+     *
+     * @return Camera capture session type. Regular and vendor specific types are supported but
+     * not high speed values. The extension can return -1 in which case the camera capture session
+     * will be configured to use the default regular type.
+     *
+     * @since 1.4
+     */
+    int getSessionType();
+}
\ No newline at end of file
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java
index a301166..d1bb69a 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java
@@ -16,10 +16,11 @@
 
 package androidx.camera.extensions.impl.advanced;
 
-
-import android.annotation.SuppressLint;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.SessionConfiguration;
+
+import androidx.annotation.NonNull;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -29,9 +30,9 @@
 /**
  * A builder implementation to help OEM build the {@link Camera2SessionConfigImpl} instance.
  */
-@SuppressLint("UnknownNullness")
 public class Camera2SessionConfigImplBuilder {
     private int mSessionTemplateId = CameraDevice.TEMPLATE_PREVIEW;
+    private int mSessionType = SessionConfiguration.SESSION_REGULAR;
     Map<CaptureRequest.Key<?>, Object> mSessionParameters = new HashMap<>();
     List<Camera2OutputConfigImpl> mCamera2OutputConfigs = new ArrayList<>();
 
@@ -41,8 +42,9 @@
     /**
      * Adds a output config.
      */
+    @NonNull
     public Camera2SessionConfigImplBuilder addOutputConfig(
-            Camera2OutputConfigImpl outputConfig) {
+            @NonNull Camera2OutputConfigImpl outputConfig) {
         mCamera2OutputConfigs.add(outputConfig);
         return this;
     }
@@ -50,8 +52,9 @@
     /**
      * Sets session parameters.
      */
+    @NonNull
     public <T> Camera2SessionConfigImplBuilder addSessionParameter(
-            CaptureRequest.Key<T> key, T value) {
+            @NonNull CaptureRequest.Key<T> key, @NonNull T value) {
         mSessionParameters.put(key, value);
         return this;
     }
@@ -59,6 +62,7 @@
     /**
      * Sets the template id for session parameters request.
      */
+    @NonNull
     public Camera2SessionConfigImplBuilder setSessionTemplateId(int templateId) {
         mSessionTemplateId = templateId;
         return this;
@@ -74,6 +78,7 @@
     /**
      * Gets the session parameters.
      */
+    @NonNull
     public Map<CaptureRequest.Key<?>, Object> getSessionParameters() {
         return mSessionParameters;
     }
@@ -81,35 +86,48 @@
     /**
      * Gets all the output configs.
      */
+    @NonNull
     public List<Camera2OutputConfigImpl> getCamera2OutputConfigs() {
         return mCamera2OutputConfigs;
     }
 
     /**
+     * Gets the camera capture session type.
+     */
+    public int getSessionType() {
+        return mSessionType;
+    }
+
+    /**
      * Builds a {@link Camera2SessionConfigImpl} instance.
      */
+    @NonNull
     public Camera2SessionConfigImpl build() {
         return new Camera2SessionConfigImplImpl(this);
     }
 
     private static class Camera2SessionConfigImplImpl implements
             Camera2SessionConfigImpl {
-        int mSessionTemplateId;
-        Map<CaptureRequest.Key<?>, Object> mSessionParameters;
-        List<Camera2OutputConfigImpl> mCamera2OutputConfigs;
+        private final int mSessionTemplateId;
+        private final int mSessionType;
+        private final Map<CaptureRequest.Key<?>, Object> mSessionParameters;
+        private final List<Camera2OutputConfigImpl> mCamera2OutputConfigs;
 
-        Camera2SessionConfigImplImpl(Camera2SessionConfigImplBuilder builder) {
+        Camera2SessionConfigImplImpl(@NonNull Camera2SessionConfigImplBuilder builder) {
             mSessionTemplateId = builder.getSessionTemplateId();
             mSessionParameters = builder.getSessionParameters();
             mCamera2OutputConfigs = builder.getCamera2OutputConfigs();
+            mSessionType = builder.getSessionType();
         }
 
         @Override
+        @NonNull
         public List<Camera2OutputConfigImpl> getOutputConfigs() {
             return mCamera2OutputConfigs;
         }
 
         @Override
+        @NonNull
         public Map<CaptureRequest.Key<?>, Object> getSessionParameters() {
             return mSessionParameters;
         }
@@ -118,6 +136,11 @@
         public int getSessionTemplateId() {
             return mSessionTemplateId;
         }
+
+        @Override
+        public int getSessionType() {
+            return mSessionType;
+        }
     }
 }
 
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java
index 06157dc3..35bcdaf 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java
@@ -16,13 +16,15 @@
 
 package androidx.camera.extensions.impl.advanced;
 
-import android.annotation.SuppressLint;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.util.Range;
 import android.util.Size;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import java.util.List;
 import java.util.Map;
 
@@ -33,60 +35,82 @@
  *
  * @since 1.2
  */
-@SuppressLint("UnknownNullness")
 public class HdrAdvancedExtenderImpl implements AdvancedExtenderImpl {
     public HdrAdvancedExtenderImpl() {
     }
 
     @Override
-    public boolean isExtensionAvailable(String cameraId,
-            Map<String, CameraCharacteristics> characteristicsMap) {
+    public boolean isExtensionAvailable(@NonNull String cameraId,
+            @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
-    public void init(String cameraId,
-            Map<String, CameraCharacteristics> characteristicsMap) {
+    public void init(@NonNull String cameraId,
+            @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @Nullable
     public Range<Long> getEstimatedCaptureLatencyRange(
-            String cameraId, Size size, int imageFormat) {
+            @NonNull String cameraId, @Nullable Size size, int imageFormat) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(
-            String cameraId) {
+            @NonNull String cameraId) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
 
     @Override
+    @NonNull
     public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(
-            String cameraId) {
+            @NonNull String cameraId) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
-    public List<Size> getSupportedYuvAnalysisResolutions(
-            String cameraId) {
+    @NonNull
+    public Map<Integer, List<Size>> getSupportedPostviewResolutions(
+            @NonNull Size captureSize) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @Nullable
+    public List<Size> getSupportedYuvAnalysisResolutions(@NonNull String cameraId) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    @NonNull
     public SessionProcessorImpl createSessionProcessor() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
-}
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageProcessorImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageProcessorImpl.java
index 6209e0c..330f0a2a 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageProcessorImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageProcessorImpl.java
@@ -52,5 +52,5 @@
             long timestampNs,
             ImageReferenceImpl imageReference,
             String physicalCameraId
-            );
+    );
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java
index a58f8c4..2144588 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java
@@ -16,19 +16,20 @@
 
 package androidx.camera.extensions.impl.advanced;
 
-import android.annotation.SuppressLint;
 import android.util.Size;
 
+import androidx.annotation.NonNull;
+
 /**
  * Surface will be created by constructing a ImageReader.
  *
  * @since 1.2
  */
-@SuppressLint("UnknownNullness")
 public interface ImageReaderOutputConfigImpl extends Camera2OutputConfigImpl {
     /**
      * Returns the size of the surface.
      */
+    @NonNull
     Size getSize();
 
     /**
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageReferenceImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageReferenceImpl.java
index aafba7d..647fbf9 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageReferenceImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageReferenceImpl.java
@@ -16,9 +16,10 @@
 
 package androidx.camera.extensions.impl.advanced;
 
-import android.annotation.SuppressLint;
 import android.media.Image;
 
+import androidx.annotation.Nullable;
+
 /**
  * A Image reference container that enables the Image sharing between Camera2/CameraX and OEM
  * using reference counting. The wrapped Image will be closed once the reference count
@@ -28,7 +29,6 @@
  *
  * @since 1.2
  */
-@SuppressLint("UnknownNullness")
 public interface ImageReferenceImpl {
 
     /**
@@ -48,5 +48,6 @@
      * Return the Android image. This object MUST not be closed directly.
      * Returns null when the reference count is zero.
      */
+    @Nullable
     Image get();
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java
index 97da5c1..5c32de8 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java
@@ -16,13 +16,15 @@
 
 package androidx.camera.extensions.impl.advanced;
 
-import android.annotation.SuppressLint;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.util.Range;
 import android.util.Size;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import java.util.List;
 import java.util.Map;
 
@@ -33,59 +35,82 @@
  *
  * @since 1.2
  */
-@SuppressLint("UnknownNullness")
 public class NightAdvancedExtenderImpl implements AdvancedExtenderImpl {
     public NightAdvancedExtenderImpl() {
     }
 
     @Override
-    public boolean isExtensionAvailable(String cameraId,
-            Map<String, CameraCharacteristics> characteristicsMap) {
+    public boolean isExtensionAvailable(@NonNull String cameraId,
+            @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
-    public void init(String cameraId,
-            Map<String, CameraCharacteristics> characteristicsMap) {
+    public void init(@NonNull String cameraId,
+            @NonNull Map<String, CameraCharacteristics> characteristicsMap) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @Nullable
     public Range<Long> getEstimatedCaptureLatencyRange(
-            String cameraId, Size size, int imageFormat) {
+            @NonNull String cameraId, @Nullable Size size, int imageFormat) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(
-            String cameraId) {
+            @NonNull String cameraId) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(
-            String cameraId) {
+            @NonNull String cameraId) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
+    public Map<Integer, List<Size>> getSupportedPostviewResolutions(
+            @NonNull Size captureSize) {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    @Nullable
     public List<Size> getSupportedYuvAnalysisResolutions(
-            String cameraId) {
+            @NonNull String cameraId) {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public SessionProcessorImpl createSessionProcessor() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
 
     @Override
+    @NonNull
     public List<CaptureResult.Key> getAvailableCaptureResultKeys() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public boolean isCaptureProcessProgressAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
+
+    @Override
+    public boolean isPostviewAvailable() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceConfigurationImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceConfigurationImpl.java
new file mode 100644
index 0000000..2f8a70b
--- /dev/null
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceConfigurationImpl.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.extensions.impl.advanced;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * For specifying the output surface configurations for the extension.
+ *
+ * @since 1.4
+ */
+public interface OutputSurfaceConfigurationImpl {
+    /**
+     * gets the preview {@link OutputSurfaceImpl}.
+     */
+    @NonNull
+    OutputSurfaceImpl getPreviewOutputSurface();
+
+    /**
+     * gets the still capture {@link OutputSurfaceImpl}.
+     */
+    @NonNull
+    OutputSurfaceImpl getImageCaptureOutputSurface();
+
+    /**
+     * gets the image analysis {@link OutputSurfaceImpl}.
+     */
+    @Nullable
+    OutputSurfaceImpl getImageAnalysisOutputSurface();
+
+    /**
+     * gets the postview {@link OutputSurfaceImpl}.
+     */
+    @Nullable
+    OutputSurfaceImpl getPostviewOutputSurface();
+}
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java
index fe93f85..621ea695 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java
@@ -16,25 +16,28 @@
 
 package androidx.camera.extensions.impl.advanced;
 
-import android.annotation.SuppressLint;
 import android.util.Size;
 import android.view.Surface;
 
+import androidx.annotation.NonNull;
+
 /**
  * For specifying output surface of the extension.
  *
  * @since 1.2
  */
-@SuppressLint("UnknownNullness")
 public interface OutputSurfaceImpl {
     /**
      * Gets the surface.
      */
+    @NonNull
     Surface getSurface();
 
+
     /**
      * Gets the size.
      */
+    @NonNull
     Size getSize();
 
     /**
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/RequestProcessorImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/RequestProcessorImpl.java
index 003f105..5fc8a6f 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/RequestProcessorImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/RequestProcessorImpl.java
@@ -16,12 +16,13 @@
 
 package androidx.camera.extensions.impl.advanced;
 
-import android.annotation.SuppressLint;
 import android.hardware.camera2.CaptureFailure;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.TotalCaptureResult;
 
+import androidx.annotation.NonNull;
+
 import java.util.List;
 import java.util.Map;
 
@@ -30,33 +31,32 @@
  *
  * @since 1.2
  */
-@SuppressLint("UnknownNullness")
 public interface RequestProcessorImpl {
     /**
      * Sets a {@link ImageProcessorImpl} to receive {@link ImageReferenceImpl} to process.
      */
-    void setImageProcessor(int outputconfigId, ImageProcessorImpl imageProcessor);
+    void setImageProcessor(int outputconfigId, @NonNull ImageProcessorImpl imageProcessor);
 
     /**
      * Submits a request.
      * @return the id of the capture sequence or -1 in case the processor encounters a fatal error
      *         or receives an invalid argument.
      */
-    int submit(Request request, Callback callback);
+    int submit(@NonNull Request request, @NonNull Callback callback);
 
     /**
      * Submits a list of requests.
      * @return the id of the capture sequence or -1 in case the processor encounters a fatal error
      *         or receives an invalid argument.
      */
-    int submit(List<Request> requests, Callback callback);
+    int submit(@NonNull List<Request> requests, @NonNull Callback callback);
 
     /**
      * Set repeating requests.
      * @return the id of the capture sequence or -1 in case the processor encounters a fatal error
      *         or receives an invalid argument.
      */
-    int setRepeating(Request request, Callback callback);
+    int setRepeating(@NonNull Request request, @NonNull Callback callback);
 
 
     /**
@@ -78,16 +78,19 @@
          * Gets the target ids of {@link Camera2OutputConfigImpl} which identifies corresponding
          * Surface to be the targeted for the request.
          */
+        @NonNull
         List<Integer> getTargetOutputConfigIds();
 
         /**
          * Gets all the parameters.
          */
+        @NonNull
         Map<CaptureRequest.Key<?>, Object> getParameters();
 
         /**
          * Gets the template id.
          */
+        @NonNull
         Integer getTemplateId();
     }
 
@@ -96,24 +99,24 @@
      */
     interface Callback {
         void onCaptureStarted(
-                Request request,
+                @NonNull Request request,
                 long frameNumber,
                 long timestamp);
 
         void onCaptureProgressed(
-                Request request,
-                CaptureResult partialResult);
+                @NonNull Request request,
+                @NonNull CaptureResult partialResult);
 
         void onCaptureCompleted(
-                Request request,
-                TotalCaptureResult totalCaptureResult);
+                @NonNull Request request,
+                @NonNull TotalCaptureResult totalCaptureResult);
 
         void onCaptureFailed(
-                Request request,
-                CaptureFailure captureFailure);
+                @NonNull Request request,
+                @NonNull CaptureFailure captureFailure);
 
         void onCaptureBufferLost(
-                Request request,
+                @NonNull Request request,
                 long frameNumber,
                 int outputStreamId);
 
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java
index b87f4bd..9875af2 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java
@@ -16,13 +16,16 @@
 
 package androidx.camera.extensions.impl.advanced;
 
-import android.annotation.SuppressLint;
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
+import android.util.Pair;
 import android.view.Surface;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import java.util.Map;
 
 /**
@@ -59,16 +62,66 @@
  *
  * @since 1.2
  */
-@SuppressLint("UnknownNullness")
 public interface SessionProcessorImpl {
     /**
      * Initializes the session for the extension. This is where the OEMs allocate resources for
      * preparing a CameraCaptureSession. After initSession() is called, the camera ID,
      * cameraCharacteristics and context will not change until deInitSession() has been called.
      *
-     * <p>CameraX specifies the output surface configurations for preview, image capture and image
-     * analysis[optional]. And OEM returns a {@link Camera2SessionConfigImpl} which consists of a
-     * list of {@link Camera2OutputConfigImpl} and session parameters. The
+     * <p>CameraX / Camera2 specifies the output surface configurations for preview using
+     * {@link OutputSurfaceConfigurationImpl#getPreviewOutputSurface}, image capture using
+     * {@link OutputSurfaceConfigurationImpl#getImageCaptureOutputSurface}, and image analysis
+     * [optional] using {@link OutputSurfaceConfigurationImpl#getImageAnalysisOutputSurface}.
+     * And OEM returns a {@link Camera2SessionConfigImpl} which consists of a list of
+     * {@link Camera2OutputConfigImpl} and session parameters. The {@link Camera2SessionConfigImpl}
+     * will be used to configure the CameraCaptureSession.
+     *
+     * <p>OEM is responsible for outputting correct camera images output to these output surfaces.
+     * OEM can have the following options to enable the output:
+     * <pre>
+     * (1) Add these output surfaces in CameraCaptureSession directly using
+     * {@link Camera2OutputConfigImplBuilder#newSurfaceConfig(Surface)} }. Processing is done in
+     * HAL.
+     *
+     * (2) Use surface sharing with other surface by calling
+     * {@link Camera2OutputConfigImplBuilder#addSurfaceSharingOutputConfig(Camera2OutputConfigImpl)}
+     * to add the output surface to the other {@link Camera2OutputConfigImpl}.
+     *
+     * (3) Process output from other surfaces (RAW, YUV..) and write the result to the output
+     * surface. The output surface won't be contained in the returned
+     * {@link Camera2SessionConfigImpl}.
+     * </pre>
+     *
+     * <p>{@link Camera2OutputConfigImplBuilder} and {@link Camera2SessionConfigImplBuilder}
+     * implementations are provided in the stub for OEM to construct the
+     * {@link Camera2OutputConfigImpl} and {@link Camera2SessionConfigImpl} instances.
+     *
+     * @param surfaceConfigs contains output surfaces for preview, image capture, and an
+     *                       optional output config for image analysis (YUV_420_888).
+     * @return a {@link Camera2SessionConfigImpl} consisting of a list of
+     * {@link Camera2OutputConfigImpl} and session parameters which will decide the
+     * {@link android.hardware.camera2.params.SessionConfiguration} for configuring the
+     * CameraCaptureSession. Please note that the OutputConfiguration list may not be part of any
+     * supported or mandatory stream combination BUT OEM must ensure this list will always
+     * produce a valid camera capture session.
+     *
+     * @since 1.4
+     */
+    @NonNull
+    Camera2SessionConfigImpl initSession(
+            @NonNull String cameraId,
+            @NonNull Map<String, CameraCharacteristics> cameraCharacteristicsMap,
+            @NonNull Context context,
+            @NonNull OutputSurfaceConfigurationImpl surfaceConfigs);
+
+    /**
+     * Initializes the session for the extension. This is where the OEMs allocate resources for
+     * preparing a CameraCaptureSession. After initSession() is called, the camera ID,
+     * cameraCharacteristics and context will not change until deInitSession() has been called.
+     *
+     * <p>CameraX / Camera 2 specifies the output surface configurations for preview, image capture
+     * and image analysis[optional]. And OEM returns a {@link Camera2SessionConfigImpl} which
+     * consists of a list of {@link Camera2OutputConfigImpl} and session parameters. The
      * {@link Camera2SessionConfigImpl} will be used to configure the CameraCaptureSession.
      *
      * <p>OEM is responsible for outputting correct camera images output to these output surfaces.
@@ -102,13 +155,14 @@
      * supported or mandatory stream combination BUT OEM must ensure this list will always
      * produce a valid camera capture session.
      */
+    @NonNull
     Camera2SessionConfigImpl initSession(
-            String cameraId,
-            Map<String, CameraCharacteristics> cameraCharacteristicsMap,
-            Context context,
-            OutputSurfaceImpl previewSurfaceConfig,
-            OutputSurfaceImpl imageCaptureSurfaceConfig,
-            OutputSurfaceImpl imageAnalysisSurfaceConfig);
+            @NonNull String cameraId,
+            @NonNull Map<String, CameraCharacteristics> cameraCharacteristicsMap,
+            @NonNull Context context,
+            @NonNull OutputSurfaceImpl previewSurfaceConfig,
+            @NonNull OutputSurfaceImpl imageCaptureSurfaceConfig,
+            @Nullable OutputSurfaceImpl imageAnalysisSurfaceConfig);
 
     /**
      * Notify to de-initialize the extension. This callback will be invoked after
@@ -123,7 +177,7 @@
      * expected that the OEM would (eventually) update the repeating request if the keys are
      * supported. Setting a value to null explicitly un-sets the value.
      */
-    void setParameters(Map<CaptureRequest.Key<?>, Object> parameters);
+    void setParameters(@NonNull Map<CaptureRequest.Key<?>, Object> parameters);
 
     /**
      * CameraX / Camera2 will call this interface in response to client requests involving
@@ -139,7 +193,8 @@
      *
      * @since 1.3
      */
-    int startTrigger(Map<CaptureRequest.Key<?>, Object> triggers, CaptureCallback callback);
+    int startTrigger(@NonNull Map<CaptureRequest.Key<?>, Object> triggers,
+            @NonNull CaptureCallback callback);
 
     /**
      * This will be invoked once after the {@link android.hardware.camera2.CameraCaptureSession}
@@ -147,7 +202,7 @@
      * requests or set repeating requests. This ExtensionRequestProcessor will be valid to use
      * until onCaptureSessionEnd is called.
      */
-    void onCaptureSessionStart(RequestProcessorImpl requestProcessor);
+    void onCaptureSessionStart(@NonNull RequestProcessorImpl requestProcessor);
 
     /**
      * This will be invoked before the {@link android.hardware.camera2.CameraCaptureSession} is
@@ -164,7 +219,7 @@
      * @param callback a callback to report the status.
      * @return the id of the capture sequence.
      */
-    int startRepeating(CaptureCallback callback);
+    int startRepeating(@NonNull CaptureCallback callback);
 
     /**
      * Stop the repeating request. To prevent OEM from not calling stopRepeating, CameraX will
@@ -187,7 +242,27 @@
      * @param callback a callback to report the status.
      * @return the id of the capture sequence.
      */
-    int startCapture(CaptureCallback callback);
+    int startCapture(@NonNull CaptureCallback callback);
+
+    /**
+     * Start a multi-frame capture with a postview. {@link #startCapture(CaptureCallback)}
+     * will be used for captures without a postview request.
+     *
+     * Postview will be available before the capture. Upon postview completion,
+     * {@code OnImageAvailableListener#onImageAvailable} will be called on the ImageReader
+     * that creates the postview output surface. When the capture is completed,
+     * {@link CaptureCallback#onCaptureSequenceCompleted} is called and
+     * {@code OnImageAvailableListener#onImageAvailable} will also be called on the ImageReader
+     * that creates the image capture output surface.
+     *
+     * <p>Only one capture can perform at a time. Starting a capture when another capture is
+     * running will cause onCaptureFailed to be called immediately.
+     *
+     * @param callback a callback to report the status.
+     * @return the id of the capture sequence.
+     * @since 1.4
+     */
+    int startCaptureWithPostview(@NonNull CaptureCallback callback);
 
     /**
      * Abort all capture tasks.
@@ -195,6 +270,30 @@
     void abortCapture(int captureSequenceId);
 
     /**
+     * Returns the dynamically calculated capture latency pair in milliseconds.
+     *
+     * <p>In contrast to {@link AdvancedExtenderImpl#getEstimatedCaptureLatencyRange} this method is
+     * guaranteed to be called after {@link #onCaptureSessionStart}.
+     * The measurement is expected to take in to account dynamic parameters such as the current
+     * scene, the state of 3A algorithms, the state of internal HW modules and return a more
+     * accurate assessment of the still capture latency.</p>
+     *
+     * @return pair that includes the estimated input frame/frames camera capture latency as the
+     * first field. This is the time between {@link #onCaptureStarted} and
+     * {@link #onCaptureProcessStarted}. The second field value includes the estimated
+     * post-processing latency. This is the time between {@link #onCaptureProcessStarted} until
+     * the processed frame returns back to the client registered surface.
+     * Both first and second values will be in milliseconds. The total still capture latency will be
+     * the sum of both the first and second values of the pair.
+     * The pair is expected to be null if the dynamic latency estimation is not supported.
+     * If clients have not configured a still capture output, then this method can also return a
+     * null pair.
+     * @since 1.4
+     */
+    @Nullable
+    Pair<Long, Long> getRealtimeCaptureLatency();
+
+    /**
      * Callback for notifying the status of {@link #startCapture(CaptureCallback)} and
      * {@link #startRepeating(CaptureCallback)}.
      */
@@ -276,10 +375,23 @@
          *                             as part of this callback. Both Camera2 and CameraX guarantee
          *                             that those two settings and results are always supported and
          *                             applied by the corresponding framework.
-         *
-         * @since 1.3
          */
         void onCaptureCompleted(long timestamp, int captureSequenceId,
-                Map<CaptureResult.Key, Object> result);
+                @NonNull Map<CaptureResult.Key, Object> result);
+
+        /**
+         * Capture progress callback that needs to be called when the process capture is
+         * ongoing and includes the estimated progress of the processing.
+         *
+         * <p>Extensions must ensure that they always call this callback with monotonically
+         * increasing values.</p>
+         *
+         * <p>Extensions are allowed to trigger this callback multiple times but at the minimum the
+         * callback is expected to be called once when processing is done with value 100.</p>
+         *
+         * @param progress             Value between 0 and 100.
+         * @since 1.4
+         */
+        void onCaptureProcessProgressed(int progress);
     }
-}
+}
\ No newline at end of file
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/SurfaceOutputConfigImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/SurfaceOutputConfigImpl.java
index 1a23ce0..72e8816 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/SurfaceOutputConfigImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/SurfaceOutputConfigImpl.java
@@ -16,18 +16,19 @@
 
 package androidx.camera.extensions.impl.advanced;
 
-import android.annotation.SuppressLint;
 import android.view.Surface;
 
+import androidx.annotation.NonNull;
+
 /**
  * Use Surface directly to create the OutputConfiguration.
  *
  * @since 1.2
  */
-@SuppressLint("UnknownNullness")
 public interface SurfaceOutputConfigImpl extends Camera2OutputConfigImpl {
     /**
      * Get the {@link Surface}. It'll return valid surface only when type is TYPE_SURFACE.
      */
+    @NonNull
     Surface getSurface();
 }
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/AdvancedSessionProcessor.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/AdvancedSessionProcessor.java
index 618f7b3..ac7b358 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/AdvancedSessionProcessor.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/AdvancedSessionProcessor.java
@@ -489,5 +489,9 @@
                 Map<CaptureResult.Key, Object> result) {
             mCaptureCallback.onCaptureCompleted(timestamp, captureSequenceId, result);
         }
+
+        @Override
+        public void onCaptureProcessProgressed(int progress) {
+        }
     }
 }
diff --git a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/sessionprocessor/Camera2OutputConfigConverterTest.kt b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/sessionprocessor/Camera2OutputConfigConverterTest.kt
index 17d520a..e55cb1f 100644
--- a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/sessionprocessor/Camera2OutputConfigConverterTest.kt
+++ b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/sessionprocessor/Camera2OutputConfigConverterTest.kt
@@ -149,7 +149,7 @@
         assertThat(outputConfig.surfaceSharingOutputConfigs.size)
             .isEqualTo(outputConfigImpl.surfaceSharingOutputConfigs?.size ?: 0)
         outputConfig.surfaceSharingOutputConfigs.forEachIndexed { index, sharingConfig ->
-            val sharedConfigImpl = outputConfigImpl.surfaceSharingOutputConfigs[index]
+            val sharedConfigImpl = outputConfigImpl.surfaceSharingOutputConfigs!![index]
             assertOutputConfigElements(sharingConfig, sharedConfigImpl)
         }
 
diff --git a/camera/camera-testing/build.gradle b/camera/camera-testing/build.gradle
index 1f00552..0a6597be 100644
--- a/camera/camera-testing/build.gradle
+++ b/camera/camera-testing/build.gradle
@@ -93,7 +93,7 @@
 androidx {
     name = "Jetpack Camera Testing Library"
     type = LibraryType.INTERNAL_TEST_LIBRARY
-    publish = Publish.NONE
+    publish = Publish.SNAPSHOT_ONLY
     inceptionYear = "2019"
     description = "Testing components for the Jetpack Camera Library, a library providing a " +
             "consistent and reliable camera foundation that enables great camera driven " +"" +
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java b/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java
index a87cc14..198e8c24 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java
@@ -1040,6 +1040,7 @@
     public static TestRule grantCameraPermissionAndPreTest(@Nullable PreTestCamera cameraTestRule,
             @Nullable PreTestCameraIdList cameraIdListTestRule) {
         RuleChain rule = RuleChain.outerRule(GrantPermissionRule.grant(Manifest.permission.CAMERA));
+        rule = rule.around(new IgnoreProblematicDeviceRule());
         if (cameraIdListTestRule != null) {
             rule = rule.around(cameraIdListTestRule);
         }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/EncoderProfilesUtil.java b/camera/camera-testing/src/main/java/androidx/camera/testing/EncoderProfilesUtil.java
index 4c84b93..170b11b 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/EncoderProfilesUtil.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/EncoderProfilesUtil.java
@@ -155,7 +155,25 @@
             int videoFrameWidth,
             int videoFrameHeight
     ) {
-        VideoProfileProxy videoProfile = VideoProfileProxy.create(
+        VideoProfileProxy videoProfile = createFakeVideoProfileProxy(videoFrameWidth,
+                videoFrameHeight);
+        AudioProfileProxy audioProfile = createFakeAudioProfileProxy();
+
+        return ImmutableEncoderProfilesProxy.create(
+                DEFAULT_DURATION,
+                DEFAULT_OUTPUT_FORMAT,
+                Collections.singletonList(audioProfile),
+                Collections.singletonList(videoProfile)
+        );
+    }
+
+    /** A utility method to create a VideoProfileProxy with some default values. */
+    @NonNull
+    public static VideoProfileProxy createFakeVideoProfileProxy(
+            int videoFrameWidth,
+            int videoFrameHeight
+    ) {
+        return VideoProfileProxy.create(
                 DEFAULT_VIDEO_CODEC,
                 DEFAULT_VIDEO_MEDIA_TYPE,
                 DEFAULT_VIDEO_BITRATE,
@@ -167,7 +185,12 @@
                 DEFAULT_VIDEO_CHROMA_SUBSAMPLING,
                 DEFAULT_VIDEO_HDR_FORMAT
         );
-        AudioProfileProxy audioProfile = AudioProfileProxy.create(
+    }
+
+    /** A utility method to create an AudioProfileProxy with some default values. */
+    @NonNull
+    public static AudioProfileProxy createFakeAudioProfileProxy() {
+        return AudioProfileProxy.create(
                 DEFAULT_AUDIO_CODEC,
                 DEFAULT_AUDIO_MEDIA_TYPE,
                 DEFAULT_AUDIO_BITRATE,
@@ -175,13 +198,6 @@
                 DEFAULT_AUDIO_CHANNELS,
                 DEFAULT_AUDIO_PROFILE
         );
-
-        return ImmutableEncoderProfilesProxy.create(
-                DEFAULT_DURATION,
-                DEFAULT_OUTPUT_FORMAT,
-                Collections.singletonList(audioProfile),
-                Collections.singletonList(videoProfile)
-        );
     }
 
     // This class is not instantiable.
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/IgnoreProblematicDeviceRule.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/IgnoreProblematicDeviceRule.kt
new file mode 100644
index 0000000..32d1274
--- /dev/null
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/IgnoreProblematicDeviceRule.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.camera.testing
+
+import android.os.Build
+import android.util.Log
+import androidx.annotation.RequiresApi
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import org.junit.AssumptionViolatedException
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * Test class to set the TestRule should not be run on the problematic devices.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+class IgnoreProblematicDeviceRule : TestRule {
+    private var avdName: String = try {
+        val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+        device.executeShellCommand("getprop ro.kernel.qemu.avd_name").filterNot {
+            it == '_' || it == '-' || it == ' '
+        }
+    } catch (e: Exception) {
+        Log.d("ProblematicDeviceRule", "Cannot get avd name", e)
+        ""
+    }
+
+    private val pixel2Api26Emulator = isEmulator && avdName.contains(
+        "Pixel2", ignoreCase = true
+    ) && Build.VERSION.SDK_INT == Build.VERSION_CODES.O
+
+    private val api21Emulator = isEmulator && Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP
+
+    private val isProblematicDevices = pixel2Api26Emulator || api21Emulator
+
+    override fun apply(base: Statement, description: Description): Statement {
+        return object : Statement() {
+            override fun evaluate() {
+                if (isProblematicDevices) {
+                    throw AssumptionViolatedException(
+                        "CameraDevice of the emulator may not be well prepared for camera" +
+                            " related tests. Ignore the test: " + description.displayName +
+                            ". To test on emulator devices, please remove the " +
+                            "IgnoreProblematicDeviceRule from the test class."
+                    )
+                } else {
+                    base.evaluate()
+                }
+            }
+        }
+    }
+
+    companion object {
+        // Sync from TestRequestBuilder.RequiresDeviceFilter
+        private const val EMULATOR_HARDWARE_GOLDFISH = "goldfish"
+        private const val EMULATOR_HARDWARE_RANCHU = "ranchu"
+        private const val EMULATOR_HARDWARE_GCE = "gce_x86"
+        private val emulatorHardwareNames: Set<String> = setOf(
+            EMULATOR_HARDWARE_GOLDFISH,
+            EMULATOR_HARDWARE_RANCHU,
+            EMULATOR_HARDWARE_GCE
+        )
+
+        private val isEmulator = emulatorHardwareNames.contains(Build.HARDWARE.lowercase())
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/LabTestRule.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/LabTestRule.kt
index dae1356..dd5ac4e 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/LabTestRule.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/LabTestRule.kt
@@ -18,6 +18,7 @@
 
 import android.util.Log
 import androidx.annotation.RequiresApi
+import androidx.camera.core.CameraSelector
 import androidx.camera.testing.LabTestRule.LabTestFrontCamera
 import androidx.camera.testing.LabTestRule.LabTestOnly
 import androidx.camera.testing.LabTestRule.LabTestRearCamera
@@ -142,5 +143,23 @@
         fun isInLabTest(): Boolean {
             return Log.isLoggable("MH", Log.DEBUG)
         }
+
+        /**
+         * Checks if it is CameraX lab environment where the enabled camera uses the specified
+         * [lensFacing] direction.
+         *
+         * For example, if [lensFacing] is [CameraSelector.LENS_FACING_BACK], this method will
+         * return true if the rear camera is enabled on a device in CameraX lab environment.
+         *
+         * @param lensFacing the required camera direction relative to the device screen.
+         * @return if enabled camera is in same direction as [lensFacing] in CameraX lab environment
+         */
+        @JvmStatic
+        fun isLensFacingEnabledInLabTest(@CameraSelector.LensFacing lensFacing: Int) =
+            when (lensFacing) {
+                CameraSelector.LENS_FACING_BACK -> Log.isLoggable("rearCameraE2E", Log.DEBUG)
+                CameraSelector.LENS_FACING_FRONT -> Log.isLoggable("frontCameraE2E", Log.DEBUG)
+                else -> false
+            }
     }
 }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java
index cd5d637..b3ff181 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java
@@ -30,6 +30,7 @@
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.impl.AttachedSurfaceInfo;
 import androidx.camera.core.impl.CameraDeviceSurfaceManager;
+import androidx.camera.core.impl.CameraMode;
 import androidx.camera.core.impl.StreamSpec;
 import androidx.camera.core.impl.SurfaceConfig;
 import androidx.camera.core.impl.UseCaseConfig;
@@ -69,17 +70,9 @@
     }
 
     @Override
-    public boolean checkSupported(
-            boolean isConcurrentCameraModeOn,
-            @NonNull String cameraId,
-            @Nullable List<SurfaceConfig> surfaceConfigList) {
-        return false;
-    }
-
-    @Override
     @Nullable
     public SurfaceConfig transformSurfaceConfig(
-            boolean isConcurrentCameraModeOn,
+            @CameraMode.Mode int cameraMode,
             @NonNull String cameraId,
             int imageFormat,
             @NonNull Size size) {
@@ -92,7 +85,7 @@
     @NonNull
     @Override
     public Map<UseCaseConfig<?>, StreamSpec> getSuggestedStreamSpecs(
-            boolean isConcurrentCameraModeOn, @NonNull String cameraId,
+            @CameraMode.Mode int cameraMode, @NonNull String cameraId,
             @NonNull List<AttachedSurfaceInfo> existingSurfaces,
             @NonNull Map<UseCaseConfig<?>, List<Size>> newUseCaseConfigsSupportedSizeMap) {
         List<UseCaseConfig<?>> newUseCaseConfigs =
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java
index 29855d5..41abf57 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java
@@ -16,6 +16,8 @@
 
 package androidx.camera.testing.fakes;
 
+import static androidx.camera.core.DynamicRange.SDR;
+
 import android.util.Range;
 import android.util.Rational;
 import android.util.Size;
@@ -26,6 +28,7 @@
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.CameraSelector;
 import androidx.camera.core.CameraState;
+import androidx.camera.core.DynamicRange;
 import androidx.camera.core.ExposureState;
 import androidx.camera.core.FocusMeteringAction;
 import androidx.camera.core.TorchState;
@@ -47,8 +50,10 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
@@ -64,6 +69,7 @@
                     new Range<>(30, 30),
                     new Range<>(60, 60))
     );
+    private static final Set<DynamicRange> DEFAULT_DYNAMIC_RANGES = Collections.singleton(SDR);
     private final String mCameraId;
     private final int mSensorRotation;
     @CameraSelector.LensFacing
@@ -73,6 +79,8 @@
     private final Map<Integer, List<Size>> mSupportedResolutionMap = new HashMap<>();
     private final Map<Integer, List<Size>> mSupportedHighResolutionMap = new HashMap<>();
     private MutableLiveData<CameraState> mCameraStateLiveData;
+
+    private final Set<DynamicRange> mSupportedDynamicRanges = new HashSet<>(DEFAULT_DYNAMIC_RANGES);
     private String mImplementationType = IMPLEMENTATION_TYPE_FAKE;
 
     // Leave uninitialized to support camera-core:1.0.0 dependencies.
@@ -206,6 +214,12 @@
         return resolutions != null ? resolutions : Collections.emptyList();
     }
 
+    @NonNull
+    @Override
+    public Set<DynamicRange> getSupportedDynamicRanges() {
+        return mSupportedDynamicRanges;
+    }
+
     @Override
     public void addSessionCaptureCallback(@NonNull Executor executor,
             @NonNull CameraCaptureCallback callback) {
@@ -294,6 +308,12 @@
         mIntrinsicZoomRatio = zoomRatio;
     }
 
+    /** Set the supported dynamic ranges for testing */
+    public void setSupportedDynamicRanges(@NonNull Set<DynamicRange> dynamicRanges) {
+        mSupportedDynamicRanges.clear();
+        mSupportedDynamicRanges.addAll(dynamicRanges);
+    }
+
     @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
     static final class FakeExposureState implements ExposureState {
         @Override
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java
index 2267928..88ae896 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeUseCaseConfig.java
@@ -25,7 +25,6 @@
 import androidx.annotation.RequiresApi;
 import androidx.camera.core.CameraSelector;
 import androidx.camera.core.MirrorMode;
-import androidx.camera.core.ResolutionSelector;
 import androidx.camera.core.UseCase;
 import androidx.camera.core.impl.CaptureConfig;
 import androidx.camera.core.impl.Config;
@@ -36,6 +35,7 @@
 import androidx.camera.core.impl.SessionConfig;
 import androidx.camera.core.impl.UseCaseConfig;
 import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType;
+import androidx.camera.core.resolutionselector.ResolutionSelector;
 
 import java.util.List;
 import java.util.UUID;
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/mocks/MockConsumer.java b/camera/camera-testing/src/main/java/androidx/camera/testing/mocks/MockConsumer.java
index cdd4e39..575215b 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/mocks/MockConsumer.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/mocks/MockConsumer.java
@@ -183,11 +183,11 @@
         mClassTypeToVerify = classType;
         mCallTimes = callTimes;
         mInOrder = inOrder;
-        snapshotVerifyingEventList();
 
         CountDownLatch latch = null;
         boolean isVerified;
         synchronized (mLock) {
+            snapshotVerifyingEventList();
             isVerified = isVerified();
             if (!isVerified && timeoutInMillis != NO_TIMEOUT) {
                 latch = mLatch = new CountDownLatch(1);
diff --git a/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java b/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java
index 0f3fa1c..6d549cf 100644
--- a/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java
+++ b/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManagerTest.java
@@ -33,6 +33,7 @@
 import androidx.annotation.NonNull;
 import androidx.camera.core.ImageAnalysis;
 import androidx.camera.core.impl.AttachedSurfaceInfo;
+import androidx.camera.core.impl.CameraMode;
 import androidx.camera.core.impl.StreamSpec;
 import androidx.camera.core.impl.SurfaceConfig;
 import androidx.camera.core.impl.UseCaseConfig;
@@ -88,7 +89,7 @@
         UseCaseConfig<?> preview = new FakeUseCaseConfig.Builder().getUseCaseConfig();
         UseCaseConfig<?> analysis = new ImageAnalysis.Builder().getUseCaseConfig();
         mFakeCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
-                /* isConcurrentCameraModeOn = */false,
+                CameraMode.DEFAULT,
                 FAKE_CAMERA_ID0,
                 emptyList(),
                 createConfigOutputSizesMap(preview, analysis));
@@ -104,7 +105,7 @@
                         new Size(1, 1),
                         new Range<>(30, 30));
         mFakeCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
-                /* isConcurrentCameraModeOn = */false,
+                CameraMode.DEFAULT,
                 FAKE_CAMERA_ID0,
                 singletonList(analysis), createConfigOutputSizesMap(preview, video));
     }
@@ -115,7 +116,7 @@
         UseCaseConfig<?> video = new FakeUseCaseConfig.Builder().getUseCaseConfig();
         UseCaseConfig<?> analysis = new ImageAnalysis.Builder().getUseCaseConfig();
         mFakeCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
-                /* isConcurrentCameraModeOn = */false,
+                CameraMode.DEFAULT,
                 FAKE_CAMERA_ID0,
                 Collections.emptyList(), createConfigOutputSizesMap(preview, video, analysis));
     }
@@ -124,12 +125,12 @@
     public void canRetrieveInsertedSuggestedStreamSpecs() {
         Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecsCamera0 =
                 mFakeCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
-                        /* isConcurrentCameraModeOn = */false,
+                        CameraMode.DEFAULT,
                         FAKE_CAMERA_ID0,
                         Collections.emptyList(), createConfigOutputSizesMap(mFakeUseCaseConfig));
         Map<UseCaseConfig<?>, StreamSpec> suggestedStreamSpecCamera1 =
                 mFakeCameraDeviceSurfaceManager.getSuggestedStreamSpecs(
-                        /* isConcurrentCameraModeOn = */false,
+                        CameraMode.DEFAULT,
                         FAKE_CAMERA_ID1,
                         Collections.emptyList(), createConfigOutputSizesMap(mFakeUseCaseConfig));
 
diff --git a/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraInfoTest.java b/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraInfoTest.java
index b5d9233..7855897 100644
--- a/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraInfoTest.java
+++ b/camera/camera-testing/src/test/java/androidx/camera/testing/fakes/FakeCameraInfoTest.java
@@ -79,4 +79,9 @@
         assertThat(mFakeCameraInfo.getSupportedFpsRanges()).isNotEmpty();
 
     }
+
+    @Test
+    public void canRetrieveSupportedDynamicRanges() {
+        assertThat(mFakeCameraInfo.getSupportedDynamicRanges()).isNotEmpty();
+    }
 }
diff --git a/camera/camera-video/api/current.txt b/camera/camera-video/api/current.txt
index 208d9cd..379c11e 100644
--- a/camera/camera-video/api/current.txt
+++ b/camera/camera-video/api/current.txt
@@ -132,7 +132,9 @@
   }
 
   @RequiresApi(21) public final class VideoCapture<T extends androidx.camera.video.VideoOutput> extends androidx.camera.core.UseCase {
+    method public int getMirrorMode();
     method public T getOutput();
+    method public android.util.Range<java.lang.Integer!> getTargetFramerate();
     method public int getTargetRotation();
     method public void setTargetRotation(int);
     method public void setTargetRotationDegrees(int);
@@ -142,6 +144,8 @@
   @RequiresApi(21) public static final class VideoCapture.Builder<T extends androidx.camera.video.VideoOutput> implements androidx.camera.core.ExtendableBuilder<androidx.camera.video.VideoCapture> {
     ctor public VideoCapture.Builder(T);
     method public androidx.camera.video.VideoCapture<T!> build();
+    method public androidx.camera.video.VideoCapture.Builder<T!> setMirrorMode(int);
+    method public androidx.camera.video.VideoCapture.Builder<T!> setTargetFramerate(android.util.Range<java.lang.Integer!>);
     method public androidx.camera.video.VideoCapture.Builder<T!> setTargetRotation(int);
   }
 
diff --git a/camera/camera-video/api/public_plus_experimental_current.txt b/camera/camera-video/api/public_plus_experimental_current.txt
index 208d9cd..379c11e 100644
--- a/camera/camera-video/api/public_plus_experimental_current.txt
+++ b/camera/camera-video/api/public_plus_experimental_current.txt
@@ -132,7 +132,9 @@
   }
 
   @RequiresApi(21) public final class VideoCapture<T extends androidx.camera.video.VideoOutput> extends androidx.camera.core.UseCase {
+    method public int getMirrorMode();
     method public T getOutput();
+    method public android.util.Range<java.lang.Integer!> getTargetFramerate();
     method public int getTargetRotation();
     method public void setTargetRotation(int);
     method public void setTargetRotationDegrees(int);
@@ -142,6 +144,8 @@
   @RequiresApi(21) public static final class VideoCapture.Builder<T extends androidx.camera.video.VideoOutput> implements androidx.camera.core.ExtendableBuilder<androidx.camera.video.VideoCapture> {
     ctor public VideoCapture.Builder(T);
     method public androidx.camera.video.VideoCapture<T!> build();
+    method public androidx.camera.video.VideoCapture.Builder<T!> setMirrorMode(int);
+    method public androidx.camera.video.VideoCapture.Builder<T!> setTargetFramerate(android.util.Range<java.lang.Integer!>);
     method public androidx.camera.video.VideoCapture.Builder<T!> setTargetRotation(int);
   }
 
diff --git a/camera/camera-video/api/restricted_current.txt b/camera/camera-video/api/restricted_current.txt
index 208d9cd..379c11e 100644
--- a/camera/camera-video/api/restricted_current.txt
+++ b/camera/camera-video/api/restricted_current.txt
@@ -132,7 +132,9 @@
   }
 
   @RequiresApi(21) public final class VideoCapture<T extends androidx.camera.video.VideoOutput> extends androidx.camera.core.UseCase {
+    method public int getMirrorMode();
     method public T getOutput();
+    method public android.util.Range<java.lang.Integer!> getTargetFramerate();
     method public int getTargetRotation();
     method public void setTargetRotation(int);
     method public void setTargetRotationDegrees(int);
@@ -142,6 +144,8 @@
   @RequiresApi(21) public static final class VideoCapture.Builder<T extends androidx.camera.video.VideoOutput> implements androidx.camera.core.ExtendableBuilder<androidx.camera.video.VideoCapture> {
     ctor public VideoCapture.Builder(T);
     method public androidx.camera.video.VideoCapture<T!> build();
+    method public androidx.camera.video.VideoCapture.Builder<T!> setMirrorMode(int);
+    method public androidx.camera.video.VideoCapture.Builder<T!> setTargetFramerate(android.util.Range<java.lang.Integer!>);
     method public androidx.camera.video.VideoCapture.Builder<T!> setTargetRotation(int);
   }
 
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java b/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java
index 7dc5a90..eb803dd 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java
@@ -32,6 +32,7 @@
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_HIGH_RESOLUTION_DISABLED;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_SESSION_CONFIG_UNPACKER;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_SURFACE_OCCUPANCY_PRIORITY;
+import static androidx.camera.core.impl.UseCaseConfig.OPTION_TARGET_FRAME_RATE;
 import static androidx.camera.core.impl.UseCaseConfig.OPTION_ZSL_DISABLED;
 import static androidx.camera.core.impl.utils.Threads.isMainThread;
 import static androidx.camera.core.impl.utils.TransformUtils.rectToString;
@@ -72,7 +73,7 @@
 import androidx.camera.core.ImageCapture;
 import androidx.camera.core.Logger;
 import androidx.camera.core.MirrorMode;
-import androidx.camera.core.ResolutionSelector;
+import androidx.camera.core.Preview;
 import androidx.camera.core.SurfaceRequest;
 import androidx.camera.core.UseCase;
 import androidx.camera.core.ViewPort;
@@ -104,6 +105,7 @@
 import androidx.camera.core.processing.DefaultSurfaceProcessor;
 import androidx.camera.core.processing.SurfaceEdge;
 import androidx.camera.core.processing.SurfaceProcessorNode;
+import androidx.camera.core.resolutionselector.ResolutionSelector;
 import androidx.camera.video.StreamInfo.StreamState;
 import androidx.camera.video.impl.VideoCaptureConfig;
 import androidx.camera.video.internal.VideoValidatedEncoderProfilesProxy;
@@ -253,6 +255,21 @@
     }
 
     /**
+     * Returns the target frame rate range for the associated VideoCapture use case.
+     *
+     * <p>The rotation can be set prior to constructing a VideoCapture using
+     * {@link VideoCapture.Builder#setTargetFramerate(Range)}
+     * If not set, the target frame rate defaults to the value of
+     * {@link StreamSpec#FRAME_RATE_RANGE_UNSPECIFIED}
+     *
+     * @return The rotation of the intended target.
+     */
+    @NonNull
+    public Range<Integer> getTargetFramerate() {
+        return getTargetFramerateInternal();
+    }
+
+    /**
      * Sets the desired rotation of the output video.
      *
      * <p>Valid values include: {@link Surface#ROTATION_0}, {@link Surface#ROTATION_90},
@@ -366,17 +383,14 @@
         setTargetRotation(orientationDegreesToSurfaceRotation(degrees));
     }
 
-    // TODO: to public API
     /**
      * Returns the mirror mode.
      *
      * <p>The mirror mode is set by {@link VideoCapture.Builder#setMirrorMode(int)}. If not set,
-     * it is defaults to {@link MirrorMode#MIRROR_MODE_OFF}.
+     * it defaults to {@link MirrorMode#MIRROR_MODE_OFF}.
      *
      * @return The mirror mode of the intended target.
-     *
      */
-    @RestrictTo(Scope.LIBRARY_GROUP)
     @MirrorMode.Mirror
     public int getMirrorMode() {
         return getMirrorModeInternal();
@@ -384,6 +398,7 @@
 
     /**
      * {@inheritDoc}
+     *
      */
     @SuppressWarnings("unchecked")
     @RestrictTo(Scope.LIBRARY_GROUP)
@@ -409,6 +424,7 @@
 
     /**
      * {@inheritDoc}
+     *
      */
     @Override
     @RestrictTo(Scope.LIBRARY_GROUP)
@@ -419,6 +435,7 @@
 
     /**
      * {@inheritDoc}
+     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
@@ -445,6 +462,7 @@
 
     /**
      * {@inheritDoc}
+     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
@@ -465,6 +483,7 @@
 
     /**
      * {@inheritDoc}
+     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @NonNull
@@ -479,6 +498,7 @@
 
     /**
      * {@inheritDoc}
+     *
      */
     @NonNull
     @RestrictTo(Scope.LIBRARY_GROUP)
@@ -682,6 +702,7 @@
     }
 
     /**
+     *
      */
     @Nullable
     @RestrictTo(Scope.TESTS)
@@ -694,6 +715,7 @@
      *
      * <p>These values may be overridden by the implementation. They only provide a minimum set of
      * defaults that are implementation independent.
+     *
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     public static final class Defaults implements ConfigProvider<VideoCaptureConfig<?>> {
@@ -1309,6 +1331,7 @@
 
         /**
          * {@inheritDoc}
+         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @Override
@@ -1319,6 +1342,7 @@
 
         /**
          * {@inheritDoc}
+         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
@@ -1390,6 +1414,7 @@
          * setTargetAspectRatio is not supported on VideoCapture
          *
          * <p>To set aspect ratio, see {@link Recorder.Builder#setAspectRatio(int)}.
+         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
@@ -1432,19 +1457,21 @@
             return this;
         }
 
-        // TODO: to public API
         /**
          * Sets the mirror mode.
          *
          * <p>Valid values include: {@link MirrorMode#MIRROR_MODE_OFF},
-         * {@link MirrorMode#MIRROR_MODE_ON} and {@link MirrorMode#MIRROR_MODE_FRONT_ON}.
-         * If not set, it is defaults to {@link MirrorMode#MIRROR_MODE_OFF}.
+         * {@link MirrorMode#MIRROR_MODE_ON} and {@link MirrorMode#MIRROR_MODE_ON_FRONT_ONLY}.
+         * If not set, it defaults to {@link MirrorMode#MIRROR_MODE_OFF}.
+         *
+         * <p>This API only changes the mirroring behavior on VideoCapture, but does not affect
+         * other UseCases. If the application wants to be consistent with the default
+         * {@link Preview} behavior where the rear camera is not mirrored but the front camera is
+         * mirrored, then {@link MirrorMode#MIRROR_MODE_ON_FRONT_ONLY} is recommended.
          *
          * @param mirrorMode The mirror mode of the intended target.
          * @return The current Builder.
-         *
          */
-        @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
         @Override
         public Builder<T> setMirrorMode(@MirrorMode.Mirror int mirrorMode) {
@@ -1456,6 +1483,7 @@
          * setTargetResolution is not supported on VideoCapture
          *
          * <p>To set resolution, see {@link Recorder.Builder#setQualitySelector(QualitySelector)}.
+         *
          */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
@@ -1606,5 +1634,22 @@
             getMutableConfig().insertOption(OPTION_HIGH_RESOLUTION_DISABLED, disabled);
             return this;
         }
+
+        /**
+         * Sets the target frame rate range for the associated VideoCapture use case.
+         *
+         * <p>This target will be used as a part of the heuristics for the algorithm that determines
+         * the final frame rate range and resolution of all concurrently bound use cases.
+         * <p>It is not guaranteed that this target frame rate will be the final range,
+         * as other use cases as well as frame rate restrictions of the device may affect the
+         * outcome of the algorithm that chooses the actual frame rate.
+         *
+         * @param targetFrameRate the target frame rate range.
+         */
+        @NonNull
+        public Builder<T> setTargetFramerate(@NonNull Range<Integer> targetFrameRate) {
+            getMutableConfig().insertOption(OPTION_TARGET_FRAME_RATE, targetFrameRate);
+            return this;
+        }
     }
 }
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/DynamicRangeMatchedEncoderProfilesProvider.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/DynamicRangeMatchedEncoderProfilesProvider.java
new file mode 100644
index 0000000..b8c05c1
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/DynamicRangeMatchedEncoderProfilesProvider.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.camera.video.internal;
+
+import static androidx.camera.video.internal.utils.DynamicRangeUtil.DR_TO_VP_BIT_DEPTH_MAP;
+import static androidx.camera.video.internal.utils.DynamicRangeUtil.DR_TO_VP_FORMAT_MAP;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.DynamicRange;
+import androidx.camera.core.impl.EncoderProfilesProvider;
+import androidx.camera.core.impl.EncoderProfilesProxy;
+import androidx.camera.core.impl.EncoderProfilesProxy.ImmutableEncoderProfilesProxy;
+import androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * An implementation that provides {@link EncoderProfilesProxy} containing video information
+ * matched with the target {@link DynamicRange}.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class DynamicRangeMatchedEncoderProfilesProvider implements EncoderProfilesProvider {
+
+    private final EncoderProfilesProvider mEncoderProfilesProvider;
+    private final DynamicRange mDynamicRange;
+    private final Map<Integer, EncoderProfilesProxy> mEncoderProfilesCache = new HashMap<>();
+
+    public DynamicRangeMatchedEncoderProfilesProvider(@NonNull EncoderProfilesProvider provider,
+            @NonNull DynamicRange dynamicRange) {
+        mEncoderProfilesProvider = provider;
+        mDynamicRange = dynamicRange;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean hasProfile(int quality) {
+        if (!mEncoderProfilesProvider.hasProfile(quality)) {
+            return false;
+        }
+
+        return getProfilesInternal(quality) != null;
+    }
+
+    /** {@inheritDoc} */
+    @Nullable
+    @Override
+    public EncoderProfilesProxy getAll(int quality) {
+        return getProfilesInternal(quality);
+    }
+
+    @Nullable
+    private EncoderProfilesProxy getProfilesInternal(int quality) {
+        if (mEncoderProfilesCache.containsKey(quality)) {
+            return mEncoderProfilesCache.get(quality);
+        }
+
+        EncoderProfilesProxy profiles = null;
+        if (mEncoderProfilesProvider.hasProfile(quality)) {
+            EncoderProfilesProxy baseProfiles = mEncoderProfilesProvider.getAll(quality);
+            profiles = filterUnmatchedDynamicRange(baseProfiles, mDynamicRange);
+            mEncoderProfilesCache.put(quality, profiles);
+        }
+
+        return profiles;
+    }
+
+    @Nullable
+    private static EncoderProfilesProxy filterUnmatchedDynamicRange(
+            @Nullable EncoderProfilesProxy encoderProfiles, @NonNull DynamicRange dynamicRange) {
+        if (encoderProfiles == null) {
+            return null;
+        }
+
+        List<VideoProfileProxy> validVideoProfiles = new ArrayList<>();
+        for (VideoProfileProxy videoProfile : encoderProfiles.getVideoProfiles()) {
+            if (isBitDepthMatched(videoProfile, dynamicRange) && isHdrFormatMatched(videoProfile,
+                    dynamicRange)) {
+                validVideoProfiles.add(videoProfile);
+            }
+        }
+
+        return validVideoProfiles.isEmpty() ? null : ImmutableEncoderProfilesProxy.create(
+                encoderProfiles.getDefaultDurationSeconds(),
+                encoderProfiles.getRecommendedFileFormat(),
+                encoderProfiles.getAudioProfiles(),
+                validVideoProfiles
+        );
+    }
+
+    private static boolean isBitDepthMatched(@NonNull VideoProfileProxy videoProfile,
+            @NonNull DynamicRange dynamicRange) {
+        Set<Integer> matchedBitDepths = DR_TO_VP_BIT_DEPTH_MAP.get(dynamicRange.getBitDepth());
+
+        return matchedBitDepths != null && matchedBitDepths.contains(videoProfile.getBitDepth());
+    }
+
+    private static boolean isHdrFormatMatched(@NonNull VideoProfileProxy videoProfile,
+            @NonNull DynamicRange dynamicRange) {
+        Set<Integer> matchedHdrFormats = DR_TO_VP_FORMAT_MAP.get(dynamicRange.getFormat());
+
+        return matchedHdrFormats != null && matchedHdrFormats.contains(videoProfile.getHdrFormat());
+    }
+}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/PreviewStretchWhenVideoCaptureIsBoundQuirk.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/PreviewStretchWhenVideoCaptureIsBoundQuirk.java
index 6e433bf..fdec2c4 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/PreviewStretchWhenVideoCaptureIsBoundQuirk.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/PreviewStretchWhenVideoCaptureIsBoundQuirk.java
@@ -23,16 +23,16 @@
 
 /**
  * <p>QuirkSummary
- *     Bug Id: b/227469801
+ *     Bug Id: b/227469801, b/274738266
  *     Description: Quirk indicates Preview is stretched when VideoCapture is bound.
- *     Device(s): Samsung J3, Samsung J7, Samsung J1 Ace neo and Oppo A37F
+ *     Device(s): Samsung J3, Samsung J5, Samsung J7, Samsung J1 Ace neo and Oppo A37F
  */
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 public class PreviewStretchWhenVideoCaptureIsBoundQuirk implements Quirk {
 
     static boolean load() {
         return isHuaweiP8Lite() || isSamsungJ3() || isSamsungJ7() || isSamsungJ1AceNeo()
-                || isOppoA37F();
+                || isOppoA37F() || isSamsungJ5();
     }
 
     private static boolean isHuaweiP8Lite() {
@@ -45,6 +45,11 @@
                 && "sm-j320f".equalsIgnoreCase(Build.MODEL);
     }
 
+    private static boolean isSamsungJ5() {
+        return "Samsung".equalsIgnoreCase(Build.MANUFACTURER)
+                && "sm-j510fn".equalsIgnoreCase(Build.MODEL);
+    }
+
     private static boolean isSamsungJ7() {
         return "Samsung".equalsIgnoreCase(Build.MANUFACTURER)
                 && "sm-j700f".equalsIgnoreCase(Build.MODEL);
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/utils/DynamicRangeUtil.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/utils/DynamicRangeUtil.java
new file mode 100644
index 0000000..2b081c7
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/utils/DynamicRangeUtil.java
@@ -0,0 +1,76 @@
+/*
+ * 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.video.internal.utils;
+
+import static android.media.EncoderProfiles.VideoProfile.HDR_DOLBY_VISION;
+import static android.media.EncoderProfiles.VideoProfile.HDR_HDR10;
+import static android.media.EncoderProfiles.VideoProfile.HDR_HDR10PLUS;
+import static android.media.EncoderProfiles.VideoProfile.HDR_HLG;
+import static android.media.EncoderProfiles.VideoProfile.HDR_NONE;
+
+import static androidx.camera.core.DynamicRange.BIT_DEPTH_10_BIT;
+import static androidx.camera.core.DynamicRange.BIT_DEPTH_8_BIT;
+import static androidx.camera.core.DynamicRange.BIT_DEPTH_UNSPECIFIED;
+import static androidx.camera.core.DynamicRange.FORMAT_DOLBY_VISION;
+import static androidx.camera.core.DynamicRange.FORMAT_HDR10;
+import static androidx.camera.core.DynamicRange.FORMAT_HDR10_PLUS;
+import static androidx.camera.core.DynamicRange.FORMAT_HDR_UNSPECIFIED;
+import static androidx.camera.core.DynamicRange.FORMAT_HLG;
+import static androidx.camera.core.DynamicRange.FORMAT_SDR;
+import static androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy.BIT_DEPTH_10;
+import static androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy.BIT_DEPTH_8;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+
+import androidx.annotation.RequiresApi;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Utility class for dynamic range related operations.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class DynamicRangeUtil {
+
+    public static final Map<Integer, Set<Integer>> DR_TO_VP_BIT_DEPTH_MAP = new HashMap<>();
+    public static final Map<Integer, Set<Integer>> DR_TO_VP_FORMAT_MAP = new HashMap<>();
+
+    private DynamicRangeUtil() {
+    }
+
+    static {
+        // DynamicRange bit depth to VideoProfile bit depth.
+        DR_TO_VP_BIT_DEPTH_MAP.put(BIT_DEPTH_8_BIT, new HashSet<>(singletonList(BIT_DEPTH_8)));
+        DR_TO_VP_BIT_DEPTH_MAP.put(BIT_DEPTH_10_BIT, new HashSet<>(singletonList(BIT_DEPTH_10)));
+        DR_TO_VP_BIT_DEPTH_MAP.put(BIT_DEPTH_UNSPECIFIED,
+                new HashSet<>(asList(BIT_DEPTH_8, BIT_DEPTH_10)));
+
+        // DynamicRange format to VideoProfile HDR format.
+        DR_TO_VP_FORMAT_MAP.put(FORMAT_SDR, new HashSet<>(singletonList(HDR_NONE)));
+        DR_TO_VP_FORMAT_MAP.put(FORMAT_HDR_UNSPECIFIED,
+                new HashSet<>(asList(HDR_HLG, HDR_HDR10, HDR_HDR10PLUS, HDR_DOLBY_VISION)));
+        DR_TO_VP_FORMAT_MAP.put(FORMAT_HLG, new HashSet<>(singletonList(HDR_HLG)));
+        DR_TO_VP_FORMAT_MAP.put(FORMAT_HDR10, new HashSet<>(singletonList(HDR_HDR10)));
+        DR_TO_VP_FORMAT_MAP.put(FORMAT_HDR10_PLUS, new HashSet<>(singletonList(HDR_HDR10PLUS)));
+        DR_TO_VP_FORMAT_MAP.put(FORMAT_DOLBY_VISION,
+                new HashSet<>(singletonList(HDR_DOLBY_VISION)));
+    }
+}
diff --git a/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt b/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt
index c03128a..d0e5fb9 100644
--- a/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt
+++ b/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt
@@ -43,7 +43,7 @@
 import androidx.camera.core.CameraSelector.LENS_FACING_BACK
 import androidx.camera.core.CameraSelector.LENS_FACING_FRONT
 import androidx.camera.core.CameraXConfig
-import androidx.camera.core.MirrorMode.MIRROR_MODE_FRONT_ON
+import androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY
 import androidx.camera.core.MirrorMode.MIRROR_MODE_OFF
 import androidx.camera.core.MirrorMode.MIRROR_MODE_ON
 import androidx.camera.core.SurfaceRequest
@@ -796,8 +796,8 @@
 
     @Test
     fun canGetSetMirrorMode() {
-        val videoCapture = createVideoCapture(mirrorMode = MIRROR_MODE_FRONT_ON)
-        assertThat(videoCapture.mirrorMode).isEqualTo(MIRROR_MODE_FRONT_ON)
+        val videoCapture = createVideoCapture(mirrorMode = MIRROR_MODE_ON_FRONT_ONLY)
+        assertThat(videoCapture.mirrorMode).isEqualTo(MIRROR_MODE_ON_FRONT_ONLY)
     }
 
     @Test
diff --git a/camera/camera-video/src/test/java/androidx/camera/video/internal/DynamicRangeMatchedEncoderProfilesProviderTest.kt b/camera/camera-video/src/test/java/androidx/camera/video/internal/DynamicRangeMatchedEncoderProfilesProviderTest.kt
new file mode 100644
index 0000000..7c9a766
--- /dev/null
+++ b/camera/camera-video/src/test/java/androidx/camera/video/internal/DynamicRangeMatchedEncoderProfilesProviderTest.kt
@@ -0,0 +1,216 @@
+/*
+ * 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.video.internal
+
+import android.media.CamcorderProfile.QUALITY_1080P
+import android.media.EncoderProfiles.VideoProfile.HDR_DOLBY_VISION
+import android.media.EncoderProfiles.VideoProfile.HDR_HDR10
+import android.media.EncoderProfiles.VideoProfile.HDR_HDR10PLUS
+import android.media.EncoderProfiles.VideoProfile.HDR_HLG
+import android.media.EncoderProfiles.VideoProfile.HDR_NONE
+import android.os.Build
+import androidx.camera.core.DynamicRange
+import androidx.camera.core.DynamicRange.BIT_DEPTH_10_BIT
+import androidx.camera.core.DynamicRange.FORMAT_DOLBY_VISION
+import androidx.camera.core.DynamicRange.FORMAT_HDR10
+import androidx.camera.core.DynamicRange.FORMAT_HDR10_PLUS
+import androidx.camera.core.DynamicRange.FORMAT_HLG
+import androidx.camera.core.DynamicRange.HDR_UNSPECIFIED_10_BIT
+import androidx.camera.core.DynamicRange.SDR
+import androidx.camera.core.impl.EncoderProfilesProvider
+import androidx.camera.core.impl.EncoderProfilesProxy
+import androidx.camera.core.impl.EncoderProfilesProxy.ImmutableEncoderProfilesProxy
+import androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy
+import androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy.BIT_DEPTH_10
+import androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy.BIT_DEPTH_8
+import androidx.camera.testing.EncoderProfilesUtil
+import androidx.camera.testing.EncoderProfilesUtil.RESOLUTION_1080P
+import androidx.camera.testing.EncoderProfilesUtil.createFakeAudioProfileProxy
+import androidx.camera.testing.EncoderProfilesUtil.createFakeVideoProfileProxy
+import androidx.camera.testing.fakes.FakeEncoderProfilesProvider
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class DynamicRangeMatchedEncoderProfilesProviderTest {
+
+    private val defaultProvider = createFakeEncoderProfilesProvider(
+        arrayOf(Pair(QUALITY_1080P, PROFILES_1080P_FULL_DYNAMIC_RANGE))
+    )
+
+    @Test
+    fun hasNoProfile_canNotGetProfiles() {
+        val emptyProvider = createFakeEncoderProfilesProvider()
+        val sdrProvider = DynamicRangeMatchedEncoderProfilesProvider(emptyProvider, SDR)
+        val hlgProvider = DynamicRangeMatchedEncoderProfilesProvider(emptyProvider, HLG)
+        val hdr10Provider = DynamicRangeMatchedEncoderProfilesProvider(emptyProvider, HDR10)
+        val hdr10PlusProvider =
+            DynamicRangeMatchedEncoderProfilesProvider(emptyProvider, HDR10_PLUS)
+        val dolbyProvider = DynamicRangeMatchedEncoderProfilesProvider(emptyProvider, DOLBY_VISION)
+        val hdrUnspecifiedProvider =
+            DynamicRangeMatchedEncoderProfilesProvider(emptyProvider, HDR_UNSPECIFIED_10_BIT)
+
+        assertThat(sdrProvider.hasProfile(QUALITY_1080P)).isFalse()
+        assertThat(hlgProvider.hasProfile(QUALITY_1080P)).isFalse()
+        assertThat(hdr10Provider.hasProfile(QUALITY_1080P)).isFalse()
+        assertThat(hdr10PlusProvider.hasProfile(QUALITY_1080P)).isFalse()
+        assertThat(dolbyProvider.hasProfile(QUALITY_1080P)).isFalse()
+        assertThat(hdrUnspecifiedProvider.hasProfile(QUALITY_1080P)).isFalse()
+        assertThat(sdrProvider.getAll(QUALITY_1080P)).isNull()
+        assertThat(hlgProvider.getAll(QUALITY_1080P)).isNull()
+        assertThat(hdr10Provider.getAll(QUALITY_1080P)).isNull()
+        assertThat(hdr10PlusProvider.getAll(QUALITY_1080P)).isNull()
+        assertThat(dolbyProvider.getAll(QUALITY_1080P)).isNull()
+        assertThat(hdrUnspecifiedProvider.getAll(QUALITY_1080P)).isNull()
+    }
+
+    @Test
+    fun sdr_onlyContainsSdrProfile() {
+        val provider = DynamicRangeMatchedEncoderProfilesProvider(defaultProvider, SDR)
+
+        assertThat(provider.hasProfile(QUALITY_1080P)).isTrue()
+        val videoProfiles = provider.getAll(QUALITY_1080P)!!.videoProfiles
+        assertThat(videoProfiles.size == 1).isTrue()
+        assertThat(videoProfiles[0].hdrFormat == HDR_NONE).isTrue()
+        assertThat(videoProfiles[0].bitDepth == BIT_DEPTH_8).isTrue()
+    }
+
+    @Test
+    fun hlg_onlyContainsHlgProfile() {
+        val provider = DynamicRangeMatchedEncoderProfilesProvider(defaultProvider, HLG)
+
+        assertThat(provider.hasProfile(QUALITY_1080P)).isTrue()
+        val videoProfiles = provider.getAll(QUALITY_1080P)!!.videoProfiles
+        assertThat(videoProfiles.size == 1).isTrue()
+        assertThat(videoProfiles[0].hdrFormat == HDR_HLG).isTrue()
+        assertThat(videoProfiles[0].bitDepth == BIT_DEPTH_10).isTrue()
+    }
+
+    @Test
+    fun hdr10_onlyContainsHdr10Profile() {
+        val provider = DynamicRangeMatchedEncoderProfilesProvider(defaultProvider, HDR10)
+
+        assertThat(provider.hasProfile(QUALITY_1080P)).isTrue()
+        val videoProfiles = provider.getAll(QUALITY_1080P)!!.videoProfiles
+        assertThat(videoProfiles.size == 1).isTrue()
+        assertThat(videoProfiles[0].hdrFormat == HDR_HDR10).isTrue()
+        assertThat(videoProfiles[0].bitDepth == BIT_DEPTH_10).isTrue()
+    }
+
+    @Test
+    fun hdr10Plus_onlyContainsHdr10PlusProfile() {
+        val provider = DynamicRangeMatchedEncoderProfilesProvider(defaultProvider, HDR10_PLUS)
+
+        assertThat(provider.hasProfile(QUALITY_1080P)).isTrue()
+        val videoProfiles = provider.getAll(QUALITY_1080P)!!.videoProfiles
+        assertThat(videoProfiles.size == 1).isTrue()
+        assertThat(videoProfiles[0].hdrFormat == HDR_HDR10PLUS).isTrue()
+        assertThat(videoProfiles[0].bitDepth == BIT_DEPTH_10).isTrue()
+    }
+
+    @Test
+    fun dolbyVision_onlyContainsDolbyVisionProfile() {
+        val provider = DynamicRangeMatchedEncoderProfilesProvider(defaultProvider, DOLBY_VISION)
+
+        assertThat(provider.hasProfile(QUALITY_1080P)).isTrue()
+        val videoProfiles = provider.getAll(QUALITY_1080P)!!.videoProfiles
+        assertThat(videoProfiles.size == 1).isTrue()
+        assertThat(videoProfiles[0].hdrFormat == HDR_DOLBY_VISION).isTrue()
+        assertThat(videoProfiles[0].bitDepth == BIT_DEPTH_10).isTrue()
+    }
+
+    @Test
+    fun hdrUnspecified_containsAllHdrProfiles() {
+        val provider =
+            DynamicRangeMatchedEncoderProfilesProvider(defaultProvider, HDR_UNSPECIFIED_10_BIT)
+
+        assertThat(provider.hasProfile(QUALITY_1080P)).isTrue()
+        val videoProfiles = provider.getAll(QUALITY_1080P)!!.videoProfiles
+        assertThat(videoProfiles.size == 4).isTrue()
+        assertThat(videoProfiles[0].hdrFormat == HDR_HLG).isTrue()
+        assertThat(videoProfiles[1].hdrFormat == HDR_HDR10).isTrue()
+        assertThat(videoProfiles[2].hdrFormat == HDR_HDR10PLUS).isTrue()
+        assertThat(videoProfiles[3].hdrFormat == HDR_DOLBY_VISION).isTrue()
+        assertThat(videoProfiles[0].bitDepth == BIT_DEPTH_10).isTrue()
+        assertThat(videoProfiles[1].bitDepth == BIT_DEPTH_10).isTrue()
+        assertThat(videoProfiles[2].bitDepth == BIT_DEPTH_10).isTrue()
+        assertThat(videoProfiles[3].bitDepth == BIT_DEPTH_10).isTrue()
+    }
+
+    private fun createFakeEncoderProfilesProvider(
+        qualityToProfilesPairs: Array<Pair<Int, EncoderProfilesProxy>> = emptyArray()
+    ): EncoderProfilesProvider {
+        return FakeEncoderProfilesProvider.Builder().also { builder ->
+            for (pair in qualityToProfilesPairs) {
+                builder.add(pair.first, pair.second)
+            }
+        }.build()
+    }
+
+    companion object {
+        private val HLG = DynamicRange(FORMAT_HLG, BIT_DEPTH_10_BIT)
+        private val HDR10 = DynamicRange(FORMAT_HDR10, BIT_DEPTH_10_BIT)
+        private val HDR10_PLUS = DynamicRange(FORMAT_HDR10_PLUS, BIT_DEPTH_10_BIT)
+        private val DOLBY_VISION = DynamicRange(FORMAT_DOLBY_VISION, BIT_DEPTH_10_BIT)
+        private val VIDEO_PROFILES_1080P_SDR =
+            createFakeVideoProfileProxy(RESOLUTION_1080P.width, RESOLUTION_1080P.height)
+        private val VIDEO_PROFILES_1080P_HLG =
+            VIDEO_PROFILES_1080P_SDR.modifyDynamicRangeInfo(HDR_HLG, BIT_DEPTH_10)
+        private val VIDEO_PROFILES_1080P_HDR10 =
+            VIDEO_PROFILES_1080P_SDR.modifyDynamicRangeInfo(HDR_HDR10, BIT_DEPTH_10)
+        private val VIDEO_PROFILES_1080P_HDR10_PLUS =
+            VIDEO_PROFILES_1080P_SDR.modifyDynamicRangeInfo(HDR_HDR10PLUS, BIT_DEPTH_10)
+        private val VIDEO_PROFILES_1080P_DOLBY_VISION =
+            VIDEO_PROFILES_1080P_SDR.modifyDynamicRangeInfo(HDR_DOLBY_VISION, BIT_DEPTH_10)
+        private val PROFILES_1080P_FULL_DYNAMIC_RANGE = ImmutableEncoderProfilesProxy.create(
+            EncoderProfilesUtil.DEFAULT_DURATION,
+            EncoderProfilesUtil.DEFAULT_OUTPUT_FORMAT,
+            listOf(createFakeAudioProfileProxy()),
+            listOf(
+                VIDEO_PROFILES_1080P_SDR,
+                VIDEO_PROFILES_1080P_HLG,
+                VIDEO_PROFILES_1080P_HDR10,
+                VIDEO_PROFILES_1080P_HDR10_PLUS,
+                VIDEO_PROFILES_1080P_DOLBY_VISION
+            )
+        )
+
+        private fun VideoProfileProxy.modifyDynamicRangeInfo(
+            hdrFormat: Int,
+            bitDepth: Int
+        ): VideoProfileProxy {
+            return VideoProfileProxy.create(
+                this.codec,
+                this.mediaType,
+                this.bitrate,
+                this.frameRate,
+                this.width,
+                this.height,
+                this.profile,
+                bitDepth,
+                this.chromaSubsampling,
+                hdrFormat
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-view/api/current.txt b/camera/camera-view/api/current.txt
index f711d87..3bd4fea 100644
--- a/camera/camera-view/api/current.txt
+++ b/camera/camera-view/api/current.txt
@@ -2,6 +2,7 @@
 package androidx.camera.view {
 
   @RequiresApi(21) public abstract class CameraController {
+    method @MainThread public void clearEffects();
     method @MainThread public void clearImageAnalysisAnalyzer();
     method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
     method @MainThread public androidx.camera.core.CameraControl? getCameraControl();
@@ -26,7 +27,7 @@
     method @MainThread public boolean isPinchToZoomEnabled();
     method @MainThread public boolean isTapToFocusEnabled();
     method @MainThread public void setCameraSelector(androidx.camera.core.CameraSelector);
-    method @MainThread public void setEffects(java.util.Set<androidx.camera.core.CameraEffect!>?);
+    method @MainThread public void setEffects(java.util.Set<androidx.camera.core.CameraEffect!>);
     method @MainThread public void setEnabledUseCases(int);
     method @MainThread public void setImageAnalysisAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
     method @MainThread public void setImageAnalysisBackgroundExecutor(java.util.concurrent.Executor?);
diff --git a/camera/camera-view/api/public_plus_experimental_current.txt b/camera/camera-view/api/public_plus_experimental_current.txt
index 6989168..b4027bc 100644
--- a/camera/camera-view/api/public_plus_experimental_current.txt
+++ b/camera/camera-view/api/public_plus_experimental_current.txt
@@ -2,6 +2,7 @@
 package androidx.camera.view {
 
   @RequiresApi(21) public abstract class CameraController {
+    method @MainThread public void clearEffects();
     method @MainThread public void clearImageAnalysisAnalyzer();
     method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
     method @MainThread public androidx.camera.core.CameraControl? getCameraControl();
@@ -29,7 +30,7 @@
     method @MainThread public boolean isTapToFocusEnabled();
     method @MainThread @androidx.camera.view.video.ExperimentalVideo public boolean isVideoCaptureEnabled();
     method @MainThread public void setCameraSelector(androidx.camera.core.CameraSelector);
-    method @MainThread public void setEffects(java.util.Set<androidx.camera.core.CameraEffect!>?);
+    method @MainThread public void setEffects(java.util.Set<androidx.camera.core.CameraEffect!>);
     method @MainThread public void setEnabledUseCases(int);
     method @MainThread public void setImageAnalysisAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
     method @MainThread public void setImageAnalysisBackgroundExecutor(java.util.concurrent.Executor?);
diff --git a/camera/camera-view/api/restricted_current.txt b/camera/camera-view/api/restricted_current.txt
index f711d87..3bd4fea 100644
--- a/camera/camera-view/api/restricted_current.txt
+++ b/camera/camera-view/api/restricted_current.txt
@@ -2,6 +2,7 @@
 package androidx.camera.view {
 
   @RequiresApi(21) public abstract class CameraController {
+    method @MainThread public void clearEffects();
     method @MainThread public void clearImageAnalysisAnalyzer();
     method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
     method @MainThread public androidx.camera.core.CameraControl? getCameraControl();
@@ -26,7 +27,7 @@
     method @MainThread public boolean isPinchToZoomEnabled();
     method @MainThread public boolean isTapToFocusEnabled();
     method @MainThread public void setCameraSelector(androidx.camera.core.CameraSelector);
-    method @MainThread public void setEffects(java.util.Set<androidx.camera.core.CameraEffect!>?);
+    method @MainThread public void setEffects(java.util.Set<androidx.camera.core.CameraEffect!>);
     method @MainThread public void setEnabledUseCases(int);
     method @MainThread public void setImageAnalysisAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
     method @MainThread public void setImageAnalysisBackgroundExecutor(java.util.concurrent.Executor?);
diff --git a/camera/camera-view/src/androidTest/java/androidx/camera/view/CameraControllerDeviceTest.kt b/camera/camera-view/src/androidTest/java/androidx/camera/view/CameraControllerDeviceTest.kt
index e6266ed..429a03c 100644
--- a/camera/camera-view/src/androidTest/java/androidx/camera/view/CameraControllerDeviceTest.kt
+++ b/camera/camera-view/src/androidTest/java/androidx/camera/view/CameraControllerDeviceTest.kt
@@ -168,7 +168,7 @@
         assertThat(controller!!.mPreview.effect).isNotNull()
 
         // Act: clear the effects
-        instrumentation.runOnMainSync { controller!!.setEffects(null) }
+        instrumentation.runOnMainSync { controller!!.clearEffects() }
 
         // Assert: preview no longer has the effect.
         assertThat(controller!!.mPreview.effect).isNull()
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 4716d0c..3005a78 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
@@ -191,7 +191,6 @@
 
     /**
      * Bitmask options to enable/disable use cases.
-     *
      */
     @OptIn(markerClass = ExperimentalVideo.class)
     @Retention(RetentionPolicy.SOURCE)
@@ -702,7 +701,6 @@
      *
      * <p> Mirror the output image if front camera is used and if the flag is not set explicitly by
      * the app.
-     *
      */
     @VisibleForTesting
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -1906,9 +1904,6 @@
      * {@link IllegalArgumentException}. Once called, CameraX will rebind the {@link UseCase}
      * with the effects applied. Effects not in the list are automatically removed.
      *
-     * <p>To remove an effect, call this method with a list without the effect. To remove all
-     * effects, call this method with {@code null} value or a empty set.
-     *
      * <p>The method throws {@link IllegalArgumentException} if the effects combination is not
      * supported by CameraX. Please see the Javadoc of {@link UseCaseGroup.Builder#addEffect} to
      * see the supported effects combinations.
@@ -1918,7 +1913,7 @@
      * @see UseCaseGroup.Builder#addEffect
      */
     @MainThread
-    public void setEffects(@Nullable Set<CameraEffect> effects) {
+    public void setEffects(@NonNull Set<CameraEffect> effects) {
         checkMainThread();
         if (Objects.equals(mEffects, effects)) {
             // Same effect. No change needed.
@@ -1928,12 +1923,20 @@
             // Unbind to make sure the pipelines will be recreated.
             mCameraProvider.unbindAll();
         }
-        if (effects == null) {
-            mEffects.clear();
-        } else {
-            mEffects.clear();
-            mEffects.addAll(effects);
-        }
+        mEffects.clear();
+        mEffects.addAll(effects);
+        startCameraAndTrackStates();
+    }
+
+    /**
+     * Removes all effects.
+     *
+     * <p>Once called, CameraX will remove all the effects and rebind the {@link UseCase}.
+     */
+    @MainThread
+    public void clearEffects() {
+        checkMainThread();
+        mEffects.clear();
         startCameraAndTrackStates();
     }
 
@@ -1980,7 +1983,6 @@
      *
      * <p> Preview is required. If it is null, then controller is not ready. Return null and ignore
      * other use cases.
-     *
      */
     @Nullable
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -2066,7 +2068,6 @@
 
         /**
          * Possible value for {@link #getAspectRatio()}
-         *
          */
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         @Retention(RetentionPolicy.SOURCE)
diff --git a/camera/camera-viewfinder/build.gradle b/camera/camera-viewfinder/build.gradle
index ea7bfde..39c752c 100644
--- a/camera/camera-viewfinder/build.gradle
+++ b/camera/camera-viewfinder/build.gradle
@@ -27,12 +27,14 @@
     api("androidx.annotation:annotation:1.2.0")
     implementation("androidx.annotation:annotation-experimental:1.1.0-rc01")
     implementation(libs.guavaListenableFuture)
-    implementation("androidx.core:core:1.3.2")
-    implementation("androidx.concurrent:concurrent-futures:1.0.0")
-    implementation(project(":concurrent:concurrent-futures-ktx"))
+    implementation("androidx.core:core:1.7.0")
+    implementation("androidx.concurrent:concurrent-futures:1.1.0")
+    implementation("androidx.concurrent:concurrent-futures-ktx:1.2.0-alpha01")
     implementation(libs.autoValueAnnotations)
     implementation("androidx.appcompat:appcompat:1.1.0")
     implementation("androidx.test.espresso:espresso-idling-resource:3.1.0")
+    implementation(libs.kotlinCoroutinesCore)
+    implementation(libs.kotlinCoroutinesAndroid)
 
     // Added for annotation-experimental
     compileOnly(libs.kotlinStdlib)
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 88c8b26..870c376 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
@@ -35,6 +35,7 @@
 import android.content.Context
 import android.os.Build
 import androidx.camera.camera2.Camera2Config
+import androidx.camera.core.CameraSelector
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.CameraXUtil
 import androidx.camera.testing.fakes.FakeLifecycleOwner
@@ -121,6 +122,8 @@
 
     @Test
     fun initialRecorder_canMakeRecorderReady(): Unit = runBlocking {
+        Assume.assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_FRONT))
+
         viewModel.initialRecorder(context, lifecycleOwner)
 
         assertThat(viewModel.isRecorderReady).isTrue()
@@ -167,6 +170,8 @@
 
     @Test
     fun startAndStopRecording_canWorkCorrectlyAfterRecorderReady(): Unit = runBlocking {
+        Assume.assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_FRONT))
+
         // Arrange.
         viewModel.initialRecorder(context, lifecycleOwner)
 
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/FocusMeteringDeviceTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/FocusMeteringDeviceTest.kt
index f96b0b1..42ed11b 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/FocusMeteringDeviceTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/FocusMeteringDeviceTest.kt
@@ -39,6 +39,7 @@
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraPipeConfigTestRule
 import androidx.camera.testing.CameraUtil
+import androidx.camera.testing.LabTestRule.Companion.isLensFacingEnabledInLabTest
 import androidx.camera.testing.fakes.FakeLifecycleOwner
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.filters.LargeTest
@@ -311,6 +312,85 @@
         assertFutureCompletes(future)
     }
 
+    /**
+     * The following tests check if a device can complete 3A convergence, by setting an auto
+     * cancellation with [FocusMeteringAction.Builder.setAutoCancelDuration] which ensures throwing
+     * an exception in case of a timeout.
+     *
+     * Since some devices may require a long time to complete convergence, we are setting a long
+     * [FocusMeteringAction.mAutoCancelDurationInMillis] in these tests.
+     */
+
+    @Test
+    fun futureCompletes_whenFocusMeteringStartedWithLongCancelDuration() = runBlocking {
+        Assume.assumeTrue(
+            "Not CameraX lab environment," +
+                " or lensFacing:${cameraSelector.lensFacing!!} camera is not enabled",
+            isLensFacingEnabledInLabTest(lensFacing = cameraSelector.lensFacing!!)
+        )
+
+        Assume.assumeTrue(
+            "No AF/AE/AWB region available on this device!",
+            hasMeteringRegion(cameraSelector)
+        )
+
+        val focusMeteringAction = FocusMeteringAction.Builder(validMeteringPoint)
+            .setAutoCancelDuration(5_000, TimeUnit.MILLISECONDS)
+            .build()
+
+        val resultFuture = camera.cameraControl.startFocusAndMetering(focusMeteringAction)
+
+        assertFutureCompletes(resultFuture)
+    }
+
+    @Test
+    fun futureCompletes_whenOnlyAfFocusMeteringStartedWithLongCancelDuration() = runBlocking {
+        Assume.assumeTrue(
+            "Not CameraX lab environment," +
+                " or lensFacing:${cameraSelector.lensFacing!!} camera is not enabled",
+            isLensFacingEnabledInLabTest(lensFacing = cameraSelector.lensFacing!!)
+        )
+
+        Assume.assumeTrue(
+            "No AF region available on this device!",
+            hasMeteringRegion(cameraSelector, FLAG_AF)
+        )
+
+        val focusMeteringAction = FocusMeteringAction.Builder(
+            validMeteringPoint,
+            FLAG_AF
+        ).setAutoCancelDuration(5_000, TimeUnit.MILLISECONDS)
+            .build()
+
+        val resultFuture = camera.cameraControl.startFocusAndMetering(focusMeteringAction)
+
+        assertFutureCompletes(resultFuture)
+    }
+
+    @Test
+    fun futureCompletes_whenAeAwbFocusMeteringStartedWithLongCancelDuration() = runBlocking {
+        Assume.assumeTrue(
+            "Not CameraX lab environment," +
+                " or lensFacing:${cameraSelector.lensFacing!!} camera is not enabled",
+            isLensFacingEnabledInLabTest(lensFacing = cameraSelector.lensFacing!!)
+        )
+
+        Assume.assumeTrue(
+            "No AE/AWB region available on this device!",
+            hasMeteringRegion(cameraSelector, FLAG_AE or FLAG_AWB)
+        )
+
+        val focusMeteringAction = FocusMeteringAction.Builder(
+            validMeteringPoint,
+            FLAG_AE or FLAG_AWB
+        ).setAutoCancelDuration(5_000, TimeUnit.MILLISECONDS)
+            .build()
+
+        val resultFuture = camera.cameraControl.startFocusAndMetering(focusMeteringAction)
+
+        assertFutureCompletes(resultFuture)
+    }
+
     private fun hasMeteringRegion(
         selector: CameraSelector,
         @FocusMeteringAction.MeteringMode flags: Int = FLAG_AF or FLAG_AE or FLAG_AWB
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageAnalysisTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageAnalysisTest.kt
index 17d87da..96508df 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageAnalysisTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageAnalysisTest.kt
@@ -32,9 +32,10 @@
 import androidx.camera.core.ImageAnalysis.BackpressureStrategy
 import androidx.camera.core.ImageCapture
 import androidx.camera.core.ImageProxy
-import androidx.camera.core.ResolutionSelector
 import androidx.camera.core.impl.ImageOutputConfig
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.core.resolutionselector.HighResolution
+import androidx.camera.core.resolutionselector.ResolutionSelector
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraPipeConfigTestRule
 import androidx.camera.testing.CameraUtil
@@ -139,6 +140,8 @@
 
     @Test
     fun exceedMaxImagesWithoutClosing_doNotCrash() = runBlocking {
+        assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_FRONT))
+
         // Arrange.
         val queueDepth = 3
         val semaphore = Semaphore(0)
@@ -428,9 +431,11 @@
         assumeTrue(maxHighResolutionOutputSize != null)
 
         val resolutionSelector = ResolutionSelector.Builder()
-                .setPreferredResolution(maxHighResolutionOutputSize!!)
-                .setHighResolutionEnabled(true)
-                .build()
+            .setHighResolutionEnabledFlags(HighResolution.FLAG_DEFAULT_MODE_ON)
+            .setResolutionFilter { _, _ ->
+                listOf(maxHighResolutionOutputSize)
+            }
+            .build()
 
         val imageAnalysis = ImageAnalysis.Builder()
             .setResolutionSelector(resolutionSelector)
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
index 119b3a0..c8c1d21 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
@@ -48,7 +48,6 @@
 import androidx.camera.core.ImageCaptureException
 import androidx.camera.core.ImageProxy
 import androidx.camera.core.Preview
-import androidx.camera.core.ResolutionSelector
 import androidx.camera.core.UseCase
 import androidx.camera.core.UseCaseGroup
 import androidx.camera.core.ViewPort
@@ -63,6 +62,8 @@
 import androidx.camera.core.impl.utils.CameraOrientationUtil
 import androidx.camera.core.impl.utils.Exif
 import androidx.camera.core.internal.compat.workaround.ExifRotationAvailability
+import androidx.camera.core.resolutionselector.HighResolution
+import androidx.camera.core.resolutionselector.ResolutionSelector
 import androidx.camera.integration.core.util.CameraPipeUtil
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraPipeConfigTestRule
@@ -1181,6 +1182,8 @@
 
     @Test
     fun useCaseCanBeReusedInDifferentCamera() = runBlocking {
+        assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_FRONT))
+
         val useCase = defaultBuilder.build()
         withContext(Dispatchers.Main) {
             cameraProvider.bindToLifecycle(fakeLifecycleOwner, BACK_SELECTOR, useCase)
@@ -1582,11 +1585,12 @@
         // Only runs the test when the device has high resolution output sizes
         assumeTrue(maxHighResolutionOutputSize != null)
 
-        val resolutionSelector =
-            ResolutionSelector.Builder()
-                .setPreferredResolution(maxHighResolutionOutputSize!!)
-                .setHighResolutionEnabled(true)
-                .build()
+        val resolutionSelector = ResolutionSelector.Builder()
+            .setHighResolutionEnabledFlags(HighResolution.FLAG_DEFAULT_MODE_ON)
+            .setResolutionFilter { _, _ ->
+                listOf(maxHighResolutionOutputSize)
+            }
+            .build()
         val sensorOrientation = CameraUtil.getSensorOrientation(BACK_SELECTOR.lensFacing!!)
         // Sets the target rotation to the camera sensor orientation to avoid the captured image
         // buffer data rotated by the HAL and impact the final image resolution check
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/camera2/PreviewTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/camera2/PreviewTest.kt
index 8348922..f7b75c4 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/camera2/PreviewTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/camera2/PreviewTest.kt
@@ -29,11 +29,12 @@
 import androidx.camera.core.CameraXConfig
 import androidx.camera.core.ImageCapture
 import androidx.camera.core.Preview
-import androidx.camera.core.ResolutionSelector
 import androidx.camera.core.UseCase
 import androidx.camera.core.impl.ImageOutputConfig
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
 import androidx.camera.core.internal.CameraUseCaseAdapter
+import androidx.camera.core.resolutionselector.HighResolution
+import androidx.camera.core.resolutionselector.ResolutionSelector
 import androidx.camera.testing.CameraPipeConfigTestRule
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.CameraUtil.PreTestCameraIdList
@@ -107,11 +108,15 @@
     @Before
     @Throws(ExecutionException::class, InterruptedException::class)
     fun setUp() {
+        assumeTrue(CameraUtil.hasCameraWithLensFacing(cameraSelector.lensFacing!!))
+
         context = ApplicationProvider.getApplicationContext()
         CameraXUtil.initialize(context!!, cameraConfig).get()
 
         // init CameraX before creating Preview to get preview size with CameraX's context
-        defaultBuilder = Preview.Builder.fromConfig(Preview.DEFAULT_CONFIG.config)
+        defaultBuilder = Preview.Builder.fromConfig(Preview.DEFAULT_CONFIG.config).also {
+            it.mutableConfig.removeOption(ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO)
+        }
         surfaceFutureSemaphore = Semaphore( /*permits=*/0)
         safeToReleaseSemaphore = Semaphore( /*permits=*/0)
     }
@@ -421,6 +426,8 @@
     @Test
     @Throws(InterruptedException::class)
     fun useCaseCanBeReusedInDifferentCamera() {
+        assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_FRONT))
+
         val preview = defaultBuilder!!.build()
         instrumentation.runOnMainSync { preview.setSurfaceProvider(getSurfaceProvider(null)) }
 
@@ -548,9 +555,10 @@
         // Arrange.
         val resolutionSelector =
             ResolutionSelector.Builder()
-                .setMaxResolution(maxHighResolutionOutputSize!!)
-                .setPreferredResolution(maxHighResolutionOutputSize)
-                .setHighResolutionEnabled(true)
+                .setHighResolutionEnabledFlags(HighResolution.FLAG_DEFAULT_MODE_ON)
+                .setResolutionFilter { _, _ ->
+                    listOf(maxHighResolutionOutputSize)
+                }
                 .build()
         val preview = Preview.Builder().setResolutionSelector(resolutionSelector).build()
 
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 04bdd7d..c745a9f 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
@@ -24,6 +24,7 @@
 import static androidx.camera.core.ImageCapture.FLASH_MODE_AUTO;
 import static androidx.camera.core.ImageCapture.FLASH_MODE_OFF;
 import static androidx.camera.core.ImageCapture.FLASH_MODE_ON;
+import static androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY;
 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;
@@ -1514,7 +1515,9 @@
             if (mVideoQuality != QUALITY_AUTO) {
                 builder.setQualitySelector(QualitySelector.from(mVideoQuality));
             }
-            VideoCapture<Recorder> videoCapture = VideoCapture.withOutput(builder.build());
+            VideoCapture<Recorder> videoCapture = new VideoCapture.Builder<>(builder.build())
+                    .setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY)
+                    .build();
             useCases.add(videoCapture);
         }
         return useCases;
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/AdvancedExtenderValidation.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/AdvancedExtenderValidation.kt
index 783e7d8..d615435 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/AdvancedExtenderValidation.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/AdvancedExtenderValidation.kt
@@ -204,8 +204,8 @@
     private fun createAnalysisOutput(
         impl: AdvancedExtenderImpl,
         sizeCategory: SizeCategory
-    ): OutputSurfaceImpl {
-        val analysisSizes = impl.getSupportedYuvAnalysisResolutions(cameraId)
+    ): OutputSurfaceImpl? {
+        val analysisSizes = impl.getSupportedYuvAnalysisResolutions(cameraId) ?: return null
         assertThat(analysisSizes).isNotEmpty()
 
         var analysisSize = getSizeByClass(analysisSizes, sizeCategory)
@@ -338,8 +338,8 @@
             outputConfiguration.setPhysicalCameraId(outputConfigImpl.physicalCameraId)
         }
 
-        if (outputConfigImpl.surfaceSharingOutputConfigs != null) {
-            for (surfaceSharingOutputConfig in outputConfigImpl.surfaceSharingOutputConfigs) {
+        outputConfigImpl.surfaceSharingOutputConfigs?.let {
+            for (surfaceSharingOutputConfig in it) {
                 val sharingOutputConfiguration = getOutputConfiguration(surfaceSharingOutputConfig)
                 outputConfiguration.addSurface(sharingOutputConfiguration.surface!!)
                 outputConfiguration.enableSurfaceSharing()
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java
index 49cb97b..edecc85 100644
--- a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java
+++ b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/CameraControllerFragment.java
@@ -378,7 +378,7 @@
             mCameraController.setEffects(
                     new HashSet<>(asList(mToneMappingSurfaceEffect, mToneMappingImageEffect)));
         } else {
-            mCameraController.setEffects(null);
+            mCameraController.clearEffects();
         }
     }
 
diff --git a/car/app/app-automotive/src/main/java/androidx/car/app/activity/LauncherActivity.java b/car/app/app-automotive/src/main/java/androidx/car/app/activity/LauncherActivity.java
index 942b59f..724b75e 100644
--- a/car/app/app-automotive/src/main/java/androidx/car/app/activity/LauncherActivity.java
+++ b/car/app/app-automotive/src/main/java/androidx/car/app/activity/LauncherActivity.java
@@ -60,7 +60,7 @@
     static Intent getDefaultIntent(Context context) {
         Intent intent =
                 new Intent(Intent.ACTION_MAIN).addCategory(
-                        Intent.CATEGORY_DEFAULT);
+                        Intent.CATEGORY_DEFAULT).setPackage(context.getPackageName());
         ResolveInfo resolveInfoList = context.getPackageManager().resolveActivity(intent,
                 PackageManager.MATCH_DEFAULT_ONLY);
 
diff --git a/car/app/app-samples/navigation/automotive/src/main/AndroidManifest.xml b/car/app/app-samples/navigation/automotive/src/main/AndroidManifest.xml
index df83e57..fe3031d 100644
--- a/car/app/app-samples/navigation/automotive/src/main/AndroidManifest.xml
+++ b/car/app/app-samples/navigation/automotive/src/main/AndroidManifest.xml
@@ -47,8 +47,7 @@
 
   <application
     android:label="@string/app_name"
-    android:icon="@drawable/ic_launcher"
-    android:extractNativeLibs="false">
+    android:icon="@drawable/ic_launcher">
 
     <meta-data
         android:name="com.android.automotive"
diff --git a/car/app/app-samples/navigation/automotive/src/main/AndroidManifestWithSdkVersion.xml b/car/app/app-samples/navigation/automotive/src/main/AndroidManifestWithSdkVersion.xml
index 0fb4867..091708d 100644
--- a/car/app/app-samples/navigation/automotive/src/main/AndroidManifestWithSdkVersion.xml
+++ b/car/app/app-samples/navigation/automotive/src/main/AndroidManifestWithSdkVersion.xml
@@ -56,8 +56,7 @@
 
   <application
     android:label="@string/app_name"
-    android:icon="@drawable/ic_launcher"
-    android:extractNativeLibs="false">
+    android:icon="@drawable/ic_launcher">
 
     <meta-data
         android:name="com.android.automotive"
diff --git a/car/app/app-samples/navigation/mobile/src/main/AndroidManifest.xml b/car/app/app-samples/navigation/mobile/src/main/AndroidManifest.xml
index b9ba68b..5ea244a 100644
--- a/car/app/app-samples/navigation/mobile/src/main/AndroidManifest.xml
+++ b/car/app/app-samples/navigation/mobile/src/main/AndroidManifest.xml
@@ -30,8 +30,7 @@
 
   <application
       android:label="@string/app_name"
-      android:icon="@drawable/ic_launcher"
-      android:extractNativeLibs="false">
+      android:icon="@drawable/ic_launcher">
 
     <activity
         android:name="androidx.car.app.sample.navigation.common.app.MainActivity"
diff --git a/car/app/app-samples/navigation/mobile/src/main/AndroidManifestWithSdkVersion.xml b/car/app/app-samples/navigation/mobile/src/main/AndroidManifestWithSdkVersion.xml
index 3fda109c..d88bd0d 100644
--- a/car/app/app-samples/navigation/mobile/src/main/AndroidManifestWithSdkVersion.xml
+++ b/car/app/app-samples/navigation/mobile/src/main/AndroidManifestWithSdkVersion.xml
@@ -56,8 +56,7 @@
 
     <application
         android:icon="@drawable/ic_launcher"
-        android:label="@string/app_name"
-        android:extractNativeLibs="false">
+        android:label="@string/app_name">
 
          <meta-data
              android:name="com.android.automotive"
@@ -105,4 +104,4 @@
         </activity>
 
     </application>
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml b/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml
index 92aba90..5dac37e 100644
--- a/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml
+++ b/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml
@@ -60,7 +60,6 @@
   <application
       android:label="@string/app_name"
       android:icon="@drawable/ic_launcher"
-      android:extractNativeLibs="false"
       android:supportsRtl="true">
 
     <meta-data
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 e1efe8c..bb049ca 100644
--- a/car/app/app-samples/showcase/automotive/src/main/AndroidManifestWithSdkVersion.xml
+++ b/car/app/app-samples/showcase/automotive/src/main/AndroidManifestWithSdkVersion.xml
@@ -68,8 +68,7 @@
 
   <application
       android:label="@string/app_name"
-      android:icon="@drawable/ic_launcher"
-      android:extractNativeLibs="false">
+      android:icon="@drawable/ic_launcher">
 
     <meta-data
         android:name="com.android.automotive"
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/StartScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/StartScreen.java
index cea546c..3d7465b 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/StartScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/StartScreen.java
@@ -54,7 +54,7 @@
 
         listBuilder.addItem(createRowForScreen(R.string.nav_demos_title,
                 createCarIconForImage(R.drawable.ic_map_white_48dp),
-                new NavigationDemosScreen(getCarContext())));
+                NavigationDemosScreen.createScreen(getCarContext())));
 
         return new ListTemplate.Builder()
                 .setSingleList(listBuilder.build())
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/NavigationDemosScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/NavigationDemosScreen.java
index 1d29ce4..f9de967 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/NavigationDemosScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/NavigationDemosScreen.java
@@ -19,14 +19,9 @@
 import androidx.annotation.NonNull;
 import androidx.car.app.CarContext;
 import androidx.car.app.Screen;
-import androidx.car.app.constraints.ConstraintManager;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ActionStrip;
+import androidx.car.app.ScreenManager;
 import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.ListTemplate;
 import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
 import androidx.car.app.sample.showcase.common.R;
 import androidx.car.app.sample.showcase.common.screens.navigationdemos.MapTemplateWithListDemoScreen;
 import androidx.car.app.sample.showcase.common.screens.navigationdemos.MapTemplateWithPaneDemoScreen;
@@ -37,6 +32,7 @@
 import androidx.car.app.sample.showcase.common.screens.navigationdemos.PlaceListNavigationTemplateDemoScreen;
 import androidx.car.app.sample.showcase.common.screens.navigationdemos.PlaceListTemplateBrowseDemoScreen;
 import androidx.car.app.sample.showcase.common.screens.navigationdemos.RoutePreviewDemoScreen;
+import androidx.car.app.sample.showcase.common.screens.paging.PagedListTemplate;
 import androidx.car.app.versioning.CarAppApiLevels;
 import androidx.core.graphics.drawable.IconCompat;
 
@@ -44,121 +40,110 @@
 import java.util.List;
 
 /** A screen showing a list of navigation demos */
-public final class NavigationDemosScreen extends Screen {
-    private static final int MAX_PAGES = 2;
+public final class NavigationDemosScreen extends PagedListTemplate.RowList {
+    private final @NonNull CarContext mCarContext;
 
-    private final int mPage;
-
-    public NavigationDemosScreen(@NonNull CarContext carContext) {
-        this(carContext, /* page= */ 0);
+    private NavigationDemosScreen(@NonNull CarContext carContext) {
+        this.mCarContext = carContext;
     }
 
-    public NavigationDemosScreen(@NonNull CarContext carContext, int page) {
-        super(carContext);
-        mPage = page;
+    /** Creates a screen with navigation demos */
+    @NonNull
+    public static Screen createScreen(@NonNull CarContext carContext) {
+        return new PagedListTemplate(new NavigationDemosScreen(carContext), carContext);
     }
 
     @NonNull
     @Override
-    public Template onGetTemplate() {
-        ItemList.Builder listBuilder = new ItemList.Builder();
-
-        int listLimit = getCarContext().getCarService(ConstraintManager.class).getContentLimit(
-                ConstraintManager.CONTENT_LIMIT_TYPE_LIST);
-
+    protected List<Row> getRows(@NonNull ScreenManager screenManager) {
         List<Row> screenList = new ArrayList<>();
 
-        screenList.add(createRow(buildCarIcon(R.drawable.ic_explore_white_24dp),
-                getCarContext().getString(R.string.nav_template_demos_title),
-                new NavigationTemplateDemoScreen(getCarContext())));
-
-        screenList.add(createRow(getCarContext().getString(
-                        R.string.place_list_nav_template_demo_title),
-                new PlaceListNavigationTemplateDemoScreen(getCarContext())));
-
-        screenList.add(createRow(getCarContext().getString(
-                        R.string.route_preview_template_demo_title),
-                new RoutePreviewDemoScreen(getCarContext())));
-
-        screenList.add(createRow(getCarContext().getString(
-                        R.string.notification_template_demo_title),
-                new NavigationNotificationsDemoScreen(getCarContext())));
-
-        screenList.add(createRow(getCarContext().getString(R.string.nav_map_template_demo_title),
-                new NavigationMapOnlyScreen(getCarContext())));
+        screenList.add(createRow(
+                screenManager,
+                buildCarIcon(R.drawable.ic_explore_white_24dp),
+                mCarContext.getString(R.string.nav_template_demos_title),
+                new NavigationTemplateDemoScreen(mCarContext)
+        ));
 
         screenList.add(createRow(
-                getCarContext().getString(R.string.place_list_template_demo_title),
-                new PlaceListTemplateBrowseDemoScreen(getCarContext())));
+                screenManager,
+                mCarContext.getString(R.string.place_list_nav_template_demo_title),
+                new PlaceListNavigationTemplateDemoScreen(mCarContext)
+        ));
 
-        screenList.add(createRow(getCarContext().getString(R.string.map_template_list_demo_title),
-                new MapTemplateWithListDemoScreen(getCarContext())));
+        screenList.add(createRow(
+                screenManager,
+                mCarContext.getString(R.string.route_preview_template_demo_title),
+                new RoutePreviewDemoScreen(mCarContext)
+        ));
 
-        screenList.add(createRow(getCarContext().getString(R.string.map_template_pane_demo_title),
-                new MapTemplateWithPaneDemoScreen(getCarContext())));
+        screenList.add(createRow(
+                screenManager,
+                mCarContext.getString(R.string.notification_template_demo_title),
+                new NavigationNotificationsDemoScreen(mCarContext)
+        ));
 
-        if (getCarContext().getCarAppApiLevel() >= CarAppApiLevels.LEVEL_6) {
+        screenList.add(createRow(
+                screenManager,
+                mCarContext.getString(R.string.nav_map_template_demo_title),
+                new NavigationMapOnlyScreen(mCarContext)
+        ));
+
+        screenList.add(createRow(
+                screenManager,
+                mCarContext.getString(R.string.place_list_template_demo_title),
+                new PlaceListTemplateBrowseDemoScreen(mCarContext)
+        ));
+
+        screenList.add(createRow(
+                screenManager,
+                mCarContext.getString(R.string.map_template_list_demo_title),
+                new MapTemplateWithListDemoScreen(mCarContext)
+        ));
+
+        screenList.add(createRow(
+                screenManager,
+                mCarContext.getString(R.string.map_template_pane_demo_title),
+                new MapTemplateWithPaneDemoScreen(mCarContext)
+        ));
+
+        if (mCarContext.getCarAppApiLevel() >= CarAppApiLevels.LEVEL_6) {
             screenList.add(
-                    createRow(getCarContext().getString(R.string.map_template_toggle_demo_title),
-                        new MapTemplateWithToggleDemoScreen(getCarContext())));
+                    createRow(
+                            screenManager,
+                            mCarContext.getString(R.string.map_template_toggle_demo_title),
+                            new MapTemplateWithToggleDemoScreen(mCarContext)));
         }
 
-        // If the screenArray size is under the limit, we will show all of them on the first page.
-        // Otherwise we will show them in multiple pages.
-        if (screenList.size() <= listLimit) {
-            for (int i = 0; i < screenList.size(); i++) {
-                listBuilder.addItem(screenList.get(i));
-            }
-        } else {
-            int currentItemStartIndex = mPage * listLimit;
-            int currentItemEndIndex = Math.min(currentItemStartIndex + listLimit,
-                    screenList.size());
-            for (int i = currentItemStartIndex; i < currentItemEndIndex; i++) {
-                listBuilder.addItem(screenList.get(i));
-            }
-        }
+        return screenList;
+    }
 
-        ListTemplate.Builder builder = new ListTemplate.Builder()
-                .setSingleList(listBuilder.build())
-                .setTitle(getCarContext().getString(R.string.nav_demos_title))
-                .setHeaderAction(Action.BACK);
-
-        // If the current page does not cover the last item, we will show a More button
-        if ((mPage + 1) * listLimit < screenList.size() && mPage + 1 < MAX_PAGES) {
-            builder.setActionStrip(new ActionStrip.Builder()
-                    .addAction(new Action.Builder()
-                            .setTitle(getCarContext().getString(R.string.more_action_title))
-                            .setOnClickListener(() -> {
-                                getScreenManager().push(
-                                        new NavigationDemosScreen(getCarContext(), mPage + 1));
-                            })
-                            .build())
-                    .build());
-        }
-
-        return builder.build();
+    @NonNull
+    @Override
+    protected String getTemplateTitle() {
+        return mCarContext.getString(R.string.nav_demos_title);
     }
 
     private CarIcon buildCarIcon(int imageId) {
         return new CarIcon.Builder(
                 IconCompat.createWithResource(
-                        getCarContext(),
+                        mCarContext,
                         imageId))
                 .build();
     }
 
-    private Row createRow(String title, Screen screen) {
+    private Row createRow(ScreenManager screenManager, String title, Screen screen) {
         return new Row.Builder()
                 .setTitle(title)
-                .setOnClickListener(() -> getScreenManager().push(screen))
+                .setOnClickListener(() -> screenManager.push(screen))
                 .build();
     }
 
-    private Row createRow(CarIcon image, String title, Screen screen) {
+    private Row createRow(ScreenManager screenManager, CarIcon image, String title, Screen screen) {
         return new Row.Builder()
                 .setImage(image)
                 .setTitle(title)
-                .setOnClickListener(() -> getScreenManager().push(screen))
+                .setOnClickListener(() -> screenManager.push(screen))
                 .setBrowsable(true)
                 .build();
     }
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/paging/PagedListTemplate.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/paging/PagedListTemplate.java
new file mode 100644
index 0000000..721c6bb
--- /dev/null
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/paging/PagedListTemplate.java
@@ -0,0 +1,114 @@
+/*
+ * 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.car.app.sample.showcase.common.screens.paging;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarContext;
+import androidx.car.app.Screen;
+import androidx.car.app.ScreenManager;
+import androidx.car.app.constraints.ConstraintManager;
+import androidx.car.app.model.Action;
+import androidx.car.app.model.ActionStrip;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.ListTemplate;
+import androidx.car.app.model.Row;
+import androidx.car.app.model.Template;
+import androidx.car.app.sample.showcase.common.R;
+
+import java.util.List;
+
+/**
+ * A generic screen featuring a paged list template.
+ *
+ * <p> Paging is useful to avoid truncation in situations where list length is aggressively limited.
+ */
+public class PagedListTemplate extends Screen {
+    private static final int MAX_PAGES = 2;
+
+    private final RowList mRowList;
+    private final int mPage;
+
+    public PagedListTemplate(@NonNull RowList rowList, @NonNull CarContext carContext) {
+        this(rowList, carContext, /* page= */ 0);
+    }
+
+    public PagedListTemplate(@NonNull RowList rowList, @NonNull CarContext carContext, int page) {
+        super(carContext);
+        mRowList = rowList;
+        mPage = page;
+    }
+
+    @NonNull
+    @Override
+    public Template onGetTemplate() {
+        ItemList.Builder listBuilder = new ItemList.Builder();
+
+        int listLimit = getCarContext().getCarService(ConstraintManager.class).getContentLimit(
+                ConstraintManager.CONTENT_LIMIT_TYPE_LIST);
+
+        List<Row> screenList = mRowList.getRows(getScreenManager());
+
+        // If the screenArray size is under the limit, we will show all of them on the first page.
+        // Otherwise we will show them in multiple pages.
+        if (screenList.size() <= listLimit) {
+            for (int i = 0; i < screenList.size(); i++) {
+                listBuilder.addItem(screenList.get(i));
+            }
+        } else {
+            int currentItemStartIndex = mPage * listLimit;
+            int currentItemEndIndex = Math.min(currentItemStartIndex + listLimit,
+                    screenList.size());
+            for (int i = currentItemStartIndex; i < currentItemEndIndex; i++) {
+                listBuilder.addItem(screenList.get(i));
+            }
+        }
+
+        ListTemplate.Builder builder = new ListTemplate.Builder()
+                .setSingleList(listBuilder.build())
+                .setTitle(mRowList.getTemplateTitle())
+                .setHeaderAction(Action.BACK);
+
+        // If the current page does not cover the last item, we will show a More button
+        if ((mPage + 1) * listLimit < screenList.size() && mPage + 1 < MAX_PAGES) {
+            builder.setActionStrip(new ActionStrip.Builder()
+                    .addAction(new Action.Builder()
+                            .setTitle(getCarContext().getString(R.string.more_action_title))
+                            .setOnClickListener(() -> {
+                                getScreenManager().push(
+                                        new PagedListTemplate(
+                                                mRowList,
+                                                getCarContext(),
+                                                mPage + 1
+                                        )
+                                );
+                            })
+                            .build())
+                    .build());
+        }
+
+        return builder.build();
+    }
+
+    /** A list of rows, used to populate a {@link PagedListTemplate} */
+    public abstract static class RowList {
+        @NonNull
+        protected abstract List<Row> getRows(@NonNull ScreenManager screenManager);
+
+        @NonNull
+        protected abstract String getTemplateTitle();
+    }
+}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/templatelayouts/ListTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/templatelayouts/ListTemplateDemoScreen.java
index ef9ebce..d198248 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/templatelayouts/ListTemplateDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/templatelayouts/ListTemplateDemoScreen.java
@@ -27,6 +27,7 @@
 import androidx.car.app.model.Template;
 import androidx.car.app.sample.showcase.common.R;
 import androidx.car.app.sample.showcase.common.screens.templatelayouts.listtemplates.ContentProviderIconsDemoScreen;
+import androidx.car.app.sample.showcase.common.screens.templatelayouts.listtemplates.EmptyListDemoScreen;
 import androidx.car.app.sample.showcase.common.screens.templatelayouts.listtemplates.RadioButtonListDemoScreen;
 import androidx.car.app.sample.showcase.common.screens.templatelayouts.listtemplates.SecondaryActionsAndDecorationDemoScreen;
 import androidx.car.app.sample.showcase.common.screens.templatelayouts.listtemplates.SectionedItemListDemoScreen;
@@ -64,6 +65,16 @@
         listBuilder.addItem(buildRowForTemplate(
                 new SectionedItemListDemoScreen(getCarContext()),
                 R.string.sectioned_item_list_demo_title));
+
+        // ========================================================================
+        // WARNING: 6 demos have been added above, which is the max list size for some users/devs.
+        // Demos added below may be truncated from the list in certain regions.
+        // ========================================================================
+
+        listBuilder.addItem(buildRowForTemplate(
+                new EmptyListDemoScreen(getCarContext()),
+                R.string.empty_list_demo_title));
+
         return new ListTemplate.Builder()
                 .setSingleList(listBuilder.build())
                 .setTitle(getCarContext().getString(R.string.list_template_demo_title))
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/templatelayouts/listtemplates/EmptyListDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/templatelayouts/listtemplates/EmptyListDemoScreen.java
new file mode 100644
index 0000000..865dd17
--- /dev/null
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/templatelayouts/listtemplates/EmptyListDemoScreen.java
@@ -0,0 +1,49 @@
+/*
+ * 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.car.app.sample.showcase.common.screens.templatelayouts.listtemplates;
+
+import static androidx.car.app.model.Action.BACK;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarContext;
+import androidx.car.app.Screen;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.ListTemplate;
+import androidx.car.app.model.Template;
+import androidx.car.app.sample.showcase.common.R;
+
+/** A screen demonstrating empty lists */
+public class EmptyListDemoScreen extends Screen {
+    public EmptyListDemoScreen(@NonNull CarContext carContext) {
+        super(carContext);
+    }
+
+    @NonNull
+    @Override
+    public Template onGetTemplate() {
+        return new ListTemplate.Builder()
+                .setSingleList(
+                        new ItemList.Builder()
+                                .setNoItemsMessage(
+                                        getCarContext().getString(R.string.empty_list_message)
+                                )
+                                .build()
+                )
+                .setHeaderAction(BACK)
+                .build();
+    }
+}
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-af/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-af/strings.xml
index 89d345f..0cf0d63 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-af/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-af/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Bykomende teks"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Voorbeeld van kiesbare lys"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Taakbeperkingdemonstrasie"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Taaklimiet is bereik\nAs jy voortgaan, sal die program verplig word om te stop"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Taak se stap %1$d van %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Klik om aan te beweeg"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Besoek asseblief die verskillende template en maak seker die motor is in rymodus"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Lys 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Lys 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Subteks onder elke lys"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Demonstrasie van leë lys"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Die lys is leeg"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstrasies oor diverse template"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Stal demonstrasies uit"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demonstrasie van templaatuitlegte"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml
index 0226e81..b61a927 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-am/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"አንዳንድ ተጨማሪ ጽሁፍ"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"የናሙና ሊመረጥ የሚችል ዝርዝር"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"የተግባር ገደብ ቅንጭብ ማሳያ"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"የተግባር ገደብ ላይ ደርሷል\nወደፊት መሄድ መተግበሪያውን በኃይል ያስቆማል"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"የተግባር እርምጃ %1$d ከ%2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"ወደፊት ለመሄድ ጠቅ ያድርጉ"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"እባክዎ የተለያዩ የቅንብር ደንቦችን ይጎብኙ እና መኪናው የማሽከርከር ሁነታ ላይ እንደሆነ ያረጋግጡ"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"ዝርዝር 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"ዝርዝር 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"ከእያንዳንዱ ዝርዝር ስር የግርጌ ጽሑፍ"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"ባዶ የዝርዝር ቅንጭብ ማሳያ"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"ዝርዝሩ ባዶ ነው"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"የተለያዩ ቅንብር ደንቦች ቅንጭብ ማሳያዎች"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"የመሳያ ቅንጭብ ማሳያ"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"የቅንብር ደንብ አቀማመጥ ቅንጭብ ማሳያዎች"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ar/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ar/strings.xml
index d347a04..5d3b85f 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ar/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ar/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"بعض النصوص الإضافية"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"نموذج لقائمة قابلة للاختيار"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"إصدار تجريبي لتقييد المهمّة"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"تم الوصول إلى حد المهمّة.\nسيؤدي التقدم إلى فرض إيقاف التطبيق."</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"‏خطوة المهمة: %1$d من %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"انقر للتقدم إلى الأمام."</string>
     <string name="task_content_allowed" msgid="545061986612431190">"يُرجى الانتقال إلى النماذج المختلفة والتأكّد من أنّ السيارة في وضع القيادة."</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"القائمة 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"القائمة 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"نص فرعي تحت كل قائمة"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"عرض تقديمي لقائمة فارغة"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"القائمة فارغة."</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"إصدارات تجريبية لنموذج ميزات متنوّعة"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"عرض الإصدارات التجريبية"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"عروض توضيحية لتنسيقات النماذج"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-as/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-as/strings.xml
index 6014a5f..cc3c569 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-as/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-as/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"কিছু অতিৰিক্ত পাঠ"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"বাছনিযোগ্য সূচীৰ নমুনা"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"কাৰ্য প্ৰতিবন্ধিত কৰাৰ ডেম’"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"কাৰ্যৰ সীমাত উপনীত হৈছে\nআগবাঢ়ি গ’লে এপ্‌টো বলপূৰ্বকভাৱে বন্ধ কৰা হ’ব"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"কাৰ্যৰ পদক্ষেপ %2$d টাৰ %1$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"আগবাঢ়ি যাবলৈ ক্লিক কৰক"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"অনুগ্ৰহ কৰি বিভিন্ন টেমপ্লে’টসমূহ চাওক আৰু গাড়ীখন ড্ৰাইভিং ম’ডত থকাটো নিশ্চিত কৰক"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"সূচী ১"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"সূচী ২"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"প্ৰতিখন সূচীৰ অন্তৰ্গত উপপাঠ"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"খালী সূচীৰ ডেম’"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"সূচীখন খালী হৈ আছে"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"সানমিহলি টেম্পলে’টৰ ডেম’"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"ডেম’ দেখুৱাওক"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"টেমপ্লে’ট লে’আউটৰ ডেম’"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-az/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-az/strings.xml
index d02a701..585f3e4 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-az/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-az/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Bəzi əlavə mətn"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Nümunə seçilə bilən siyahı"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Tapşırıq Məhdudiyyəti Demosu"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Tapşırıq limitinə çatdınız\nİrəli getmək tətbiqi məcburi dayandıracaq"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Tapşırıq mərhələsi %1$d/%2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"İrəli keçmək üçün klikləyin"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Müxtəlif şablonları ziyarət edin və avtomobilin sürücülük rejimində olduğuna əmin olun"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Siyahı 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Siyahı 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Hər siyahı üzrə alt mətn"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Boş siyahı demosu"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Siyahı boşdur"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Digər Şablon Demoları"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Vitrin Demoları"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Şablon Düzən Demoları"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-b+sr+Latn/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-b+sr+Latn/strings.xml
index 63c19f3..390aa0c 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-b+sr+Latn/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-b+sr+Latn/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Dodatni tekst"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Primer liste koja može da se izabere"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demonstracija ograničenja za zadatke"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Dostignuto je ograničenje zadataka\nAko nastavite, aplikacija će se prinudno zaustaviti"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"%1$d. korak zadatka od %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Kliknite da biste išli napred"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Posetite različite šablone i uverite se da je automobil u režimu vožnje"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Lista 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Lista 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Podtekst ispod svake liste"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Demonstracija prazne liste"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Lista je prazna"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstracije različitih šablona"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demonstracije prikazivanja"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demonstracije izgleda šablona"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-be/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-be/strings.xml
index a1cda80..884e9b1 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-be/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-be/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Дадатковы тэкст"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Узор спіса выбару"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Дэманстрацыя абмежавання колькасці задач"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Дасягнута максімальная колькасць задач\nДалейшы рух прывядзе да прымусовага спынення праграмы"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Крок задачы %1$d з %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Націсніце, каб рухацца далей"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Праглядзіце розныя шаблоны і пераканайцеся, што аўтамабіль знаходзіцца ў рэжыме \"За рулём\""</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Спіс 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Спіс 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Тэкст пад кожным спісам"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Дэмаверсія пустога спіса"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Спіс пусты"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Дэманстрацыі розных шаблонаў"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Дэманстрацыі выбранага"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Дэманстрацыі макета шаблона"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-bg/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-bg/strings.xml
index e4844e6..bf7a5b8 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-bg/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-bg/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Допълнителен текст"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Примерен списък с опции"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Демонстрация на ограничението за задачи"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Ограничението за задачи е достигнато\nАко продължите, приложението ще бъде спряно принудително"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Стъпка %1$d от %2$d в задачата"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Кликнете, за да продължите напред"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Отворете отделните шаблони, докато автомобилът е в режим за шофиране"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Списък 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Списък 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Подтекст под всеки списък"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Демонстрация на празен списък"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Списъкът е празен"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Демонстрации на разни шаблони"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Демонстрации на Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Демонстрации на оформления за шаблон"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-bn/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-bn/strings.xml
index cc25232..e5e612a 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-bn/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-bn/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"অতিরিক্ত টেক্সট"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"বেছে নেওয়া যায় এমন তালিকার নমুনা"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"টাস্কের উপর আরোপ করা বিধিনিষেধ সম্পর্কিত ডেমো"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"টাস্কের সীমা পেরিয়ে গেছে\nআরও এগোলে, অ্যাপকে জোর করে বন্ধ করা হবে"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"টাস্কের %2$d ধাপের %1$d নম্বর ধাপ"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"এগিয়ে যাওয়ার জন্য ক্লিক করুন"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"বিভিন্ন টেমপ্লেট দেখুন ও নিশ্চিত করুন যে গাড়িটি \'ড্রাইভিং\' মোডে আছে"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"তালিকা ১"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"তালিকা ২"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"প্রতিটি তালিকায় থাকা সাবটেক্সট"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"ডেমোর খালি তালিকা"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"তালিকায় কিছু নেই"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"অন্যান্য টেমপ্লেটের ডেমো"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"ডেমো শোকেস করুন"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"টেমপ্লেট লেআউট সংক্রান্ত ডেমো"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-bs/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-bs/strings.xml
index 8f0caf4..e9f6b13 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-bs/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-bs/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Neki dodatni tekst"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Uzorak liste s odabirima"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demo verzija ograničenja za zadatak"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Dostignuto je ograničenje za zadatak\nAko nastavite, prisilno ćete zaustaviti aplikaciju"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Korak zadatka: %1$d od %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Kliknite da idete naprijed"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Posjetite različite šablone i pobrinite se da automobil bude u načinu rada za vožnju"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"1. lista"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"2. lista"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Podtekst ispod svake liste"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Demonstracija prazne liste"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Lista je prazna"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demo verzije raznih šablona"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demo verzije predstavljanja"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demo verzije rasporeda predloška"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ca/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ca/strings.xml
index 0c95fc9..5c42026 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ca/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ca/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Text addicional"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Llista seleccionable de mostra"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demostració de restricció de tasques"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"S\'ha arribat al límit de tasques\nEn continuar es forçarà l\'aturada de l\'aplicació"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Pas de la tasca %1$d de %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Fes clic per anar endavant"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Visita les diferents plantilles i comprova que el cotxe sigui en mode de conducció"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Llista 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Llista 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Subtext a sota de cada llista"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Demostració de llista buida"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"La llista és buida"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demostracions de plantilles diverses"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demostracions de Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demostracions de dissenys de plantilles"</string>
@@ -334,7 +337,7 @@
     <string name="voice_access_demo_title" msgid="3825223890895361496">"Pantalla de demostració de Voice Access"</string>
     <string name="user_interactions_demo_title" msgid="1356952319161314986">"Interaccions dels usuaris"</string>
     <string name="request_permission_menu_demo_title" msgid="4796486779527427017">"Demostracions de sol·licitud de permisos"</string>
-    <string name="application_overflow_title" msgid="396427940886169325">"Validador del menú addicional de l\'aplicació"</string>
+    <string name="application_overflow_title" msgid="396427940886169325">"Validador del menú de desbordament de l\'aplicació"</string>
     <string name="application_overflow_subtitle1" msgid="7429415605726615529">"Prova les següents plantilles mentre canvies"</string>
     <string name="application_overflow_subtitle2" msgid="4385123036846369714">"el vehicle de l\'estat aparcat a mode de conducció"</string>
     <string name="perm_group" msgid="3834918337351876270">"Grup de permisos"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-cs/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-cs/strings.xml
index 3c7db09..2dfc4cc 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-cs/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-cs/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Nějaký další text"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Vzor seznamu s výběrem"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Ukázka omezení úkolů"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Byl dosažen limit úkolů\nPokračováním vynutíte ukončení aplikace"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Krok úkolu %1$d z %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Kliknutím přejdete vpřed"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Navštivte různé šablony a ujistěte se, že je auto v režimu jízdy"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Seznam 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Seznam 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Popis pod každým seznamem"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Ukázka prázdného seznamu"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Seznam je prázdný"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Ukázky různých šablon"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Ukázky Výběru"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Ukázky šablon rozvržení"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-da/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-da/strings.xml
index 4881ef8..f9b5b49 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-da/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-da/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Noget yderligere tekst"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Eksempel på en liste, der kan vælges"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demonstration af grænsen for opgaver"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Grænsen for opgaver blev nået\nHvis du fortsætter, tvinges appen til at standse"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Opgavetrin %1$d af %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Klik for at gå videre"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Gå til de forskellige skabeloner, og tjek, at bilen er i køretilstand"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Liste 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Liste 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Tekst under hver liste"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Demo på tom liste"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Listen er tom"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstrationer af diverse skabeloner"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Vis demonstrationer"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demoer for skabelonlayout"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-de/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-de/strings.xml
index ea7398b..529e49f 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-de/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-de/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Zusätzlicher Text"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Beispiel für auswählbare Liste"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demo der Aufgabenbeschränkung"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Aufgabenlimit erreicht\nDurch Fortfahren wird das Beenden der App erzwungen"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Aufgabe: Schritt %1$d von %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Klicken, um fortzufahren"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Bitte rufe die verschiedenen Vorlagen auf und achte darauf, dass sich das Auto im Fahrmodus befindet"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Liste 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Liste 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Subtext unter jeder Liste"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Leere Liste – Demo"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Die Liste ist leer"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demos der verschiedenen Vorlagen"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demos anzeigen"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demo der Layoutvorlagen"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-el/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-el/strings.xml
index 6b7cf3d..fe903a9 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-el/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-el/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Λίγο πρόσθετο κείμενο"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Επιλέξιμη λίστα δείγματος"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Επίδειξη περιορισμού εργασιών"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Συμπληρώθηκε το όριο εργασιών\nΜε μετάβαση προς τα εμπρός, θα γίνει αναγκαστική διακοπή της εφαρμογής."</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Βήμα εργασίας %1$d από %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Κάντε κλικ για μετάβαση εμπρός"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Επισκεφτείτε διαφορετικά πρότυπα και διασφαλίστε ότι το αυτοκίνητό σας είναι σε λειτουργία οδήγησης"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Λίστα 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Λίστα 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Δευτερεύον κείμενο κάτω από κάθε λίστα"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Επίδειξη κενής λίστας"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Η λίστα είναι κενή"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Διάφορες επιδείξεις προτύπων"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Προβολή επιδείξεων"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Επιδείξεις διάταξης προτύπου"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-en-rAU/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-en-rAU/strings.xml
index 00c576f..fe1a80b 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-en-rAU/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-en-rAU/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Some additional text"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Sample selectable list"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Task restriction demo"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Task limit reached\nGoing forward will force stop the app"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Task step %1$d of %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Click to go forward"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Please visit the different templates and ensure that the car is in driving mode"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"List 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"List 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Subtext under each list"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Empty list demo"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"The list is empty"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Misc templates demos"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Showcase demos"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Template layout demos"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-en-rCA/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-en-rCA/strings.xml
index e74be41..3ac9b76 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-en-rCA/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-en-rCA/strings.xml
@@ -298,7 +298,7 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Some additional text"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Sample selectable list"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Task Restriction Demo"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Task limit reached\nGoing forward will force stop the app"</string>
+    <string name="task_limit_reached_msg" msgid="7162842196382260992">"This will overflow the step count, and lead you to a new screen."</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Task step %1$d of %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Click to go forward"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Please visit the different templates and ensure the car is in driving mode"</string>
@@ -327,6 +327,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"List 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"List 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Subtext under each list"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Empty List Demo"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"The list is empty"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Misc Templates Demos"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Showcase Demos"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Template Layout Demos"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-en-rGB/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-en-rGB/strings.xml
index 00c576f..fe1a80b 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-en-rGB/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-en-rGB/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Some additional text"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Sample selectable list"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Task restriction demo"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Task limit reached\nGoing forward will force stop the app"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Task step %1$d of %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Click to go forward"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Please visit the different templates and ensure that the car is in driving mode"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"List 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"List 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Subtext under each list"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Empty list demo"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"The list is empty"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Misc templates demos"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Showcase demos"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Template layout demos"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-en-rIN/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-en-rIN/strings.xml
index 00c576f..fe1a80b 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-en-rIN/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-en-rIN/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Some additional text"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Sample selectable list"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Task restriction demo"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Task limit reached\nGoing forward will force stop the app"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Task step %1$d of %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Click to go forward"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Please visit the different templates and ensure that the car is in driving mode"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"List 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"List 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Subtext under each list"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Empty list demo"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"The list is empty"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Misc templates demos"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Showcase demos"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Template layout demos"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-en-rXC/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-en-rXC/strings.xml
index f2b3cc0..b8771e8 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-en-rXC/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-en-rXC/strings.xml
@@ -298,7 +298,7 @@
     <string name="some_additional_text" msgid="4009872495806318260">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‎‎‏‎‏‏‏‏‎‏‏‎‏‏‏‎‎‏‎‏‎‎‎‏‎‏‎‏‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎Some additional text‎‏‎‎‏‎"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‎‏‎‎‏‎‏‏‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‎‎‎‎‏‏‎‏‏‏‎‎‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎Sample selectable list‎‏‎‎‏‎"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‎‎‏‎‏‏‏‎‎‏‏‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‏‏‎‎‏‎‏‎‏‏‏‎‏‏‏‏‎‏‎‏‏‏‎‏‎Task Restriction Demo‎‏‎‎‏‎"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‏‏‏‏‎‏‏‎‎‏‎‎‎‏‏‎‎‎‏‎‏‎‎‎‎‏‎‎‎Task limit reached‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Going forward will force stop the app‎‏‎‎‏‎"</string>
+    <string name="task_limit_reached_msg" msgid="7162842196382260992">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‎‏‏‏‏‎‎‎‎‏‏‏‎‎‎‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‏‎‏‏‎‎‎‏‏‏‏‏‎‎‎‎‎‎‎‎‎This will overflow the step count, and lead you to a new screen.‎‏‎‎‏‎"</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‎‎‏‎‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‏‏‎‎‎‏‏‎‎‏‏‏‏‏‏‎Task step %1$d of %2$d‎‏‎‎‏‎"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‎‎‏‏‎‏‏‎‎‏‎‎‎‏‎‎‏‏‎Click to go forward‎‏‎‎‏‎"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‏‎‎‏‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎‏‎‎‏‎‎‏‎‎‏‎‏‎‏‎‏‏‎‎Please visit the different templates and ensure the car is in driving mode‎‏‎‎‏‎"</string>
@@ -327,6 +327,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎‎‏‏‎‏‎‏‎‎‎‏‏‏‏‎‏‎‏‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‏‎‎‏‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎List 1‎‏‎‎‏‎"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‏‏‏‏‏‏‎‎‎‎‎‎‏‎‎‎‎‏‏‏‏‎‏‎‎‏‎‏‏‏‏‎‎‎‏‎List 2‎‏‎‎‏‎"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‎‎‏‎‏‎‎‎‎‏‏‏‎‏‏‎‎‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‎Subtext under each list‎‏‎‎‏‎"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‏‏‎‏‎‏‎‎‎‎‎‎‎‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‎‏‏‏‏‎‎‎‏‎‎‎‏‏‏‎‎‎‏‏‎‏‏‏‎Empty List Demo‎‏‎‎‏‎"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‏‎‏‎‏‎‎‏‎‎‏‎‏‎‎The list is empty‎‏‎‎‏‎"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‎‏‏‎‎‏‏‏‎‎‎‏‎‎‎‎‎‏‎‎‎‏‎‎‎‎‎‎‎‏‎‏‎‎‎‏‎‎‎‏‏‎‏‏‎‎‏‏‎‎‏‎‎Misc Templates Demos‎‏‎‎‏‎"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‎‎‏‏‎‏‎‎‏‏‏‎‏‎‎‏‎‎‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‏‎‎‎‎‎‎‏‏‎‎‎‎Showcase Demos‎‏‎‎‏‎"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‏‏‏‎‎‎‎‎‏‏‎‏‏‎‎‏‎‎‎‏‎‎‎‏‏‎‎‎‎‏‏‏‏‎‎‏‎‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‏‏‏‎Template Layout Demos‎‏‎‎‏‎"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-es-rUS/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-es-rUS/strings.xml
index d4b16a9..e31dae5 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-es-rUS/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-es-rUS/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Texto adicional"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Lista seleccionable de muestra"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demostración de la restricción de tareas"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Se alcanzó el límite de tareas\nSi avanzas, se forzará la detención de la app"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Paso %1$d de %2$d de la tarea"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Haz clic para avanzar"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Visita las diferentes plantillas y asegúrate de que el automóvil esté en modo de conducción"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Lista 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Lista 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Subtexto debajo de cada lista"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Demostración sobre cómo se vacía una lista"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"La lista está vacía"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demostraciones de plantillas varias"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demostraciones de Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demostración de plantilla de diseño"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-es/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-es/strings.xml
index 35c6bec..4577fab 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-es/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-es/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Texto adicional"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Ejemplo de lista seleccionable"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demo de restricción de tareas"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Se ha alcanzado el límite de tareas\nSi continuas, se forzará la detención de la aplicación"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Paso %1$d de %2$d de la tarea"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Haz clic para continuar"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Consulta las diferentes plantillas y comprueba que el coche esté en modo de conducción"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Lista 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Lista 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Subtexto debajo de cada lista"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Demostración de lista vacía"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"La lista está vacía"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Otras demos de plantillas"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demos de Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demos de diseño de plantillas"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-et/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-et/strings.xml
index 7dcd4ca..e856d66 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-et/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-et/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Lisatekst"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Valitava loendi näidis"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Ülesannete piiramise demo"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Ülesannete piirang on saavutatud\nJätkamisel rakendus sundsuletakse"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Ülesande toiming %1$d %2$d-st"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Klõpsake edasiliikumiseks"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Vaadake eri mallid üle ja veenduge, et auto oleks sõidurežiimis"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Loend 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Loend 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Iga loendi all olev alltekst"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Tühja loendi demo"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"See loend on tühi"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Mitmesuguste mallide demod"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Esiletõstmise demod"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Malli paigutuse demod"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-eu/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-eu/strings.xml
index a39db4e..c6edfed 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-eu/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-eu/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Testu gehigarri apur bat"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Hauta daitekeen zerrenda baten lagina"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Zereginak mugatzeko demo-bertsioa"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Zereginen mugara iritsi zara\nAurrera eginez gero, aplikazioa gelditzera behartuko duzu"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Zereginaren %1$d/%2$d urratsa"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Aurrera egiteko, sakatu hau"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Ikusi txantiloiak eta ziurtatu ibilgailua gidatze moduan dagoela"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Zerrenda 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Zerrenda 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Zerrenda bakoitzaren beheko azpitestua"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Zerrenda huts baten adibidea"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Zerrenda hutsik dago"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Bestelako txantiloien demo-bertsioak"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Erakutsi demo-bertsioak"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Txantiloi-diseinuen demo-bertsioak"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-fa/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-fa/strings.xml
index 6b14091..5f761b8 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-fa/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-fa/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"چند نوشتار دیگر"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"فهرست نمونه قابل‌انتخاب"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"محدودیت تکلیف نمونه"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"به حد مجاز تکلیف رسیدید\nاگر ادامه دهید، برنامه به‌اجبار متوقف خواهد شد"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"‏مرحله %1$d از %2$d تکلیف"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"برای ادامه دادن، کلیک کنید"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"لطفاً از الگوهای مختلف بازدید کنید و مطمئن شوید خودرو در حالت رانندگی باشد"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"فهرست ۱"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"فهرست ۲"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"نوشتار فرعی زیر هر فهرست"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"نمونه فهرست خالی"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"فهرست خالی است"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"الگوهای متفرقه نمونه"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"نمایش نمونه‌ها"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"نسخه‌های نمونه طرح‌بندی الگو"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-fi/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-fi/strings.xml
index 8ad1625..d611c45 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-fi/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-fi/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Hieman lisätekstiä"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Lista valittavista näytteistä"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Tehtävärajan esittely"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Tehtäväraja saavutettu\nEteneminen pakottaa sovelluksen sulkeutumaan"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Tehtävän vaihe %1$d/%2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Siirry eteenpäin klikkaamalla"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Tarkista eri mallit ja varmista, että auto on ajotilassa"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Lista 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Lista 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Kunkin listan alla oleva alateksti"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Tyhjä esittelylista"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Lista on tyhjä"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Sekalaisten mallien esittelyt"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Showcase-esittelyt"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Malliasettelujen esittelyt"</string>
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 76b6626..67ec32a 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
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Du texte supplémentaire"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Exemple de liste sélectionnable"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Démo de la restriction des tâches"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Limite de la tâche atteinte\nAller de l\'avant forcera l\'arrêt de l\'application"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Étape %1$d de %2$d de la tâche"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Cliquez pour avancer"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Veuillez consulter les différents modèles et vous assurer que la voiture est en mode Voiture"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Liste 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Liste 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Sous-titre de chaque liste"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Démo de liste vide"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"La liste est vide"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Démos de divers modèles"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Présenter les démos"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Démos de mise en page du modèle"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-fr/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-fr/strings.xml
index 9e261b2..8b990bf 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-fr/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-fr/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Texte supplémentaire"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Exemple de liste sélectionnable"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Démo des restrictions de tâche"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Limite de tâche atteinte\nContinuer forcera l\'arrêt de l\'appli"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Étape %1$d sur %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Cliquer pour continuer"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Veuillez consulter les différents modèles et vous assurer que le véhicule est en mode Voiture."</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Liste 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Liste 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Texte sous chaque liste"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Vider la liste de démonstration"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"La liste est vide"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Démos de divers modèles"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Présenter les démos"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Démos de mise en page du modèle"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-gl/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-gl/strings.xml
index 8086d0a..1a64654 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-gl/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-gl/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Texto adicional"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Exemplo de lista seleccionable"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demostración de restrición de tarefas"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Alcanzouse o límite de tarefas\nSe continúas, forzarase a detención da aplicación"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Paso %1$d de %2$d da tarefa"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Fai clic para continuar"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Consulta os diferentes modelos e comproba que o coche estea no modo de condución"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Lista 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Lista 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Texto secundario debaixo de cada lista"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Versión de demostración de lista baleira"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"A lista está baleira"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Outras demostracións de modelos"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demostracións de Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demostracións de deseños de modelo"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-gu/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-gu/strings.xml
index f56e44e..01606eb 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-gu/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-gu/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"અમુક વધારાની ટેક્સ્ટ"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"પસંદ કરી શકાય તેની નમૂનારૂપી સૂચિ"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"કાર્ય પ્રતિબંધનો ડેમો"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"કાર્યની મર્યાદાએ પહોંચી ગયા\nઆગળ વધવાથી ઍપ ફરજિયાત બંધ થઈ જશે"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"%2$dમાંથી %1$d કાર્ય માટેનું પગલું"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"આગળ વધવા માટે ક્લિક કરો"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"કૃપા કરીને વિભિન્ન નમૂનાઓની મુલાકાત લો અને ખાતરી કરો કે કાર ડ્રાઇવિંગ મોડમાં છે"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"સૂચિ 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"સૂચિ 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"દરેક સૂચિ હેઠળની પેટાટેક્સ્ટ"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"ખાલી સૂચિનો ડેમો"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"સૂચિ ખાલી છે"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"વિવિધ નમૂનાઓના ડેમો"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"ડેમો બતાવો"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"નમૂનાના લેઆઉટનો ડેમો"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-hi/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-hi/strings.xml
index d98d811..f25eae1 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-hi/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-hi/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"अतिरिक्त टेक्स्ट"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"चुनी जा सकने वाली सूची का सैंपल"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"टास्क पर लगी पाबंदी से जुड़ा डेमो"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"टास्क की सीमा पूरी हुई\nआगे जाने पर, ऐप्लिकेशन को ज़बरदस्ती रोक दिया जाएगा"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"टास्क के %2$d चरणों में से %1$d चरण"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"आगे जाने के लिए क्लिक करें"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"कृपया अलग-अलग टेंप्लेट पर जाकर, यह पक्का करें कि कार ड्राइविंग मोड में है"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"सूची 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"सूची 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"हर सूची के नीचे सबटेक्स्ट"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"खाली सूची का डेमो"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"सूची खाली है"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"दूसरे टेंप्लेट के डेमो"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"डेमो दिखाएं"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"टेंप्लेट लेआउट के डेमो"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-hr/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-hr/strings.xml
index 56ea02e..74d861c 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-hr/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-hr/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Dodatni tekst"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Popis uzoraka koji se može birati"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Pokazna verzija ograničenja zadatka"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Dosegnuto ograničenje zadatka\nAko nastavite, aplikacija će se prisilno zaustaviti"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Korak zadatka: %1$d od %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Kliknite da biste nastavili"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Posjetite različite predloške i potvrdite da je automobil u načinu za vožnju"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Popis 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Popis 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Podtekst ispod svakog popisa"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Pokazna verzija praznog popisa"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Popis je prazan"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Pokazne verzije raznih predložaka"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Prikaži pokazne verzije"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Pokazne verzije izgleda predloška"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-hu/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-hu/strings.xml
index 418ef42..74ead8c 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-hu/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-hu/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Némi további szöveg"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Minta kiválasztható lista"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Feladatkorlátozás – demó"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Feladatkorlát elérve\nA továbblépés az app bezárását kényszeríti"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"%2$d/%1$d. lépés a feladatban"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Kattintson a továbblépéshez"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Keresse fel a különböző sablonokat, és ellenőrizze, hogy az autó vezetési módban van-e"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"1. lista"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"2. lista"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Alszöveg az egyes listák alatt"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Üres lista – bemutató"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"A lista üres"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Egyéb sablonok – demók"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Kirakat – demók"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Sablonelrendezések bemutatói"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-hy/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-hy/strings.xml
index 2d43574..9021fa9 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-hy/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-hy/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Լրացուցիչ տեքստ"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Ընտրելու տարբերակներով ցանկի օրինակ"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Առաջադրանքների սահմանափակման դեմո"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Առաջադրանքների սահմանաչափը սպառված է։\nԵթե շարունակեք, հավելվածի աշխատանքը կկանգնեցվի։"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Առաջադրանքի քայլ %1$d՝ %2$d-ից"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Սեղմեք՝ առաջ անցնելու համար"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Դիտեք տարբեր ձևանմուշներ և համոզվեք, որ մեքենան վարելու ռեժիմում է"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Ցանկ 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Ցանկ 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Նկարագրություն՝ յուրաքանչյուր ցանկի տակ"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Դատարկ ցանկի ցուցադրում"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Ցանկը դատարկ է"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Այլ ձևանմուշների դեմոներ"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Ցուցադրել դեմոները"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Ձևանմուշի դասավորության դեմոներ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-in/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-in/strings.xml
index efd56c4..b4af37a 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-in/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-in/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Beberapa teks tambahan"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Contoh daftar yang dapat dipilih"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demo Batasan Tugas"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Batas tugas tercapai\nJika dilanjutkan, aplikasi akan dipaksa berhenti"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Langkah tugas %1$d dari %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Klik untuk melanjutkan"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Buka template lain dan pastikan mobil dalam mode mengemudi"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Daftar 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Daftar 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Subteks dalam setiap daftar"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Demo Daftar Kosong"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Daftar ini kosong"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demo Template Lain-Lain"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demo Berita Pilihan"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demo Tata Letak Template"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-is/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-is/strings.xml
index 06c9d74..a86d49a 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-is/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-is/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Einhver viðbótartexti"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Dæmi um veljanlegan lista"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Sýnishorn verkefnismarka"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Verkefnismörkum náð\nEf haldið er áfram verður lokun forrits þvinguð"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Verkefnisskref %1$d af %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Smelltu til að halda áfram"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Opnaðu mismunandi sniðmát og gakktu úr skugga um að bíllinn sé í akstursstillingu"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Listi 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Listi 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Texti undir hverjum lista"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Sýnishorn af auðum lista"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Listinn er auður"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Sýnishorn ýmissa sniðmáta"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Prufuútgáfur Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Sýnishorn sniðmátsuppsetningar"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-it/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-it/strings.xml
index 418a52d..6df2b82 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-it/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-it/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Testo aggiuntivo"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Elenco selezionabile esempi"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demo Limite attività"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Limite attività raggiunto\nSe prosegui forzerai l\'interruzione dell\'app"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Passaggio attività %1$d di %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Fai clic per proseguire"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Controlla i diversi modelli e assicurati che l\'auto sia in modalità Auto"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Elenco 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Elenco 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Sottotesto sotto ogni elenco"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Demo dell\'elenco vuoto"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"L\'elenco è vuoto"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demo modelli vari"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demo Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demo Layout modello"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-iw/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-iw/strings.xml
index 7d6bc35..a674e01 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-iw/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-iw/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"עוד טקסט"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"דוגמה של רשימה שאפשר לבחור"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"הדגמה של הגבלת המשימות"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"הגעת להגבלת המשימות\nהמשך יוביל לסגירה ידנית של האפליקציה"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"‏שלב %1$d מתוך %2$d במשימה"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"יש ללחוץ כדי להמשיך"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"יש לבדוק את התבניות השונות ולוודא שהמכונית במצב נהיגה"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"רשימה 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"רשימה 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"טקסט משנה מתחת לכל רשימה"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"הדגמה של רשימה ריקה"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"הרשימה ריקה"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"הדגמות של תבניות שונות"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"הדגמות תצוגה"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"הדגמות של אפשרויות פריסה של תבנית"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ja/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ja/strings.xml
index 1f5aa3c..a4d9ac2 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ja/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ja/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"追加のテキスト"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"選択可能リストの例"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"タスク制限のデモ"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"タスクの上限に達しました\n続行するとアプリが強制停止されます"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"タスクのステップ %1$d / %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"クリックして続行"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"別のテンプレートにアクセスして自動車が運転モードであることを確認してください"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"リスト 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"リスト 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"各リストのサブテキスト"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"空のリストのデモ"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"このリストは空です"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"その他のテンプレートのデモ"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"デモを表示"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"テンプレート レイアウトのデモ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ka/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ka/strings.xml
index 872a4e0..51911ba 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ka/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ka/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"რაღაც დამატებითი ტექსტი"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"არჩევითი სია"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"ამოცანების გამკაცრების დემო"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"მიღწეულია ამოცანების ლიმიტი\nგაგრძელება გამოიწვევს აპის ძალით შეჩერებას"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"ამოცანის ეტაპი: %1$d/%2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"დააწკაპუნეთ გასაგრძელებლად"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"გთხოვთ, ეწვიოთ სხვადასხვა შაბლონებს, რათა დარწმუნდეთ, რომ მანქანა საავტომობილო რეჟიმშია"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"სია 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"სია 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"ქვეტექსტი თითოეული სიის ქვეშ"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"ცარიელი სია სადემონსტრაციოდ"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"სია ცარიელია"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"სხვადასხვა შაბლონური დემოები"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"დემოების ჩვენება"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"შაბლონის განლაგების დემო"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-kk/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-kk/strings.xml
index 317dfc35..71f3db9 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-kk/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-kk/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Қосымша мәтін"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Үзіндіні таңдауға болатын тізім"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Тапсырманы шектеудің демо нұсқасы"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Тапсырма өз шегіне жетті.\nІлгері жүрсеңіз, қолданба күштеп тоқтатылады."</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"%1$d/%2$d тапсырма қадамы"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Ілгері жүру үшін басыңыз."</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Әртүрлі үлгілерді қарап, автокөліктің жүргізу режимінде тұрғанына көз жеткізіңіз."</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"1-тізім"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"2-тізім"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Әр тізім астындағы түсініктеме мәтін"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Бос тізім (демо)"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Тізім бос."</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"\"Басқалары\" үлгісінің демо нұсқасы"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Презентацияның демо нұсқасы"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Формат үлгісі (демо нұсқалары)"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-km/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-km/strings.xml
index 5164aa5..8a612f2 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-km/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-km/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"អត្ថបទ​បន្ថែមមួយចំនួន"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"បញ្ជីគំរូដែល​អាចជ្រើសរើសបាន"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"គំរូបង្ហាញការដាក់​កំហិតកិច្ចការ"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"បានឈានដល់​ដែនកំណត់កិច្ចការហើយ\nការបន្តទៀត​នឹងបង្ខំឱ្យកម្មវិធីបញ្ឈប់"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"ជំហានកិច្ចការទី %1$d នៃ %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"ចុចដើម្បីបន្ត"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"សូមចូលមើលទម្រង់គំរូផ្សេងៗ និងធ្វើឱ្យប្រាកដថា រថយន្តស្ថិតក្នុងមុខងារបើកបរ"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"បញ្ជីទី 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"បញ្ជីទី 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"អត្ថបទរងនៅក្រោមបញ្ជីនីមួយៗ"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"គំរូបង្ហាញបញ្ជីទទេ"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"បញ្ជីគឺទទេ"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"គំរូបង្ហាញនៃ​ទម្រង់គំរូផ្សេងៗ"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"គំរូបង្ហាញអំពីការតាំងរំលេច"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"គំរូបង្ហាញនៃប្លង់​ទម្រង់គំរូ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-kn/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-kn/strings.xml
index 153447c..9aa6f73 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-kn/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-kn/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"ಕೆಲವು ಹೆಚ್ಚುವರಿ ಪಠ್ಯ"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"ಆಯ್ಕೆ ಮಾಡಬಹುದಾದ ಮಾದರಿಯ ಪಟ್ಟಿ"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"ಕಾರ್ಯದ ನಿರ್ಬಂಧಿತ ಡೆಮೋ"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"ಕಾರ್ಯದ ಮಿತಿಯನ್ನು ತಲುಪಿದ್ದೀರಿ\nಮುಂದುವರಿಸಿದರೆ ಆ್ಯಪ್ ಅನ್ನು ಬಲವಂತವಾಗಿ ನಿಲ್ಲಿಸಲಾಗುತ್ತದೆ"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"ಕಾರ್ಯದ ಹಂತ %1$d of %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"ಮುಂದೆ ಹೋಗಲು ಕ್ಲಿಕ್ ಮಾಡಿ"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"ವಿವಿಧ ಟೆಂಪ್ಲೇಟ್‌ಗಳಿಗೆ ಭೇಟಿ ನೀಡಿ ಮತ್ತು ಕಾರು, ಡ್ರೈವಿಂಗ್ ಮೋಡ್‌ನಲ್ಲಿರುವುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"ಪಟ್ಟಿ 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"ಪಟ್ಟಿ 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"ಪ್ರತಿ ಪಟ್ಟಿಯ ಅಡಿಯಲ್ಲಿ ಉಪಪಠ್ಯ"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"ಖಾಲಿ ಪಟ್ಟಿಯ ಡೆಮೊ"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"ಪಟ್ಟಿ ಖಾಲಿಯಾಗಿದೆ"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"ಇತರ ಟೆಂಪ್ಲೇಟ್‌ಗಳ ಡೆಮೋಗಳು"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Showcase ಡೆಮೋಗಳು"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"ಟೆಂಪ್ಲೇಟ್ ಲೇಔಟ್ ಡೆಮೋಗಳು"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ko/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ko/strings.xml
index dab016f..db15b3e 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ko/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ko/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"일부 추가 텍스트"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"샘플 선택 가능 목록"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"작업 제한사항 데모"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"작업 제한에 도달함\n계속 진행하면 앱이 강제 종료됩니다"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"작업 단계 %1$d/%2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"클릭하여 진행"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"다른 템플릿을 방문하여 자동차가 운전 모드인지 확인하세요."</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"목록 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"목록 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"각 목록 아래 하위 텍스트"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"빈 목록 데모"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"목록이 비어 있음"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"기타 템플릿 데모"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"쇼케이스 데모"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"템플릿 레이아웃 데모"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ky/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ky/strings.xml
index 2f22c34..4ff4eee 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ky/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ky/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Айрым кошумча текст"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Тандалуучу үлгү тизмеси"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Тапшырманы чектөө демосу"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Тапшырманын чегине жетти\nАлдыга жылсаңыз, колдонмо мажбурлап токтотулат"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Тапшырма кадамы %1$d of %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Алдыга өтүү үчүн чыкылдатыңыз"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Башка үлгүлөргө өтүп, унаа айдоо режиминде экенин текшериңиз"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"1-тизме"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"2-тизме"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Ар бир тизмеге берилген кошумча текст"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Бош тизменин көрүнүшү"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Тизме бош"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Башка үлгүлөрдүн демолору"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Showcase демолору"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Үлгү калыптарынын демолору"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-lo/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-lo/strings.xml
index 28e6f5e..4315400 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-lo/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-lo/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"ຂໍ້ຄວາມເພີ່ມເຕີມບາງຢ່າງ"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"ລາຍຊື່ທີ່ເລືອກໄດ້ຂອງຕົວຢ່າງ"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"ເດໂມການຈຳກັດໜ້າວຽກ"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"ຮອດຂີດຈຳກັດໜ້າວຽກແລ້ວ\nຕໍ່ໄປຈະບັງຄັບຢຸດແອັບ"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"ຂັ້ນຕອນໜ້າວຽກທີ %1$d ຈາກທັງໝົດ %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"ຄລິກເພື່ອໄປໜ້າ"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"ກະລຸນາເຂົ້າໄປແມ່ແບບອື່ນ ແລະ ກວດໃຫ້ແນ່ໃຈວ່າລົດຢູ່ໃນໂໝດຂັບຂີ່"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"ລາຍຊື່ 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"ລາຍຊື່ 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"ຂໍ້ຄວາມຍ່ອຍພາຍໃຕ້ແຕ່ລະລາຍຊື່"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"ເດໂມລາຍການທີ່ຫວ່າງເປົ່າ"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"ລາຍການຫວ່າງເປົ່າ"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"ເດໂມແມ່ແບບອື່ນໆ"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"ເດໂມ Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"ເດໂມໂຄງຮ່າງແມ່ແບບ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-lt/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-lt/strings.xml
index 8132540..3a8d187 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-lt/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-lt/strings.xml
@@ -298,7 +298,7 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Šiek tiek papildomo teksto"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Pasirenkamo sąrašo pavyzdys"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Užduoties apribojimo demonstracinė versija"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Pasiektas užduočių apribojimas\nTęsiant programa bus priverstinai sustabdyta"</string>
+    <string name="task_limit_reached_msg" msgid="7162842196382260992">"Bus viršytas veiksmų skaičius ir būsite nukreipti į naują ekraną."</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"%1$d užduoties veiksmas iš %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Spustelėkite, jei norite tęsti"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Peržiūrėkite kitus šablonus ir įsitikinkite, kad automobilis veikia vairavimo režimu"</string>
@@ -327,6 +327,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"1 sąrašas"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"2 sąrašas"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Kiekvieno sąrašo paantraštė"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Tuščio sąrašo demonstracinė versija"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Sąrašas yra tuščias"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Įvairių šablonų demonstracinės versijos"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Rodyti demonstracines versijas"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Šablonų išdėstymo demonstracinės versijos"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-lv/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-lv/strings.xml
index e7f6ba0..bd90c35 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-lv/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-lv/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Papildu teksts"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Atlasāma saraksta paraugs"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Uzdevumu ierobežojuma demonstrācija"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Sasniegts uzdevumu ierobežojums\nTurpinot lietotnes darbība tiks apturēta piespiedu kārtā."</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Uzdevuma darbība: %1$d. no %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Noklikšķiniet, lai dotos tālāk"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Lūdzu, skatiet dažādas veidnes un nodrošiniet, ka automašīna ir braukšanas režīmā."</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"1. saraksts"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"2. saraksts"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Katra saraksta apakšteksts"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Tukša saraksta demonstrācija"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Saraksts ir tukšs"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Dažādu veidņu demonstrācijas"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Showcase demonstrācijas"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Veidņu izkārtojumu demonstrācijas"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-mk/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-mk/strings.xml
index 44e1a78..57f648f 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-mk/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-mk/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Дополнителен текст"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Пример за текст што може да се избере"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Демо за ограничување за задачи"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Достигнато е ограничувањето за задачи\nАко продолжите, апликацијата ќе се исклучи присилно"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Преземете чекор %1$d од %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Кликнете за да одите нанапред"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Одете на различните шаблони и погрижете се автомобилот да е во режим на возење"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Список 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Список 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Поттекст под секој список"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Демо со празен список"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Списокот е празен"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Разни демоа за шаблони"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Демоа за прикажување"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Демоа за распоред на шаблони"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ml/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ml/strings.xml
index 88e1f7e..e8c6b49 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ml/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ml/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"ഏതാനും അധിക ടെക്സ്റ്റ്"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"തിരഞ്ഞെടുക്കാവുന്നവയുടെ സാമ്പിൾ ലിസ്റ്റ്"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"ടാസ്ക്ക് നിയന്ത്രണ ഡെമോ"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"ടാസ്ക്ക് പരിധിയെത്തി\nമുന്നോട്ട് പോകുന്നത് ആപ്പ് നിർബന്ധിതമായി നിർത്താൻ കാരണമാകും"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"%2$d ടാസ്ക്ക് ഘട്ടങ്ങളിൽ %1$d -ാമത്തേത്"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"മുന്നോട്ട് പോകാൻ ക്ലിക്ക് ചെയ്യുക"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"വ്യത്യസ്ത ടെംപ്ലേറ്റുകൾ സന്ദർശിച്ച്, ഡ്രൈവിംഗ് മോഡിലാണ് കാർ ഉള്ളതെന്ന് ഉറപ്പാക്കുക"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"ലിസ്റ്റ് 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"ലിസ്റ്റ് 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"ഓരോ ലിസ്‌റ്റിന് കീഴിലും സബ്‌ടെക്‌സ്‌റ്റ്"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"ശൂന്യമായ ലിസ്‌റ്റിന്റെ ഡെമോ"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"ലിസ്‌റ്റിൽ ഒന്നുമില്ല"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"പലവക ടെംപ്ലേറ്റ് ഡെമോകൾ"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"ഡെമോകൾ ഷോക്കേസ് ചെയ്യുക"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"ടെംപ്ലേറ്റ് ലേഔട്ട് ഡെമോകൾ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-mn/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-mn/strings.xml
index f2dc5c0..3ad2e00 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-mn/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-mn/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Зарим нэмэлт текст"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Сонгох боломжтой жагсаалтын жишээ"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Ажлын хязгаарлалтын демо"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Ажлын хязгаарт хүрсэн\nҮргэлжлүүлснээр аппыг хүчээр зогсооно"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Ажлын %2$d-н %1$d-р алхам"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Үргэлжлүүлэхийн тулд товшино уу"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Өөр загварт зочилж, машин нь втомашин жолоодох горимд байгаа эсэхийг баталгаажуулна уу"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"1-р жагсаалт"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"2-р жагсаалт"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Жагсаалт тус бүрийн доорх дэд текст"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Хоосон жагсаалтын демо"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Жагсаалт хоосон байна"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Холимог загварын демо"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Showcase-н демо"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Загварын бүдүүвч бүхий демо"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-mr/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-mr/strings.xml
index d5010fe..a50b85a 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-mr/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-mr/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"काही अतिरिक्त मजकूर"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"नमुन्याची निवडण्यायोग्य सूची"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"टास्कशी संबंधित निर्बंधाचा डेमो"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"टास्कची मर्यादा गाठली आहे\nपुढे सुरू ठेवल्यास, अ‍ॅप सक्तीने थांबवले जाईल"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"टास्कची %1$d पैकी %2$d पायरी"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"पुढे जाण्यासाठी क्लिक करा"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"कृपया विविध टेंप्लेटना भेट द्या आणि कार ही ड्रायव्हिंग मोडमध्ये असल्याची खात्री करा"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"पहिली सूची"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"दुसरी सूची"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"प्रत्येक सूचीच्या खाली सबटेक्स्ट"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"रिकामा सूची डेमो"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"ही सूची रिकामी आहे"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"इतर टेंप्लेटचे डेमो"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"डेमो दाखवा"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"टेंप्लेट लेआउट डेमो"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ms/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ms/strings.xml
index 6470ccc..5930db7 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ms/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ms/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Beberapa teks tambahan"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Senarai sampel yang boleh dipilih"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demo Sekatan Tugasan"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Had tugasan dicapai\nMaju ke hadapan akan henti paksa apl"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Langkah tugasan %1$d daripada %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Klik untuk maju"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Sila lawati templat yang berbeza dan pastikan kereta berada dalam mod memandu"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Senarai 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Senarai 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Subteks dalam setiap senarai"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Kosongkan Demo Senarai"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Senarai ini kosong"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Pelbagai Demo Templat"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demo Wadah"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demo Reka Letak Templat"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-my/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-my/strings.xml
index 1fe586c..fbcf8d4 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-my/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-my/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"ထပ်တိုးစာသားအချို့"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"နမူနာ ရွေးချယ်နိုင်သည့်စာရင်း"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"လုပ်ဆောင်စရာကန့်သတ်မှု သရုပ်ပြချက်"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"လုပ်ဆောင်စရာကန့်သတ်ချက် ရောက်သွားပြီ\nရှေ့ဆက်သွားခြင်းက အက်ပ်ကို မဖြစ်မနေရပ်ခိုင်းမည်"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"လုပ်ဆောင်စရာအဆင့် %2$d ခုအနက် %1$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"ရှေ့ဆက်သွားရန် နှိပ်ပါ"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"ပုံစံအမျိုးမျိုးကို ဝင်ကြည့်ပြီး ကားသည် မောင်းနှင်မုဒ်တွင်ရှိကြောင်း သေချာပါစေ"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"စာရင်း ၁"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"စာရင်း ၂"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"စာရင်းတစ်ခုစီ၏အောက်ရှိ ရှင်းလင်းချက်"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"စာရင်းအလွတ် သရုပ်ပြ"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"ဤစာရင်းသည် အလွတ်ဖြစ်သည်"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"အထွေထွေ ပုံစံသရုပ်ပြချက်များ"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Showcase သရုပ်ပြချက်များ"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"နမူနာပုံစံ သရုပ်ပြချက်များ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-nb/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-nb/strings.xml
index e153193..d9b2bd4 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-nb/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-nb/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Noe tilleggstekst"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Eksempel på velgbar liste"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demo av oppgavebegrensning"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Oppgavegrensen er nådd\nHvis du fortsetter, blir appen tvunget til å avslutte"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Oppgavetrinn %1$d av %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Klikk for å fortsette"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Gå til de ulike malene, og forsikre deg om at bilen er i kjøremodus"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Liste 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Liste 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Undertekst under hver liste"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Demo med tom liste"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Listen er tom"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demoer av diverse maler"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demoer i fokus"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demoer av mallayout"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ne/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ne/strings.xml
index 1b4389e..7eb5dd6 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ne/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ne/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"केही अतिरिक्त टेक्स्ट"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"चयन गर्न मिल्ने सूचीको नमुना"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"कार्यमा लगाइने प्रतिबन्धको डेमो"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"कार्यको सीमा सकियो\nतपाईं अगाडि बढ्नुभयो भने एप जबरजस्ती रोकिने छ"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"%2$d कार्यको %1$d औँ चरण"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"अगाडि बढ्न क्लिक गर्नुहोस्"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"कृपया फरक-फरक टेम्प्लेट प्रयोग गर्नुहोस् र कार ड्राइभिङ मोडमा छ भन्ने कुरा सुनिश्चित गर्नुहोस्"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"सूची १"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"सूची २"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"प्रत्येक सूचीको मुनि रहेको सबटेक्स्ट"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"खाली सूचीको डेमो"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"यो सूची खाली छ"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"टेम्प्लेटका विविध डेमोहरू"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"सोकेसहरूको डेमो"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"टेम्प्लेट लेआउटका डेमोहरू"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-nl/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-nl/strings.xml
index 72e7ec8..bbb3e7b 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-nl/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-nl/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Wat extra tekst"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Voorbeeld selecteerbare lijst"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demo van taakbeperking"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Taaklimiet bereikt\nAls je doorgaat, wordt de app geforceerd gestopt"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Taakstap %1$d van %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Klik om verder te gaan"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Open de verschillende templates en zorg dat de auto in de rijstand staat"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Lijst 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Lijst 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Subtekst onder elke lijst"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Lege lijstdemo"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"De lijst is leeg"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Diverse templatedemo\'s"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demo\'s laten zien"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demo\'s voor opmaaktemplate"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-or/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-or/strings.xml
index e9f4767..846b23e 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-or/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-or/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"କିଛି ଅତିରିକ୍ତ ଟେକ୍ସଟ"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"ଚୟନ କରାଯାଇପାରୁଥିବା ନମୁନାର ତାଲିକା"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"ଟାସ୍କ ପ୍ରତିବନ୍ଧିତ କରିବାର ଡେମୋ"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"ଟାସ୍କର ସୀମାରେ ପହଞ୍ଚିଯାଇଛି\nଆଗକୁ ଗଲେ ଆପଟି ବାଧ୍ୟତାର ସହ ବନ୍ଦ ହୋଇଯିବ"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"%2$dଟିରୁ %1$d ନମ୍ବର ଟାସ୍କର ଷ୍ଟେପ"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"ଆଗକୁ ଯିବା ପାଇଁ କ୍ଲିକ କରନ୍ତୁ"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"ଦୟାକରି ବିଭିନ୍ନ ଟେମ୍ପଲେଟକୁ ଭିଜିଟ କରନ୍ତୁ ଏବଂ କାରଟି ଡ୍ରାଇଭିଂ ମୋଡରେ ଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"ତାଲିକା 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"ତାଲିକା 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"ପ୍ରତ୍ୟେକ ତାଲିକା ତଳେ ସବଟେକ୍ସଟ"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"ଖାଲି ତାଲିକାର ଡେମୋ"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"ତାଲିକା ଖାଲି ଅଛି"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"ବିବିଧ ଟେମ୍ପଲେଟର ଡେମୋ"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Showcase ଡେମୋ"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"ଟେମ୍ପଲେଟ ଲେଆଉଟର ଡେମୋଗୁଡ଼ିକ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-pa/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-pa/strings.xml
index 66be396..0831c41 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-pa/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-pa/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"ਕੁਝ ਵਧੀਕ ਲਿਖਤ"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"ਸੈਂਪਲ ਚੁਣਨਯੋਗ ਸੂਚੀ"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"ਕਾਰਜ ਸੰਬੰਧੀ ਪਾਬੰਦੀ ਦਾ ਡੈਮੋ"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"ਕਾਰਜ ਸੀਮਾ ਪੂਰੀ ਹੋ ਗਈ\nਜਾਰੀ ਰੱਖਣ \'ਤੇ ਐਪ ਜ਼ਬਰਦਸਤੀ ਬੰਦ ਹੋ ਜਾਵੇਗੀ"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"%2$d ਵਿੱਚੋਂ %1$d ਕਾਰਜ ਪੜਾਅ"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"ਅੱਗੇ ਜਾਣ ਲਈ ਕਲਿੱਕ ਕਰੋ"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"ਕਿਰਪਾ ਕਰਕੇ ਵੱਖ-ਵੱਖ ਟੈਮਪਲੇਟਾਂ \'ਤੇ ਜਾਓ ਅਤੇ ਪੱਕਾ ਕਰੋ ਕਿ ਕਾਰ ਡਰਾਈਵਿੰਗ ਮੋਡ ਵਿੱਚ ਹੈ"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"ਸੂਚੀ 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"ਸੂਚੀ 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"ਹਰੇਕ ਸੂਚੀ ਦੇ ਹੇਠਾਂ ਸਬਟੈਕਸਟ"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"ਖਾਲੀ ਸੂਚੀ ਡੈਮੋ"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"ਇਹ ਸੂਚੀ ਖਾਲੀ ਹੈ"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"ਫੁਟਕਲ ਟੈਮਪਲੇਟਾਂ ਦੇ ਡੈਮੋ"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"ਸ਼ੋਅਕੇਸ ਦੇ ਡੈਮੋ"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"ਟੈਮਪਲੇਟ ਖਾਕੇ ਦੇ ਡੈਮੋ"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-pl/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-pl/strings.xml
index c737a55..62c8589 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-pl/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-pl/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Dodatkowy tekst"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Przykładowa lista z możliwością wyboru"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Wersja demonstracyjna ograniczenia dotyczącego zadań"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Osiągnięto limit zadań\nKontynuowanie spowoduje wymuszenie zatrzymania aplikacji"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Krok zadania %1$d z %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Kliknij, aby przejść dalej"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Sprawdź różne szablony i upewnij się, że samochód jest w trybie jazdy"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Lista 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Lista 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Tekst pod każdą listą"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Wersja demonstracyjna pustej listy"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Lista jest pusta"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Wersje demonstracyjne różnych szablonów"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Wersje demonstracyjne do prezentacji"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Wersje demonstracyjne szablonów układu"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-pt-rBR/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-pt-rBR/strings.xml
index cf867f8..b778a35d 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-pt-rBR/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-pt-rBR/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Mais texto"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Lista selecionável de exemplos"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demonstração de restrição de tarefa"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Limite da tarefa atingido\nContinuar vai forçar a parada do app"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Etapa da tarefa: %1$d de %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Clique para continuar"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Visite os diferentes modelos e confira se o veículo está no modo carro"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Lista 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Lista 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Subtexto abaixo de cada lista"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Demonstração de lista vazia"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"A lista está vazia"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstração de modelos diversos"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demonstrações em destaque"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demonstrações de modelos de layout"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-pt-rPT/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-pt-rPT/strings.xml
index b79330d..699f3c1 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-pt-rPT/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-pt-rPT/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Texto adicional"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Lista selecionável de exemplo"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demonstração de restrição de tarefa"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"O limite de tarefas foi atingido\nSe avançar, vai forçar a paragem da app"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Passo da tarefa %1$d de %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Clique para avançar"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Visite os diferentes modelos e certifique-se de que o carro está no modo de condução"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Lista 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Lista 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Subtexto abaixo de cada lista"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Demonstração da lista vazia"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"A lista está vazia"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstrações de modelos diversos"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demonstrações de destaque"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demonstrações do esquema do modelo"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-pt/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-pt/strings.xml
index cf867f8..b778a35d 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-pt/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-pt/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Mais texto"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Lista selecionável de exemplos"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demonstração de restrição de tarefa"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Limite da tarefa atingido\nContinuar vai forçar a parada do app"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Etapa da tarefa: %1$d de %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Clique para continuar"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Visite os diferentes modelos e confira se o veículo está no modo carro"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Lista 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Lista 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Subtexto abaixo de cada lista"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Demonstração de lista vazia"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"A lista está vazia"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstração de modelos diversos"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demonstrações em destaque"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demonstrações de modelos de layout"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ro/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ro/strings.xml
index 4fe39dc..db14fba3 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ro/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ro/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Text suplimentar"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Exemplu de listă care poate fi selectată"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demonstrație pentru restricția privind activitățile"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"A fost atinsă limita pentru activități\nDacă alegi să continui, aplicația se va opri forțat"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Pasul activității: %1$d din %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Dă clic pentru a merge înainte"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Accesează diferitele șabloane și asigură-te că mașina este în modul Cu mașina"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Lista 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Lista 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Subtextul de sub fiecare listă"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Listă goală demonstrativă"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Lista este goală"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstrații diverse cu șabloane"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demonstrații pentru Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demonstrații cu aspecte de șablon"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ru/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ru/strings.xml
index e1791af..c8e1345 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ru/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ru/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Дополнительный текст"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Пример списка с возможностью выбора"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Демонстрация ограничения для задач"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Достигнут предел по числу задач.\nЕсли продолжить, работа приложения будет остановлена."</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Шаг задачи: %1$d из %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Нажмите, чтобы продолжить"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Посмотрите разные шаблоны и убедитесь, что автомобиль находится в режиме вождения"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Список 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Список 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Описание под каждым списком"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Демонстрация пустого списка"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Список пуст."</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Демонстрации прочих шаблонов"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Демонстрации Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Макет шаблона (режим демонстрации)"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-si/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-si/strings.xml
index e2af18a..abcdbb0d5 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-si/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-si/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"සමහර අතිරේක පෙළ"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"නියැදි තේරිය හැකි ලැයිස්තුව"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"කාර්ය සීමා කිරීම් ආදර්ශනය"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"කාර්ය සීමාව ළඟා විය\nඉදිරියට යාම යෙදුම බලෙන් නවත්වනු ඇත"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"%2$d කින් %1$dවන කාර්ය පියවර"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"ඉදිරියට යාමට ක්ලික් කරන්න"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"විවිධ අච්චු වෙත පැමිණ මෝටර් රථය ධාවන ප්‍රකාරයේ ඇති බව තහවුරු කර ගන්න"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"ලැයිස්තුව 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"ලැයිස්තුව 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"එක් එක් ලැයිස්තුව යටතේ උප පෙළ"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"හිස් ලැයිස්තු ආදර්ශනය"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"ලැයිස්තුව හිස් ය"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"විවිධ අච්චු ආදර්ශන"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"ප්‍රකාශක තේරූ ආදර්ශන"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"අච්චු පිරිසැලසුම් ආදර්ශන"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sk/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sk/strings.xml
index 14f88e9..560acd0 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sk/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sk/strings.xml
@@ -298,7 +298,7 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Nejaký dodatočný text"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Ukážka vybrateľného zoznamu"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Obmedzenie úlohy – demo"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Bolo dosiahnuté obmedzenie úlohy\nAk budete pokračovať, aplikácia sa vynútene zastaví"</string>
+    <string name="task_limit_reached_msg" msgid="7162842196382260992">"Rozbalí sa počet krokov a systém vás privedie na novú obrazovku."</string>
     <string name="task_step_of_title" msgid="2791717962535723839">"Krok úlohy: %1$d z %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Pokračujte kliknutím"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Prejdite na rôzne šablóny a uistite sa, že auto je režime v aute"</string>
@@ -327,6 +327,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"1. zoznam"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"2. zoznam"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Podtext pod každým zoznamom"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Prázdny zoznam – demo"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Zoznam je prázdny"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Rôzne šablóny – demá"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Výber – demá"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demo rozložení šablóny"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sl/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sl/strings.xml
index aefe212..0147db9 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sl/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sl/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Nekaj dodatnega besedila"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Vzorčni seznam, ki omogoča izbiro"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Predstavitvena različica omejitve opravil"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Dosežena je omejitev opravil.\nČe nadaljujete, bo aplikacija prisilno ustavljena."</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Korak opravila %1$d od %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Kliknite, če želite nadaljevati."</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Odprite različne predloge in poskrbite, da je avtomobil v načinu vožnje."</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Seznam 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Seznam 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Podbesedilo pod posameznim seznamom"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Predstavitvena različica praznega seznama"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Seznam je prazen"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Predstavitvene različice različnih predlog"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Predstavitvene različice izpostavljenih stvari"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Predstavitvene različice postavitev predlog"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sq/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sq/strings.xml
index 301e3ec..97a23cb 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sq/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sq/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Pak tekst shtesë"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Shabllon i listës së zgjedhshme"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demonstrimi i kufizimit të detyrës"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"U arrit kufiri i detyrës\nVazhdimi përpara do ta ndalojë aplikacionin me forcë"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Hapi i detyrës: %1$d nga %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Kliko për të vazhduar përpara"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Vizito shabllonet e ndryshme dhe sigurohu që makina të jetë në modalitetin e lëvizjes"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Lista 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Lista 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Nënteksti poshtë çdo liste"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Boshatis listën e demonstrimit"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Lista është bosh"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demonstrime shabllonesh të ndryshme"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Demonstrime të prezantimit"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demonstrimet e strukturës së shabllonit"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sr/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sr/strings.xml
index 4c63418..32594a5 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sr/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sr/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Додатни текст"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Пример листе која може да се изабере"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Демонстрација ограничења за задатке"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Достигнуто је ограничење задатака\nАко наставите, апликација ће се принудно зауставити"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"%1$d. корак задатка од %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Кликните да бисте ишли напред"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Посетите различите шаблоне и уверите се да је аутомобил у режиму вожње"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Листа 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Листа 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Подтекст испод сваке листе"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Демонстрација празне листе"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Листа је празна"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Демонстрације различитих шаблона"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Демонстрације приказивања"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Демонстрације изгледа шаблона"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sv/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sv/strings.xml
index 0ffb8d6..15100c2 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sv/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sv/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Lite mer text"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Exempel på valbar lista"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demo för uppgiftsbegränsning"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Gränsen för antal uppgifter har uppnåtts\nOm du fortsätter tvingas appen att avslutas"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Uppgiftssteg %1$d av %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Klicka för att gå vidare"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Öppna de olika mallarna och se till att bilen är i körläge"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Lista 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Lista 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Undertext under varje lista"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Demo av tom lista"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Listan är tom"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Demor för övriga mallar"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Visa demor"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Demor för mallayouter"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-sw/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-sw/strings.xml
index 2fb1bb3..3f1baf5 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-sw/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-sw/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Baadhi ya maandishi ya ziada"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Sampuli ya orodha inayoweza kuchaguliwa"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Onyesho la Kikwazo cha Shughuli"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Kikomo cha shughuli kimefikiwa\nKuendelea kutalazimisha kuzima programu"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Hatua ya shughuli %1$d kati ya %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Bofya ili uendelee"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Tafadhali tembelea violezo tofauti na uhakikishe kuwa gari liko katika hali ya kuendesha gari"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Orodha ya 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Orodha ya 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Dokezo chini ya kila orodha"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Onyesho la Orodha Isiyo na Chochote"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Orodha hii haina chochote"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Maonyesho ya Violezo Anuwai"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Maonyesho ya Kuangazia"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Maonyesho ya Kiolezo cha Muundo"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ta/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ta/strings.xml
index cf9a242..ce4bd10 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ta/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ta/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"சில கூடுதல் உரை"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"தேர்ந்தெடுக்கக்கூடிய மாதிரிப் பட்டியல்"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"பணிக் கட்டுப்பாட்டின் டெமோ"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"பணி வரம்பை எட்டிவிட்டது.\nமேலும் தொடர்ந்தால் ஆப்ஸ் உடனே நிறுத்தப்படும்"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"%1$d / %2$d படியை எடுங்கள்"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"முன்னேறிச் செல்ல கிளிக் செய்யவும்"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"வெவ்வேறு டெம்ப்ளேட்களைப் பாருங்கள். கார் \'வாகனம் ஓட்டும் பயன்முறையில்\' இருப்பதை உறுதிசெய்துகொள்ளுங்கள்."</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"பட்டியல் 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"பட்டியல் 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"ஒவ்வொரு பட்டியலுக்கும் கீழுள்ள சுருக்க விவரம்"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"காலிப் பட்டியலுக்கான டெமோ"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"பட்டியல் காலியாக உள்ளது"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"மற்ற டெம்ப்ளேட்டுகளின் டெமோக்கள்"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"ஷோகேஸின் டெமோக்கள்"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"டெம்ப்ளேட் தளவமைப்பின் டெமோக்கள்"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-te/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-te/strings.xml
index 48c2552..41e3948 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-te/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-te/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"కొంత అదనపు టెక్స్ట్"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"ఎంచుకోదగిన నమూనాల లిస్ట్"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"టాస్క్ పరిమితి డెమో"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"టాస్క్‌ల పరిమితిని చేరుకున్నారు\nఇంకా టాస్క్‌లను ఎంచుకుంటే యాప్ ఫోర్స్ స్టాప్ అవుతుంది"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"%2$dలో %1$d టాస్క్ దశ"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"ముందుకు వెళ్లడానికి క్లిక్ చేయండి"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"దయచేసి వివిధ టెంప్లేట్‌లను సందర్శించండి, కారు డ్రైవింగ్ మోడ్‌లో ఉందని నిర్ధారించుకోండి"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"లిస్ట్ 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"లిస్ట్ 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"ప్రతి లిస్ట్ కింద సబ్‌టెక్స్ట్"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"ఖాళీ లిస్ట్ డెమో"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"మీ లిస్ట్ ఖాళీగా ఉంది"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"ఇతర టెంప్లేట్‌ల డెమోలు"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"డెమోలను ప్రదర్శించండి"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"టెంప్లేట్ లేఅవుట్ డెమోలు"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-th/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-th/strings.xml
index b2d06cb..cec852b 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-th/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-th/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"ข้อความเพิ่มเติมบางส่วน"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"ตัวอย่างรายการที่เลือกได้"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"การสาธิตข้อจำกัดงาน"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"ถึงขีดจำกัดงานแล้ว\nการดำเนินการต่อจะบังคับให้แอปหยุด"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"งานขั้นตอนที่ %1$d จาก %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"คลิกเพื่อดำเนินการต่อ"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"โปรดไปที่เทมเพลตอื่นและตรวจสอบว่ารถอยู่ในโหมดขับรถ"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"รายการที่ 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"รายการที่ 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"ข้อความย่อยข้างใต้แต่ละรายการ"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"การสาธิตรายการที่ว่างเปล่า"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"รายการว่างเปล่า"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"การสาธิตเทมเพลตเบ็ดเตล็ด"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"การสาธิต Showcase"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"การสาธิตเลย์เอาต์เทมเพลต"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-tl/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-tl/strings.xml
index d7db66f..0b06b88 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-tl/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-tl/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Ilang karagdagang text"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Sample na mapipiling listahan"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Demo ng Paghihigpit sa Gawain"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Naabot na ang limitasyon ng gawain\nSapilitang ihihinto ang app kapag nagpatuloy"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Gawin ang hakbang %1$d sa %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Mag-click para magpatuloy"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Pakibisita ang iba\'t ibang template at tiyaking nasa driving mode ang kotse"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Listahan 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Listahan 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Subtext sa ilalim ng bawat listahan"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Demo ng Walang Lamang Listahan"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Walang laman ang listahan"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Iba Pang Demo ng Mga Template"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Itampok ang Mga Demo"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Mga Demo ng Layout ng Template"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-tr/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-tr/strings.xml
index 4c965fb..4aae2ee 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-tr/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-tr/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Bazı ek metinler"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Örnek seçilebilir listesi"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Görev Sınırlaması Demosu"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Görev sınırına ulaşıldı\nDevam ederseniz uygulama zorla durdurulacak"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"%1$d/%2$d görev adımı"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Devam etmek için tıklayın"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Lütfen farklı şablonları inceleyip arabanın sürüş modunda olduğundan emin olun"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Liste 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Liste 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Her listenin altında alt metin"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Boş Liste Demosu"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Liste boş"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Çeşitli Şablon Demoları"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Öne Çıkan Demoları"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Şablon Düzeni Demoları"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-uk/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-uk/strings.xml
index 3e7b486..b1fa452 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-uk/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-uk/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Додатковий текст"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Зразок списку з можливістю вибору"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Демонстрація ліміту завдань"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Досягнуто ліміту завдань\nЯкщо продовжити, програму буде примусово зупинено"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Крок завдання %1$d з %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Натисніть, щоб продовжити"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Перегляньте різні шаблони й переконайтеся, що автомобіль перебуває в режимі кермування"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Список 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Список 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Додатковий текст під кожним списком"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Демонстрація пустого списку"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Список пустий"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Демонстрації інших шаблонів"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Увімкнути демонстрації"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Демонстрації макетів шаблонів"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-ur/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-ur/strings.xml
index 438c100..589f8eb 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-ur/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-ur/strings.xml
@@ -302,7 +302,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"کچھ اضافی ٹیکسٹ"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"قابل انتخاب نمونہ کی فہرست"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"ٹاسک کی پابندی کا ڈیمو"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"ٹاسک کی حد پوری ہو گئی\nآگے بڑھنے سے ایپ کو زبردستی روک دیا جائے گا"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"‏%2$d میں سے ‎%1$d ٹاسک کا مرحلہ"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"آگے بڑھنے کے لیے کلک کریں"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"براہ کرم مختلف ٹیمپلیٹس دیکھیں اور یقینی بنائیں کہ کار ڈرائیونگ موڈ میں ہے"</string>
@@ -331,6 +332,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"فہرست 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"فہرست 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"ہر فہرست کے نیچے ذیلی ٹیکسٹ"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"خالی فہرست کا ڈیمو"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"فہرست خالی ہے"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"متفرق تمثیلات کے ڈیموز"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"ڈیموز دکھائیں"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"تمثیل کے لے آؤٹ کے ڈیموز"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-uz/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-uz/strings.xml
index 491d966..07299d6 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-uz/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-uz/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Yana qoʻshimcha matn"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Belgilanuvchi roʻyxat namunasi"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Vazifa cheklovi namoyishi"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Vazifa chekloviga yetildi\nDavom etish ilovani majburiy toʻxtatadi."</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Vazifa qadami: %1$d / %2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Oldinga oʻtish uchun bosing"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Turli andozalarni ochib koʻring va avtomobil haydash rejimida ekanini tekshiring"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Roʻyxat 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Roʻyxat 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Har bir roʻyxat ostida quyi matn"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Boʻsh roʻyxat demosi"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Roʻyxat boʻsh"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Aralash andozalar demolari"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Namoyish demolari"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Andoza dizayni demolari"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-vi/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-vi/strings.xml
index e386507..1e26c4c 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-vi/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-vi/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Một số văn bản bổ sung"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Danh sách có thể chọn mẫu"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Bản demo tác vụ hạn chế"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Đã đạt đến giới hạn nhiệm vụ\nNếu tiếp tục sẽ buộc phải dừng ứng dụng"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Bước %1$d trong số %2$d của nhiệm vụ"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Nhấp để tiếp tục"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Vui lòng truy cập nhiều mẫu riêng biệt và đảm bảo xe đang ở chế độ lái"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Danh sách 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Danh sách 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Nội dung phụ dưới mỗi danh sách"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Bản minh hoạ danh sách trống"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Danh sách này đang trống"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Bản demo biểu mẫu Misc"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Bản demo nổi bật"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Bản minh hoạ bố cục mẫu"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-zh-rCN/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-zh-rCN/strings.xml
index a5ecad9..56b2117 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-zh-rCN/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-zh-rCN/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"一些其他文本"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"可选列表示例"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"任务限制演示"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"已达到任务限制\n继续会强制停止应用"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"任务第 %1$d 步(共 %2$d 步)"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"点击以继续"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"请访问不同模板并确保此车辆处于驾驶模式"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"列表 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"列表 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"每个列表下的辅助文本"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"空列表演示"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"列表为空"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"其他模板演示"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"展示演示"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"模板布局演示"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml
index 0a10a88..85a888d 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-zh-rHK/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"一些其他文字"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"可選取的清單範本"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"「工作限制」示範"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"工作已達上限\n繼續使用將強制停止應用程式"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"工作步驟 %1$d,共 %2$d 個步驟"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"點擊即可繼續使用"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"請查看各種範本,並確保汽車處於駕駛模式"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"清單 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"清單 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"每個清單的說明文字"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"空白名單示範"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"名單中沒有任何成員"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"其他範本示範"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"展示示範"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"「範本版面配置」示範"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-zh-rTW/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-zh-rTW/strings.xml
index a91421b..4ba2bdb 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-zh-rTW/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-zh-rTW/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"一些其他文字"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"可選取的清單範本"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"「工作限制」示範"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"工作已達上限\n繼續使用將強制停止應用程式"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"工作步驟 %1$d,共 %2$d 個步驟"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"點選即可繼續使用"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"請前往不同範本並確認車輛處於行車模式"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"清單 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"清單 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"每個清單底下的子文字"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"空白名單示範"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"名單中沒有任何成員"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"其他範本示範"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"「展示」示範"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"「範本版面配置」示範"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values-zu/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values-zu/strings.xml
index b0147da..78d8eb5 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values-zu/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values-zu/strings.xml
@@ -298,7 +298,8 @@
     <string name="some_additional_text" msgid="4009872495806318260">"Omunye umbhalo owengeziwe"</string>
     <string name="sample_additional_list" msgid="5085372891301576306">"Isampula lohlu olukhethekayo"</string>
     <string name="task_restriction_demo_title" msgid="2212084350718766941">"Idemo Yokukhawulelwa Komsebenzi"</string>
-    <string name="task_limit_reached_msg" msgid="6038763366777119364">"Umkhawulo womsebenzi ufinyelelwe\nUkuqhubekela phambili kuzophoqa ukumisa i-app"</string>
+    <!-- no translation found for task_limit_reached_msg (7162842196382260992) -->
+    <skip />
     <string name="task_step_of_title" msgid="2791717962535723839">"Isinyathelo somsebenzi %1$d kwengu-%2$d"</string>
     <string name="task_step_of_text" msgid="4646729781462227219">"Chofoza ukuze uye phambili"</string>
     <string name="task_content_allowed" msgid="545061986612431190">"Sicela uvakashele izifanekiso ezahlukene futhi uqinisekise ukuthi imoto ikumodi yokushayela"</string>
@@ -327,6 +328,8 @@
     <string name="sectioned_item_list_one_title" msgid="6594204350307263338">"Uhlu 1"</string>
     <string name="sectioned_item_list_two_title" msgid="4941545381743022833">"Uhlu 2"</string>
     <string name="sectioned_item_list_subtext" msgid="2963927693547537519">"Umbhalo ongaphansi kohlu ngalunye"</string>
+    <string name="empty_list_demo_title" msgid="5989013634820902455">"Idemo Yohlu Olungenalutho"</string>
+    <string name="empty_list_message" msgid="5331395517560728138">"Uhlu alunalutho"</string>
     <string name="misc_templates_demos_title" msgid="6077169010255928114">"Amademo Ezifanekiso Ezixubile"</string>
     <string name="showcase_demos_title" msgid="1542092687878113304">"Bonisa Amademo"</string>
     <string name="template_layouts_demo_title" msgid="788249269446087847">"Ama-demo Esakhiwo Sesifanekiso"</string>
diff --git a/car/app/app-samples/showcase/common/src/main/res/values/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values/strings.xml
index 64644f3..81a7dbd 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values/strings.xml
@@ -381,7 +381,7 @@
 
   <!-- TaskRestrictionDemoScreen -->
   <string name="task_restriction_demo_title">Task Restriction Demo</string>
-  <string name="task_limit_reached_msg">Task limit reached\nGoing forward will force stop the app</string>
+  <string name="task_limit_reached_msg">This will overflow the step count, and lead you to a new screen. </string>
   <string name="task_step_of_title">Task step %1$d of %2$d</string>
   <string name="task_step_of_text">Click to go forward</string>
   <string name="task_content_allowed">Please visit the different templates and ensure the car is in driving mode</string>
@@ -418,6 +418,10 @@
   <string name="sectioned_item_list_two_title">List 2</string>
   <string name="sectioned_item_list_subtext">Subtext under each list</string>
 
+  <!-- EmptyListDemoScreen -->
+  <string name="empty_list_demo_title">Empty List Demo</string>
+  <string name="empty_list_message">The list is empty</string>
+
   <!-- StartScreen -->
   <string name="misc_templates_demos_title">Misc Templates Demos</string>
   <string name="cal_api_level_prefix" translatable="false">CAL API Level: %d</string>
diff --git a/car/app/app-samples/showcase/mobile/src/main/AndroidManifest.xml b/car/app/app-samples/showcase/mobile/src/main/AndroidManifest.xml
index d72c73a..59a17ab 100644
--- a/car/app/app-samples/showcase/mobile/src/main/AndroidManifest.xml
+++ b/car/app/app-samples/showcase/mobile/src/main/AndroidManifest.xml
@@ -41,7 +41,6 @@
   <application
       android:label="@string/app_name"
       android:icon="@drawable/ic_launcher"
-      android:extractNativeLibs="false"
       android:supportsRtl="true">
 
     <meta-data
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 25976cb..ea3b0c54 100644
--- a/car/app/app-samples/showcase/mobile/src/main/AndroidManifestWithSdkVersion.xml
+++ b/car/app/app-samples/showcase/mobile/src/main/AndroidManifestWithSdkVersion.xml
@@ -48,8 +48,7 @@
 
   <application
       android:label="@string/app_name"
-      android:icon="@drawable/ic_launcher"
-      android:extractNativeLibs="false">
+      android:icon="@drawable/ic_launcher">
 
     <meta-data
         android:name="com.google.android.gms.car.application"
diff --git a/car/app/app/api/public_plus_experimental_current.txt b/car/app/app/api/public_plus_experimental_current.txt
index 25638b0..7f3f5f9 100644
--- a/car/app/app/api/public_plus_experimental_current.txt
+++ b/car/app/app/api/public_plus_experimental_current.txt
@@ -36,6 +36,7 @@
     method public androidx.car.app.Session onCreateSession();
     method @androidx.car.app.annotations.RequiresCarApi(6) public androidx.car.app.Session onCreateSession(androidx.car.app.SessionInfo);
     method public final boolean onUnbind(android.content.Intent);
+    field @androidx.car.app.annotations.ExperimentalCarApi public static final String CATEGORY_CALLING_APP = "androidx.car.app.category.CALLING";
     field @Deprecated public static final String CATEGORY_CHARGING_APP = "androidx.car.app.category.CHARGING";
     field @androidx.car.app.annotations.RequiresCarApi(6) public static final String CATEGORY_FEATURE_CLUSTER = "androidx.car.app.category.FEATURE_CLUSTER";
     field @androidx.car.app.annotations.ExperimentalCarApi public static final String CATEGORY_IOT_APP = "androidx.car.app.category.IOT";
@@ -44,6 +45,7 @@
     field @Deprecated public static final String CATEGORY_PARKING_APP = "androidx.car.app.category.PARKING";
     field public static final String CATEGORY_POI_APP = "androidx.car.app.category.POI";
     field @androidx.car.app.annotations.ExperimentalCarApi public static final String CATEGORY_SETTINGS_APP = "androidx.car.app.category.SETTINGS";
+    field @androidx.car.app.annotations.ExperimentalCarApi public static final String CATEGORY_WEATHER_APP = "androidx.car.app.category.WEATHER";
     field public static final String SERVICE_INTERFACE = "androidx.car.app.CarAppService";
   }
 
@@ -866,7 +868,7 @@
   @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(6) public class CarMessage {
     method public androidx.car.app.model.CarText getBody();
     method public long getReceivedTimeEpochMillis();
-    method public androidx.core.app.Person getSender();
+    method public androidx.core.app.Person? getSender();
     method public boolean isRead();
   }
 
@@ -876,7 +878,7 @@
     method public androidx.car.app.messaging.model.CarMessage.Builder setBody(androidx.car.app.model.CarText);
     method public androidx.car.app.messaging.model.CarMessage.Builder setRead(boolean);
     method public androidx.car.app.messaging.model.CarMessage.Builder setReceivedTimeEpochMillis(long);
-    method public androidx.car.app.messaging.model.CarMessage.Builder setSender(androidx.core.app.Person);
+    method public androidx.car.app.messaging.model.CarMessage.Builder setSender(androidx.core.app.Person?);
   }
 
   @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi public interface ConversationCallback {
@@ -894,6 +896,7 @@
     method public androidx.car.app.model.CarIcon? getIcon();
     method public String getId();
     method public java.util.List<androidx.car.app.messaging.model.CarMessage!> getMessages();
+    method public androidx.core.app.Person getSelf();
     method public androidx.car.app.model.CarText getTitle();
     method public boolean isGroupConversation();
   }
@@ -906,6 +909,7 @@
     method public androidx.car.app.messaging.model.ConversationItem.Builder setIcon(androidx.car.app.model.CarIcon);
     method public androidx.car.app.messaging.model.ConversationItem.Builder setId(String);
     method public androidx.car.app.messaging.model.ConversationItem.Builder setMessages(java.util.List<androidx.car.app.messaging.model.CarMessage!>);
+    method public androidx.car.app.messaging.model.ConversationItem.Builder setSelf(androidx.core.app.Person);
     method public androidx.car.app.messaging.model.ConversationItem.Builder setTitle(androidx.car.app.model.CarText);
   }
 
@@ -992,13 +996,17 @@
   }
 
   @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public class Badge {
+    method public androidx.car.app.model.CarColor? getBackgroundColor();
+    method public androidx.car.app.model.CarIcon? getIcon();
     method public boolean hasDot();
   }
 
   public static final class Badge.Builder {
     ctor public Badge.Builder();
     method public androidx.car.app.model.Badge build();
+    method public androidx.car.app.model.Badge.Builder setBackgroundColor(androidx.car.app.model.CarColor);
     method public androidx.car.app.model.Badge.Builder setHasDot(boolean);
+    method public androidx.car.app.model.Badge.Builder setIcon(androidx.car.app.model.CarIcon);
   }
 
   @androidx.car.app.annotations.CarProtocol public final class CarColor {
@@ -1145,8 +1153,9 @@
   public static final class GridItem.Builder {
     ctor public GridItem.Builder();
     method public androidx.car.app.model.GridItem build();
-    method @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public androidx.car.app.model.GridItem.Builder setBadge(androidx.car.app.model.Badge);
     method public androidx.car.app.model.GridItem.Builder setImage(androidx.car.app.model.CarIcon);
+    method @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public androidx.car.app.model.GridItem.Builder setImage(androidx.car.app.model.CarIcon, androidx.car.app.model.Badge);
+    method @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public androidx.car.app.model.GridItem.Builder setImage(androidx.car.app.model.CarIcon, int, androidx.car.app.model.Badge);
     method public androidx.car.app.model.GridItem.Builder setImage(androidx.car.app.model.CarIcon, int);
     method public androidx.car.app.model.GridItem.Builder setLoading(boolean);
     method public androidx.car.app.model.GridItem.Builder setOnClickListener(androidx.car.app.model.OnClickListener);
diff --git a/car/app/app/build.gradle b/car/app/app/build.gradle
index 5807cb4..ba71e90 100644
--- a/car/app/app/build.gradle
+++ b/car/app/app/build.gradle
@@ -72,7 +72,7 @@
 }
 
 project.ext {
-    latestCarAppApiLevel = "6"
+    latestCarAppApiLevel = "7"
 }
 
 android {
diff --git a/car/app/app/src/main/java/androidx/car/app/CarAppService.java b/car/app/app/src/main/java/androidx/car/app/CarAppService.java
index 3f4b442..8f216df 100644
--- a/car/app/app/src/main/java/androidx/car/app/CarAppService.java
+++ b/car/app/app/src/main/java/androidx/car/app/CarAppService.java
@@ -144,6 +144,18 @@
     @ExperimentalCarApi
     public static final String CATEGORY_MESSAGING_APP = "androidx.car.app.category.MESSAGING";
 
+    /**
+     * Used in the app manifest to declare that this app supports calling.
+     */
+    @ExperimentalCarApi
+    public static final String CATEGORY_CALLING_APP = "androidx.car.app.category.CALLING";
+
+    /**
+     * Used to declare that this app is a weather app in the manifest.
+     */
+    @ExperimentalCarApi
+    public static final String CATEGORY_WEATHER_APP = "androidx.car.app.category.WEATHER";
+
     private static final String AUTO_DRIVE = "AUTO_DRIVE";
 
     @NonNull
diff --git a/car/app/app/src/main/java/androidx/car/app/messaging/model/CarMessage.java b/car/app/app/src/main/java/androidx/car/app/messaging/model/CarMessage.java
index 26429f7..8614a79 100644
--- a/car/app/app/src/main/java/androidx/car/app/messaging/model/CarMessage.java
+++ b/car/app/app/src/main/java/androidx/car/app/messaging/model/CarMessage.java
@@ -37,7 +37,7 @@
 @RequiresCarApi(6)
 @KeepFields
 public class CarMessage {
-    @NonNull
+    @Nullable
     private final Bundle mSender;
     @NonNull
     private final CarText mBody;
@@ -47,7 +47,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(
-                getPersonHashCode(getSender()),
+                PersonsEqualityHelper.getPersonHashCode(getSender()),
                 mBody,
                 mReceivedTimeEpochMillis,
                 mIsRead
@@ -65,53 +65,14 @@
         CarMessage otherCarMessage = (CarMessage) other;
 
         return
-                arePeopleEqual(getSender(), otherCarMessage.getSender())
+                PersonsEqualityHelper.arePersonsEqual(getSender(), otherCarMessage.getSender())
                         && Objects.equals(mBody, otherCarMessage.mBody)
                         && mReceivedTimeEpochMillis == otherCarMessage.mReceivedTimeEpochMillis
-                        && mIsRead == otherCarMessage.mIsRead
-                ;
-    }
-
-    // TODO(b/266877597): Move to androidx.core.app.Person
-    private static boolean arePeopleEqual(Person person1, Person person2) {
-        // If a unique ID was provided, use it
-        String key1 = person1.getKey();
-        String key2 = person2.getKey();
-        if (key1 != null || key2 != null) {
-            return Objects.equals(key1, key2);
-        }
-
-        // CharSequence doesn't have well-defined "equals" behavior -- convert to String instead
-        String name1 = Objects.toString(person1.getName());
-        String name2 = Objects.toString(person2.getName());
-
-        // Fallback: Compare field-by-field
-        return
-                Objects.equals(name1, name2)
-                        && Objects.equals(person1.getUri(), person2.getUri())
-                        && Objects.equals(person1.isBot(), person2.isBot())
-                        && Objects.equals(person1.isImportant(), person2.isImportant());
-    }
-
-    // TODO(b/266877597): Move to androidx.core.app.Person
-    private static int getPersonHashCode(Person person) {
-        // If a unique ID was provided, use it
-        String key = person.getKey();
-        if (key != null) {
-            return key.hashCode();
-        }
-
-        // Fallback: Use hash code for individual fields
-        return Objects.hash(
-                person.getName(),
-                person.getUri(),
-                person.isBot(),
-                person.isImportant()
-        );
+                        && mIsRead == otherCarMessage.mIsRead;
     }
 
     CarMessage(@NonNull Builder builder) {
-        this.mSender = requireNonNull(builder.mSender).toBundle();
+        this.mSender = builder.mSender == null ? null : requireNonNull(builder.mSender).toBundle();
         this.mBody = requireNonNull(builder.mBody);
         this.mReceivedTimeEpochMillis = builder.mReceivedTimeEpochMillis;
         this.mIsRead = builder.mIsRead;
@@ -119,17 +80,22 @@
 
     /** Default constructor for serialization. */
     private CarMessage() {
-        this.mSender = new Person.Builder().setName("").build().toBundle();
+        this.mSender = null;
         this.mBody = new CarText.Builder("").build();
         this.mReceivedTimeEpochMillis = 0;
         this.mIsRead = false;
     }
 
 
-    /** Returns a {@link Person} representing the message sender */
-    @NonNull
+    /**
+     * Returns a {@link Person} representing the message sender.
+     *
+     * <p> For self-sent messages, this method will return {@code null} or
+     * {@link ConversationItem#getSelf()}.
+     */
+    @Nullable
     public Person getSender() {
-        return Person.fromBundle(mSender);
+        return mSender == null ? null : Person.fromBundle(mSender);
     }
 
     /** Returns a {@link CarText} representing the message body */
@@ -158,7 +124,7 @@
         boolean mIsRead;
 
         /** Sets a {@link Person} representing the message sender */
-        public @NonNull Builder setSender(@NonNull Person sender) {
+        public @NonNull Builder setSender(@Nullable Person sender) {
             mSender = sender;
             return this;
         }
diff --git a/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java b/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java
index 4c98df4..5956a59 100644
--- a/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java
+++ b/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java
@@ -21,6 +21,7 @@
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.SuppressLint;
+import android.os.Bundle;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -32,6 +33,7 @@
 import androidx.car.app.model.CarText;
 import androidx.car.app.model.Item;
 import androidx.car.app.utils.CollectionUtils;
+import androidx.core.app.Person;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -47,6 +49,8 @@
     private final String mId;
     @NonNull
     private final CarText mTitle;
+    @NonNull
+    private final Bundle mSelf;
     @Nullable
     private final CarIcon mIcon;
     private final boolean mIsGroupConversation;
@@ -58,6 +62,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(
+                PersonsEqualityHelper.getPersonHashCode(getSelf()),
                 mId,
                 mTitle,
                 mIcon,
@@ -80,6 +85,8 @@
                 Objects.equals(mId, otherConversationItem.mId)
                         && Objects.equals(mTitle, otherConversationItem.mTitle)
                         && Objects.equals(mIcon, otherConversationItem.mIcon)
+                        && PersonsEqualityHelper
+                            .arePersonsEqual(getSelf(), otherConversationItem.getSelf())
                         && mIsGroupConversation == otherConversationItem.mIsGroupConversation
                         && Objects.equals(mMessages, otherConversationItem.mMessages)
                 ;
@@ -88,6 +95,7 @@
     ConversationItem(@NonNull Builder builder) {
         this.mId = requireNonNull(builder.mId);
         this.mTitle = requireNonNull(builder.mTitle);
+        this.mSelf = requireNonNull(builder.mSelf).toBundle();
         this.mIcon = builder.mIcon;
         this.mIsGroupConversation = builder.mIsGroupConversation;
         this.mMessages = requireNonNull(CollectionUtils.unmodifiableCopy(builder.mMessages));
@@ -100,6 +108,7 @@
     private ConversationItem() {
         mId = "";
         mTitle = new CarText.Builder("").build();
+        mSelf = new Person.Builder().setName("").build().toBundle();
         mIcon = null;
         mIsGroupConversation = false;
         mMessages = new ArrayList<>();
@@ -133,6 +142,12 @@
         return mTitle;
     }
 
+    /** Returns a {@link Person} for the conversation */
+    @NonNull
+    public Person getSelf() {
+        return Person.fromBundle(mSelf);
+    }
+
     /** Returns a {@link CarIcon} for the conversation, or {@code null} if not set */
     @Nullable
     public CarIcon getIcon() {
@@ -167,6 +182,8 @@
         @Nullable
         CarText mTitle;
         @Nullable
+        Person mSelf;
+        @Nullable
         CarIcon mIcon;
         boolean mIsGroupConversation;
         @Nullable
@@ -204,6 +221,13 @@
             return this;
         }
 
+        /** Sets a {@link Person} for the conversation */
+        @NonNull
+        public Builder setSelf(@NonNull Person self) {
+            mSelf = self;
+            return this;
+        }
+
         /**
          * Specifies whether this conversation involves 3+ participants (a "group" conversation)
          *
diff --git a/car/app/app/src/main/java/androidx/car/app/messaging/model/PersonsEqualityHelper.java b/car/app/app/src/main/java/androidx/car/app/messaging/model/PersonsEqualityHelper.java
new file mode 100644
index 0000000..e5268d6
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/messaging/model/PersonsEqualityHelper.java
@@ -0,0 +1,72 @@
+/*
+ * 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.car.app.messaging.model;
+
+import androidx.annotation.Nullable;
+import androidx.core.app.Person;
+
+import java.util.Objects;
+
+/** Helper functions to compare two {@link Person} object. */
+class PersonsEqualityHelper {
+
+    /** Calculate the hashcode for {@link Person} object. */
+    // TODO(b/266877597): Move to androidx.core.app.Person
+    public static int getPersonHashCode(@Nullable Person person) {
+        if (person == null) {
+            return 0;
+        }
+
+        // If a unique ID was provided, use it
+        String key = person.getKey();
+        if (key != null) {
+            return key.hashCode();
+        }
+
+        // Fallback: Use hash code for individual fields
+        return Objects.hash(person.getName(), person.getUri(), person.isBot(),
+                person.isImportant());
+    }
+
+    /** Compare two {@link Person} objects. */
+    // TODO(b/266877597): Move to androidx.core.app.Person
+    public static boolean arePersonsEqual(@Nullable Person person1, @Nullable Person person2) {
+        if (person1 == null && person2 == null) {
+            return true;
+        } else if (person1 == null || person2 == null) {
+            return false;
+        }
+
+        // If a unique ID was provided, use it
+        String key1 = person1.getKey();
+        String key2 = person2.getKey();
+        if (key1 != null || key2 != null) {
+            return Objects.equals(key1, key2);
+        }
+
+        // CharSequence doesn't have well-defined "equals" behavior -- convert to String instead
+        String name1 = Objects.toString(person1.getName());
+        String name2 = Objects.toString(person2.getName());
+
+        // Fallback: Compare field-by-field
+        return
+                Objects.equals(name1, name2)
+                        && Objects.equals(person1.getUri(), person2.getUri())
+                        && Objects.equals(person1.isBot(), person2.isBot())
+                        && Objects.equals(person1.isImportant(), person2.isImportant());
+    }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Badge.java b/car/app/app/src/main/java/androidx/car/app/model/Badge.java
index 7d799df..ade526c 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Badge.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Badge.java
@@ -39,27 +39,49 @@
 public class Badge {
 
     private final boolean mHasDot;
+    @Nullable
+    private final CarColor mBackgroundColor;
+    @Nullable
+    private final CarIcon mIcon;
 
     /**
      * Returns whether the badge has a dot.
      *
-     * @see Builder#setHasDot()
+     * @see Builder#setHasDot(boolean)
      */
     public boolean hasDot() {
         return mHasDot;
     }
 
+    /**
+     * Returns the dot background color.
+     */
+    @Nullable
+    public CarColor getBackgroundColor() {
+        return mBackgroundColor;
+    }
+
+    /**
+     * Returns the badge icon.
+     *
+     * @see Builder#setIcon(CarIcon)
+     */
+    @Nullable
+    public CarIcon getIcon() {
+        return mIcon;
+    }
+
     @Override
     @NonNull
     public String toString() {
-        return "[hasDot: "
-                + mHasDot
-                + "]";
+        return "[hasDot: " + mHasDot
+                + ", backgroundColor: " + mBackgroundColor
+                + ", icon: " + mIcon + "]";
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mHasDot);
+        return Objects.hash(mHasDot, mBackgroundColor, mIcon);
     }
 
     @Override
@@ -72,21 +94,31 @@
         }
         Badge otherBadge = (Badge) other;
 
-        return mHasDot == otherBadge.mHasDot;
+        return mHasDot == otherBadge.mHasDot
+                && Objects.equals(mBackgroundColor, otherBadge.mBackgroundColor)
+                && Objects.equals(mIcon, otherBadge.mIcon);
     }
 
     Badge(Builder builder) {
         mHasDot = builder.mHasDot;
+        mBackgroundColor = builder.mBackgroundColor;
+        mIcon = builder.mIcon;
     }
 
     /** Constructs an empty instance, used by serialization code. */
     private Badge() {
         mHasDot = false;
+        mBackgroundColor = null;
+        mIcon = null;
     }
 
     /** A builder of {@link Badge}. */
     public static final class Builder {
         boolean mHasDot;
+        @Nullable
+        CarColor mBackgroundColor;
+        @Nullable
+        CarIcon mIcon;
 
         /**
          * Enables a circular dot that denotes some sort of alert, notification, etc.
@@ -98,14 +130,40 @@
         }
 
         /**
+         * Sets the color of the dot to the given {@code backgroundColor}.
+         */
+        @NonNull
+        public Builder setBackgroundColor(@NonNull CarColor backgroundColor) {
+            mBackgroundColor = backgroundColor;
+            return this;
+        }
+
+        /**
+         * Sets an icon to be displayed as a badge.
+         *
+         * <p>An icon badge gives context about the associated element on which it is displayed. For
+         * example, a work profile icon badge is displayed with an app icon to indicate that
+         * it is a work app.
+         */
+        @NonNull
+        public Builder setIcon(@NonNull CarIcon icon) {
+            mIcon = icon;
+            return this;
+        }
+
+        /**
          * Constructs the {@link Badge} defined by this builder.
          *
-         * @throws IllegalStateException if no property is set on the badge.
+         * @throws IllegalStateException if the badge doesn't have a dot or an icon.
          */
         @NonNull
         public Badge build() {
-            if (!mHasDot) {
-                throw new IllegalStateException("At least one property must be set on the badge");
+            if (!mHasDot && mIcon == null) {
+                throw new IllegalStateException("A badge must have a dot or an icon set");
+            }
+            if (!mHasDot && mBackgroundColor != null) {
+                throw new IllegalStateException("The dot must be enabled to set the background "
+                        + "color.");
             }
             return new Badge(this);
         }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/GridItem.java b/car/app/app/src/main/java/androidx/car/app/model/GridItem.java
index a7afa7f..c16bb89 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/GridItem.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/GridItem.java
@@ -349,6 +349,47 @@
         }
 
         /**
+         * Sets an image to show in the grid item with the given {@link Badge} to be displayed over
+         * the image, with the default size {@link #IMAGE_TYPE_LARGE}.
+         *
+         * <p>A dot badge denotes some sort of call to action or notification and is
+         * displayed in the upper right corner of the image. An icon badge gives additional
+         * context about the image and is displayed in the lower right corner.
+         *
+         * @throws NullPointerException if {@code image} or {@code badge} is {@code null}
+         * @see #setImage(CarIcon, int)
+         */
+        @NonNull
+        @ExperimentalCarApi
+        @RequiresCarApi(7)
+        public Builder setImage(@NonNull CarIcon image, @NonNull Badge badge) {
+            requireNonNull(badge);
+            mBadge = badge;
+            return setImage(requireNonNull(image));
+        }
+
+        /**
+         * Sets an image to show in the grid item with the given {@code imageType} and given
+         * {@link Badge} to be displayed over the image.
+         *
+         * <p>A dot badge denotes a call to action or notification and is
+         * displayed in the upper right corner of the image. An icon badge gives additional
+         * context about the image and is displayed in the lower right corner.
+         *
+         * @throws NullPointerException if {@code image} or {@code badge} is {@code null}
+         * @see #setImage(CarIcon, int)
+         */
+        @NonNull
+        @ExperimentalCarApi
+        @RequiresCarApi(7)
+        public Builder setImage(@NonNull CarIcon image, @GridItemImageType int imageType,
+                @NonNull Badge badge) {
+            requireNonNull(badge);
+            mBadge = badge;
+            return setImage(requireNonNull(image), imageType);
+        }
+
+        /**
          * Sets an image to show in the grid item with the given {@code imageType}.
          *
          * <p>For a custom {@link CarIcon}, its {@link androidx.core.graphics.drawable.IconCompat}
@@ -394,23 +435,6 @@
         }
 
         /**
-         * Sets a {@link Badge} to be displayed over the grid item image.
-         *
-         * <p>A badge denotes some sort of call to action, notification, alert, etc.
-         *
-         * <p>A badge can only be set if an image is also set on the grid item.
-         *
-         * @param badge the {@link Badge} to display.
-         */
-        @NonNull
-        @ExperimentalCarApi
-        @RequiresCarApi(7)
-        public Builder setBadge(@NonNull Badge badge) {
-            mBadge = badge;
-            return this;
-        }
-
-        /**
          * Constructs the {@link GridItem} defined by this builder.
          *
          * @throws IllegalStateException if the grid item's title is not set, if the grid item's
diff --git a/car/app/app/src/test/java/androidx/car/app/messaging/model/CarMessageTest.java b/car/app/app/src/test/java/androidx/car/app/messaging/model/CarMessageTest.java
index f250727..a7a2fb2 100644
--- a/car/app/app/src/test/java/androidx/car/app/messaging/model/CarMessageTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/messaging/model/CarMessageTest.java
@@ -51,18 +51,6 @@
     // Ignore nullability, so we can null out a builder field
     @SuppressWarnings("ConstantConditions")
     @Test
-    public void build_throwsException_ifSenderMissing() {
-        assertThrows(
-                NullPointerException.class,
-                () -> TestConversationFactory.createMinimalMessageBuilder()
-                        .setSender(null)
-                        .build()
-        );
-    }
-
-    // Ignore nullability, so we can null out a builder field
-    @SuppressWarnings("ConstantConditions")
-    @Test
     public void build_throwsException_ifMessageBodyMissing() {
         assertThrows(
                 NullPointerException.class,
diff --git a/car/app/app/src/test/java/androidx/car/app/messaging/model/ConversationItemTest.java b/car/app/app/src/test/java/androidx/car/app/messaging/model/ConversationItemTest.java
index 9f61ade..2090047 100644
--- a/car/app/app/src/test/java/androidx/car/app/messaging/model/ConversationItemTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/messaging/model/ConversationItemTest.java
@@ -110,6 +110,12 @@
                         .createFullyPopulatedConversationItemBuilder()
                         .setGroupConversation(!fullyPopulatedItem.isGroupConversation())
                         .build();
+        ConversationItem modifiedSelf =
+                TestConversationFactory
+                        .createFullyPopulatedConversationItemBuilder()
+                        .setSelf(
+                                TestConversationFactory.createFullyPopulatedPersonBuilder().build())
+                        .build();
         List<CarMessage> modifiedMessages = new ArrayList<>(1);
         modifiedMessages.add(
                 TestConversationFactory
@@ -125,6 +131,7 @@
         ConversationItem modifiedConversationCallback =
                 TestConversationFactory
                         .createFullyPopulatedConversationItemBuilder()
+                        .setSelf(TestConversationFactory.createMinimalPersonBuilder().build())
                         .setConversationCallback(new ConversationCallback() {
                             @Override
                             public void onMarkAsRead() {
@@ -144,6 +151,7 @@
         assertNotEqual(fullyPopulatedItem, modifiedIcon);
         assertNotEqual(fullyPopulatedItem, modifiedGroupStatus);
         assertNotEqual(fullyPopulatedItem, modifiedMessageList);
+        assertNotEqual(fullyPopulatedItem, modifiedSelf);
 
         // NOTE: Conversation Callback does not affect equality
         assertEqual(fullyPopulatedItem, modifiedConversationCallback);
diff --git a/car/app/app/src/test/java/androidx/car/app/messaging/model/PersonsEqualityHelperTest.java b/car/app/app/src/test/java/androidx/car/app/messaging/model/PersonsEqualityHelperTest.java
new file mode 100644
index 0000000..13b4d4f
--- /dev/null
+++ b/car/app/app/src/test/java/androidx/car/app/messaging/model/PersonsEqualityHelperTest.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.car.app.messaging.model;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.Uri;
+
+import androidx.core.app.Person;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+/** Tests for {@link PersonsEqualityHelper}. */
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+public class PersonsEqualityHelperTest {
+
+    @Test
+    public void equalsAndHashCode_minimalPersons_areEqual() {
+        Person person1 =
+                TestConversationFactory.createMinimalPersonBuilder().build();
+        Person person2 =
+                TestConversationFactory.createMinimalPersonBuilder().build();
+
+        assertThat(PersonsEqualityHelper.arePersonsEqual(person1, person2)).isTrue();
+        assertThat(PersonsEqualityHelper.getPersonHashCode(person1)).isEqualTo(
+                PersonsEqualityHelper.getPersonHashCode(person2));
+    }
+
+    @Test
+    public void equalsAndHashCode_nullPersons_areEqual() {
+        assertThat(PersonsEqualityHelper.getPersonHashCode(null)).isEqualTo(
+                PersonsEqualityHelper.getPersonHashCode(null));
+        assertThat(PersonsEqualityHelper.arePersonsEqual(null, null)).isTrue();
+    }
+
+    @Test
+    public void equalsAndHashCode_differentName_areNotEqual() {
+        Person person1 =
+                TestConversationFactory.createMinimalPersonBuilder().setName("Person1").build();
+        Person person2 =
+                TestConversationFactory.createMinimalPersonBuilder().setName("Person2").build();
+
+        assertThat(PersonsEqualityHelper.arePersonsEqual(person1, person2)).isFalse();
+        assertThat(PersonsEqualityHelper.getPersonHashCode(person1)).isNotEqualTo(
+                PersonsEqualityHelper.getPersonHashCode(person2));
+    }
+
+    @Test
+    public void equalsAndHashCode_differentKey_areNotEqual() {
+        Person person1 =
+                TestConversationFactory.createMinimalPersonBuilder().setKey("Person1").build();
+        Person person2 =
+                TestConversationFactory.createMinimalPersonBuilder().setKey("Person2").build();
+
+        assertThat(PersonsEqualityHelper.arePersonsEqual(person1, person2)).isFalse();
+        assertThat(PersonsEqualityHelper.getPersonHashCode(person1)).isNotEqualTo(
+                PersonsEqualityHelper.getPersonHashCode(person2));
+    }
+
+    @Test
+    public void equalsAndHashCode_differentUri_areNotEqual() {
+        Uri uri1 =
+                Uri.parse("http://foo.com/test/sender/uri1");
+        Uri uri2 =
+                Uri.parse("http://foo.com/test/sender/uri2");
+        Person person1 =
+                TestConversationFactory.createMinimalPersonBuilder().setUri(
+                        uri1.toString()).build();
+        Person person2 =
+                TestConversationFactory.createMinimalPersonBuilder().setName(
+                        uri2.toString()).build();
+
+        assertThat(PersonsEqualityHelper.arePersonsEqual(person1, person2)).isFalse();
+        assertThat(PersonsEqualityHelper.getPersonHashCode(person1)).isNotEqualTo(
+                PersonsEqualityHelper.getPersonHashCode(person2));
+    }
+
+    @Test
+    public void equalsAndHashCode_differentBot_areNotEqual() {
+        Person person1 =
+                TestConversationFactory.createMinimalPersonBuilder().setBot(true).build();
+        Person person2 =
+                TestConversationFactory.createMinimalPersonBuilder().setBot(false).build();
+
+        assertThat(PersonsEqualityHelper.arePersonsEqual(person1, person2)).isFalse();
+        assertThat(PersonsEqualityHelper.getPersonHashCode(person1)).isNotEqualTo(
+                PersonsEqualityHelper.getPersonHashCode(person2));
+    }
+
+    @Test
+    public void equalsAndHashCode_differentImportant_areNotEqual() {
+        Person person1 =
+                TestConversationFactory.createMinimalPersonBuilder().setImportant(true).build();
+        Person person2 =
+                TestConversationFactory.createMinimalPersonBuilder().setImportant(false).build();
+
+        assertThat(PersonsEqualityHelper.arePersonsEqual(person1, person2)).isFalse();
+        assertThat(PersonsEqualityHelper.getPersonHashCode(person1)).isNotEqualTo(
+                PersonsEqualityHelper.getPersonHashCode(person2));
+    }
+}
diff --git a/car/app/app/src/test/java/androidx/car/app/messaging/model/TestConversationFactory.java b/car/app/app/src/test/java/androidx/car/app/messaging/model/TestConversationFactory.java
index 19359bb..dc3097b 100644
--- a/car/app/app/src/test/java/androidx/car/app/messaging/model/TestConversationFactory.java
+++ b/car/app/app/src/test/java/androidx/car/app/messaging/model/TestConversationFactory.java
@@ -98,7 +98,6 @@
      */
     public static CarMessage.Builder createMinimalMessageBuilder() {
         return new CarMessage.Builder()
-                .setSender(createMinimalPerson())
                 .setBody(CarText.create("Message body"));
     }
 
@@ -118,6 +117,7 @@
      */
     public static CarMessage.Builder createFullyPopulatedMessageBuilder() {
         return createMinimalMessageBuilder()
+                .setSender(createFullyPopulatedPerson())
                 .setRead(true)
                 .setReceivedTimeEpochMillis(12345);
     }
@@ -146,6 +146,7 @@
         return new ConversationItem.Builder()
                 .setId("conversation_id")
                 .setTitle(CarText.create("Conversation Title"))
+                .setSelf(createMinimalPerson())
                 .setMessages(messages)
                 .setConversationCallback(EMPTY_CONVERSATION_CALLBACK);
     }
diff --git a/car/app/app/src/test/java/androidx/car/app/model/BadgeTest.java b/car/app/app/src/test/java/androidx/car/app/model/BadgeTest.java
index d6655c0..96b1797 100644
--- a/car/app/app/src/test/java/androidx/car/app/model/BadgeTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/BadgeTest.java
@@ -27,10 +27,25 @@
 @RunWith(RobolectricTestRunner.class)
 public class BadgeTest {
     @Test
-    public void build_withDot() {
-        Badge b = new Badge.Builder().setHasDot(true).build();
+    public void build_withDotAndIcon() {
+        Badge b = new Badge.Builder().setHasDot(true).setIcon(CarIcon.ALERT).build();
 
         assertThat(b.hasDot()).isEqualTo(true);
+        assertThat(b.getIcon()).isEqualTo(CarIcon.ALERT);
+    }
+
+    @Test
+    public void build_withIcon() {
+        Badge b = new Badge.Builder().setIcon(CarIcon.ALERT).build();
+
+        assertThat(b.getIcon()).isEqualTo(CarIcon.ALERT);
+    }
+
+    public void build_withDotAndBackgroundColor() {
+        Badge b = new Badge.Builder().setHasDot(true).setBackgroundColor(CarColor.PRIMARY).build();
+
+        assertThat(b.hasDot()).isEqualTo(true);
+        assertThat(b.getBackgroundColor()).isEqualTo(CarColor.PRIMARY);
     }
 
     @Test
@@ -45,26 +60,55 @@
     }
 
     @Test
-    public void equals() {
-        Badge b1 = new Badge.Builder().setHasDot(true).build();
-        Badge b2 = new Badge.Builder().setHasDot(true).build();
+    public void build_setBackgroundColorWithoutDot_ThrowsException() {
+        assertThrows(IllegalStateException.class,
+                () -> new Badge.Builder()
+                        .setIcon(CarIcon.ALERT)
+                        .setBackgroundColor(CarColor.PRIMARY).build());
+    }
 
-        assertThat(b1.equals(b1)).isTrue();
+    @Test
+    public void equals() {
+        Badge b1 = new Badge.Builder()
+                .setHasDot(true)
+                .setBackgroundColor(CarColor.PRIMARY)
+                .setIcon(CarIcon.ALERT).build();
+        Badge b2 = new Badge.Builder()
+                .setHasDot(true)
+                .setBackgroundColor(CarColor.PRIMARY)
+                .setIcon(CarIcon.ALERT).build();
+
         assertThat(b1.equals(b2)).isTrue();
     }
 
     @Test
-    public void notEquals() {
-        Badge b = new Badge.Builder().setHasDot(true).build();
-        Object o = new Object();
+    public void notEquals_differentProperty() {
+        Badge b1 = new Badge.Builder().setHasDot(true).build();
+        Badge b2 = new Badge.Builder().setIcon(CarIcon.ALERT).build();
 
-        assertThat(b.equals(o)).isFalse();
+        assertThat(b1.equals(b2)).isFalse();
+    }
+
+    @Test
+    public void notEquals_differentBackgroundColor() {
+        Badge b1 = new Badge.Builder().setHasDot(true).setBackgroundColor(CarColor.BLUE).build();
+        Badge b2 = new Badge.Builder().setHasDot(true).setBackgroundColor(CarColor.RED).build();
+
+        assertThat(b1.equals(b2)).isFalse();
+    }
+
+    @Test
+    public void notEquals_differentIcons() {
+        Badge b1 = new Badge.Builder().setIcon(CarIcon.ALERT).build();
+        Badge b2 = new Badge.Builder().setIcon(CarIcon.ERROR).build();
+
+        assertThat(b1.equals(b2)).isFalse();
     }
 
     @Test
     public void string() {
         Badge b = new Badge.Builder().setHasDot(true).build();
 
-        assertThat(b.toString()).isEqualTo("[hasDot: true]");
+        assertThat(b.toString()).isEqualTo("[hasDot: true, backgroundColor: null, icon: null]");
     }
 }
diff --git a/car/app/app/src/test/java/androidx/car/app/model/GridItemTest.java b/car/app/app/src/test/java/androidx/car/app/model/GridItemTest.java
index af77551..6856d45 100644
--- a/car/app/app/src/test/java/androidx/car/app/model/GridItemTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/GridItemTest.java
@@ -163,19 +163,19 @@
     }
 
     @Test
-    public void create_loadingWithBadge_throws() {
+    public void create_setImagewithBadge() {
         Badge b = new Badge.Builder().setHasDot(true).build();
-        GridItem.Builder builder =
-                new GridItem.Builder().setTitle("Title").setLoading(true).setBadge(b);
+        GridItem gridItem =
+                new GridItem.Builder().setTitle("Title").setImage(BACK, b).build();
 
-        assertThrows(IllegalStateException.class, () -> builder.build());
+        assertThat(gridItem.getBadge()).isEqualTo(b);
     }
 
     @Test
-    public void create_withBadge() {
+    public void create_setImagewithTypeAndBadge() {
         Badge b = new Badge.Builder().setHasDot(true).build();
-        GridItem gridItem =
-                new GridItem.Builder().setTitle("Title").setImage(BACK).setBadge(b).build();
+        GridItem gridItem = new GridItem.Builder().setTitle("Title")
+                .setImage(BACK, GridItem.IMAGE_TYPE_ICON, b).build();
 
         assertThat(gridItem.getBadge()).isEqualTo(b);
     }
@@ -188,13 +188,11 @@
         GridItem g1 = new GridItem.Builder()
                 .setTitle(title)
                 .setText(text)
-                .setImage(BACK)
-                .setBadge(badge).build();
+                .setImage(BACK, badge).build();
         GridItem g2 = new GridItem.Builder()
                 .setTitle(title)
                 .setText(text)
-                .setImage(BACK)
-                .setBadge(badge).build();
+                .setImage(BACK, badge).build();
 
         assertThat(g1).isEqualTo(g2);
     }
@@ -231,7 +229,7 @@
     public void notEquals_differentBadge() {
         Badge badge = new Badge.Builder().setHasDot(true).build();
         GridItem withBadge = new GridItem.Builder()
-                .setTitle("Title").setImage(BACK).setBadge(badge).build();
+                .setTitle("Title").setImage(BACK, badge).build();
         GridItem noBadge = new GridItem.Builder().setTitle("Title").setImage(BACK).build();
 
         assertThat(withBadge).isNotEqualTo(noBadge);
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt
index 3829597..82dcf95 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt
@@ -309,7 +309,7 @@
                     if (isTraceInProgress()) {
                       traceEventEnd()
                     }
-                    %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                    %composer@Test.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
                       Test(condition, %composer, updateChangedFlags(%changed or 0b0001))
                     }
                     return
@@ -381,7 +381,7 @@
                     if (isTraceInProgress()) {
                       traceEventEnd()
                     }
-                    %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                    %composer@Test.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
                       Test(a, b, %composer, updateChangedFlags(%changed or 0b0001))
                     }
                     return
@@ -397,7 +397,7 @@
                     if (isTraceInProgress()) {
                       traceEventEnd()
                     }
-                    %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                    %composer@Test.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
                       Test(a, b, %composer, updateChangedFlags(%changed or 0b0001))
                     }
                     return
@@ -705,7 +705,7 @@
                       if (isTraceInProgress()) {
                         traceEventEnd()
                       }
-                      %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                      %composer@testInline_M1_W_Return_Func.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
                         testInline_M1_W_Return_Func(condition, %composer, updateChangedFlags(%changed or 0b0001))
                       }
                       return
@@ -852,7 +852,7 @@
                       if (isTraceInProgress()) {
                         traceEventEnd()
                       }
-                      %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                      %composer@test_CM1_CCM1_RetFun.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
                         test_CM1_CCM1_RetFun(condition, %composer, updateChangedFlags(%changed or 0b0001))
                       }
                       return
@@ -1330,7 +1330,7 @@
                     if (isTraceInProgress()) {
                       traceEventEnd()
                     }
-                    %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                    %composer@test_CM1_RetFun.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
                       test_CM1_RetFun(condition, %composer, updateChangedFlags(%changed or 0b0001))
                     }
                     return
@@ -6287,4 +6287,59 @@
         }
         """
     )
+
+    @Test
+    fun testEarlyReturnFromCrossInlinedLambda() = verifyComposeIrTransform(
+        source = """
+            import androidx.compose.runtime.*
+
+            @Composable
+            private fun Test(param: String?) {
+                Dialog {
+                    if (false) Test(param)
+                }
+            }
+        """,
+        extra = """
+            import androidx.compose.runtime.*
+
+            @Composable
+            internal inline fun Dialog(crossinline block: @Composable () -> Unit) {}
+        """.trimIndent(),
+        expectedTransformed = """
+           @Composable
+           private fun Test(param: String?, %composer: Composer?, %changed: Int) {
+             %composer = %composer.startRestartGroup(<>)
+             sourceInformation(%composer, "C(Test)<Dialog>:Test.kt")
+             val %dirty = %changed
+             if (%changed and 0b1110 === 0) {
+               %dirty = %dirty or if (%composer.changed(param)) 0b0100 else 0b0010
+             }
+             if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
+               if (isTraceInProgress()) {
+                 traceEventStart(<>, %dirty, -1, <>)
+               }
+               Dialog({ %composer: Composer?, %changed: Int ->
+                 sourceInformationMarkerStart(%composer, <>, "C:Test.kt")
+                 %composer.startReplaceableGroup(<>)
+                 sourceInformation(%composer, "<Test(p...>")
+                 if (false) {
+                   Test(param, %composer, 0b1110 and %dirty)
+                 }
+                 %composer.endReplaceableGroup()
+                 sourceInformationMarkerEnd(%composer)
+               }, %composer, 0)
+               if (isTraceInProgress()) {
+                 traceEventEnd()
+               }
+             } else {
+               %composer.skipToGroupEnd()
+             }
+             %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+               Test(param, %composer, updateChangedFlags(%changed or 0b0001))
+             }
+           }
+        """.trimIndent(),
+        dumpTree = true
+    )
 }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
index b4e644c..edd8cf3 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
@@ -1489,7 +1489,7 @@
                       traceEventStart(<>, %changed, -1, <>)
                     }
                     used(it)
-                    A(x, 0, %composer, 0b1110 and %dirty, 0b0010)
+                    A(x, 0, %composer, 0b1110 and %dirty@Test, 0b0010)
                     if (isTraceInProgress()) {
                       traceEventEnd()
                     }
@@ -2792,7 +2792,7 @@
                         if (isTraceInProgress()) {
                           traceEventStart(<>, %dirty, -1, <>)
                         }
-                        B(x, y, z, %composer, 0b1110 and %dirty or 0b01110000 and %dirty shl 0b0011 or 0b001110000000 and %dirty shl 0b0110, 0)
+                        B(x, y, z, %composer, 0b1110 and %dirty@A or 0b01110000 and %dirty@A.<anonymous> shl 0b0011 or 0b001110000000 and %dirty shl 0b0110, 0)
                         if (isTraceInProgress()) {
                           traceEventEnd()
                         }
@@ -2800,7 +2800,7 @@
                         %composer.skipToGroupEnd()
                       }
                     }, %composer, 0b0110)
-                    B(x, y, 0, %composer, 0b1110 and %dirty or 0b01110000 and %dirty shl 0b0011, 0b0100)
+                    B(x, y, 0, %composer, 0b1110 and %dirty@A or 0b01110000 and %dirty shl 0b0011, 0b0100)
                     if (isTraceInProgress()) {
                       traceEventEnd()
                     }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RunComposableTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RunComposableTests.kt
index 3bfd3f1..4aad0a8 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RunComposableTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RunComposableTests.kt
@@ -94,6 +94,264 @@
         }
     }
 
+    @Test
+    fun testExpectWithGetExpectedPropertyInDefaultValueExpression() {
+        runCompose(
+            testFunBody = """
+                ExpectComposable { value ->
+                    results["defaultValue"] = value
+                }
+                ExpectComposable({ expectedProperty + expectedProperty.reversed() }) { value ->
+                    results["anotherValue"] = value
+                }
+            """.trimIndent(),
+            files = mapOf(
+                "Expect.kt" to """
+                    import androidx.compose.runtime.*
+
+                    expect val expectedProperty: String
+
+                    @Composable
+                    expect fun ExpectComposable(
+                        value: () -> String = { expectedProperty },
+                        content: @Composable (v: String) -> Unit
+                    )
+                """.trimIndent(),
+                "Actual.kt" to """
+                    import androidx.compose.runtime.*
+
+                    actual val expectedProperty = "actualExpectedProperty"
+
+                    @Composable
+                    actual fun ExpectComposable(
+                        value: () -> String,
+                        content: @Composable (v: String) -> Unit
+                    ) {
+                        content(value())
+                    }
+                """.trimIndent()
+            )
+        ) { results ->
+            assertEquals("actualExpectedProperty", results["defaultValue"])
+            assertEquals(
+                "actualExpectedProperty" + "actualExpectedProperty".reversed(),
+                results["anotherValue"]
+            )
+        }
+    }
+
+    @Test
+    fun testExpectWithComposableExpressionInDefaultValue() {
+        runCompose(
+            testFunBody = """
+                ExpectComposable { value ->
+                    results["defaultValue"] = value
+                }
+                ExpectComposable("anotherValue") { value ->
+                    results["anotherValue"] = value
+                }
+            """.trimIndent(),
+            files = mapOf(
+                "Expect.kt" to """
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    fun defaultValueComposable(): String {
+                        return "defaultValueComposable"
+                    }
+
+                    @Composable
+                    expect fun ExpectComposable(
+                        value: String = defaultValueComposable(),
+                        content: @Composable (v: String) -> Unit
+                    )
+                """.trimIndent(),
+                "Actual.kt" to """
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    actual fun ExpectComposable(
+                        value: String,
+                        content: @Composable (v: String) -> Unit
+                    ) {
+                        content(value)
+                    }
+                """.trimIndent()
+            )
+        ) { results ->
+            assertEquals("defaultValueComposable", results["defaultValue"])
+            assertEquals("anotherValue", results["anotherValue"])
+        }
+    }
+
+    @Test
+    fun testExpectWithTypedParameter() {
+        runCompose(
+            testFunBody = """
+                ExpectComposable<String>("aeiouy") { value ->
+                    results["defaultValue"] = value
+                }
+                ExpectComposable<String>("aeiouy", { "anotherValue" }) { value ->
+                    results["anotherValue"] = value
+                }
+            """.trimIndent(),
+            files = mapOf(
+                "Expect.kt" to """
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    expect fun <T> ExpectComposable(
+                        value: T,
+                        composeValue: @Composable () -> T = { value },
+                        content: @Composable (T) -> Unit
+                    )
+                """.trimIndent(),
+                "Actual.kt" to """
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    actual fun <T> ExpectComposable(
+                        value: T,
+                        composeValue: @Composable () -> T,
+                        content: @Composable (T) -> Unit
+                    ) {
+                        content(composeValue())
+                    }
+                """.trimIndent()
+            )
+        ) { results ->
+            assertEquals("aeiouy", results["defaultValue"])
+            assertEquals("anotherValue", results["anotherValue"])
+        }
+    }
+
+    @Test
+    fun testExpectWithRememberInDefaultValueExpression() {
+        runCompose(
+            testFunBody = """
+                ExpectComposable { value ->
+                    results["defaultValue"] = value
+                }
+                ExpectComposable(remember { "anotherRememberedValue" }) { value ->
+                    results["anotherValue"] = value
+                }
+            """.trimIndent(),
+            files = mapOf(
+                "Expect.kt" to """
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    expect fun ExpectComposable(
+                        value: String = remember { "rememberedDefaultValue" },
+                        content: @Composable (v: String) -> Unit
+                    )
+                """.trimIndent(),
+                "Actual.kt" to """
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    actual fun ExpectComposable(
+                        value: String,
+                        content: @Composable (v: String) -> Unit
+                    ) {
+                        content(value)
+                    }
+                """.trimIndent()
+            )
+        ) { results ->
+            assertEquals("rememberedDefaultValue", results["defaultValue"])
+            assertEquals("anotherRememberedValue", results["anotherValue"])
+        }
+    }
+
+    @Test
+    fun testExpectWithDefaultValueUsingAnotherArgument() {
+        runCompose(
+            testFunBody = """
+                ExpectComposable("AbccbA") { value ->
+                    results["defaultValue"] = value
+                }
+                ExpectComposable("123", { s -> s + s.reversed() }) { value ->
+                    results["anotherValue"] = value
+                }
+            """.trimIndent(),
+            files = mapOf(
+                "Expect.kt" to """
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    expect fun ExpectComposable(
+                        value: String,
+                        composeText: (String) -> String = { value },
+                        content: @Composable (v: String) -> Unit
+                    )
+                """.trimIndent(),
+                "Actual.kt" to """
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    actual fun ExpectComposable(
+                        value: String,
+                        composeText: (String) -> String,
+                        content: @Composable (v: String) -> Unit
+                    ) {
+                        content(composeText(value))
+                    }
+                """.trimIndent()
+            )
+        ) { results ->
+            assertEquals("AbccbA", results["defaultValue"])
+            assertEquals("123321", results["anotherValue"])
+        }
+    }
+
+    @Test
+    fun testNonComposableFunWithComposableParam() {
+        runCompose(
+            testFunBody = """
+                savedContentLambda = null
+                ExpectFunWithComposableParam { value ->
+                    results["defaultValue"] = value
+                }
+                savedContentLambda!!.invoke()
+
+                savedContentLambda = null
+                ExpectFunWithComposableParam("3.14") { value ->
+                    results["anotherValue"] = value
+                }
+                savedContentLambda!!.invoke()
+            """.trimIndent(),
+            files = mapOf(
+                "Expect.kt" to """
+                    import androidx.compose.runtime.*
+
+                    var savedContentLambda: (@Composable () -> Unit)? = null
+
+                    expect fun ExpectFunWithComposableParam(
+                        value: String = "000",
+                        content: @Composable (v: String) -> Unit
+                    )
+                """.trimIndent(),
+                "Actual.kt" to """
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    actual fun ExpectFunWithComposableParam(
+                        value: String,
+                        content: @Composable (v: String) -> Unit
+                    ) {
+                        savedContentLambda = {
+                            content(value)
+                        }
+                    }
+                """.trimIndent()
+            )
+        ) { results ->
+            assertEquals("000", results["defaultValue"])
+            assertEquals("3.14", results["anotherValue"])
+        }
+    }
+
     // This method was partially borrowed/copy-pasted from RobolectricComposeTester
     // where some of the code was commented out. Those commented out parts are needed here.
     private fun runCompose(
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TargetAnnotationsTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TargetAnnotationsTransformTests.kt
index 90b67be..b7d9f1b 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TargetAnnotationsTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TargetAnnotationsTransformTests.kt
@@ -911,7 +911,7 @@
           val tmp0_measurePolicy = localBoxMeasurePolicy
           Layout({ %composer: Composer?, %changed: Int ->
             sourceInformationMarkerStart(%composer, <>, "C<conten...>:Test.kt")
-            content(LocalBoxScopeInstance, %composer, 0b0110 or 0b01110000 and %changed)
+            content(LocalBoxScopeInstance, %composer, 0b0110 or 0b01110000 and %changed@LocalBox)
             sourceInformationMarkerEnd(%composer)
           }, modifier, tmp0_measurePolicy, %composer, 0b000110000000 or 0b01110000 and %changed shl 0b0011, 0)
           %composer.endReplaceableGroup()
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TraceInformationTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TraceInformationTest.kt
index fe08eba..0a5b091 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TraceInformationTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TraceInformationTest.kt
@@ -136,7 +136,7 @@
                     if (isTraceInProgress()) {
                       traceEventEnd()
                     }
-                    %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                    %composer@Test.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
                       Test(condition, %composer, updateChangedFlags(%changed or 0b0001))
                     }
                     return
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt
index bbb5435..4629463 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt
@@ -108,7 +108,7 @@
             metrics
         ).lower(moduleFragment)
 
-        CopyDefaultValuesFromExpectLowering().lower(moduleFragment)
+        CopyDefaultValuesFromExpectLowering(pluginContext).lower(moduleFragment)
 
         val mangler = when {
             pluginContext.platform.isJs() -> JsManglerIr
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
index 9f48540..49ba87ca 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
@@ -122,7 +122,7 @@
          * The maven version string of this compiler. This string should be updated before/after every
          * release.
          */
-        const val compilerVersion: String = "1.4.3"
+        const val compilerVersion: String = "1.4.4"
         private val minimumRuntimeVersion: String
             get() = runtimeVersionToMavenVersionTable[minimumRuntimeVersionInt] ?: "unknown"
     }
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
index fbea978..d3e97ae 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
@@ -27,6 +27,7 @@
 import androidx.compose.compiler.plugins.kotlin.analysis.knownStable
 import androidx.compose.compiler.plugins.kotlin.analysis.stabilityOf
 import androidx.compose.compiler.plugins.kotlin.irTrace
+import com.intellij.openapi.progress.ProcessCanceledException
 import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
 import org.jetbrains.kotlin.backend.jvm.ir.isInlineClassType
 import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
@@ -1121,6 +1122,8 @@
 inline fun <T> includeFileNameInExceptionTrace(file: IrFile, body: () -> T): T {
     try {
         return body()
+    } catch (e: ProcessCanceledException) {
+        throw e
     } catch (e: Exception) {
         throw Exception("IR lowering failed at: ${file.name}", e)
     }
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
index 05236bc..0dd6696 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
@@ -888,7 +888,7 @@
             scope.realizeGroup {
                 irComposite(statements = listOfNotNull(
                     if (emitTraceMarkers) irTraceEventEnd() else null,
-                    irEndReplaceableGroup()
+                    irEndReplaceableGroup(scope = scope)
                 ))
             }
         }
@@ -916,9 +916,9 @@
                 *bodyPreamble.statements.toTypedArray(),
                 *transformed.statements.toTypedArray(),
                 when {
-                    !elideGroups -> irEndReplaceableGroup()
+                    !elideGroups -> irEndReplaceableGroup(scope = scope)
                     collectSourceInformation && !hasExplicitGroups ->
-                        irSourceInformationMarkerEnd(body)
+                        irSourceInformationMarkerEnd(body, scope)
                     else -> null
                 },
                 returnVar?.let { irReturn(declaration.symbol, irGet(it)) }
@@ -926,7 +926,7 @@
         )
         if (elideGroups && !hasExplicitGroups && collectSourceInformation) {
             scope.realizeEndCalls {
-                irSourceInformationMarkerEnd(body)
+                irSourceInformationMarkerEnd(body, scope)
             }
         }
 
@@ -970,7 +970,7 @@
                 sourceInformationPreamble.statements.add(
                     irSourceInformationMarkerStart(body, scope)
                 )
-                bodyEpilogue.statements.add(irSourceInformationMarkerEnd(body))
+                bodyEpilogue.statements.add(irSourceInformationMarkerEnd(body, scope))
             } else {
                 sourceInformationPreamble.statements.add(irSourceInformation(scope))
             }
@@ -1042,7 +1042,7 @@
 
         if (collectSourceInformation && isInlineLambda) {
             scope.realizeEndCalls {
-                irSourceInformationMarkerEnd(body)
+                irSourceInformationMarkerEnd(body, scope)
             }
         }
 
@@ -1588,7 +1588,7 @@
                 )
 
                 // composer.endMovableGroup()
-                skipPreamble.statements.add(irEndMovableGroup())
+                skipPreamble.statements.add(irEndMovableGroup(scope))
 
                 // if (dirty and 0b0110 === 0) {
                 //   dirty = dirty or 0b0010
@@ -1763,7 +1763,7 @@
             statements = listOfNotNull(
                 outerReceiver,
                 irSafeCall(
-                    irEndRestartGroup(),
+                    irEndRestartGroup(scope),
                     updateScopeFunction.symbol,
                     lambda
                 ),
@@ -1910,6 +1910,12 @@
         )
     }
 
+    private fun Scope.BlockScope.irCurrentComposer(
+        startOffset: Int = UNDEFINED_OFFSET,
+        endOffset: Int = UNDEFINED_OFFSET,
+    ): IrExpression =
+        irCurrentComposer(startOffset, endOffset, nearestComposer ?: nearestComposer())
+
     private fun IrElement.sourceKey(): Int {
         var hash = currentFunctionScope
             .function
@@ -1958,7 +1964,7 @@
     ): IrExpression {
         return irWithSourceInformation(
             irMethodCall(
-                irCurrentComposer(startOffset, endOffset),
+                scope.irCurrentComposer(startOffset, endOffset),
                 startReplaceableFunction,
                 startOffset,
                 endOffset
@@ -1982,7 +1988,7 @@
         val sourceInformation = irCall(
             sourceInformationFunction
         ).also {
-            it.putValueArgument(0, irCurrentComposer())
+            it.putValueArgument(0, scope.irCurrentComposer())
         }
         recordSourceParameter(sourceInformation, 1, scope)
         return sourceInformation
@@ -1998,7 +2004,7 @@
             element.startOffset,
             element.endOffset
         ).also {
-            it.putValueArgument(0, irCurrentComposer())
+            it.putValueArgument(0, scope.irCurrentComposer())
             it.putValueArgument(1, key)
             recordSourceParameter(it, 2, scope)
         }
@@ -2048,13 +2054,14 @@
 
     private fun irSourceInformationMarkerEnd(
         element: IrElement,
+        scope: Scope.BlockScope
     ): IrExpression {
         return irCall(
             sourceInformationMarkerEndFunction,
             element.startOffset,
             element.endOffset
         ).also {
-            it.putValueArgument(0, irCurrentComposer())
+            it.putValueArgument(0, scope.irCurrentComposer())
         }
     }
 
@@ -2076,7 +2083,7 @@
             irSet(
                 nearestComposer(),
                 irMethodCall(
-                    irCurrentComposer(),
+                    scope.irCurrentComposer(),
                     startRestartGroupFunction,
                     element.startOffset,
                     element.endOffset
@@ -2088,8 +2095,8 @@
         )
     }
 
-    private fun irEndRestartGroup(): IrExpression {
-        return irMethodCall(irCurrentComposer(), endRestartGroupFunction)
+    private fun irEndRestartGroup(scope: Scope.BlockScope): IrExpression {
+        return irMethodCall(scope.irCurrentComposer(), endRestartGroupFunction)
     }
 
     private fun irCache(
@@ -2147,10 +2154,11 @@
 
     private fun irEndReplaceableGroup(
         startOffset: Int = UNDEFINED_OFFSET,
-        endOffset: Int = UNDEFINED_OFFSET
+        endOffset: Int = UNDEFINED_OFFSET,
+        scope: Scope.BlockScope
     ): IrExpression {
         return irMethodCall(
-            irCurrentComposer(startOffset, endOffset),
+            scope.irCurrentComposer(startOffset, endOffset),
             endReplaceableFunction,
             startOffset,
             endOffset
@@ -2168,7 +2176,7 @@
     ): IrExpression {
         return irWithSourceInformation(
             irMethodCall(
-                irCurrentComposer(),
+                scope.irCurrentComposer(),
                 startMovableFunction,
                 element.startOffset,
                 element.endOffset
@@ -2180,12 +2188,12 @@
         )
     }
 
-    private fun irEndMovableGroup(): IrExpression {
-        return irMethodCall(irCurrentComposer(), endMovableFunction)
+    private fun irEndMovableGroup(scope: Scope.BlockScope): IrExpression {
+        return irMethodCall(scope.irCurrentComposer(), endMovableFunction)
     }
 
-    private fun irEndToMarker(marker: IrExpression): IrExpression {
-        return irMethodCall(irCurrentComposer(), endToMarkerFunction!!).apply {
+    private fun irEndToMarker(marker: IrExpression, scope: Scope.BlockScope): IrExpression {
+        return irMethodCall(scope.irCurrentComposer(), endToMarkerFunction!!).apply {
             putValueArgument(0, marker)
         }
     }
@@ -2273,7 +2281,9 @@
 
     private fun IrBlock.withReplaceableGroupStatements(scope: Scope.BlockScope): IrExpression {
         currentFunctionScope.metrics.recordGroup()
-        scope.realizeGroup(::irEndReplaceableGroup)
+        scope.realizeGroup {
+            irEndReplaceableGroup(scope = scope)
+        }
         return when {
             // if the scope ends with a return call, then it will get properly ended if we
             // just push the end call on the scope because of the way returns get transformed in
@@ -2299,7 +2309,7 @@
                         startOffset = startOffset,
                         endOffset = endOffset
                     )
-                ) + statements + listOf(irEndReplaceableGroup(startOffset, endOffset))
+                ) + statements + listOf(irEndReplaceableGroup(startOffset, endOffset, scope))
             )
         }
     }
@@ -2319,11 +2329,13 @@
                         startOffset = startOffset,
                         endOffset = endOffset,
                     ),
-                    irEndReplaceableGroup(startOffset, endOffset)
+                    irEndReplaceableGroup(startOffset, endOffset, scope)
                 )
             )
         }
-        scope.realizeGroup(::irEndReplaceableGroup)
+        scope.realizeGroup {
+            irEndReplaceableGroup(scope = scope)
+        }
         return when {
             // if the scope ends with a return call, then it will get properly ended if we
             // just push the end call on the scope because of the way returns get transformed in
@@ -2343,7 +2355,7 @@
                             endOffset = endOffset
                         )
                     ),
-                    after = listOf(irEndReplaceableGroup(startOffset, endOffset))
+                    after = listOf(irEndReplaceableGroup(startOffset, endOffset, scope))
                 )
             }
         }
@@ -2402,10 +2414,12 @@
                 if (before.statements.isEmpty()) {
                     metrics.recordGroup()
                     before.statements.add(irStartReplaceableGroup(this, scope))
-                    after.statements.add(irEndReplaceableGroup())
+                    after.statements.add(irEndReplaceableGroup(scope = scope))
                 }
             },
-            makeEnd = ::irEndReplaceableGroup
+            makeEnd = {
+                irEndReplaceableGroup(scope = scope)
+            }
         )
         return wrap(
             listOf(before),
@@ -2526,13 +2540,14 @@
                             scope.markReturn(extraEndLocation)
                         } else {
                             val functionScope = scope
+                            val targetScope = currentScope as? Scope.BlockScope ?: functionScope
                             if (functionScope.isInlinedLambda) {
                                 val marker = irGet(functionScope.allocateMarker())
-                                extraEndLocation(irEndToMarker(marker))
+                                extraEndLocation(irEndToMarker(marker, targetScope))
                             } else {
                                 val marker = functionScope.allocateMarker()
                                 functionScope.markReturn {
-                                    extraEndLocation(irEndToMarker(irGet(marker)))
+                                    extraEndLocation(irEndToMarker(irGet(marker), targetScope))
                                     extraEndLocation(it)
                                 }
                             }
@@ -3186,7 +3201,7 @@
                     scope
                 ),
                 block,
-                irEndMovableGroup(),
+                irEndMovableGroup(scope),
                 after,
                 resultVar?.let { irGet(resultVar) }
             )
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/CopyDefaultValuesFromExpectLowering.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/CopyDefaultValuesFromExpectLowering.kt
index 439cfbe..1ce5f87 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/CopyDefaultValuesFromExpectLowering.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/CopyDefaultValuesFromExpectLowering.kt
@@ -16,15 +16,42 @@
 
 package androidx.compose.compiler.plugins.kotlin.lower
 
+import androidx.compose.compiler.plugins.kotlin.ComposeFqNames
 import androidx.compose.compiler.plugins.kotlin.hasComposableAnnotation
-import org.jetbrains.kotlin.descriptors.FunctionDescriptor
+import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
+import org.jetbrains.kotlin.descriptors.MemberDescriptor
 import org.jetbrains.kotlin.ir.IrStatement
 import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
+import org.jetbrains.kotlin.ir.declarations.IrClass
+import org.jetbrains.kotlin.ir.declarations.IrEnumEntry
 import org.jetbrains.kotlin.ir.declarations.IrFunction
 import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
+import org.jetbrains.kotlin.ir.declarations.IrProperty
+import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
+import org.jetbrains.kotlin.ir.declarations.IrValueParameter
+import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
+import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
+import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
+import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
+import org.jetbrains.kotlin.ir.symbols.IrEnumEntrySymbol
+import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
+import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
+import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
+import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
+import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
+import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
+import org.jetbrains.kotlin.ir.util.DeepCopyIrTreeWithSymbols
+import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper
+import org.jetbrains.kotlin.ir.util.DeepCopyTypeRemapper
+import org.jetbrains.kotlin.ir.util.hasAnnotation
+import org.jetbrains.kotlin.ir.util.patchDeclarationParents
+import org.jetbrains.kotlin.ir.util.referenceFunction
 import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
+import org.jetbrains.kotlin.ir.visitors.acceptVoid
 import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
-import org.jetbrains.kotlin.resolve.multiplatform.findCompatibleExpectsForActual
+import org.jetbrains.kotlin.resolve.descriptorUtil.module
+import org.jetbrains.kotlin.resolve.descriptorUtil.propertyIfAccessor
+import org.jetbrains.kotlin.resolve.multiplatform.findCompatibleActualsForExpected
 
 /**
  * [ComposableFunctionBodyTransformer] relies on presence of default values in
@@ -37,55 +64,207 @@
  * This lowering needs to run before [ComposableFunctionBodyTransformer] and
  * before [ComposerParamTransformer].
  *
- * Fixes https://github.com/JetBrains/compose-jb/issues/1407
+ * Fixes:
+ * https://github.com/JetBrains/compose-jb/issues/1407
+ * https://github.com/JetBrains/compose-multiplatform/issues/2816
+ * https://github.com/JetBrains/compose-multiplatform/issues/2806
+ *
+ * This implementation is borrowed from Kotlin's ExpectToActualDefaultValueCopier.
+ * Currently, it heavily relies on descriptors to find expect for actuals or vice versa:
+ * findCompatibleActualsForExpected.
+ * Unlike ExpectToActualDefaultValueCopier, this lowering performs its transformations
+ * only for functions marked with @Composable annotation or
+ * for functions with @Composable lambdas in parameters.
+ *
+ * TODO(karpovich): When adding support for FIR we'll need to use different API.
+ * Likely: fun FirBasedSymbol<*>.getSingleCompatibleExpectForActualOrNull(): FirBasedSymbol<*>?
  */
 @OptIn(ObsoleteDescriptorBasedAPI::class)
-class CopyDefaultValuesFromExpectLowering : ModuleLoweringPass {
+class CopyDefaultValuesFromExpectLowering(
+    pluginContext: IrPluginContext
+) : ModuleLoweringPass, IrElementTransformerVoid() {
+
+    private val symbolTable = pluginContext.symbolTable
+
+    private fun isApplicable(declaration: IrFunction): Boolean {
+        return declaration.hasComposableAnnotation() ||
+            declaration.valueParameters.any {
+                it.type.hasAnnotation(ComposeFqNames.Composable)
+            }
+    }
+
+    override fun visitFunction(declaration: IrFunction): IrStatement {
+        val original = super.visitFunction(declaration) as? IrFunction ?: return declaration
+
+        if (!original.isExpect || !isApplicable(original)) {
+            return original
+        }
+
+        val actualForExpected = original.findActualForExpected()
+
+        original.valueParameters.forEachIndexed { index, expectValueParameter ->
+            val actualValueParameter = actualForExpected.valueParameters[index]
+            val expectDefaultValue = expectValueParameter.defaultValue
+            if (expectDefaultValue != null) {
+                actualValueParameter.defaultValue = expectDefaultValue
+                    .remapExpectValueSymbols()
+                    .patchDeclarationParents(actualForExpected)
+
+                // Remove a default value in the expect fun in order to prevent
+                // Kotlin expect/actual-related lowerings trying to copy the default values again
+                expectValueParameter.defaultValue = null
+            }
+        }
+        return original
+    }
 
     override fun lower(module: IrModuleFragment) {
-        // it uses FunctionDescriptor since current API (findCompatibleExpectedForActual)
-        // can return only a descriptor
-        val expectComposables = mutableMapOf<FunctionDescriptor, IrFunction>()
+        module.transformChildrenVoid(this)
+    }
 
-        // first pass to find expect functions with default values
-        module.transformChildrenVoid(object : IrElementTransformerVoid() {
-            override fun visitFunction(declaration: IrFunction): IrStatement {
-                if (declaration.isExpect && declaration.hasComposableAnnotation()) {
-                    val hasDefaultValues = declaration.valueParameters.any {
-                        it.defaultValue != null
+    private inline fun <reified T : IrFunction> T.findActualForExpected(): T =
+        symbolTable.referenceFunction(descriptor.findActualForExpect()).owner as T
+
+    private fun IrProperty.findActualForExpected(): IrProperty =
+        symbolTable.referenceProperty(descriptor.findActualForExpect()).owner
+
+    private fun IrClass.findActualForExpected(): IrClass =
+        symbolTable.referenceClass(descriptor.findActualForExpect()).owner
+
+    private fun IrEnumEntry.findActualForExpected(): IrEnumEntry =
+        symbolTable.referenceEnumEntry(descriptor.findActualForExpect()).owner
+
+    private inline fun <reified T : MemberDescriptor> T.findActualForExpect(): T {
+        if (!this.isExpect) error(this)
+        return (findCompatibleActualsForExpected(module).singleOrNull() ?: error(this)) as T
+    }
+
+    private fun IrExpressionBody.remapExpectValueSymbols(): IrExpressionBody {
+        class SymbolRemapper : DeepCopySymbolRemapper() {
+            override fun getReferencedClass(symbol: IrClassSymbol) =
+                if (symbol.descriptor.isExpect)
+                    symbol.owner.findActualForExpected().symbol
+                else super.getReferencedClass(symbol)
+
+            override fun getReferencedClassOrNull(symbol: IrClassSymbol?) =
+                symbol?.let { getReferencedClass(it) }
+
+            override fun getReferencedClassifier(symbol: IrClassifierSymbol): IrClassifierSymbol =
+                when (symbol) {
+                    is IrClassSymbol -> getReferencedClass(symbol)
+                    is IrTypeParameterSymbol -> remapExpectTypeParameter(symbol).symbol
+                    else -> error("Unexpected symbol $symbol ${symbol.descriptor}")
+                }
+
+            override fun getReferencedConstructor(symbol: IrConstructorSymbol) =
+                if (symbol.descriptor.isExpect)
+                    symbol.owner.findActualForExpected().symbol
+                else super.getReferencedConstructor(symbol)
+
+            override fun getReferencedFunction(symbol: IrFunctionSymbol): IrFunctionSymbol =
+                when (symbol) {
+                    is IrSimpleFunctionSymbol -> getReferencedSimpleFunction(symbol)
+                    is IrConstructorSymbol -> getReferencedConstructor(symbol)
+                    else -> error("Unexpected symbol $symbol ${symbol.descriptor}")
+                }
+
+            override fun getReferencedSimpleFunction(symbol: IrSimpleFunctionSymbol) = when {
+                symbol.descriptor.isExpect -> symbol.owner.findActualForExpected().symbol
+
+                symbol.descriptor.propertyIfAccessor.isExpect -> {
+                    val property = symbol.owner.correspondingPropertySymbol!!.owner
+                    val actualPropertyDescriptor = property.descriptor.findActualForExpect()
+                    val accessorDescriptor = when (symbol.owner) {
+                        property.getter -> actualPropertyDescriptor.getter!!
+                        property.setter -> actualPropertyDescriptor.setter!!
+                        else -> error("Unexpected accessor of $symbol ${symbol.descriptor}")
                     }
-                    if (hasDefaultValues) {
-                        expectComposables[declaration.descriptor] = declaration
+                    symbolTable.referenceFunction(accessorDescriptor) as IrSimpleFunctionSymbol
+                }
+
+                else -> super.getReferencedSimpleFunction(symbol)
+            }
+
+            override fun getReferencedProperty(symbol: IrPropertySymbol) =
+                if (symbol.descriptor.isExpect)
+                    symbol.owner.findActualForExpected().symbol
+                else
+                    super.getReferencedProperty(symbol)
+
+            override fun getReferencedEnumEntry(symbol: IrEnumEntrySymbol): IrEnumEntrySymbol =
+                if (symbol.descriptor.isExpect)
+                    symbol.owner.findActualForExpected().symbol
+                else
+                    super.getReferencedEnumEntry(symbol)
+
+            override fun getReferencedValue(symbol: IrValueSymbol) =
+                remapExpectValue(symbol)?.symbol ?: super.getReferencedValue(symbol)
+        }
+
+        val symbolRemapper = SymbolRemapper()
+        acceptVoid(symbolRemapper)
+
+        return transform(
+            transformer = DeepCopyIrTreeWithSymbols(
+                symbolRemapper, DeepCopyTypeRemapper(symbolRemapper)
+            ),
+            data = null
+        )
+    }
+
+    private fun remapExpectTypeParameter(symbol: IrTypeParameterSymbol): IrTypeParameter {
+        val parameter = symbol.owner
+        val parent = parameter.parent
+
+        return when (parent) {
+            is IrClass ->
+                if (!parent.descriptor.isExpect)
+                    parameter
+                else parent.findActualForExpected().typeParameters[parameter.index]
+
+            is IrFunction ->
+                if (!parent.descriptor.isExpect)
+                    parameter
+                else parent.findActualForExpected().typeParameters[parameter.index]
+
+            else -> error(parent)
+        }
+    }
+
+    private fun remapExpectValue(symbol: IrValueSymbol): IrValueParameter? {
+        if (symbol !is IrValueParameterSymbol) {
+            return null
+        }
+
+        val parameter = symbol.owner
+        val parent = parameter.parent
+
+        return when (parent) {
+            is IrClass ->
+                if (!parent.descriptor.isExpect)
+                    null
+                else {
+                    assert(parameter == parent.thisReceiver)
+                    parent.findActualForExpected().thisReceiver!!
+                }
+
+            is IrFunction ->
+                if (!parent.descriptor.isExpect)
+                    null
+                else when (parameter) {
+                    parent.dispatchReceiverParameter ->
+                        parent.findActualForExpected().dispatchReceiverParameter!!
+
+                    parent.extensionReceiverParameter ->
+                        parent.findActualForExpected().extensionReceiverParameter!!
+
+                    else -> {
+                        assert(parent.valueParameters[parameter.index] == parameter)
+                        parent.findActualForExpected().valueParameters[parameter.index]
                     }
                 }
-                return super.visitFunction(declaration)
-            }
-        })
 
-        // second pass to set corresponding default values
-        module.transformChildrenVoid(object : IrElementTransformerVoid() {
-            override fun visitFunction(declaration: IrFunction): IrStatement {
-                if (declaration.descriptor.isActual && declaration.hasComposableAnnotation()) {
-                    val compatibleExpects = declaration.descriptor.findCompatibleExpectsForActual {
-                        module.descriptor == it
-                    }
-                    if (compatibleExpects.isNotEmpty()) {
-                        val expectFun = compatibleExpects.firstOrNull {
-                            it in expectComposables
-                        }?.let {
-                            expectComposables[it]
-                        }
-
-                        if (expectFun != null) {
-                            declaration.valueParameters.forEachIndexed { index, it ->
-                                it.defaultValue =
-                                    it.defaultValue ?: expectFun.valueParameters[index].defaultValue
-                            }
-                        }
-                    }
-                }
-                return super.visitFunction(declaration)
-            }
-        })
+            else -> error(parent)
+        }
     }
 }
\ No newline at end of file
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt
index dd2a8ed..4782eb0 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt
@@ -39,6 +39,7 @@
 import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
 import org.jetbrains.kotlin.ir.declarations.IrTypeAlias
 import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
+import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
 import org.jetbrains.kotlin.ir.declarations.IrValueParameter
 import org.jetbrains.kotlin.ir.declarations.IrVariable
 import org.jetbrains.kotlin.ir.expressions.IrBlock
@@ -106,6 +107,7 @@
 import org.jetbrains.kotlin.ir.util.isAnnotationClass
 import org.jetbrains.kotlin.ir.util.isInterface
 import org.jetbrains.kotlin.ir.util.isObject
+import org.jetbrains.kotlin.ir.util.kotlinFqName
 import org.jetbrains.kotlin.ir.util.parentAsClass
 import org.jetbrains.kotlin.ir.util.primaryConstructor
 import org.jetbrains.kotlin.ir.util.statements
@@ -131,11 +133,17 @@
         .replace(Regex("}\\n(\\s)*,", RegexOption.MULTILINE), "},")
 }
 
+class Scope(
+    val owner: IrFunction? = null,
+    val localValues: HashSet<IrValueDeclaration> = hashSetOf()
+)
+
 class IrSourcePrinterVisitor(
     out: Appendable,
     indentUnit: String = "  ",
 ) : IrElementVisitorVoid {
     private val printer = Printer(out, indentUnit)
+    private var currentScope: Scope = Scope()
 
     private fun IrElement.print() {
         accept(this@IrSourcePrinterVisitor, null)
@@ -146,6 +154,18 @@
 
     fun printType(type: IrType) = type.renderSrc()
 
+    private inline fun IrFunction.scoped(block: (IrFunction) -> Unit) {
+        val previousScope = currentScope
+        currentScope = Scope(
+            this,
+            HashSet(valueParameters)
+        )
+
+        block(this)
+
+        currentScope = previousScope
+    }
+
     private inline fun indented(body: () -> Unit) {
         printer.pushIndent()
         body()
@@ -197,47 +217,49 @@
 
     override fun visitSimpleFunction(declaration: IrSimpleFunction) {
         if (declaration.origin == IrDeclarationOrigin.FAKE_OVERRIDE) return
-        declaration.printAnnotations(onePerLine = true)
-        if (declaration.overriddenSymbols.isNotEmpty()) {
-            print("override ")
-        } else {
-            if (
-                declaration.visibility != DescriptorVisibilities.PUBLIC &&
-                declaration.visibility != DescriptorVisibilities.LOCAL
-            ) {
-                print(declaration.visibility.toString().lowercase(Locale.ROOT))
-                print(" ")
+        declaration.scoped {
+            declaration.printAnnotations(onePerLine = true)
+            if (declaration.overriddenSymbols.isNotEmpty()) {
+                print("override ")
+            } else {
+                if (
+                    declaration.visibility != DescriptorVisibilities.PUBLIC &&
+                    declaration.visibility != DescriptorVisibilities.LOCAL
+                ) {
+                    print(declaration.visibility.toString().lowercase(Locale.ROOT))
+                    print(" ")
+                }
+                if (declaration.modality != Modality.FINAL) {
+                    print(declaration.modality.toString().lowercase(Locale.ROOT))
+                    print(" ")
+                }
             }
-            if (declaration.modality != Modality.FINAL) {
-                print(declaration.modality.toString().lowercase(Locale.ROOT))
-                print(" ")
+            if (declaration.isSuspend) {
+                print("suspend ")
             }
+            print("fun ")
+            if (declaration.typeParameters.isNotEmpty()) {
+                print("<")
+                declaration.typeParameters.printJoin(", ")
+                print("> ")
+            }
+            declaration.extensionReceiverParameter?.let {
+                print(it.type.renderSrc())
+                print(".")
+            }
+            print(declaration.name)
+            print("(")
+            declaration.valueParameters.printJoin(", ")
+            print(")")
+            if (!declaration.returnType.isUnit()) {
+                print(": ")
+                print(
+                    declaration.returnType.renderSrc()
+                )
+            }
+            print(" ")
+            declaration.printBody()
         }
-        if (declaration.isSuspend) {
-            print("suspend ")
-        }
-        print("fun ")
-        if (declaration.typeParameters.isNotEmpty()) {
-            print("<")
-            declaration.typeParameters.printJoin(", ")
-            print("> ")
-        }
-        declaration.extensionReceiverParameter?.let {
-            print(it.type.renderSrc())
-            print(".")
-        }
-        print(declaration.name)
-        print("(")
-        declaration.valueParameters.printJoin(", ")
-        print(")")
-        if (!declaration.returnType.isUnit()) {
-            print(": ")
-            print(
-                declaration.returnType.renderSrc()
-            )
-        }
-        print(" ")
-        declaration.printBody()
     }
 
     fun IrFunction.printBody() {
@@ -551,20 +573,22 @@
     }
 
     fun IrFunction.printAsLambda() {
-        print("{")
-        val parameters = valueParameters
-        if (parameters.isNotEmpty()) {
-            print(" ")
-            parameters.printJoin(", ")
-            println(" ->")
-        } else {
+        scoped {
+            print("{")
+            val parameters = valueParameters
+            if (parameters.isNotEmpty()) {
+                print(" ")
+                parameters.printJoin(", ")
+                println(" ->")
+            } else {
+                println()
+            }
+            indented {
+                body?.print()
+            }
             println()
+            println("}")
         }
-        indented {
-            body?.print()
-        }
-        println()
-        println("}")
     }
 
     override fun visitTypeOperator(expression: IrTypeOperatorCall) {
@@ -855,6 +879,8 @@
     }
 
     override fun visitVariable(declaration: IrVariable) {
+        currentScope.localValues.add(declaration)
+
         if (declaration.isLateinit) {
             print("lateinit")
         }
@@ -875,7 +901,16 @@
     }
 
     override fun visitGetValue(expression: IrGetValue) {
-        print(expression.symbol.owner.name)
+        val owner = expression.symbol.owner
+        print(owner.name)
+
+        if (
+            owner.parent != currentScope.owner &&
+            currentScope.localValues.any { it.name == owner.name }
+        ) {
+            print("@")
+            print(owner.parent.kotlinFqName)
+        }
     }
 
     override fun visitField(declaration: IrField) {
@@ -964,7 +999,7 @@
             }
         }
         indented {
-            declaration.getter?.let {
+            declaration.getter?.scoped {
                 if (it.origin != IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR) {
                     println()
                     it.printAnnotations()
@@ -977,7 +1012,7 @@
                     println("}")
                 }
             }
-            declaration.setter?.let {
+            declaration.setter?.scoped {
                 if (it.origin != IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR) {
                     println()
                     it.printAnnotations()
diff --git a/compose/foundation/foundation-layout/OWNERS b/compose/foundation/foundation-layout/OWNERS
index 1e34489..74ae12a 100644
--- a/compose/foundation/foundation-layout/OWNERS
+++ b/compose/foundation/foundation-layout/OWNERS
@@ -3,3 +3,4 @@
 soboleva@google.com
 andreykulikov@google.com
 malkov@google.com
+uokoye@google.com
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AlignmentLine.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AlignmentLine.kt
index a088418..36aa00e 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AlignmentLine.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AlignmentLine.kt
@@ -17,17 +17,17 @@
 package androidx.compose.foundation.layout
 
 import androidx.compose.runtime.Stable
-import androidx.compose.ui.layout.AlignmentLine
-import androidx.compose.ui.layout.HorizontalAlignmentLine
-import androidx.compose.ui.layout.LayoutModifier
-import androidx.compose.ui.layout.Measurable
-import androidx.compose.ui.layout.MeasureScope
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.AlignmentLine
 import androidx.compose.ui.layout.FirstBaseline
+import androidx.compose.ui.layout.HorizontalAlignmentLine
 import androidx.compose.ui.layout.LastBaseline
+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.InspectorValueInfo
 import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
@@ -68,7 +68,7 @@
     before: Dp = Dp.Unspecified,
     after: Dp = Dp.Unspecified
 ): Modifier = this.then(
-    AlignmentLineOffsetDp(
+    AlignmentLineOffsetDpElement(
         alignmentLine,
         before,
         after,
@@ -114,7 +114,7 @@
     before: TextUnit = TextUnit.Unspecified,
     after: TextUnit = TextUnit.Unspecified
 ): Modifier = this.then(
-    AlignmentLineOffsetTextUnit(
+    AlignmentLineOffsetTextUnitElement(
         alignmentLine,
         before,
         after,
@@ -188,12 +188,12 @@
         if (!bottom.isUnspecified) Modifier.paddingFrom(LastBaseline, after = bottom) else Modifier
     )
 
-private class AlignmentLineOffsetDp(
+private class AlignmentLineOffsetDpElement(
     val alignmentLine: AlignmentLine,
     val before: Dp,
     val after: Dp,
-    inspectorInfo: InspectorInfo.() -> Unit
-) : LayoutModifier, InspectorValueInfo(inspectorInfo) {
+    val inspectorInfo: InspectorInfo.() -> Unit
+) : ModifierNodeElement<AlignmentLineOffsetDpNode>() {
     init {
         require(
             (before.value >= 0f || before == Dp.Unspecified) &&
@@ -203,11 +203,32 @@
         }
     }
 
-    override fun MeasureScope.measure(
-        measurable: Measurable,
-        constraints: Constraints
-    ): MeasureResult {
-        return alignmentLineOffsetMeasure(alignmentLine, before, after, measurable, constraints)
+    override fun create(): AlignmentLineOffsetDpNode {
+        return AlignmentLineOffsetDpNode(
+            alignmentLine,
+            before,
+            after
+        )
+    }
+
+    override fun update(node: AlignmentLineOffsetDpNode): AlignmentLineOffsetDpNode {
+        node.alignmentLine = alignmentLine
+        node.before = before
+        node.after = after
+        return node
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        inspectorInfo()
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        val otherModifier = other as? AlignmentLineOffsetDpElement ?: return false
+
+        return alignmentLine == otherModifier.alignmentLine &&
+            before == otherModifier.before &&
+            after == otherModifier.after
     }
 
     override fun hashCode(): Int {
@@ -216,36 +237,33 @@
         result = 31 * result + after.hashCode()
         return result
     }
-
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        val otherModifier = other as? AlignmentLineOffsetDp ?: return false
-
-        return alignmentLine == otherModifier.alignmentLine &&
-            before == otherModifier.before &&
-            after == otherModifier.after
-    }
-
-    override fun toString(): String =
-        "AlignmentLineOffset(alignmentLine=$alignmentLine, before=$before, after=$after)"
 }
 
-private class AlignmentLineOffsetTextUnit(
-    val alignmentLine: AlignmentLine,
-    val before: TextUnit,
-    val after: TextUnit,
-    inspectorInfo: InspectorInfo.() -> Unit
-) : LayoutModifier, InspectorValueInfo(inspectorInfo) {
+private class AlignmentLineOffsetDpNode(
+    var alignmentLine: AlignmentLine,
+    var before: Dp,
+    var after: Dp,
+) : LayoutModifierNode, Modifier.Node() {
+
     override fun MeasureScope.measure(
         measurable: Measurable,
         constraints: Constraints
     ): MeasureResult {
-        return alignmentLineOffsetMeasure(
+        return alignmentLineOffsetMeasure(alignmentLine, before, after, measurable, constraints)
+    }
+}
+
+private class AlignmentLineOffsetTextUnitElement(
+    val alignmentLine: AlignmentLine,
+    val before: TextUnit,
+    val after: TextUnit,
+    val inspectorInfo: InspectorInfo.() -> Unit
+) : ModifierNodeElement<AlignmentLineOffsetTextUnitNode>() {
+    override fun create(): AlignmentLineOffsetTextUnitNode {
+        return AlignmentLineOffsetTextUnitNode(
             alignmentLine,
-            if (!before.isUnspecified) before.toDp() else Dp.Unspecified,
-            if (!after.isUnspecified) after.toDp() else Dp.Unspecified,
-            measurable,
-            constraints
+            before,
+            after
         )
     }
 
@@ -258,15 +276,42 @@
 
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
-        val otherModifier = other as? AlignmentLineOffsetTextUnit ?: return false
+        val otherModifier = other as? AlignmentLineOffsetTextUnitElement ?: return false
 
         return alignmentLine == otherModifier.alignmentLine &&
             before == otherModifier.before &&
             after == otherModifier.after
     }
 
-    override fun toString(): String =
-        "AlignmentLineOffset(alignmentLine=$alignmentLine, before=$before, after=$after)"
+    override fun InspectorInfo.inspectableProperties() {
+        return inspectorInfo()
+    }
+
+    override fun update(node: AlignmentLineOffsetTextUnitNode): AlignmentLineOffsetTextUnitNode {
+        node.alignmentLine = alignmentLine
+        node.before = before
+        node.after = after
+        return node
+    }
+}
+
+private class AlignmentLineOffsetTextUnitNode(
+    var alignmentLine: AlignmentLine,
+    var before: TextUnit,
+    var after: TextUnit,
+) : LayoutModifierNode, Modifier.Node() {
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints
+    ): MeasureResult {
+        return alignmentLineOffsetMeasure(
+            alignmentLine,
+            if (!before.isUnspecified) before.toDp() else Dp.Unspecified,
+            if (!after.isUnspecified) after.toDp() else Dp.Unspecified,
+            measurable,
+            constraints
+        )
+    }
 }
 
 private fun MeasureScope.alignmentLineOffsetMeasure(
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AspectRatio.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AspectRatio.kt
index ef7f397..d42d7f0 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AspectRatio.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AspectRatio.kt
@@ -17,15 +17,15 @@
 package androidx.compose.foundation.layout
 
 import androidx.compose.runtime.Stable
-import androidx.compose.ui.layout.LayoutModifier
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureScope
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.IntrinsicMeasurable
 import androidx.compose.ui.layout.IntrinsicMeasureScope
 import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.platform.InspectorValueInfo
 import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.IntSize
@@ -56,7 +56,7 @@
     ratio: Float,
     matchHeightConstraintsFirst: Boolean = false
 ) = this.then(
-    AspectRatioModifier(
+    AspectRatioElement(
         ratio,
         matchHeightConstraintsFirst,
         debugInspectorInfo {
@@ -67,15 +67,45 @@
     )
 )
 
-private class AspectRatioModifier(
+private class AspectRatioElement(
     val aspectRatio: Float,
     val matchHeightConstraintsFirst: Boolean,
-    inspectorInfo: InspectorInfo.() -> Unit
-) : LayoutModifier, InspectorValueInfo(inspectorInfo) {
+    val inspectorInfo: InspectorInfo.() -> Unit
+) : ModifierNodeElement<AspectRatioNode>() {
     init {
         require(aspectRatio > 0) { "aspectRatio $aspectRatio must be > 0" }
     }
 
+    override fun create(): AspectRatioNode {
+        return AspectRatioNode(
+            aspectRatio,
+            matchHeightConstraintsFirst
+        )
+    }
+
+    override fun update(node: AspectRatioNode): AspectRatioNode {
+        node.aspectRatio = aspectRatio
+        node.matchHeightConstraintsFirst = matchHeightConstraintsFirst
+        return node
+    }
+
+    override fun InspectorInfo.inspectableProperties() { inspectorInfo() }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        val otherModifier = other as? AspectRatioElement ?: return false
+        return aspectRatio == otherModifier.aspectRatio &&
+            matchHeightConstraintsFirst == other.matchHeightConstraintsFirst
+    }
+
+    override fun hashCode(): Int =
+        aspectRatio.hashCode() * 31 + matchHeightConstraintsFirst.hashCode()
+}
+
+private class AspectRatioNode(
+    var aspectRatio: Float,
+    var matchHeightConstraintsFirst: Boolean,
+) : LayoutModifierNode, Modifier.Node() {
     override fun MeasureScope.measure(
         measurable: Measurable,
         constraints: Constraints
@@ -202,16 +232,4 @@
         }
         return IntSize.Zero
     }
-
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        val otherModifier = other as? AspectRatioModifier ?: return false
-        return aspectRatio == otherModifier.aspectRatio &&
-            matchHeightConstraintsFirst == other.matchHeightConstraintsFirst
-    }
-
-    override fun hashCode(): Int =
-        aspectRatio.hashCode() * 31 + matchHeightConstraintsFirst.hashCode()
-
-    override fun toString(): String = "AspectRatioModifier(aspectRatio=$aspectRatio)"
 }
diff --git a/compose/foundation/foundation-newtext/OWNERS b/compose/foundation/foundation-newtext/OWNERS
deleted file mode 100644
index 9b493db..0000000
--- a/compose/foundation/foundation-newtext/OWNERS
+++ /dev/null
@@ -1,13 +0,0 @@
-# Bug component: 856887
-andreykulikov@google.com
-malkov@google.com
-lpf@google.com
-tianliu@google.com
-soboleva@google.com
-ashikov@google.com
-
-# Text
-include /TEXT_OWNERS
-
-# AdapterList
-ryanmentley@google.com
diff --git a/compose/foundation/foundation-newtext/build.gradle b/compose/foundation/foundation-newtext/build.gradle
deleted file mode 100644
index 85d210a..0000000
--- a/compose/foundation/foundation-newtext/build.gradle
+++ /dev/null
@@ -1,152 +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.
- */
-
-
-import androidx.build.AndroidXComposePlugin
-import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-plugins {
-    id("AndroidXPlugin")
-    id("com.android.library")
-    id("AndroidXComposePlugin")
-}
-
-AndroidXComposePlugin.applyAndConfigureKotlinPlugin(project)
-
-dependencies {
-
-    if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block above
-         */
-        api("androidx.annotation:annotation:1.1.0")
-        api("androidx.compose.animation:animation:1.2.1")
-        api("androidx.compose.runtime:runtime:1.3.1")
-        api(project(":compose:ui:ui"))
-
-        implementation(libs.kotlinStdlibCommon)
-        implementation(project(':compose:foundation:foundation'))
-        implementation(project(":compose:foundation:foundation-layout"))
-        implementation(project(":emoji2:emoji2"))
-
-        implementation("androidx.compose.ui:ui-text:1.2.1")
-        implementation("androidx.compose.ui:ui-util:1.2.1")
-
-        lintChecks(project(":compose:foundation:foundation-lint"))
-        lintPublish(project(":compose:foundation:foundation-lint"))
-
-        testImplementation(project(":compose:test-utils"))
-        testImplementation(libs.testRules)
-        testImplementation(libs.testRunner)
-        testImplementation(libs.junit)
-        testImplementation(libs.truth)
-        testImplementation(libs.kotlinTest)
-        testImplementation(libs.mockitoCore4)
-        testImplementation(libs.mockitoKotlin4)
-
-        androidTestImplementation(project(":compose:test-utils"))
-        androidTestImplementation(project(":internal-testutils-fonts"))
-        androidTestImplementation(project(":internal-testutils-runtime"))
-        androidTestImplementation(libs.testRules)
-        androidTestImplementation(libs.testRunner)
-        androidTestImplementation(libs.testMonitor)
-        androidTestImplementation(libs.junit)
-        androidTestImplementation(libs.kotlinTest)
-        androidTestImplementation(libs.truth)
-        androidTestImplementation(libs.dexmakerMockito)
-        androidTestImplementation(libs.mockitoCore)
-        androidTestImplementation(libs.mockitoKotlin)
-    }
-}
-
-if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
-    androidXComposeMultiplatform {
-        android()
-        desktop()
-    }
-
-    kotlin {
-        /*
-         * When updating dependencies, make sure to make the an an analogous update in the
-         * corresponding block above
-         */
-        sourceSets {
-            commonMain.dependencies {
-                implementation(libs.kotlinStdlibCommon)
-                api(project(':compose:animation:animation'))
-                api(project(':compose:runtime:runtime'))
-                api(project(':compose:ui:ui'))
-
-                implementation(project(":compose:ui:ui-text"))
-                implementation(project(":compose:ui:ui-util"))
-
-                implementation(project(':compose:foundation:foundation'))
-                implementation(project(':compose:foundation:foundation-layout'))
-            }
-            androidMain.dependencies {
-                api("androidx.annotation:annotation:1.1.0")
-                implementation(project(":emoji2:emoji2"))
-            }
-
-            desktopMain.dependencies {
-                implementation(libs.kotlinStdlib)
-            }
-
-            androidTest.dependencies {
-                implementation(libs.testRules)
-                implementation(libs.testRunner)
-                implementation(libs.junit)
-                implementation(libs.mockitoCore4)
-                implementation(libs.truth)
-                implementation(libs.kotlinReflect)
-                implementation(libs.mockitoKotlin4)
-            }
-
-            commonTest.dependencies {
-                implementation(libs.kotlinTest)
-                implementation(libs.kotlinCoroutinesTest)
-            }
-
-            androidAndroidTest.dependencies {
-                implementation(project(":compose:test-utils"))
-                implementation(project(":internal-testutils-fonts"))
-                implementation(project(":internal-testutils-runtime"))
-
-                implementation(libs.testRules)
-                implementation(libs.testRunner)
-                implementation(libs.testMonitor)
-                implementation(libs.junit)
-                implementation(libs.truth)
-                implementation(libs.dexmakerMockito)
-                implementation(libs.mockitoCore)
-                implementation(libs.mockitoKotlin)
-            }
-        }
-    }
-}
-
-android {
-    namespace "androidx.compose.foundation.newtext"
-}
-
-androidx {
-    name = "Compose Foundation NewText"
-    type = LibraryType.UNSET /* This module will never be published */
-    inceptionYear = "2022"
-    description = "Working module for developing Text modifier"
-}
diff --git a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/CoreTextInlineContentTest.kt b/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/CoreTextInlineContentTest.kt
deleted file mode 100644
index 9adc7cd..0000000
--- a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/CoreTextInlineContentTest.kt
+++ /dev/null
@@ -1,240 +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.newtext.text
-
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.text.InlineTextContent
-import androidx.compose.foundation.text.appendInlineContent
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.onSizeChanged
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithTag
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.text.Placeholder
-import androidx.compose.ui.text.PlaceholderVerticalAlign
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.buildAnnotatedString
-import androidx.compose.ui.text.style.TextDirection
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.sp
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.LargeTest
-import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.verify
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@LargeTest
-@RunWith(AndroidJUnit4::class)
-class CoreTextInlineContentTest {
-
-    @get:Rule
-    val rule = createComposeRule()
-
-    private val fontSize = 10
-
-    private val textStyle = TextStyle(fontSize = fontSize.sp, fontFamily = TEST_FONT_FAMILY)
-
-    @OptIn(ExperimentalTextApi::class)
-    @Test
-    fun placeholder_changeSize_updateInlineContentSize() {
-        // Callback to monitor the size changes of a composable.
-        val onSizeChanged: (IntSize) -> Unit = mock()
-        var size by mutableStateOf(IntSize(50, 50))
-
-        rule.setContent {
-            val inlineTextContent = InlineTextContent(
-                placeholder = Placeholder(
-                    size.width.sp,
-                    size.height.sp,
-                    PlaceholderVerticalAlign.AboveBaseline
-                )
-            ) {
-                Box(modifier = Modifier.fillMaxSize().onSizeChanged(onSizeChanged))
-            }
-
-            CompositionLocalProvider(
-                LocalDensity provides Density(density = 1f, fontScale = 1f)
-            ) {
-                TextUsingModifier(
-                    text = buildAnnotatedString {
-                        append("Hello")
-                        appendInlineContent("box")
-                        append("World")
-                    },
-                    style = TextStyle(fontSize = 100.sp),
-                    inlineContent = mapOf("box" to inlineTextContent),
-                    maxLines = Int.MAX_VALUE,
-                    onTextLayout = {},
-                    overflow = TextOverflow.Clip,
-                    softWrap = true
-                )
-            }
-        }
-
-        rule.runOnIdle {
-            // Verify that the initial size is (50, 50).
-            verify(onSizeChanged).invoke(IntSize(50, 50))
-            size = IntSize(100, 100)
-        }
-        rule.waitForIdle()
-        // Verify that the size has been updated to (100, 100).
-        verify(onSizeChanged).invoke(IntSize(100, 100))
-    }
-
-    @Test
-    fun rtlLayout_inlineContent_placement() {
-        rule.setContent {
-            CompositionLocalProvider(
-                LocalLayoutDirection provides LayoutDirection.Ltr,
-            ) {
-                // LTR character, supported by sample_font
-                TestContent(
-                    predicate = "\u0061\u0061\u0061\u0061\u0061",
-                    suffix = "\u0061\u0061\u0061"
-                )
-            }
-        }
-
-        // Expected text layout; "a" is LTR, "b" is RTL"
-        // Text[aaaaa[inline-content]aaa]
-        expectInlineContentPosition(left = fontSize * 5, right = fontSize * 3)
-    }
-
-    @Test
-    fun rtlTextContent_inlineContent_placement() {
-        rule.setContent {
-            // RTL character, supported by sample_font
-            TestContent(
-                predicate = "\u05D1\u05D1\u05D1\u05D1\u05D1",
-                suffix = "\u05D1\u05D1\u05D1"
-            )
-        }
-
-        // Expected text layout; "a" is LTR, "b" is RTL"
-        // Text[bbb[inline-content]bbbbb]
-        expectInlineContentPosition(left = fontSize * 3, right = fontSize * 5)
-    }
-
-    @Test
-    fun rtlTextDirection_inlineContent_placement() {
-        rule.setContent {
-            // LTR character, supported by sample_font
-            TestContent(
-                predicate = "\u0061\u0061\u0061\u0061\u0061",
-                suffix = "\u0061\u0061\u0061",
-                textStyle = textStyle.copy(textDirection = TextDirection.Rtl)
-            )
-        }
-
-        // Expected text layout; "a" is LTR, "b" is RTL"
-        // Text[aaaaa[inline-content]aaa]
-        expectInlineContentPosition(left = fontSize * 5, right = fontSize * 3)
-    }
-
-    @Test
-    fun bidiText_inlineContent_placement() {
-        rule.setContent {
-            // RTL and LTR characters, supported by sample_font
-            TestContent(
-                predicate = "\u05D1\u05D1\u05D1\u0061\u0061",
-                suffix = "\u0061\u0061\u0061"
-            )
-        }
-
-        // Expected text layout; "a" is LTR, "b" is RTL"
-        // Text[bbbaa[inline-content]aaa]
-        expectInlineContentPosition(left = fontSize * 5, right = fontSize * 3)
-    }
-
-    @Test
-    fun bidiText_2_inlineContent_placement() {
-        rule.setContent {
-            // RTL and LTR characters, supported by sample_font
-            TestContent(
-                predicate = "\u0061\u0061\u0061\u05D1\u05D1",
-                suffix = "\u05D1\u05D1\u05D1"
-            )
-        }
-
-        // Expected text layout; "a" is LTR, "b" is RTL"
-        // Text[aaabbb[inline-content]bb]
-        expectInlineContentPosition(left = fontSize * 6, right = fontSize * 2)
-    }
-
-    @OptIn(ExperimentalTextApi::class)
-    @Composable
-    private fun TestContent(
-        predicate: String,
-        suffix: String,
-        textStyle: TextStyle = this.textStyle
-    ) {
-        CompositionLocalProvider(
-            LocalDensity provides Density(density = 1f, fontScale = 1f)
-        ) {
-            val inlineTextContent = InlineTextContent(
-                placeholder = Placeholder(
-                    fontSize.sp,
-                    fontSize.sp,
-                    PlaceholderVerticalAlign.AboveBaseline
-                )
-            ) {
-                Box(modifier = Modifier.fillMaxSize().testTag("box"))
-            }
-
-            TextUsingModifier(
-                text = buildAnnotatedString {
-                    append(predicate)
-                    appendInlineContent("box")
-                    append(suffix)
-                },
-                modifier = Modifier.testTag("text"),
-                style = textStyle,
-                inlineContent = mapOf("box" to inlineTextContent),
-                maxLines = 1
-            )
-        }
-    }
-
-    private fun expectInlineContentPosition(left: Int, right: Int) {
-        val (boxLeft, boxRight) = with(rule.onNodeWithTag("box").fetchSemanticsNode()) {
-            Pair(positionInRoot.x, positionInRoot.x + size.width)
-        }
-        val (textLeft, textRight) = with(rule.onNodeWithTag("text").fetchSemanticsNode()) {
-            Pair(positionInRoot.x, positionInRoot.x + size.width)
-        }
-
-        rule.waitForIdle()
-
-        assertThat(boxLeft - textLeft).isEqualTo(left)
-        assertThat(textRight - boxRight).isEqualTo(right)
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/TextLayoutTest.kt b/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/TextLayoutTest.kt
deleted file mode 100644
index 235fe0f..0000000
--- a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/TextLayoutTest.kt
+++ /dev/null
@@ -1,250 +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.foundation.newtext.text
-
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.FirstBaseline
-import androidx.compose.ui.layout.IntrinsicMeasurable
-import androidx.compose.ui.layout.IntrinsicMeasureScope
-import androidx.compose.ui.layout.LastBaseline
-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.onGloballyPositioned
-import androidx.compose.ui.layout.onSizeChanged
-import androidx.compose.ui.node.Ref
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.text.TextLayoutResult
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.IntSize
-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
-
-@RunWith(AndroidJUnit4::class)
-@MediumTest
-class TextLayoutTest {
-    @get:Rule
-    val rule = createComposeRule()
-
-    @Test
-    fun textLayout() {
-        val textSize = Ref<IntSize>()
-        val doubleTextSize = Ref<IntSize>()
-        rule.setContent {
-            TestingText(
-                "aa",
-                modifier = Modifier.onGloballyPositioned { coordinates ->
-                    textSize.value = coordinates.size
-                }
-            )
-            TestingText(
-                "aaaa",
-                modifier = Modifier.onGloballyPositioned { coordinates ->
-                    doubleTextSize.value = coordinates.size
-                }
-            )
-        }
-
-        rule.runOnIdle {
-            assertThat(textSize.value).isNotNull()
-            assertThat(doubleTextSize.value).isNotNull()
-            assertThat(textSize.value!!.width).isGreaterThan(0)
-            assertThat(textSize.value!!.height).isGreaterThan(0)
-            assertThat(textSize.value!!.width * 2).isEqualTo(doubleTextSize.value!!.width)
-            assertThat(textSize.value!!.height).isEqualTo(doubleTextSize.value!!.height)
-        }
-    }
-
-    @Test
-    fun textLayout_intrinsicMeasurements() {
-        val textSize = Ref<IntSize>()
-        val doubleTextSize = Ref<IntSize>()
-        var textMeasurable by mutableStateOf<Measurable?>(null)
-
-        rule.setContent {
-            TestingText(
-                "aa ",
-                modifier = Modifier.onSizeChanged { textSize.value = it }
-            )
-            TestingText(
-                "aa aa ",
-                modifier = Modifier.onSizeChanged { doubleTextSize.value = it }
-            )
-
-            Layout(
-                content = {
-                    TestingText("aa aa ")
-                },
-                measurePolicy = object : MeasurePolicy {
-                    override fun MeasureScope.measure(
-                        measurables: List<Measurable>,
-                        constraints: Constraints
-                    ): MeasureResult {
-                        measurables.forEach {
-                            it.measure(constraints)
-                        }
-                        textMeasurable = measurables.first()
-                        return layout(0, 0) {}
-                    }
-
-                    override fun IntrinsicMeasureScope.minIntrinsicWidth(
-                        measurables: List<IntrinsicMeasurable>,
-                        height: Int
-                    ) = 0
-
-                    override fun IntrinsicMeasureScope.minIntrinsicHeight(
-                        measurables: List<IntrinsicMeasurable>,
-                        width: Int
-                    ) = 0
-
-                    override fun IntrinsicMeasureScope.maxIntrinsicWidth(
-                        measurables: List<IntrinsicMeasurable>,
-                        height: Int
-                    ) = 0
-
-                    override fun IntrinsicMeasureScope.maxIntrinsicHeight(
-                        measurables: List<IntrinsicMeasurable>,
-                        width: Int
-                    ) = 0
-                }
-            )
-        }
-
-        rule.runOnIdle {
-            val textWidth = textSize.value!!.width
-            val textHeight = textSize.value!!.height
-            val doubleTextWidth = doubleTextSize.value!!.width
-
-            textMeasurable!!.let { textMeasurable ->
-                // Min width.
-                assertThat(textWidth).isEqualTo(textMeasurable.minIntrinsicWidth(0))
-                // Min height.
-                assertThat(textMeasurable.minIntrinsicHeight(textWidth))
-                    .isGreaterThan(textHeight)
-                assertThat(textHeight)
-                    .isEqualTo(textMeasurable.minIntrinsicHeight(doubleTextWidth))
-                assertThat(textHeight)
-                    .isEqualTo(textMeasurable.minIntrinsicHeight(Constraints.Infinity))
-
-                // Max width.
-                assertThat(doubleTextWidth).isEqualTo(textMeasurable.maxIntrinsicWidth(0))
-                // Max height.
-                assertThat(textMeasurable.maxIntrinsicHeight(textWidth))
-                    .isGreaterThan(textHeight)
-                assertThat(textHeight)
-                    .isEqualTo(textMeasurable.maxIntrinsicHeight(doubleTextWidth))
-                assertThat(textHeight)
-                    .isEqualTo(textMeasurable.maxIntrinsicHeight(Constraints.Infinity))
-            }
-        }
-    }
-
-    @Test
-    fun textLayout_providesBaselines_whenUnconstrained() {
-        var firstBaseline by mutableStateOf(-1)
-        var lastBaseline by mutableStateOf(-1)
-
-        rule.setContent {
-            Layout({
-                TestingText("aa")
-            }) { measurables, _ ->
-                val placeable = measurables.first().measure(Constraints())
-                firstBaseline = placeable[FirstBaseline]
-                lastBaseline = placeable[LastBaseline]
-                layout(0, 0) {}
-            }
-        }
-
-        rule.runOnIdle {
-            assertThat(firstBaseline).isGreaterThan(-1)
-            assertThat(lastBaseline).isGreaterThan(-1)
-            assertThat(firstBaseline).isEqualTo(lastBaseline)
-        }
-    }
-
-    @Test
-    fun textLayout_providesBaselines_whenZeroMaxWidth() {
-        var firstBaseline by mutableStateOf(-1)
-        var lastBaseline by mutableStateOf(-1)
-
-        rule.setContent {
-            Layout({
-                TestingText("aa")
-            }) { measurables, _ ->
-                val placeable = measurables.first().measure(Constraints(maxWidth = 0))
-                firstBaseline = placeable[FirstBaseline]
-                lastBaseline = placeable[LastBaseline]
-                layout(0, 0) {}
-            }
-        }
-
-        rule.runOnIdle {
-            assertThat(firstBaseline).isGreaterThan(-1)
-            assertThat(lastBaseline).isGreaterThan(-1)
-            assertThat(firstBaseline).isLessThan(lastBaseline)
-        }
-    }
-
-    @Test
-    fun textLayout_OnTextLayoutCallback() {
-        val resultsFromCallback = mutableListOf<TextLayoutResult>()
-        rule.setContent {
-            TestingText("aa", onTextLayout = { resultsFromCallback += it })
-        }
-
-        rule.runOnIdle {
-            assertThat(resultsFromCallback).hasSize(1)
-        }
-    }
-}
-
-@OptIn(ExperimentalTextApi::class)
-@Composable
-private fun TestingText(
-    text: String,
-    modifier: Modifier = Modifier,
-    onTextLayout: (TextLayoutResult) -> Unit = {}
-) {
-    val textStyle = remember {
-        TextStyle(fontFamily = TEST_FONT_FAMILY)
-    }
-    TextUsingModifier(
-        AnnotatedString(text),
-        style = textStyle,
-        modifier = modifier,
-        softWrap = true,
-        maxLines = Int.MAX_VALUE,
-        overflow = TextOverflow.Clip,
-        inlineContent = mapOf(),
-        onTextLayout = onTextLayout
-    )
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/TextOverflowTest.kt b/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/TextOverflowTest.kt
deleted file mode 100644
index 40f0959..0000000
--- a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/TextOverflowTest.kt
+++ /dev/null
@@ -1,163 +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.foundation.newtext.text
-
-import android.graphics.Bitmap
-import android.os.Build
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.heightIn
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.testutils.assertContainsColor
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.asAndroidBitmap
-import androidx.compose.ui.graphics.asImageBitmap
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalFontFamilyResolver
-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.text.ExperimentalTextApi
-import androidx.compose.ui.text.ParagraphStyle
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.buildAnnotatedString
-import androidx.compose.ui.text.font.createFontFamilyResolver
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.text.withStyle
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.LargeTest
-import androidx.test.filters.SdkSuppress
-import androidx.test.platform.app.InstrumentationRegistry
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@LargeTest
-@RunWith(AndroidJUnit4::class)
-class TextOverflowTest {
-    @get:Rule
-    val rule = createComposeRule()
-
-    private val density = Density(1f)
-    private val fontFamilyResolver =
-        createFontFamilyResolver(InstrumentationRegistry.getInstrumentation().context)
-    private val fontFamily = TEST_FONT_FAMILY
-
-    private val BoxTag = "wrapping box"
-
-    @OptIn(ExperimentalTextApi::class)
-    @Test
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-    fun paint_singleParagraph_withVisibleOverflow() {
-        val text = "Hello\nHello\nHello\nHello"
-
-        val lineHeight = 20
-        val boxHeight = 100
-        val boxWidth = 200
-
-        rule.setContent {
-            CompositionLocalProvider(
-                LocalDensity provides density,
-                LocalFontFamilyResolver provides fontFamilyResolver
-            ) {
-                Box(Modifier.testTag(BoxTag).size(boxWidth.dp, boxHeight.dp)) {
-                    TextUsingModifier(
-                        text = text,
-                        modifier = Modifier
-                            .fillMaxWidth()
-                            .heightIn(max = (2 * lineHeight).dp)
-                            .padding(bottom = lineHeight.dp),
-                        style = TextStyle(
-                            fontSize = 20.sp,
-                            fontFamily = fontFamily,
-                            color = Color.Red,
-                            lineHeight = 20.sp
-                        ),
-                        overflow = TextOverflow.Visible
-                    )
-                }
-            }
-        }
-
-        val boxBitmap = rule.onNodeWithTag(BoxTag).captureToImage().asAndroidBitmap()
-        val croppedBoxBitmap = Bitmap.createBitmap(
-            boxBitmap,
-            0,
-            2 * lineHeight,
-            boxWidth,
-            boxHeight - 2 * lineHeight
-        )
-        croppedBoxBitmap.asImageBitmap().assertContainsColor(Color.Red)
-    }
-
-    @OptIn(ExperimentalTextApi::class)
-    @Test
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-    fun paint_multiParagraph_withVisibleOverflow() {
-        val text = buildAnnotatedString {
-            append("Hello\nHello")
-            withStyle(ParagraphStyle(textAlign = TextAlign.Center)) {
-                append("Hello\nHello")
-            }
-        }
-        val lineHeight = 20
-        val boxHeight = 100
-        val boxWidth = 200
-
-        rule.setContent {
-            CompositionLocalProvider(
-                LocalDensity provides density,
-                LocalFontFamilyResolver provides fontFamilyResolver
-            ) {
-                Box(Modifier.testTag(BoxTag).size(boxWidth.dp, boxHeight.dp)) {
-                    TextUsingModifier(
-                        text = text,
-                        modifier = Modifier
-                            .fillMaxWidth()
-                            .heightIn(max = (2 * lineHeight).dp)
-                            .padding(bottom = lineHeight.dp),
-                        style = TextStyle(
-                            fontSize = 20.sp,
-                            fontFamily = fontFamily,
-                            color = Color.Red,
-                            lineHeight = 20.sp
-                        ),
-                        overflow = TextOverflow.Visible
-                    )
-                }
-            }
-        }
-
-        val boxBitmap = rule.onNodeWithTag(BoxTag).captureToImage().asAndroidBitmap()
-        val croppedBoxBitmap = Bitmap.createBitmap(
-            boxBitmap,
-            0,
-            2 * lineHeight,
-            boxWidth,
-            boxHeight - 2 * lineHeight
-        )
-        croppedBoxBitmap.asImageBitmap().assertContainsColor(Color.Red)
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/TextStyleInvalidationTest.kt b/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/TextStyleInvalidationTest.kt
deleted file mode 100644
index ed1996f..0000000
--- a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/TextStyleInvalidationTest.kt
+++ /dev/null
@@ -1,349 +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.foundation.newtext.text
-
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawBehind
-import androidx.compose.ui.graphics.Brush
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Shadow
-import androidx.compose.ui.graphics.drawscope.Stroke
-import androidx.compose.ui.layout.layout
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.text.PlatformTextStyle
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontFamily
-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.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.unit.IntOffset
-import androidx.compose.ui.unit.sp
-import androidx.test.filters.MediumTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameters
-
-@Suppress("DEPRECATION")
-@RunWith(Parameterized::class)
-@MediumTest
-class TextStyleInvalidationTest(private val config: Config) {
-
-    class Config(
-        private val description: String,
-        val updateStyle: (TextStyle) -> TextStyle,
-        val initializeStyle: (TextStyle) -> TextStyle = { it },
-        val invalidatesMeasure: Boolean = false,
-        val invalidatesPlacement: Boolean = false,
-        val invalidatesDraw: Boolean = false,
-    ) {
-        override fun toString(): String = buildString {
-            append(description)
-            listOfNotNull(
-                "measure".takeIf { invalidatesMeasure },
-                "placement".takeIf { invalidatesPlacement },
-                "draw".takeIf { invalidatesDraw },
-            ).joinTo(this, prefix = " ", separator = ", ") { "invalidates $it" }
-        }
-    }
-
-    @OptIn(ExperimentalTextApi::class)
-    companion object {
-        @Parameters(name = "{0}")
-        @JvmStatic
-        fun parameters() = arrayOf(
-            Config("nothing", { it }),
-            Config(
-                "color",
-                { it.copy(color = Color.Blue) },
-                invalidatesDraw = true,
-            ),
-            Config(
-                "to brush",
-                { it.copy(brush = Brush.verticalGradient(0f to Color.Blue, 1f to Color.Magenta)) },
-                invalidatesDraw = true,
-            ),
-            Config(
-                "from brush to brush",
-                initializeStyle = {
-                    it.copy(brush = Brush.verticalGradient(0f to Color.Black, 1f to Color.Magenta))
-                },
-                updateStyle = {
-                    it.copy(brush = Brush.verticalGradient(0f to Color.Blue, 1f to Color.Magenta))
-                },
-                invalidatesDraw = true,
-            ),
-            Config(
-                "alpha",
-                initializeStyle = {
-                    it.copy(
-                        alpha = 1f,
-                        brush = Brush.verticalGradient(0f to Color.Blue, 1f to Color.Magenta)
-                    )
-                },
-                updateStyle = { it.copy(alpha = 0.5f, brush = it.brush) },
-                invalidatesDraw = true,
-            ),
-            Config(
-                "fontSize",
-                { it.copy(fontSize = it.fontSize * 2) },
-                invalidatesMeasure = true,
-                invalidatesDraw = true,
-            ),
-            Config(
-                "fontWeight",
-                { it.copy(fontWeight = FontWeight(it.fontWeight!!.weight * 2)) },
-                invalidatesMeasure = true,
-                invalidatesDraw = true,
-            ),
-            Config(
-                "fontStyle",
-                { it.copy(fontStyle = FontStyle.Italic) },
-                invalidatesMeasure = true,
-                invalidatesDraw = true
-            ),
-            Config(
-                "fontSynthesis",
-                { it.copy(fontSynthesis = FontSynthesis.All) },
-                invalidatesMeasure = true,
-                invalidatesDraw = true,
-            ),
-            Config(
-                "fontFamily",
-                { it.copy(fontFamily = FontFamily.Cursive) },
-                invalidatesMeasure = true,
-                invalidatesDraw = true,
-            ),
-            Config(
-                "fontFeatureSettings",
-                initializeStyle = { it.copy(fontFeatureSettings = "a") },
-                updateStyle = { it.copy(fontFeatureSettings = "b") },
-                invalidatesMeasure = true,
-                invalidatesDraw = true,
-            ),
-            Config(
-                "letterSpacing",
-                { it.copy(letterSpacing = it.letterSpacing * 2) },
-                invalidatesMeasure = true,
-                invalidatesDraw = true,
-            ),
-            Config(
-                "baselineShift",
-                { it.copy(baselineShift = BaselineShift.Superscript) },
-                invalidatesMeasure = true,
-                invalidatesDraw = true,
-            ),
-            Config(
-                "textGeometricTransform",
-                { it.copy(textGeometricTransform = TextGeometricTransform(scaleX = 2f)) },
-                invalidatesMeasure = true,
-                invalidatesDraw = true,
-            ),
-            Config(
-                "localeList",
-                initializeStyle = { it.copy(localeList = LocaleList("en-US")) },
-                updateStyle = { it.copy(localeList = LocaleList("en-GB")) },
-                invalidatesMeasure = true,
-                invalidatesDraw = true,
-            ),
-            Config(
-                "background",
-                { it.copy(background = Color.Blue) },
-                invalidatesDraw = true,
-            ),
-            Config(
-                "textDecoration",
-                { it.copy(textDecoration = TextDecoration.LineThrough) },
-                invalidatesDraw = true,
-            ),
-            Config(
-                "to shadow",
-                { it.copy(shadow = Shadow(Color.Black, blurRadius = 4f)) },
-                invalidatesDraw = true,
-            ),
-            Config(
-                "from shadow to shadow",
-                initializeStyle = { it.copy(shadow = Shadow(Color.Black, blurRadius = 1f)) },
-                updateStyle = { it.copy(shadow = Shadow(Color.Black, blurRadius = 4f)) },
-                invalidatesDraw = true,
-            ),
-            Config(
-                "to drawStyle",
-                { it.copy(drawStyle = Stroke(width = 1f)) },
-                invalidatesDraw = true,
-            ),
-            Config(
-                "from drawStyle to drawStyle",
-                initializeStyle = { it.copy(drawStyle = Stroke(width = 0f)) },
-                updateStyle = { it.copy(drawStyle = Stroke(width = 1f)) },
-                invalidatesDraw = true,
-            ),
-            Config(
-                "textAlign",
-                { it.copy(textAlign = TextAlign.Justify) },
-                invalidatesDraw = true,
-            ),
-            Config(
-                "textDirection",
-                { it.copy(textDirection = TextDirection.Rtl) },
-                invalidatesDraw = true,
-            ),
-            Config(
-                "lineHeight",
-                { it.copy(lineHeight = it.lineHeight * 2) },
-                invalidatesMeasure = true,
-                invalidatesDraw = true,
-            ),
-            Config(
-                "textIndent",
-                { it.copy(textIndent = TextIndent(firstLine = 5.sp)) },
-                invalidatesMeasure = true,
-                invalidatesDraw = true,
-            ),
-            Config(
-                "platformStyle",
-                initializeStyle = {
-                    it.copy(platformStyle = PlatformTextStyle(includeFontPadding = true))
-                },
-                updateStyle = {
-                    it.copy(platformStyle = PlatformTextStyle(includeFontPadding = false))
-                },
-                invalidatesMeasure = true,
-                invalidatesDraw = true,
-            ),
-            Config(
-                "lineHeightStyle",
-                {
-                    it.copy(
-                        lineHeightStyle = LineHeightStyle(
-                            alignment = LineHeightStyle.Alignment.Center,
-                            trim = LineHeightStyle.Trim.FirstLineTop
-                        )
-                    )
-                },
-                invalidatesMeasure = true,
-                invalidatesDraw = true,
-            ),
-            Config(
-                "lineBreak",
-                { it.copy(lineBreak = LineBreak.Heading) },
-                invalidatesMeasure = true,
-                invalidatesDraw = true,
-            ),
-            Config(
-                "hyphens",
-                { it.copy(hyphens = Hyphens.Auto) },
-                invalidatesMeasure = true,
-                invalidatesDraw = true,
-            ),
-        )
-    }
-
-    @get:Rule
-    val rule = createComposeRule()
-
-    @OptIn(ExperimentalTextApi::class)
-    @Test
-    fun changing() {
-        // Don't leave any TextUnits Unspecified so test cases can double them to invalidate.
-        var style by mutableStateOf(
-            TextStyle(
-                color = Color.Black,
-                fontSize = 12.sp,
-                fontWeight = FontWeight.Normal,
-                fontStyle = null,
-                fontSynthesis = null,
-                fontFamily = TEST_FONT_FAMILY,
-                fontFeatureSettings = null,
-                letterSpacing = 12.sp,
-                baselineShift = null,
-                textGeometricTransform = null,
-                localeList = null,
-                background = Color.White,
-                textDecoration = null,
-                shadow = null,
-                textAlign = TextAlign.Start,
-                textDirection = TextDirection.Ltr,
-                lineHeight = 12.sp,
-                textIndent = null,
-            ).let(config.initializeStyle)
-        )
-        var measures = 0
-        var placements = 0
-        var draws = 0
-
-        rule.setContent {
-            TextUsingModifier(
-                "a",
-                style = style,
-                modifier = Modifier
-                    .layout { measurable, constraints ->
-                        measures++
-                        val placeable = measurable.measure(constraints)
-                        layout(placeable.width, placeable.height) {
-                            placements++
-                            placeable.place(IntOffset.Zero)
-                        }
-                    }
-                    .drawBehind {
-                        draws++
-                    }
-            )
-        }
-
-        rule.waitForIdle()
-        val initialMeasures = measures
-        val initialPlacements = placements
-        val initialDraws = draws
-
-        style = config.updateStyle(style)
-
-        rule.runOnIdle {
-            if (config.invalidatesMeasure) {
-                assertThat(measures).isGreaterThan(initialMeasures)
-            }
-            if (config.invalidatesPlacement) {
-                assertThat(placements).isGreaterThan(initialPlacements)
-
-                // If measure is invalidated, placement will also always be invalidated, so ensure
-                // that placement was also invalidated separately from measurement.
-                if (config.invalidatesMeasure) {
-                    assertThat(placements).isGreaterThan(measures)
-                }
-            }
-            if (config.invalidatesDraw) {
-                assertThat(draws).isGreaterThan(initialDraws)
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/TextTestExtensions.kt b/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/TextTestExtensions.kt
deleted file mode 100644
index a5d9af5..0000000
--- a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/TextTestExtensions.kt
+++ /dev/null
@@ -1,34 +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.foundation.newtext.text
-
-import androidx.compose.ui.text.font.Font
-import androidx.compose.ui.text.font.FontStyle
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.text.font.toFontFamily
-import androidx.testutils.fonts.R
-import kotlin.math.ceil
-import kotlin.math.roundToInt
-
-fun Float.toIntPx(): Int = ceil(this).roundToInt()
-
-val TEST_FONT = Font(
-    resId = R.font.sample_font,
-    weight = FontWeight.Normal,
-    style = FontStyle.Normal
-)
-
-val TEST_FONT_FAMILY = TEST_FONT.toFontFamily()
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/TextUsingModifierMinMaxLinesTest.kt b/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/TextUsingModifierMinMaxLinesTest.kt
deleted file mode 100644
index a0b14c5..0000000
--- a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/TextUsingModifierMinMaxLinesTest.kt
+++ /dev/null
@@ -1,154 +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.foundation.newtext.text
-
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.width
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.onSizeChanged
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.text.TextLayoutResult
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.unit.IntSize
-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
-
-@OptIn(ExperimentalTextApi::class)
-@LargeTest
-@RunWith(AndroidJUnit4::class)
-class TextUsingModifierMinMaxLinesTest {
-    @get:Rule
-    val rule = createComposeRule()
-
-    @Test
-    fun whenMaxLines_isBoundOnLineHeight() {
-        val text = "H\n".repeat(1000)
-        val sizes: Array<IntSize?> = Array(5) { null }
-        val layouts: Array<TextLayoutResult?> = Array(5) { null }
-        var unboundedTextSize: IntSize? = null
-
-        val textStyle = TextStyle(fontFamily = TEST_FONT_FAMILY)
-        rule.setContent {
-            for (i in sizes.indices) {
-                Box(
-                    Modifier
-                        .onSizeChanged { sizes[i] = it }
-                        .width(5.dp)) {
-                    TextUsingModifier(
-                        text,
-                        style = textStyle,
-                        maxLines = i + 1,
-                        onTextLayout = {
-                            layouts[i] = it
-                        }
-                    )
-                }
-            }
-
-            Box(
-                Modifier
-                    .onSizeChanged { unboundedTextSize = it }
-                    .width(5.dp)) {
-                TextUsingModifier(
-                    text,
-                    style = textStyle,
-                    maxLines = Int.MAX_VALUE
-                )
-            }
-        }
-        rule.runOnIdle {
-            for (i in 1 until sizes.size) {
-                assertThat(sizes[i]?.height).isGreaterThan(sizes[i - 1]?.height)
-                assertThat(layouts[i]?.lineCount).isEqualTo(i + 1)
-                // just ensure this is less than the unbounded size too
-                assertThat(unboundedTextSize?.height).isGreaterThan(sizes[i]?.height)
-            }
-        }
-    }
-
-    @Test
-    fun whenMinLines_setsLineHeight() {
-        val text = ""
-        val sizes: Array<IntSize?> = Array(5) { null }
-        val layouts: Array<TextLayoutResult?> = Array(5) { null }
-        var unboundedTextSize: IntSize? = null
-
-        val textStyle = TextStyle(fontFamily = TEST_FONT_FAMILY)
-        rule.setContent {
-            for (i in sizes.indices) {
-                Box(
-                    Modifier
-                        .onSizeChanged { sizes[i] = it }
-                        .width(5.dp)) {
-                    TextUsingModifier(
-                        text,
-                        style = textStyle,
-                        minLines = i + 1,
-                        onTextLayout = {
-                            layouts[i] = it
-                        }
-                    )
-                }
-            }
-
-            Box(
-                Modifier
-                    .onSizeChanged { unboundedTextSize = it }
-                    .width(5.dp)) {
-                TextUsingModifier(
-                    text,
-                    style = textStyle
-                )
-            }
-        }
-        rule.runOnIdle {
-            for (i in 1 until sizes.size) {
-                assertThat(sizes[i]?.height).isGreaterThan(sizes[i - 1]?.height)
-                assertThat(layouts[i]?.lineCount).isEqualTo(1)
-                // just ensure this is less than the unbounded size too
-                assertThat(unboundedTextSize?.height).isLessThan(sizes[i]?.height)
-            }
-        }
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun negativeMinLines_throws() {
-        rule.setContent {
-            TextUsingModifier(text = "", minLines = -1)
-        }
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun negativeMaxLines_throws() {
-        rule.setContent {
-            TextUsingModifier(text = "", maxLines = -1)
-        }
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun crossedMinMaxLines_throws() {
-        rule.setContent {
-            TextUsingModifier(text = "", minLines = 10, maxLines = 5)
-        }
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/modifiers/BasicTextSemanticsTest.kt b/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/modifiers/BasicTextSemanticsTest.kt
deleted file mode 100644
index 8c70d21..0000000
--- a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/modifiers/BasicTextSemanticsTest.kt
+++ /dev/null
@@ -1,63 +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.newtext.text.modifiers
-
-import androidx.compose.foundation.newtext.text.TextUsingModifier
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithText
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.MediumTest
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@MediumTest
-@RunWith(AndroidJUnit4::class)
-class BasicTextSemanticsTest {
-
-    @get:Rule
-    val rule = createComposeRule()
-
-    @OptIn(ExperimentalTextApi::class)
-    @Test
-    fun semanticsTextChanges_String() {
-        var text by mutableStateOf("before")
-        rule.setContent {
-            TextUsingModifier(text)
-        }
-        rule.onNodeWithText("before").assertExists()
-        text = "after"
-        rule.onNodeWithText("after").assertExists()
-    }
-
-    @OptIn(ExperimentalTextApi::class)
-    @Test
-    fun semanticsTextChanges_AnnotatedString() {
-        var text by mutableStateOf("before")
-        rule.setContent {
-            TextUsingModifier(AnnotatedString(text))
-        }
-        rule.onNodeWithText("before").assertExists()
-        text = "after"
-        rule.onNodeWithText("after").assertExists()
-    }
-}
diff --git a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/modifiers/MultiParagraphLayoutCacheTest.kt b/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/modifiers/MultiParagraphLayoutCacheTest.kt
deleted file mode 100644
index 471b37e..0000000
--- a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/modifiers/MultiParagraphLayoutCacheTest.kt
+++ /dev/null
@@ -1,216 +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.foundation.newtext.text.modifiers
-
-import androidx.compose.foundation.newtext.text.TEST_FONT_FAMILY
-import androidx.compose.foundation.newtext.text.toIntPx
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.SpanStyle
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.createFontFamilyResolver
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.sp
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry
-import com.google.common.truth.Truth.assertThat
-import kotlin.math.roundToInt
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class MultiParagraphLayoutCacheTest {
-
-    private val fontFamily = TEST_FONT_FAMILY
-    private val density = Density(density = 1f)
-    private val context = InstrumentationRegistry.getInstrumentation().context
-    private val fontFamilyResolver = createFontFamilyResolver(context)
-
-    @Test
-    fun minIntrinsicWidth_getter() {
-        with(density) {
-            val fontSize = 20.sp
-            val text = "Hello"
-            val spanStyle = SpanStyle(fontSize = fontSize, fontFamily = fontFamily)
-            val annotatedString = AnnotatedString(text, spanStyle)
-            val textDelegate = MultiParagraphLayoutCache(
-                text = annotatedString,
-                style = TextStyle.Default,
-                fontFamilyResolver = fontFamilyResolver,
-            ).also {
-                it.density = this
-            }
-
-            textDelegate.layoutWithConstraints(Constraints.fixed(0, 0), LayoutDirection.Ltr)
-
-            assertThat(textDelegate.minIntrinsicWidth)
-                .isEqualTo((fontSize.toPx() * text.length).toIntPx())
-        }
-    }
-
-    @Test
-    fun maxIntrinsicWidth_getter() {
-        with(density) {
-            val fontSize = 20.sp
-            val text = "Hello"
-            val spanStyle = SpanStyle(fontSize = fontSize, fontFamily = fontFamily)
-            val annotatedString = AnnotatedString(text, spanStyle)
-            val textDelegate = MultiParagraphLayoutCache(
-                text = annotatedString,
-                style = TextStyle.Default,
-                fontFamilyResolver = fontFamilyResolver,
-            ).also {
-                it.density = this
-            }
-
-            textDelegate.layoutWithConstraints(Constraints.fixed(0, 0), LayoutDirection.Ltr)
-
-            assertThat(textDelegate.maxIntrinsicWidth)
-                .isEqualTo((fontSize.toPx() * text.length).toIntPx())
-        }
-    }
-
-    @Test
-    fun TextLayoutInput_reLayout_withDifferentHeight() {
-        val textDelegate = MultiParagraphLayoutCache(
-            text = AnnotatedString("Hello World"),
-            style = TextStyle.Default,
-            fontFamilyResolver = fontFamilyResolver,
-        ).also {
-            it.density = density
-        }
-        val width = 200
-        val heightFirstLayout = 100
-        val heightSecondLayout = 200
-
-        val constraintsFirstLayout = Constraints.fixed(width, heightFirstLayout)
-        textDelegate.layoutWithConstraints(constraintsFirstLayout, LayoutDirection.Ltr)
-        val resultFirstLayout = textDelegate.layout
-        assertThat(resultFirstLayout.layoutInput.constraints).isEqualTo(constraintsFirstLayout)
-
-        val constraintsSecondLayout = Constraints.fixed(width, heightSecondLayout)
-        textDelegate.layoutWithConstraints(
-            constraintsSecondLayout,
-            LayoutDirection.Ltr
-        )
-        val resultSecondLayout = textDelegate.layout
-        assertThat(resultSecondLayout.layoutInput.constraints).isEqualTo(constraintsSecondLayout)
-    }
-
-    @Test
-    fun TextLayoutResult_reLayout_withDifferentHeight() {
-        val textDelegate = MultiParagraphLayoutCache(
-            text = AnnotatedString("Hello World"),
-            style = TextStyle.Default,
-            fontFamilyResolver = fontFamilyResolver,
-        ).also {
-            it.density = density
-        }
-        val width = 200
-        val heightFirstLayout = 100
-        val heightSecondLayout = 200
-
-        val constraintsFirstLayout = Constraints.fixed(width, heightFirstLayout)
-        textDelegate.layoutWithConstraints(constraintsFirstLayout, LayoutDirection.Ltr)
-        val resultFirstLayout = textDelegate.layout
-        assertThat(resultFirstLayout.size.height).isEqualTo(heightFirstLayout)
-
-        val constraintsSecondLayout = Constraints.fixed(width, heightSecondLayout)
-        textDelegate.layoutWithConstraints(
-            constraintsSecondLayout,
-            LayoutDirection.Ltr
-        )
-        val resultSecondLayout = textDelegate.layout
-        assertThat(resultSecondLayout.size.height).isEqualTo(heightSecondLayout)
-    }
-
-    @Test
-    fun TextLayoutResult_layout_withEllipsis_withoutSoftWrap() {
-        val fontSize = 20f
-        val text = AnnotatedString(text = "Hello World! Hello World! Hello World! Hello World!")
-        val textDelegate = MultiParagraphLayoutCache(
-            text = text,
-            style = TextStyle(fontSize = fontSize.sp),
-            fontFamilyResolver = fontFamilyResolver,
-            softWrap = false,
-            overflow = TextOverflow.Ellipsis,
-        ).also {
-            it.density = density
-        }
-
-        textDelegate.layoutWithConstraints(Constraints.fixed(0, 0), LayoutDirection.Ltr)
-        // Makes width smaller than needed.
-        val width = textDelegate.maxIntrinsicWidth / 2
-        val constraints = Constraints(maxWidth = width)
-        textDelegate.layoutWithConstraints(constraints, LayoutDirection.Ltr)
-        val layoutResult = textDelegate.layout
-
-        assertThat(layoutResult.lineCount).isEqualTo(1)
-        assertThat(layoutResult.isLineEllipsized(0)).isTrue()
-    }
-
-    @Test
-    fun TextLayoutResult_layoutWithLimitedHeight_withEllipsis() {
-        val fontSize = 20f
-        val text = AnnotatedString(text = "Hello World! Hello World! Hello World! Hello World!")
-
-        val textDelegate = MultiParagraphLayoutCache(
-            text = text,
-            style = TextStyle(fontSize = fontSize.sp),
-            fontFamilyResolver = fontFamilyResolver,
-            overflow = TextOverflow.Ellipsis,
-        ).also {
-            it.density = density
-        }
-        textDelegate.layoutWithConstraints(Constraints.fixed(0, 0), LayoutDirection.Ltr)
-        val constraints = Constraints(
-            maxWidth = textDelegate.maxIntrinsicWidth / 4,
-            maxHeight = (fontSize * 2.7).roundToInt() // fully fits at most 2 lines
-        )
-        textDelegate.layoutWithConstraints(constraints, LayoutDirection.Ltr)
-        val layoutResult = textDelegate.layout
-
-        assertThat(layoutResult.lineCount).isEqualTo(2)
-        assertThat(layoutResult.isLineEllipsized(1)).isTrue()
-    }
-
-    @Test
-    fun TextLayoutResult_sameWidth_inRtlAndLtr_withLetterSpacing() {
-        val fontSize = 20f
-        val text = AnnotatedString(text = "Hello World")
-
-        val textDelegate = MultiParagraphLayoutCache(
-            text = text,
-            style = TextStyle(fontSize = fontSize.sp, letterSpacing = 0.5.sp),
-            fontFamilyResolver = fontFamilyResolver,
-            overflow = TextOverflow.Ellipsis,
-        ).also {
-            it.density = density
-        }
-
-        textDelegate.layoutWithConstraints(Constraints(), LayoutDirection.Ltr)
-        val layoutResultLtr = textDelegate.layout
-        textDelegate.layoutWithConstraints(Constraints(), LayoutDirection.Rtl)
-        val layoutResultRtl = textDelegate.layout
-
-        assertThat(layoutResultLtr.size.width).isEqualTo(layoutResultRtl.size.width)
-    }
-}
diff --git a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/modifiers/MultiParagraphLayoutCacheWidthWithLetterSpacingTest.kt b/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/modifiers/MultiParagraphLayoutCacheWidthWithLetterSpacingTest.kt
deleted file mode 100644
index a2195df..0000000
--- a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/modifiers/MultiParagraphLayoutCacheWidthWithLetterSpacingTest.kt
+++ /dev/null
@@ -1,91 +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.foundation.newtext.text.modifiers
-
-import androidx.compose.foundation.newtext.text.TEST_FONT_FAMILY
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.createFontFamilyResolver
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.sp
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class MultiParagraphLayoutCacheWidthWithLetterSpacingTest {
-    private val fontFamily = TEST_FONT_FAMILY
-
-    /**
-     * values are exact values for the repro case (on Pixel4, Android 11)
-     */
-    private val density = Density(3.051f, 1.15f)
-    private val letterSpacing = 0.4.sp
-    private val lineHeight = 16.sp
-    private val fontSize = 12.sp
-    private val context = InstrumentationRegistry.getInstrumentation().context
-    @OptIn(ExperimentalTextApi::class)
-    private val fontFamilyResolver = createFontFamilyResolver(context)
-
-    @Test
-    fun letterSpacing_and_lineHeight() {
-        assertLineCount(
-            TextStyle(letterSpacing = letterSpacing, lineHeight = lineHeight)
-        )
-    }
-
-    @Test
-    fun only_letterSpacing() {
-        assertLineCount(TextStyle(letterSpacing = letterSpacing))
-    }
-
-    @Test
-    fun only_lineHeight() {
-        assertLineCount(TextStyle(lineHeight = lineHeight))
-    }
-
-    @Test
-    fun no_lineHeight_or_letterSpacing() {
-        assertLineCount(TextStyle())
-    }
-
-    private fun assertLineCount(style: TextStyle) {
-        val textDelegate = MultiParagraphLayoutCache(
-            text = AnnotatedString(text = "This is a callout message"),
-            style = style.copy(
-                fontFamily = fontFamily,
-                fontSize = fontSize
-            ),
-            fontFamilyResolver = fontFamilyResolver,
-            softWrap = true,
-            overflow = TextOverflow.Clip
-        ).also {
-            it.density = density
-        }
-        textDelegate.layoutWithConstraints(Constraints(), LayoutDirection.Ltr)
-        val layoutResult = textDelegate.layout
-        assertThat(layoutResult.lineCount).isEqualTo(1)
-    }
-}
diff --git a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/modifiers/TextLayoutResultIntegrationTest.kt b/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/modifiers/TextLayoutResultIntegrationTest.kt
deleted file mode 100644
index 4b8b7c1..0000000
--- a/compose/foundation/foundation-newtext/src/androidAndroidTest/kotlin/androidx/compose/foundation/newtext/text/modifiers/TextLayoutResultIntegrationTest.kt
+++ /dev/null
@@ -1,330 +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.foundation.newtext.text.modifiers
-
-import androidx.compose.foundation.newtext.text.TEST_FONT_FAMILY
-import androidx.compose.foundation.newtext.text.toIntPx
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.graphics.Canvas
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.SpanStyle
-import androidx.compose.ui.text.TextPainter
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.createFontFamilyResolver
-import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.sp
-import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.google.common.truth.IntegerSubject
-import kotlin.math.floor
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class TextLayoutResultIntegrationTest {
-
-    private val fontFamily = TEST_FONT_FAMILY
-    private val density = Density(density = 1f)
-    private val context = InstrumentationRegistry.getInstrumentation().context
-    private val fontFamilyResolver = createFontFamilyResolver(context)
-    private val layoutDirection = LayoutDirection.Ltr
-
-    @Test
-    fun width_getter() {
-        with(density) {
-            val fontSize = 20.sp
-            val text = "Hello"
-            val spanStyle = SpanStyle(fontSize = fontSize, fontFamily = fontFamily)
-            val annotatedString = AnnotatedString(text, spanStyle)
-            val textDelegate = MultiParagraphLayoutCache(
-                text = annotatedString,
-                style = TextStyle.Default,
-                fontFamilyResolver = fontFamilyResolver
-            ).also {
-                it.density = this
-            }
-
-            textDelegate.layoutWithConstraints(Constraints(0, 200), layoutDirection)
-            val layoutResult = textDelegate.layout
-
-            assertThat(layoutResult.size.width).isEqualTo(
-                (fontSize.toPx() * text.length).toIntPx()
-            )
-        }
-    }
-
-    @Test
-    fun width_getter_with_small_width() {
-        val text = "Hello"
-        val width = 80
-        val spanStyle = SpanStyle(fontSize = 20.sp, fontFamily = fontFamily)
-        val annotatedString = AnnotatedString(text, spanStyle)
-        val textDelegate = MultiParagraphLayoutCache(
-            text = annotatedString,
-            style = TextStyle.Default,
-            fontFamilyResolver = fontFamilyResolver
-        ).also {
-            it.density = density
-        }
-
-        textDelegate.layoutWithConstraints(Constraints(maxWidth = width), layoutDirection)
-        val layoutResult = textDelegate.layout
-
-        assertThat(layoutResult.size.width).isEqualTo(width)
-    }
-
-    @Test
-    fun height_getter() {
-        with(density) {
-            val fontSize = 20.sp
-            val spanStyle = SpanStyle(fontSize = fontSize, fontFamily = fontFamily)
-            val text = "hello"
-            val annotatedString = AnnotatedString(text, spanStyle)
-            val textDelegate = MultiParagraphLayoutCache(
-                text = annotatedString,
-                style = TextStyle.Default,
-                fontFamilyResolver = fontFamilyResolver
-            ).also {
-                it.density = this
-            }
-
-            textDelegate.layoutWithConstraints(Constraints(), layoutDirection)
-            val layoutResult = textDelegate.layout
-
-            assertThat(layoutResult.size.height).isEqualTo((fontSize.toPx()).toIntPx())
-        }
-    }
-
-    @Test
-    fun layout_build_layoutResult() {
-        val textDelegate = MultiParagraphLayoutCache(
-            text = AnnotatedString("hello"),
-            style = TextStyle.Default,
-            fontFamilyResolver = fontFamilyResolver
-        ).also {
-            it.density = density
-        }
-
-        textDelegate.layoutWithConstraints(Constraints(0, 20), layoutDirection)
-        val layoutResult = textDelegate.layout
-
-        assertThat(layoutResult).isNotNull()
-    }
-
-    private fun IntegerSubject.isZero() {
-        this.isEqualTo(0)
-    }
-
-    @Test
-    fun getPositionForOffset_First_Character() {
-        val text = "Hello"
-        val annotatedString = AnnotatedString(
-            text,
-            SpanStyle(fontSize = 20.sp, fontFamily = fontFamily)
-        )
-
-        val textDelegate = MultiParagraphLayoutCache(
-            text = annotatedString,
-            style = TextStyle.Default,
-            fontFamilyResolver = fontFamilyResolver
-        ).also {
-            it.density = density
-        }
-        textDelegate.layoutWithConstraints(Constraints(), layoutDirection)
-        val layoutResult = textDelegate.layout
-
-        val selection = layoutResult.getOffsetForPosition(Offset.Zero)
-
-        assertThat(selection).isZero()
-    }
-
-    @Test
-    fun getPositionForOffset_other_Character() {
-        with(density) {
-            val fontSize = 20.sp
-            val characterIndex = 2 // Start from 0.
-            val text = "Hello"
-
-            val annotatedString = AnnotatedString(
-                text,
-                SpanStyle(fontSize = fontSize, fontFamily = fontFamily)
-            )
-
-            val textDelegate = MultiParagraphLayoutCache(
-                text = annotatedString,
-                style = TextStyle.Default,
-                fontFamilyResolver = fontFamilyResolver
-            ).also {
-                it.density = this
-            }
-            textDelegate.layoutWithConstraints(Constraints(), layoutDirection)
-            val layoutResult = textDelegate.layout
-
-            val selection = layoutResult.getOffsetForPosition(
-                position = Offset((fontSize.toPx() * characterIndex + 1), 0f)
-            )
-
-            assertThat(selection).isEqualTo(characterIndex)
-        }
-    }
-
-    @Test
-    fun hasOverflowShaderFalse() {
-        val text = "Hello"
-        val spanStyle = SpanStyle(fontSize = 20.sp, fontFamily = fontFamily)
-        val annotatedString = AnnotatedString(text, spanStyle)
-        val textDelegate = MultiParagraphLayoutCache(
-            text = annotatedString,
-            style = TextStyle.Default,
-            fontFamilyResolver = fontFamilyResolver
-        ).also {
-            it.density = density
-        }
-
-        textDelegate.layoutWithConstraints(Constraints(), layoutDirection)
-        val layoutResult = textDelegate.layout
-
-        assertThat(layoutResult.hasVisualOverflow).isFalse()
-
-        // paint should not throw exception
-        TextPainter.paint(Canvas(android.graphics.Canvas()), layoutResult)
-    }
-
-    @Test
-    fun didOverflowHeight_isTrue_when_maxLines_exceeded() {
-        val text = "HelloHellowHelloHello"
-        val spanStyle = SpanStyle(fontSize = 20.sp, fontFamily = fontFamily)
-        val annotatedString = AnnotatedString(text, spanStyle)
-        val maxLines = 3
-
-        val textDelegate = MultiParagraphLayoutCache(
-            text = annotatedString,
-            style = TextStyle.Default,
-            fontFamilyResolver = fontFamilyResolver,
-            maxLines = maxLines
-        ).also {
-            it.density = density
-        }
-
-        textDelegate.layoutWithConstraints(Constraints.fixed(0, 0), LayoutDirection.Ltr)
-        // Tries to make 5 lines of text, which exceeds the given maxLines(3).
-        val maxWidth = textDelegate.maxIntrinsicWidth / 5
-        textDelegate.layoutWithConstraints(
-            Constraints(maxWidth = maxWidth),
-            layoutDirection
-        )
-        val layoutResult = textDelegate.layout
-
-        assertThat(layoutResult.didOverflowHeight).isTrue()
-    }
-
-    @Test
-    fun didOverflowHeight_isFalse_when_maxLines_notExceeded() {
-        val text = "HelloHellowHelloHello"
-        val spanStyle = SpanStyle(fontSize = 20.sp, fontFamily = fontFamily)
-        val annotatedString = AnnotatedString(text, spanStyle)
-        val maxLines = 10
-
-        val textDelegate = MultiParagraphLayoutCache(
-            text = annotatedString,
-            style = TextStyle.Default,
-            fontFamilyResolver = fontFamilyResolver,
-            maxLines = maxLines
-        ).also {
-            it.density = density
-        }
-
-        textDelegate.layoutWithConstraints(Constraints.fixed(0, 0), LayoutDirection.Ltr)
-        // Tries to make 5 lines of text, which doesn't exceed the given maxLines(10).
-        val maxWidth = textDelegate.maxIntrinsicWidth / 5
-        textDelegate.layoutWithConstraints(
-            Constraints(maxWidth = maxWidth),
-            layoutDirection
-        )
-        val layoutResult = textDelegate.layout
-
-        assertThat(layoutResult.didOverflowHeight).isFalse()
-    }
-
-    @Test
-    fun didOverflowHeight_isTrue_when_maxHeight_exceeded() {
-        val text = "HelloHellowHelloHello"
-        val spanStyle = SpanStyle(fontSize = 20.sp, fontFamily = fontFamily)
-        val annotatedString = AnnotatedString(text, spanStyle)
-
-        val textDelegate = MultiParagraphLayoutCache(
-            text = annotatedString,
-            style = TextStyle.Default,
-            fontFamilyResolver = fontFamilyResolver,
-        ).also {
-            it.density = density
-        }
-
-        textDelegate.layoutWithConstraints(
-            Constraints(),
-            layoutDirection
-        )
-
-        val maxIntrinsicsHeight = textDelegate.layout.multiParagraph.height
-
-        // Make maxHeight smaller than needed.
-        val maxHeight = floor(maxIntrinsicsHeight / 2).toInt()
-        textDelegate.layoutWithConstraints(
-            Constraints(maxHeight = maxHeight),
-            layoutDirection
-        )
-        val layoutResult = textDelegate.layout
-
-        assertThat(layoutResult.didOverflowHeight).isTrue()
-    }
-
-    @Test
-    fun didOverflowHeight_isFalse_when_maxHeight_notExceeded() {
-        val text = "HelloHellowHelloHello"
-        val spanStyle = SpanStyle(fontSize = 20.sp, fontFamily = fontFamily)
-        val annotatedString = AnnotatedString(text, spanStyle)
-
-        val textDelegate = MultiParagraphLayoutCache(
-            text = annotatedString,
-            style = TextStyle.Default,
-            fontFamilyResolver = fontFamilyResolver,
-        ).also {
-            it.density = density
-        }
-
-        textDelegate.layoutWithConstraints(
-            Constraints(),
-            layoutDirection
-        )
-        val maxIntrinsicsHeight = textDelegate.layout.multiParagraph.height
-
-        // Make max height larger than the needed.
-        val maxHeight = floor(maxIntrinsicsHeight * 2).toInt()
-        textDelegate.layoutWithConstraints(
-            Constraints(maxHeight = maxHeight),
-            layoutDirection
-        )
-        val layoutResult = textDelegate.layout
-
-        assertThat(layoutResult.didOverflowHeight).isFalse()
-    }
-}
diff --git a/compose/foundation/foundation-newtext/src/androidMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/ContextMenu.android.kt b/compose/foundation/foundation-newtext/src/androidMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/ContextMenu.android.kt
deleted file mode 100644
index f907321..0000000
--- a/compose/foundation/foundation-newtext/src/androidMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/ContextMenu.android.kt
+++ /dev/null
@@ -1,28 +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.newtext.text.copypasta
-
-import androidx.compose.foundation.newtext.text.copypasta.selection.SelectionManager
-import androidx.compose.runtime.Composable
-
-@Composable
-internal actual fun ContextMenuArea(
-    manager: SelectionManager,
-    content: @Composable () -> Unit
-) {
-    content()
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/androidMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/StringHelpers.android.kt b/compose/foundation/foundation-newtext/src/androidMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/StringHelpers.android.kt
deleted file mode 100644
index 92cb068..0000000
--- a/compose/foundation/foundation-newtext/src/androidMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/StringHelpers.android.kt
+++ /dev/null
@@ -1,44 +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.newtext.text.copypasta
-
-import androidx.emoji2.text.EmojiCompat
-import java.text.BreakIterator
-
-internal actual fun String.findPrecedingBreak(index: Int): Int {
-    val emojiBreak =
-        getEmojiCompatIfLoaded()?.getEmojiStart(this, maxOf(0, index - 1))?.takeUnless { it == -1 }
-    if (emojiBreak != null) return emojiBreak
-
-    val it = BreakIterator.getCharacterInstance()
-    it.setText(this)
-    return it.preceding(index)
-}
-
-internal actual fun String.findFollowingBreak(index: Int): Int {
-    val emojiBreak = getEmojiCompatIfLoaded()?.getEmojiEnd(this, index)?.takeUnless { it == -1 }
-    if (emojiBreak != null) return emojiBreak
-
-    val it = BreakIterator.getCharacterInstance()
-    it.setText(this)
-    return it.following(index)
-}
-
-private fun getEmojiCompatIfLoaded(): EmojiCompat? =
-    if (EmojiCompat.isConfigured())
-        EmojiCompat.get().takeIf { it.loadState == EmojiCompat.LOAD_STATE_SUCCEEDED }
-    else null
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/androidMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/TextPointerIcon.android.kt b/compose/foundation/foundation-newtext/src/androidMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/TextPointerIcon.android.kt
deleted file mode 100644
index 6645638..0000000
--- a/compose/foundation/foundation-newtext/src/androidMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/TextPointerIcon.android.kt
+++ /dev/null
@@ -1,22 +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.newtext.text.copypasta
-
-import androidx.compose.ui.input.pointer.PointerIcon
-
-internal actual val textPointerIcon: PointerIcon =
-    PointerIcon(android.view.PointerIcon.TYPE_TEXT)
diff --git a/compose/foundation/foundation-newtext/src/androidMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/AndroidSelectionHandles.android.kt b/compose/foundation/foundation-newtext/src/androidMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/AndroidSelectionHandles.android.kt
deleted file mode 100644
index 50a2e51..0000000
--- a/compose/foundation/foundation-newtext/src/androidMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/AndroidSelectionHandles.android.kt
+++ /dev/null
@@ -1,326 +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.newtext.text.copypasta.selection
-
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.newtext.text.copypasta.selection.HandleReferencePoint.TopLeft
-import androidx.compose.foundation.newtext.text.copypasta.selection.HandleReferencePoint.TopMiddle
-import androidx.compose.foundation.newtext.text.copypasta.selection.HandleReferencePoint.TopRight
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
-import androidx.compose.ui.draw.CacheDrawScope
-import androidx.compose.ui.draw.drawWithCache
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.graphics.BlendMode
-import androidx.compose.ui.graphics.Canvas
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.graphics.ImageBitmap
-import androidx.compose.ui.graphics.ImageBitmapConfig
-import androidx.compose.ui.graphics.drawscope.CanvasDrawScope
-import androidx.compose.ui.graphics.drawscope.scale
-import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.text.style.ResolvedTextDirection
-import androidx.compose.ui.unit.IntOffset
-import androidx.compose.ui.unit.IntRect
-import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.window.Popup
-import androidx.compose.ui.window.PopupPositionProvider
-import androidx.compose.ui.window.PopupProperties
-import kotlin.math.ceil
-import kotlin.math.roundToInt
-
-@Composable
-internal actual fun SelectionHandle(
-    position: Offset,
-    isStartHandle: Boolean,
-    direction: ResolvedTextDirection,
-    handlesCrossed: Boolean,
-    modifier: Modifier,
-    content: @Composable (() -> Unit)?
-) {
-    val isLeft = isLeft(isStartHandle, direction, handlesCrossed)
-    // The left selection handle's top right is placed at the given position, and vice versa.
-    val handleReferencePoint = if (isLeft) {
-        TopRight
-    } else {
-        TopLeft
-    }
-
-    HandlePopup(position = position, handleReferencePoint = handleReferencePoint) {
-        if (content == null) {
-            DefaultSelectionHandle(
-                modifier = modifier
-                    .semantics {
-                        this[SelectionHandleInfoKey] = SelectionHandleInfo(
-                            position = position
-                        )
-                    },
-                isStartHandle = isStartHandle,
-                direction = direction,
-                handlesCrossed = handlesCrossed
-            )
-        } else {
-            content()
-        }
-    }
-}
-
-@Composable
-/*@VisibleForTesting*/
-internal fun DefaultSelectionHandle(
-    modifier: Modifier,
-    isStartHandle: Boolean,
-    direction: ResolvedTextDirection,
-    handlesCrossed: Boolean
-) {
-    Spacer(
-        modifier.size(
-            HandleWidth,
-            HandleHeight
-        )
-            .drawSelectionHandle(isStartHandle, direction, handlesCrossed)
-    )
-}
-
-internal fun Modifier.drawSelectionHandle(
-    isStartHandle: Boolean,
-    direction: ResolvedTextDirection,
-    handlesCrossed: Boolean
-) = composed {
-    val handleColor = LocalTextSelectionColors.current.handleColor
-    this.then(
-        Modifier.drawWithCache {
-            val radius = size.width / 2f
-            val handleImage = createHandleImage(radius)
-            val colorFilter = ColorFilter.tint(handleColor)
-            onDrawWithContent {
-                drawContent()
-                val isLeft = isLeft(isStartHandle, direction, handlesCrossed)
-                if (isLeft) {
-                    // Flip the selection handle horizontally.
-                    scale(scaleX = -1f, scaleY = 1f) {
-                        drawImage(
-                            image = handleImage,
-                            colorFilter = colorFilter
-                        )
-                    }
-                } else {
-                    drawImage(
-                        image = handleImage,
-                        colorFilter = colorFilter
-                    )
-                }
-            }
-        }
-    )
-}
-
-/**
- * The cache for the image mask created to draw selection/cursor handle, so that we don't need to
- * recreate them.
- */
-private object HandleImageCache {
-    var imageBitmap: ImageBitmap? = null
-    var canvas: Canvas? = null
-    var canvasDrawScope: CanvasDrawScope? = null
-}
-
-/**
- * Create an image bitmap for the basic shape of a selection handle or cursor handle. It is an
- * circle with a rectangle covering its left top part.
- *
- * To draw the right selection handle, directly draw this image bitmap.
- * To draw the left selection handle, mirror the canvas first and then draw this image bitmap.
- * To draw the cursor handle, translate and rotated the canvas 45 degrees, then draw this image
- * bitmap.
- *
- * @param radius the radius of circle in selection/cursor handle.
- * CanvasDrawScope objects so that we only recreate them when necessary.
- */
-internal fun CacheDrawScope.createHandleImage(radius: Float): ImageBitmap {
-    // The edge length of the square bounding box of the selection/cursor handle. This is also
-    // the size of the bitmap needed for the bitmap mask.
-    val edge = ceil(radius).toInt() * 2
-
-    var imageBitmap = HandleImageCache.imageBitmap
-    var canvas = HandleImageCache.canvas
-    var drawScope = HandleImageCache.canvasDrawScope
-
-    // If the cached bitmap is null or too small, we need to create new bitmap.
-    if (
-        imageBitmap == null ||
-        canvas == null ||
-        edge > imageBitmap.width ||
-        edge > imageBitmap.height
-    ) {
-        imageBitmap = ImageBitmap(
-            width = edge,
-            height = edge,
-            config = ImageBitmapConfig.Alpha8
-        )
-        HandleImageCache.imageBitmap = imageBitmap
-        canvas = Canvas(imageBitmap)
-        HandleImageCache.canvas = canvas
-    }
-    if (drawScope == null) {
-        drawScope = CanvasDrawScope()
-        HandleImageCache.canvasDrawScope = drawScope
-    }
-
-    drawScope.draw(
-        this,
-        layoutDirection,
-        canvas,
-        Size(imageBitmap.width.toFloat(), imageBitmap.height.toFloat())
-    ) {
-        // Clear the previously rendered portion within this ImageBitmap as we could
-        // be re-using it
-        drawRect(
-            color = Color.Black,
-            size = size,
-            blendMode = BlendMode.Clear
-        )
-
-        // Draw the rectangle at top left.
-        drawRect(
-            color = Color(0xFF000000),
-            topLeft = Offset.Zero,
-            size = Size(radius, radius)
-        )
-        // Draw the circle
-        drawCircle(
-            color = Color(0xFF000000),
-            radius = radius,
-            center = Offset(radius, radius)
-        )
-    }
-    return imageBitmap
-}
-
-@Composable
-internal fun HandlePopup(
-    position: Offset,
-    handleReferencePoint: HandleReferencePoint,
-    content: @Composable () -> Unit
-) {
-    val intOffset = IntOffset(position.x.roundToInt(), position.y.roundToInt())
-
-    val popupPositioner = remember(handleReferencePoint, intOffset) {
-        HandlePositionProvider(handleReferencePoint, intOffset)
-    }
-
-    Popup(
-        popupPositionProvider = popupPositioner,
-        properties = PopupProperties(
-            excludeFromSystemGesture = true,
-            clippingEnabled = false
-        ),
-        content = content
-    )
-}
-
-/**
- * The enum that specifies how a selection/cursor handle is placed to its given position.
- * When this value is [TopLeft], the top left corner of the handle will be placed at the
- * given position.
- * When this value is [TopRight], the top right corner of the handle will be placed at the
- * given position.
- * When this value is [TopMiddle], the handle top edge's middle point will be placed at the given
- * position.
- */
-internal enum class HandleReferencePoint {
-    TopLeft,
-    TopRight,
-    TopMiddle
-}
-
-/**
- * This [PopupPositionProvider] for [HandlePopup]. It will position the selection handle
- * to the [offset] in its anchor layout.
- *
- * @see HandleReferencePoint
- */
-/*@VisibleForTesting*/
-internal class HandlePositionProvider(
-    private val handleReferencePoint: HandleReferencePoint,
-    private val offset: IntOffset
-) : PopupPositionProvider {
-    override fun calculatePosition(
-        anchorBounds: IntRect,
-        windowSize: IntSize,
-        layoutDirection: LayoutDirection,
-        popupContentSize: IntSize
-    ): IntOffset {
-        return when (handleReferencePoint) {
-            TopLeft ->
-                IntOffset(
-                    x = anchorBounds.left + offset.x,
-                    y = anchorBounds.top + offset.y
-                )
-            TopRight ->
-                IntOffset(
-                    x = anchorBounds.left + offset.x - popupContentSize.width,
-                    y = anchorBounds.top + offset.y
-                )
-            TopMiddle ->
-                IntOffset(
-                    x = anchorBounds.left + offset.x - popupContentSize.width / 2,
-                    y = anchorBounds.top + offset.y
-                )
-        }
-    }
-}
-
-/**
- * Computes whether the handle's appearance should be left-pointing or right-pointing.
- */
-private fun isLeft(
-    isStartHandle: Boolean,
-    direction: ResolvedTextDirection,
-    handlesCrossed: Boolean
-): Boolean {
-    return if (isStartHandle) {
-        isHandleLtrDirection(direction, handlesCrossed)
-    } else {
-        !isHandleLtrDirection(direction, handlesCrossed)
-    }
-}
-
-/**
- * This method is to check if the selection handles should use the natural Ltr pointing
- * direction.
- * If the context is Ltr and the handles are not crossed, or if the context is Rtl and the handles
- * are crossed, return true.
- *
- * In Ltr context, the start handle should point to the left, and the end handle should point to
- * the right. However, in Rtl context or when handles are crossed, the start handle should point to
- * the right, and the end handle should point to left.
- */
-/*@VisibleForTesting*/
-internal fun isHandleLtrDirection(
-    direction: ResolvedTextDirection,
-    areHandlesCrossed: Boolean
-): Boolean {
-    return direction == ResolvedTextDirection.Ltr && !areHandlesCrossed ||
-        direction == ResolvedTextDirection.Rtl && areHandlesCrossed
-}
diff --git a/compose/foundation/foundation-newtext/src/androidMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionManager.android.kt b/compose/foundation/foundation-newtext/src/androidMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionManager.android.kt
deleted file mode 100644
index fa0d056..0000000
--- a/compose/foundation/foundation-newtext/src/androidMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionManager.android.kt
+++ /dev/null
@@ -1,69 +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.newtext.text.copypasta.selection
-
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.MagnifierStyle
-import androidx.compose.foundation.magnifier
-
-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.input.key.KeyEvent
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.unit.IntSize
-
-// TODO(b/139322105) Implement for Android when hardware keyboard is implemented
-internal actual fun isCopyKeyEvent(keyEvent: KeyEvent) = false
-
-// We use composed{} to read a local, but don't provide inspector info because the underlying
-// magnifier modifier provides more meaningful inspector info.
-@OptIn(ExperimentalFoundationApi::class)
-internal actual fun Modifier.selectionMagnifier(manager: SelectionManager): Modifier {
-    // Avoid tracking animation state on older Android versions that don't support magnifiers.
-    if (!MagnifierStyle.TextDefault.isSupported) {
-        return this
-    }
-
-    return composed {
-        val density = LocalDensity.current
-        var magnifierSize by remember { mutableStateOf(IntSize.Zero) }
-        animatedSelectionMagnifier(
-            magnifierCenter = {
-                calculateSelectionMagnifierCenterAndroid(
-                    manager,
-                    magnifierSize
-                )
-            },
-            platformMagnifier = { center ->
-                Modifier
-                    .magnifier(
-                        sourceCenter = { center() },
-                        onSizeChanged = { size ->
-                            magnifierSize = with(density) {
-                                IntSize(size.width.roundToPx(), size.height.roundToPx())
-                            }
-                        },
-                        style = MagnifierStyle.TextDefault
-                    )
-            }
-        )
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/AnnotatedStringResolveInlineContent.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/AnnotatedStringResolveInlineContent.kt
deleted file mode 100644
index 26ebe38..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/AnnotatedStringResolveInlineContent.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.compose.foundation.newtext.text
-
-import androidx.compose.foundation.text.InlineTextContent
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.Placeholder
-import androidx.compose.ui.util.fastForEach
-import androidx.compose.ui.util.fastMap
-
-/**
- * Attempts to match AnnotatedString placeholders with passed [InlineTextContent]
- *
- * Matches will produce a entry in both returned lists.
- *
- * Non-matches will be ignored silently.
- */
-internal fun AnnotatedString.resolveInlineContent(
-    inlineContent: Map<String, InlineTextContent>?
-): Pair<List<PlaceholderRange>, List<InlineContentRange>> {
-    if (inlineContent.isNullOrEmpty()) {
-        return EmptyInlineContent
-    }
-    val inlineContentAnnotations = getStringAnnotations(INLINE_CONTENT_TAG, 0, text.length)
-
-    val placeholders = mutableListOf<AnnotatedString.Range<Placeholder>>()
-    val inlineComposables = mutableListOf<AnnotatedString.Range<@Composable (String) -> Unit>>()
-    inlineContentAnnotations.fastForEach { annotation ->
-        inlineContent[annotation.item]?.let { inlineTextContent ->
-            placeholders.add(
-                AnnotatedString.Range(
-                    inlineTextContent.placeholder,
-                    annotation.start,
-                    annotation.end
-                )
-            )
-            inlineComposables.add(
-                AnnotatedString.Range(
-                    inlineTextContent.children,
-                    annotation.start,
-                    annotation.end
-                )
-            )
-        }
-    }
-    return Pair(placeholders, inlineComposables)
-}
-
-internal fun AnnotatedString.hasInlineContent(): Boolean =
-    hasStringAnnotations(INLINE_CONTENT_TAG, 0, text.length)
-
-@Composable
-internal fun InlineChildren(
-    text: AnnotatedString,
-    inlineContents: List<InlineContentRange>
-) {
-    inlineContents.fastForEach { (content, start, end) ->
-        Layout(
-            content = { content(text.subSequence(start, end).text) }
-        ) { children, constrains ->
-            val placeables = children.fastMap { it.measure(constrains) }
-            layout(width = constrains.maxWidth, height = constrains.maxHeight) {
-                placeables.fastForEach { it.placeRelative(0, 0) }
-            }
-        }
-    }
-}
-
-private typealias PlaceholderRange = AnnotatedString.Range<Placeholder>
-private typealias InlineContentRange = AnnotatedString.Range<@Composable (String) -> Unit>
-internal const val INLINE_CONTENT_TAG = "androidx.compose.foundation.text.inlineContent"
-
-private val EmptyInlineContent: Pair<List<PlaceholderRange>, List<InlineContentRange>> =
-    Pair(emptyList(), emptyList())
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/Helpers.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/Helpers.kt
deleted file mode 100644
index c6ce37f..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/Helpers.kt
+++ /dev/null
@@ -1,40 +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.foundation.newtext.text
-
-import androidx.compose.ui.util.fastForEachIndexed
-import kotlin.contracts.ExperimentalContracts
-import kotlin.contracts.contract
-import kotlin.math.ceil
-import kotlin.math.roundToInt
-
-@OptIn(ExperimentalContracts::class)
-@Suppress("BanInlineOptIn") // Treat Kotlin Contracts as non-experimental.
-internal inline fun <T, R> List<T>.fastMapIndexedNotNull(
-    transform: (index: Int, T) -> R?
-): List<R> {
-    contract { callsInPlace(transform) }
-    val target = ArrayList<R>(size)
-    fastForEachIndexed { index, e ->
-        transform(index, e)?.let { target += it }
-    }
-    return target
-}
-
-internal fun Float.ceilToIntPx(): Int = ceil(this).roundToInt()
-
-internal const val DefaultMinLines = 1
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/TextUsingModifier.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/TextUsingModifier.kt
deleted file mode 100644
index ca96202..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/TextUsingModifier.kt
+++ /dev/null
@@ -1,272 +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.foundation.newtext.text
-
-import androidx.compose.foundation.newtext.text.copypasta.selection.LocalSelectionRegistrar
-import androidx.compose.foundation.newtext.text.copypasta.selection.LocalTextSelectionColors
-import androidx.compose.foundation.newtext.text.modifiers.SelectableTextAnnotatedStringElement
-import androidx.compose.foundation.newtext.text.modifiers.TextAnnotatedStringElement
-import androidx.compose.foundation.newtext.text.modifiers.SelectionController
-import androidx.compose.foundation.newtext.text.modifiers.TextStringSimpleElement
-import androidx.compose.foundation.newtext.text.modifiers.validateMinMaxLines
-import androidx.compose.foundation.text.InlineTextContent
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Rect
-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.platform.LocalFontFamilyResolver
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.text.Placeholder
-import androidx.compose.ui.text.TextLayoutResult
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.IntOffset
-import androidx.compose.ui.util.fastForEach
-import kotlin.math.floor
-import kotlin.math.roundToInt
-
-/**
- * Rewrite of BasicText
- */
-@OptIn(ExperimentalComposeUiApi::class)
-@ExperimentalTextApi
-@Composable
-fun TextUsingModifier(
-    text: String,
-    modifier: Modifier = Modifier,
-    style: TextStyle = TextStyle.Default,
-    onTextLayout: ((TextLayoutResult) -> Unit)? = null,
-    overflow: TextOverflow = TextOverflow.Clip,
-    softWrap: Boolean = true,
-    maxLines: Int = Int.MAX_VALUE,
-    minLines: Int = 1,
-) {
-    validateMinMaxLines(minLines, maxLines)
-    val selectionRegistrar = LocalSelectionRegistrar.current
-    val selectionController = if (selectionRegistrar != null) {
-        val backgroundSelectionColor = LocalTextSelectionColors.current.backgroundColor
-        remember(selectionRegistrar, backgroundSelectionColor) {
-            SelectionController(
-                selectionRegistrar,
-                backgroundSelectionColor
-            )
-        }
-    } else {
-        null
-    }
-    val finalModifier = if (selectionController != null || onTextLayout != null) {
-        modifier.textModifier(
-            AnnotatedString(text),
-            style = style,
-            onTextLayout = onTextLayout,
-            overflow = overflow,
-            softWrap = softWrap,
-            maxLines = maxLines,
-            minLines = minLines,
-            fontFamilyResolver = LocalFontFamilyResolver.current,
-            placeholders = null,
-            onPlaceholderLayout = null,
-            selectionController = selectionController
-        )
-    } else {
-        modifier then TextStringSimpleElement(
-            text,
-            style,
-            LocalFontFamilyResolver.current,
-            overflow,
-            softWrap,
-            maxLines,
-            minLines
-        )
-    }
-    Layout(finalModifier, EmptyMeasurePolicy)
-}
-
-/**
- * Rewrite of BasicText
- */
-@ExperimentalTextApi
-@Composable
-fun TextUsingModifier(
-    text: AnnotatedString,
-    modifier: Modifier = Modifier,
-    style: TextStyle = TextStyle.Default,
-    onTextLayout: (TextLayoutResult) -> Unit = {},
-    overflow: TextOverflow = TextOverflow.Clip,
-    softWrap: Boolean = true,
-    maxLines: Int = Int.MAX_VALUE,
-    minLines: Int = 1,
-    inlineContent: Map<String, InlineTextContent>? = null,
-) {
-    validateMinMaxLines(minLines, maxLines)
-    val selectionRegistrar = LocalSelectionRegistrar.current
-    val selectionController = if (selectionRegistrar != null) {
-        val backgroundSelectionColor = LocalTextSelectionColors.current.backgroundColor
-        remember(selectionRegistrar, backgroundSelectionColor) {
-            SelectionController(
-                selectionRegistrar,
-                backgroundSelectionColor
-            )
-        }
-    } else {
-        null
-    }
-
-    if (!text.hasInlineContent()) {
-        // this is the same as text: String, use all the early exits
-        Layout(
-            modifier = modifier.textModifier(
-                text = text,
-                style = style,
-                onTextLayout = onTextLayout,
-                overflow = overflow,
-                softWrap = softWrap,
-                maxLines = maxLines,
-                minLines = minLines,
-                fontFamilyResolver = LocalFontFamilyResolver.current,
-                placeholders = null,
-                onPlaceholderLayout = null,
-                selectionController = selectionController
-            ),
-            EmptyMeasurePolicy
-        )
-    } else {
-        // do the inline content allocs
-        val (placeholders, inlineComposables) = text.resolveInlineContent(inlineContent)
-        val measuredPlaceholderPositions = remember {
-            mutableStateOf<List<Rect?>?>(null)
-        }
-        Layout(
-            content = { InlineChildren(text, inlineComposables) },
-            modifier = modifier.textModifier(
-                text = text,
-                style = style,
-                onTextLayout = onTextLayout,
-                overflow = overflow,
-                softWrap = softWrap,
-                maxLines = maxLines,
-                minLines = minLines,
-                fontFamilyResolver = LocalFontFamilyResolver.current,
-                placeholders = placeholders,
-                onPlaceholderLayout = { measuredPlaceholderPositions.value = it },
-                selectionController = selectionController
-            ),
-            measurePolicy = TextMeasurePolicy { measuredPlaceholderPositions.value }
-        )
-    }
-}
-
-private object EmptyMeasurePolicy : MeasurePolicy {
-    private val placementBlock: Placeable.PlacementScope.() -> Unit = {}
-    override fun MeasureScope.measure(
-        measurables: List<Measurable>,
-        constraints: Constraints
-    ): MeasureResult {
-        return layout(constraints.maxWidth, constraints.maxHeight, placementBlock = placementBlock)
-    }
-}
-
-private class TextMeasurePolicy(
-    private val placements: () -> List<Rect?>?
-) : MeasurePolicy {
-    override fun MeasureScope.measure(
-        measurables: List<Measurable>,
-        constraints: Constraints
-    ): MeasureResult {
-        val toPlace = placements()?.fastMapIndexedNotNull { index, rect ->
-            // PlaceholderRect will be null if it's ellipsized. In that case, the corresponding
-            // inline children won't be measured or placed.
-            rect?.let {
-                Pair(
-                    measurables[index].measure(
-                        Constraints(
-                            maxWidth = floor(it.width).toInt(),
-                            maxHeight = floor(it.height).toInt()
-                        )
-                    ),
-                    IntOffset(it.left.roundToInt(), it.top.roundToInt())
-                )
-            }
-        }
-        return layout(
-            constraints.maxWidth,
-            constraints.maxHeight,
-        ) {
-            toPlace?.fastForEach { (placeable, position) ->
-                placeable.place(position)
-            }
-        }
-    }
-}
-
-@OptIn(ExperimentalComposeUiApi::class)
-private fun Modifier.textModifier(
-    text: AnnotatedString,
-    style: TextStyle,
-    onTextLayout: ((TextLayoutResult) -> Unit)?,
-    overflow: TextOverflow,
-    softWrap: Boolean,
-    maxLines: Int,
-    minLines: Int,
-    fontFamilyResolver: FontFamily.Resolver,
-    placeholders: List<AnnotatedString.Range<Placeholder>>?,
-    onPlaceholderLayout: ((List<Rect?>) -> Unit)?,
-    selectionController: SelectionController?
-): Modifier {
-    if (selectionController == null) {
-        val staticTextModifier = TextAnnotatedStringElement(
-            text,
-            style,
-            fontFamilyResolver,
-            onTextLayout,
-            overflow,
-            softWrap,
-            maxLines,
-            minLines,
-            placeholders,
-            onPlaceholderLayout,
-            null
-        )
-        return this then Modifier /* selection position */ then staticTextModifier
-    } else {
-        val selectableTextModifier = SelectableTextAnnotatedStringElement(
-            text,
-            style,
-            fontFamilyResolver,
-            onTextLayout,
-            overflow,
-            softWrap,
-            maxLines,
-            minLines,
-            placeholders,
-            onPlaceholderLayout,
-            selectionController
-        )
-        return this then selectionController.modifier then selectableTextModifier
-    }
-}
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/ContextMenu.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/ContextMenu.kt
deleted file mode 100644
index b42ab4e..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/ContextMenu.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.foundation.newtext.text.copypasta
-
-import androidx.compose.foundation.newtext.text.copypasta.selection.SelectionManager
-import androidx.compose.runtime.Composable
-
-@Composable
-internal expect fun ContextMenuArea(
-    manager: SelectionManager,
-    content: @Composable () -> Unit
-)
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/Expect.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/Expect.kt
deleted file mode 100644
index ea9d587..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/Expect.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-// ktlint-disable filename
-
-/*
- * 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.newtext.text.copypasta
-
-expect class AtomicLong(value: Long) {
-    fun get(): Long
-    fun set(value: Long)
-    fun getAndIncrement(): Long
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/LongPressTextDragObserver.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/LongPressTextDragObserver.kt
deleted file mode 100644
index 934fa91..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/LongPressTextDragObserver.kt
+++ /dev/null
@@ -1,124 +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.newtext.text.copypasta
-
-import androidx.compose.foundation.gestures.awaitFirstDown
-import androidx.compose.foundation.gestures.detectDragGestures
-import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
-import androidx.compose.foundation.gestures.awaitEachGesture
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.input.pointer.PointerInputScope
-import androidx.compose.ui.util.fastAny
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.launch
-
-internal interface TextDragObserver {
-    /**
-     * Called as soon as a down event is received. If the pointer eventually moves while remaining
-     * down, a drag gesture may be started. After this method:
-     * - [onUp] will always be called eventually, once the pointer is released.
-     * - [onStart] _may_ be called, if there is a drag that exceeds touch slop.
-     *
-     * This method will not be called before [onStart] in the case when a down event happens that
-     * may not result in a drag, e.g. on the down before a long-press that starts a selection.
-     */
-    fun onDown(point: Offset)
-
-    /**
-     * Called after [onDown] if an up event is received without dragging.
-     */
-    fun onUp()
-
-    /**
-     * Called once a drag gesture has started, which means touch slop has been exceeded.
-     * [onDown] _may_ be called before this method if the down event could not have
-     * started a different gesture.
-     */
-    fun onStart(startPoint: Offset)
-
-    fun onDrag(delta: Offset)
-
-    fun onStop()
-
-    fun onCancel()
-}
-
-internal suspend fun PointerInputScope.detectDragGesturesAfterLongPressWithObserver(
-    observer: TextDragObserver
-) = detectDragGesturesAfterLongPress(
-    onDragEnd = { observer.onStop() },
-    onDrag = { _, offset ->
-        observer.onDrag(offset)
-    },
-    onDragStart = {
-        observer.onStart(it)
-    },
-    onDragCancel = { observer.onCancel() }
-)
-
-/**
- * Detects gesture events for a [TextDragObserver], including both initial down events and drag
- * events.
- */
-internal suspend fun PointerInputScope.detectDownAndDragGesturesWithObserver(
-    observer: TextDragObserver
-) {
-    coroutineScope {
-        launch {
-            detectPreDragGesturesWithObserver(observer)
-        }
-        launch {
-            detectDragGesturesWithObserver(observer)
-        }
-    }
-}
-
-/**
- * Detects initial down events and calls [TextDragObserver.onDown] and
- * [TextDragObserver.onUp].
- */
-private suspend fun PointerInputScope.detectPreDragGesturesWithObserver(
-    observer: TextDragObserver
-) {
-    awaitEachGesture {
-        val down = awaitFirstDown()
-        observer.onDown(down.position)
-        // Wait for that pointer to come up.
-        do {
-            val event = awaitPointerEvent()
-        } while (event.changes.fastAny { it.id == down.id && it.pressed })
-        observer.onUp()
-    }
-}
-
-/**
- * Detects drag gestures for a [TextDragObserver].
- */
-private suspend fun PointerInputScope.detectDragGesturesWithObserver(
-    observer: TextDragObserver
-) {
-    detectDragGestures(
-        onDragEnd = { observer.onStop() },
-        onDrag = { _, offset ->
-            observer.onDrag(offset)
-        },
-        onDragStart = {
-            observer.onStart(it)
-        },
-        onDragCancel = { observer.onCancel() }
-    )
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/StringHelpers.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/StringHelpers.kt
deleted file mode 100644
index a63f8bf..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/StringHelpers.kt
+++ /dev/null
@@ -1,57 +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.newtext.text.copypasta
-
-import androidx.compose.ui.text.TextRange
-
-/**
- * Returns the index of the character break preceding [index].
- */
-internal expect fun String.findPrecedingBreak(index: Int): Int
-
-/**
- * Returns the index of the character break following [index]. Returns -1 if there are no more
- * breaks before the end of the string.
- */
-internal expect fun String.findFollowingBreak(index: Int): Int
-
-internal fun CharSequence.findParagraphStart(startIndex: Int): Int {
-    for (index in startIndex - 1 downTo 1) {
-        if (this[index - 1] == '\n') {
-            return index
-        }
-    }
-    return 0
-}
-
-internal fun CharSequence.findParagraphEnd(startIndex: Int): Int {
-    for (index in startIndex + 1 until this.length) {
-        if (this[index] == '\n') {
-            return index
-        }
-    }
-    return this.length
-}
-
-/**
- * Returns the text range of the paragraph at the given character offset.
- *
- * Paragraphs are separated by Line Feed character (\n).
- */
-internal fun CharSequence.getParagraphBoundary(index: Int): TextRange {
-    return TextRange(findParagraphStart(index), findParagraphEnd(index))
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/TextLayoutResultProxy.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/TextLayoutResultProxy.kt
deleted file mode 100644
index 36d962b..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/TextLayoutResultProxy.kt
+++ /dev/null
@@ -1,125 +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.foundation.newtext.text.copypasta
-
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.text.TextLayoutResult
-
-internal class TextLayoutResultProxy(val value: TextLayoutResult) {
-    // TextLayoutResult methods
-    /**
-     * Translates the position of the touch on the screen to the position in text. Because touch
-     * is relative to the decoration box, we need to translate it to the inner text field's
-     * coordinates first before calculating position of the symbol in text.
-     *
-     * @param position original position of the gesture relative to the decoration box
-     * @param coerceInVisibleBounds if true and original [position] is outside visible bounds
-     * of the inner text field, the [position] will be shifted to the closest edge of the inner
-     * text field's visible bounds. This is useful when you have a decoration box
-     * bigger than the inner text field, so when user touches to the decoration box area, the cursor
-     * goes to the beginning or the end of the visible inner text field; otherwise if we put the
-     * cursor under the touch in the invisible part of the inner text field, it would scroll to
-     * make the cursor visible. This behavior is not needed, and therefore
-     * [coerceInVisibleBounds] should be set to false, when the user drags outside visible bounds
-     * to make a selection.
-     */
-    fun getOffsetForPosition(position: Offset, coerceInVisibleBounds: Boolean = true): Int {
-        val relativePosition = position
-            .let { if (coerceInVisibleBounds) it.coercedInVisibleBoundsOfInputText() else it }
-            .relativeToInputText()
-        return value.getOffsetForPosition(relativePosition)
-    }
-
-    fun getLineForVerticalPosition(vertical: Float): Int {
-        val relativeVertical = Offset(0f, vertical)
-            .coercedInVisibleBoundsOfInputText()
-            .relativeToInputText().y
-        return value.getLineForVerticalPosition(relativeVertical)
-    }
-
-    fun getLineEnd(lineIndex: Int, visibleEnd: Boolean = false): Int =
-        value.getLineEnd(lineIndex, visibleEnd)
-
-    /** Returns true if the screen coordinates position (x,y) corresponds to a character displayed
-     * in the view. Returns false when the position is in the empty space of left/right of text.
-     */
-    fun isPositionOnText(offset: Offset): Boolean {
-        val relativeOffset = offset.coercedInVisibleBoundsOfInputText().relativeToInputText()
-        val line = value.getLineForVerticalPosition(relativeOffset.y)
-        return relativeOffset.x >= value.getLineLeft(line) &&
-            relativeOffset.x <= value.getLineRight(line)
-    }
-
-    // Shift offset
-    /** Measured bounds of the decoration box and inner text field. Together used to
-     * calculate the relative touch offset. Because touches are applied on the decoration box, we
-     * need to translate it to the inner text field coordinates.
-     */
-    var innerTextFieldCoordinates: LayoutCoordinates? = null
-    var decorationBoxCoordinates: LayoutCoordinates? = null
-
-    /**
-     * Translates the click happened on the decoration box to the position in the inner text
-     * field coordinates. This relative position is then used to determine symbol position in
-     * text using TextLayoutResult object.
-     */
-    private fun Offset.relativeToInputText(): Offset {
-        // Translates touch to the inner text field coordinates
-        return innerTextFieldCoordinates?.let { innerTextFieldCoordinates ->
-            decorationBoxCoordinates?.let { decorationBoxCoordinates ->
-                if (innerTextFieldCoordinates.isAttached && decorationBoxCoordinates.isAttached) {
-                    innerTextFieldCoordinates.localPositionOf(decorationBoxCoordinates, this)
-                } else {
-                    this
-                }
-            }
-        } ?: this
-    }
-
-    /**
-     * If click on the decoration box happens outside visible inner text field, coerce the click
-     * position to the visible edges of the inner text field.
-     */
-    private fun Offset.coercedInVisibleBoundsOfInputText(): Offset {
-        // If offset is outside visible bounds of the inner text field, use visible bounds edges
-        val visibleInnerTextFieldRect =
-            innerTextFieldCoordinates?.let { innerTextFieldCoordinates ->
-                if (innerTextFieldCoordinates.isAttached) {
-                    decorationBoxCoordinates?.localBoundingBoxOf(innerTextFieldCoordinates)
-                } else {
-                    Rect.Zero
-                }
-            } ?: Rect.Zero
-        return this.coerceIn(visibleInnerTextFieldRect)
-    }
-}
-
-private fun Offset.coerceIn(rect: Rect): Offset {
-    val xOffset = when {
-        x < rect.left -> rect.left
-        x > rect.right -> rect.right
-        else -> x
-    }
-    val yOffset = when {
-        y < rect.top -> rect.top
-        y > rect.bottom -> rect.bottom
-        else -> y
-    }
-    return Offset(xOffset, yOffset)
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/TextPointerIcon.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/TextPointerIcon.kt
deleted file mode 100644
index 3d299bc..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/TextPointerIcon.kt
+++ /dev/null
@@ -1,21 +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.foundation.newtext.text.copypasta
-
-import androidx.compose.ui.input.pointer.PointerIcon
-
-internal expect val textPointerIcon: PointerIcon
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/TouchMode.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/TouchMode.kt
deleted file mode 100644
index 54ee1ec..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/TouchMode.kt
+++ /dev/null
@@ -1,23 +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.newtext.text.copypasta
-
-/**
- * This is a temporary workaround and should be removed after proper mouse handling is settled
- * (b/171402426).
- */
-internal val isInTouchMode: Boolean = true
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/MultiWidgetSelectionDelegate.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/MultiWidgetSelectionDelegate.kt
deleted file mode 100644
index b234d34..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/MultiWidgetSelectionDelegate.kt
+++ /dev/null
@@ -1,255 +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.foundation.newtext.text.copypasta.selection
-
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.TextLayoutResult
-import androidx.compose.ui.text.TextRange
-import kotlin.math.max
-
-internal class MultiWidgetSelectionDelegate(
-    override val selectableId: Long,
-    private val coordinatesCallback: () -> LayoutCoordinates?,
-    private val layoutResultCallback: () -> TextLayoutResult?
-) : Selectable {
-
-    override fun updateSelection(
-        startHandlePosition: Offset,
-        endHandlePosition: Offset,
-        previousHandlePosition: Offset?,
-        isStartHandle: Boolean,
-        containerLayoutCoordinates: LayoutCoordinates,
-        adjustment: SelectionAdjustment,
-        previousSelection: Selection?
-    ): Pair<Selection?, Boolean> {
-        require(
-            previousSelection == null || (
-                selectableId == previousSelection.start.selectableId &&
-                    selectableId == previousSelection.end.selectableId
-                )
-        ) {
-            "The given previousSelection doesn't belong to this selectable."
-        }
-        val layoutCoordinates = getLayoutCoordinates() ?: return Pair(null, false)
-        val textLayoutResult = layoutResultCallback() ?: return Pair(null, false)
-
-        val relativePosition = containerLayoutCoordinates.localPositionOf(
-            layoutCoordinates, Offset.Zero
-        )
-        val localStartPosition = startHandlePosition - relativePosition
-        val localEndPosition = endHandlePosition - relativePosition
-        val localPreviousHandlePosition = previousHandlePosition?.let { it - relativePosition }
-
-        return getTextSelectionInfo(
-            textLayoutResult = textLayoutResult,
-            startHandlePosition = localStartPosition,
-            endHandlePosition = localEndPosition,
-            previousHandlePosition = localPreviousHandlePosition,
-            selectableId = selectableId,
-            adjustment = adjustment,
-            previousSelection = previousSelection,
-            isStartHandle = isStartHandle
-        )
-    }
-
-    override fun getSelectAllSelection(): Selection? {
-        val textLayoutResult = layoutResultCallback() ?: return null
-        val newSelectionRange = TextRange(0, textLayoutResult.layoutInput.text.length)
-
-        return getAssembledSelectionInfo(
-            newSelectionRange = newSelectionRange,
-            handlesCrossed = false,
-            selectableId = selectableId,
-            textLayoutResult = textLayoutResult
-        )
-    }
-
-    override fun getHandlePosition(selection: Selection, isStartHandle: Boolean): Offset {
-        // Check if the selection handle's selectable is the current selectable.
-        if (isStartHandle && selection.start.selectableId != this.selectableId ||
-            !isStartHandle && selection.end.selectableId != this.selectableId
-        ) {
-            return Offset.Zero
-        }
-
-        if (getLayoutCoordinates() == null) return Offset.Zero
-
-        val textLayoutResult = layoutResultCallback() ?: return Offset.Zero
-        return getSelectionHandleCoordinates(
-            textLayoutResult = textLayoutResult,
-            offset = if (isStartHandle) selection.start.offset else selection.end.offset,
-            isStart = isStartHandle,
-            areHandlesCrossed = selection.handlesCrossed
-        )
-    }
-
-    override fun getLayoutCoordinates(): LayoutCoordinates? {
-        val layoutCoordinates = coordinatesCallback()
-        if (layoutCoordinates == null || !layoutCoordinates.isAttached) return null
-        return layoutCoordinates
-    }
-
-    override fun getText(): AnnotatedString {
-        val textLayoutResult = layoutResultCallback() ?: return AnnotatedString("")
-        return textLayoutResult.layoutInput.text
-    }
-
-    override fun getBoundingBox(offset: Int): Rect {
-        val textLayoutResult = layoutResultCallback() ?: return Rect.Zero
-        val textLength = textLayoutResult.layoutInput.text.length
-        if (textLength < 1) return Rect.Zero
-        return textLayoutResult.getBoundingBox(
-            offset.coerceIn(0, textLength - 1)
-        )
-    }
-
-    override fun getRangeOfLineContaining(offset: Int): TextRange {
-        val textLayoutResult = layoutResultCallback() ?: return TextRange.Zero
-        val textLength = textLayoutResult.layoutInput.text.length
-        if (textLength < 1) return TextRange.Zero
-        val line = textLayoutResult.getLineForOffset(offset.coerceIn(0, textLength - 1))
-        return TextRange(
-            start = textLayoutResult.getLineStart(line),
-            end = textLayoutResult.getLineEnd(line, visibleEnd = true)
-        )
-    }
-}
-
-/**
- * Return information about the current selection in the Text.
- *
- * @param textLayoutResult a result of the text layout.
- * @param startHandlePosition The new positions of the moving selection handle.
- * @param previousHandlePosition The old position of the moving selection handle since the last update.
- * @param endHandlePosition the position of the selection handle that is not moving.
- * @param selectableId the id of this [Selectable].
- * @param adjustment the [SelectionAdjustment] used to process the raw selection range.
- * @param previousSelection the previous text selection.
- * @param isStartHandle whether the moving selection is the start selection handle.
- *
- * @return a pair consistent of updated [Selection] and a boolean representing whether the
- * movement is consumed.
- */
-internal fun getTextSelectionInfo(
-    textLayoutResult: TextLayoutResult,
-    startHandlePosition: Offset,
-    endHandlePosition: Offset,
-    previousHandlePosition: Offset?,
-    selectableId: Long,
-    adjustment: SelectionAdjustment,
-    previousSelection: Selection? = null,
-    isStartHandle: Boolean = true
-): Pair<Selection?, Boolean> {
-
-    val bounds = Rect(
-        0.0f,
-        0.0f,
-        textLayoutResult.size.width.toFloat(),
-        textLayoutResult.size.height.toFloat()
-    )
-
-    val isSelected =
-        SelectionMode.Vertical.isSelected(bounds, startHandlePosition, endHandlePosition)
-
-    if (!isSelected) {
-        return Pair(null, false)
-    }
-
-    val rawStartHandleOffset = getOffsetForPosition(textLayoutResult, bounds, startHandlePosition)
-    val rawEndHandleOffset = getOffsetForPosition(textLayoutResult, bounds, endHandlePosition)
-    val rawPreviousHandleOffset = previousHandlePosition?.let {
-        getOffsetForPosition(textLayoutResult, bounds, it)
-    } ?: -1
-
-    val adjustedTextRange = adjustment.adjust(
-        textLayoutResult = textLayoutResult,
-        newRawSelectionRange = TextRange(rawStartHandleOffset, rawEndHandleOffset),
-        previousHandleOffset = rawPreviousHandleOffset,
-        isStartHandle = isStartHandle,
-        previousSelectionRange = previousSelection?.toTextRange()
-    )
-    val newSelection = getAssembledSelectionInfo(
-        newSelectionRange = adjustedTextRange,
-        handlesCrossed = adjustedTextRange.reversed,
-        selectableId = selectableId,
-        textLayoutResult = textLayoutResult
-    )
-
-    // Determine whether the movement is consumed by this Selectable.
-    // If the selection has  changed, the movement is consumed.
-    // And there are also cases where the selection stays the same but selection handle raw
-    // offset has changed.(Usually this happen because of adjustment like SelectionAdjustment.Word)
-    // In this case we also consider the movement being consumed.
-    val selectionUpdated = newSelection != previousSelection
-    val handleUpdated = if (isStartHandle) {
-        rawStartHandleOffset != rawPreviousHandleOffset
-    } else {
-        rawEndHandleOffset != rawPreviousHandleOffset
-    }
-    val consumed = handleUpdated || selectionUpdated
-    return Pair(newSelection, consumed)
-}
-
-internal fun getOffsetForPosition(
-    textLayoutResult: TextLayoutResult,
-    bounds: Rect,
-    position: Offset
-): Int {
-    val length = textLayoutResult.layoutInput.text.length
-    return if (bounds.contains(position)) {
-        textLayoutResult.getOffsetForPosition(position).coerceIn(0, length)
-    } else {
-        val value = SelectionMode.Vertical.compare(position, bounds)
-        if (value < 0) 0 else length
-    }
-}
-
-/**
- * [Selection] contains a lot of parameters. It looks more clean to assemble an object of this
- * class in a separate method.
- *
- * @param newSelectionRange the final new selection text range.
- * @param handlesCrossed true if the selection handles are crossed
- * @param selectableId the id of the current [Selectable] for which the [Selection] is being
- * calculated
- * @param textLayoutResult a result of the text layout.
- *
- * @return an assembled object of [Selection] using the offered selection info.
- */
-private fun getAssembledSelectionInfo(
-    newSelectionRange: TextRange,
-    handlesCrossed: Boolean,
-    selectableId: Long,
-    textLayoutResult: TextLayoutResult
-): Selection {
-    return Selection(
-        start = Selection.AnchorInfo(
-            direction = textLayoutResult.getBidiRunDirection(newSelectionRange.start),
-            offset = newSelectionRange.start,
-            selectableId = selectableId
-        ),
-        end = Selection.AnchorInfo(
-            direction = textLayoutResult.getBidiRunDirection(max(newSelectionRange.end - 1, 0)),
-            offset = newSelectionRange.end,
-            selectableId = selectableId
-        ),
-        handlesCrossed = handlesCrossed
-    )
-}
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/Selectable.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/Selectable.kt
deleted file mode 100644
index c921a2d..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/Selectable.kt
+++ /dev/null
@@ -1,122 +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.foundation.newtext.text.copypasta.selection
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.TextRange
-
-/**
- * Provides [Selection] information for a composable to SelectionContainer. Composables who can
- * be selected should subscribe to [SelectionRegistrar] using this interface.
- */
-
-internal interface Selectable {
-    /**
-     * An ID used by [SelectionRegistrar] to identify this [Selectable]. This value should not be
-     * [SelectionRegistrar.InvalidSelectableId].
-     * When a [Selectable] is created, it can request an ID from [SelectionRegistrar] by
-     * calling [SelectionRegistrar.nextSelectableId].
-     * @see SelectionRegistrar.nextSelectableId
-     */
-    val selectableId: Long
-
-    /**
-     * Updates the [Selection] information after a selection handle being moved. This method is
-     * expected to be called consecutively during the selection handle position update.
-     *
-     * @param startHandlePosition graphical position of the start selection handle
-     * @param endHandlePosition graphical position of the end selection handle
-     * @param previousHandlePosition the previous position of the moving selection handle
-     * @param containerLayoutCoordinates [LayoutCoordinates] of the composable
-     * @param adjustment [Selection] range is adjusted according to this param
-     * @param previousSelection previous selection result on this [Selectable]
-     * @param isStartHandle whether the moving selection handle is the start selection handle
-     *
-     * @throws IllegalStateException when the given [previousSelection] doesn't belong to this
-     * selectable. In other words, one of the [Selection.AnchorInfo] in the given
-     * [previousSelection] has a selectableId that doesn't match to the [selectableId] of this
-     * selectable.
-     * @return a pair consisting of the updated [Selection] and a boolean value representing
-     * whether the movement is consumed.
-     */
-    fun updateSelection(
-        startHandlePosition: Offset,
-        endHandlePosition: Offset,
-        previousHandlePosition: Offset?,
-        isStartHandle: Boolean = true,
-        containerLayoutCoordinates: LayoutCoordinates,
-        adjustment: SelectionAdjustment,
-        previousSelection: Selection? = null
-    ): Pair<Selection?, Boolean>
-
-    /**
-     * Returns selectAll [Selection] information for a selectable composable. If no selection can be
-     * provided null should be returned.
-     *
-     * @return selectAll [Selection] information for a selectable composable. If no selection can be
-     * provided null should be returned.
-     */
-    fun getSelectAllSelection(): Selection?
-
-    /**
-     * Return the [Offset] of a [SelectionHandle].
-     *
-     * @param selection [Selection] contains the [SelectionHandle]
-     * @param isStartHandle true if it's the start handle, false if it's the end handle.
-     *
-     * @return [Offset] of this handle, based on which the [SelectionHandle] will be drawn.
-     */
-    fun getHandlePosition(selection: Selection, isStartHandle: Boolean): Offset
-
-    /**
-     * Return the [LayoutCoordinates] of the [Selectable].
-     *
-     * @return [LayoutCoordinates] of the [Selectable]. This could be null if called before
-     * composing.
-     */
-    fun getLayoutCoordinates(): LayoutCoordinates?
-
-    /**
-     * Return the [AnnotatedString] of the [Selectable].
-     *
-     * @return text content as [AnnotatedString] of the [Selectable].
-     */
-    fun getText(): AnnotatedString
-
-    /**
-     * Return the bounding box of the character for given character offset. This is currently for
-     * text.
-     * In future when we implemented other selectable Composables, we can return the bounding box of
-     * the wanted rectangle. For example, for an image selectable, this should return the
-     * bounding box of the image.
-     *
-     * @param offset a character offset
-     * @return the bounding box for the character in [Rect], or [Rect.Zero] if the selectable is
-     * empty.
-     */
-    fun getBoundingBox(offset: Int): Rect
-
-    /**
-     * Return the offsets of the start and end of the line containing [offset], or [TextRange.Zero]
-     * if the selectable is empty. These offsets are in the same "coordinate space" as
-     * [getBoundingBox], and despite being returned in a [TextRange], may not refer to offsets in
-     * actual text if the selectable contains other types of content.
-     */
-    fun getRangeOfLineContaining(offset: Int): TextRange
-}
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/Selection.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/Selection.kt
deleted file mode 100644
index 5cedac2..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/Selection.kt
+++ /dev/null
@@ -1,89 +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.foundation.newtext.text.copypasta.selection
-
-import androidx.compose.runtime.Immutable
-import androidx.compose.ui.text.TextRange
-import androidx.compose.ui.text.style.ResolvedTextDirection
-
-/**
- * Information about the current Selection.
- */
-@Immutable
-internal data class Selection(
-    /**
-     * Information about the start of the selection.
-     */
-    val start: AnchorInfo,
-
-    /**
-     * Information about the end of the selection.
-     */
-    val end: AnchorInfo,
-    /**
-     * The flag to show that the selection handles are dragged across each other. After selection
-     * is initialized, if user drags one handle to cross the other handle, this is true, otherwise
-     * it's false.
-     */
-    // If selection happens in single widget, checking [TextRange.start] > [TextRange.end] is
-    // enough.
-    // But when selection happens across multiple widgets, this value needs more complicated
-    // calculation. To avoid repeated calculation, making it as a flag is cheaper.
-    val handlesCrossed: Boolean = false
-) {
-    /**
-     * Contains information about an anchor (start/end) of selection.
-     */
-    @Immutable
-    internal data class AnchorInfo(
-        /**
-         * Text direction of the character in selection edge.
-         */
-        val direction: ResolvedTextDirection,
-
-        /**
-         * Character offset for the selection edge. This offset is within individual child text
-         * composable.
-         */
-        val offset: Int,
-
-        /**
-         * The id of the [Selectable] which contains this [Selection] Anchor.
-         */
-        val selectableId: Long
-    )
-
-    fun merge(other: Selection?): Selection {
-        if (other == null) return this
-
-        var selection = this
-        selection = if (handlesCrossed) {
-            selection.copy(start = other.start)
-        } else {
-            selection.copy(end = other.end)
-        }
-
-        return selection
-    }
-
-    /**
-     * Returns the selection offset information as a [TextRange]
-     */
-    fun toTextRange(): TextRange {
-        return TextRange(start.offset, end.offset)
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionAdjustment.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionAdjustment.kt
deleted file mode 100644
index c53cf0a..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionAdjustment.kt
+++ /dev/null
@@ -1,466 +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.foundation.newtext.text.copypasta.selection
-
-import androidx.compose.foundation.newtext.text.copypasta.getParagraphBoundary
-import androidx.compose.ui.text.TextLayoutResult
-import androidx.compose.ui.text.TextRange
-
-/**
- * Selection can be adjusted depends on context. For example, in touch mode dragging after a long
- * press adjusts selection by word. But selection by dragging handles is character precise
- * without adjustments. With a mouse, double-click selects by words and triple-clicks by paragraph.
- * @see [SelectionRegistrar.notifySelectionUpdate]
- */
-internal interface SelectionAdjustment {
-
-    /**
-     * The callback function that is called once a new selection arrives, the return value of
-     * this function will be the final selection range on the corresponding [Selectable].
-     *
-     * @param textLayoutResult the [TextLayoutResult] of the involved [Selectable].
-     * @param newRawSelectionRange the new selection range computed from the selection handle
-     * position on screen.
-     * @param previousHandleOffset the previous offset of the moving handle. When isStartHandle is
-     * true, it's the previous offset of the start handle before the movement, and vice versa.
-     * When there isn't a valid previousHandleOffset, previousHandleOffset should be -1.
-     * @param isStartHandle whether the moving handle is the start handle.
-     * @param previousSelectionRange the previous selection range, or the selection range to be
-     * updated.
-     */
-    fun adjust(
-        textLayoutResult: TextLayoutResult,
-        newRawSelectionRange: TextRange,
-        previousHandleOffset: Int,
-        isStartHandle: Boolean,
-        previousSelectionRange: TextRange?
-    ): TextRange
-
-    companion object {
-        /**
-         * The selection adjustment that does nothing and directly return the input raw
-         * selection range.
-         */
-        val None = object : SelectionAdjustment {
-            override fun adjust(
-                textLayoutResult: TextLayoutResult,
-                newRawSelectionRange: TextRange,
-                previousHandleOffset: Int,
-                isStartHandle: Boolean,
-                previousSelectionRange: TextRange?
-            ): TextRange = newRawSelectionRange
-        }
-
-        /**
-         * The character based selection. It normally won't change the raw selection range except
-         * when the input raw selection range is collapsed. In this case, it will always make
-         * sure at least one character is selected.
-         * When the given raw selection range is collapsed:
-         * a) it will always try to adjust the changing selection boundary(base on the value of
-         * isStartHandle) and makes sure the other boundary remains the same after the adjustment
-         * b) if the previous selection range is reversed, it will try to make the adjusted
-         * selection range reversed as well, and vice versa.
-         */
-        val Character = object : SelectionAdjustment {
-            override fun adjust(
-                textLayoutResult: TextLayoutResult,
-                newRawSelectionRange: TextRange,
-                previousHandleOffset: Int,
-                isStartHandle: Boolean,
-                previousSelectionRange: TextRange?
-            ): TextRange {
-                return if (newRawSelectionRange.collapsed) {
-                    // If there isn't any selection before, we assume handles are not crossed.
-                    val previousHandlesCrossed = previousSelectionRange?.reversed ?: false
-                    ensureAtLeastOneChar(
-                        offset = newRawSelectionRange.start,
-                        lastOffset = textLayoutResult.layoutInput.text.lastIndex,
-                        isStartHandle = isStartHandle,
-                        previousHandlesCrossed = previousHandlesCrossed
-                    )
-                } else {
-                    newRawSelectionRange
-                }
-            }
-        }
-
-        /**
-         * The word based selection adjustment. It will adjust the raw input selection such that
-         * the selection boundary snap to the word boundary. It will always expand the raw input
-         * selection range to the closest word boundary. If the raw selection is reversed, it
-         * will always return a reversed selection, and vice versa.
-         */
-        val Word = object : SelectionAdjustment {
-            override fun adjust(
-                textLayoutResult: TextLayoutResult,
-                newRawSelectionRange: TextRange,
-                previousHandleOffset: Int,
-                isStartHandle: Boolean,
-                previousSelectionRange: TextRange?
-            ): TextRange {
-                return adjustByBoundary(
-                    textLayoutResult = textLayoutResult,
-                    newRawSelection = newRawSelectionRange,
-                    boundaryFun = textLayoutResult::getWordBoundary
-                )
-            }
-        }
-
-        /**
-         * The paragraph based selection adjustment. It will adjust the raw input selection such
-         * that the selection boundary snap to the paragraph boundary. It will always expand the
-         * raw input selection range to the closest paragraph boundary. If the raw selection is
-         * reversed, it will always return a reversed selection, and vice versa.
-         */
-        val Paragraph = object : SelectionAdjustment {
-            override fun adjust(
-                textLayoutResult: TextLayoutResult,
-                newRawSelectionRange: TextRange,
-                previousHandleOffset: Int,
-                isStartHandle: Boolean,
-                previousSelectionRange: TextRange?
-            ): TextRange {
-                val boundaryFun = textLayoutResult.layoutInput.text::getParagraphBoundary
-                return adjustByBoundary(
-                    textLayoutResult = textLayoutResult,
-                    newRawSelection = newRawSelectionRange,
-                    boundaryFun = boundaryFun
-                )
-            }
-        }
-
-        private fun adjustByBoundary(
-            textLayoutResult: TextLayoutResult,
-            newRawSelection: TextRange,
-            boundaryFun: (Int) -> TextRange
-        ): TextRange {
-            if (textLayoutResult.layoutInput.text.isEmpty()) {
-                return TextRange.Zero
-            }
-            val maxOffset = textLayoutResult.layoutInput.text.lastIndex
-            val startBoundary = boundaryFun(newRawSelection.start.coerceIn(0, maxOffset))
-            val endBoundary = boundaryFun(newRawSelection.end.coerceIn(0, maxOffset))
-
-            // If handles are not crossed, start should be snapped to the start of the word
-            // containing the start offset, and end should be snapped to the end of the word
-            // containing the end offset. If handles are crossed, start should be snapped to the
-            // end of the word containing the start offset, and end should be snapped to the start
-            // of the word containing the end offset.
-            val start = if (newRawSelection.reversed) startBoundary.end else startBoundary.start
-            val end = if (newRawSelection.reversed) endBoundary.start else endBoundary.end
-            return TextRange(start, end)
-        }
-
-        /**
-         * A special version of character based selection that accelerates the selection update
-         * with word based selection. In short, it expands by word and shrinks by character.
-         * Here is more details of the behavior:
-         * 1. When previous selection is null, it will use word based selection.
-         * 2. When the start/end offset has moved to a different line, it will use word
-         * based selection.
-         * 3. When the selection is shrinking, it behave same as the character based selection.
-         * Shrinking means that the start/end offset is moving in the direction that makes
-         * selected text shorter.
-         * 4. The selection boundary is expanding,
-         *  a.if the previous start/end offset is not a word boundary, use character based
-         * selection.
-         *  b.if the previous start/end offset is a word boundary, use word based selection.
-         *
-         *  Notice that this selection adjustment assumes that when isStartHandle is true, only
-         *  start handle is moving(or unchanged), and vice versa.
-         */
-        val CharacterWithWordAccelerate = object : SelectionAdjustment {
-            override fun adjust(
-                textLayoutResult: TextLayoutResult,
-                newRawSelectionRange: TextRange,
-                previousHandleOffset: Int,
-                isStartHandle: Boolean,
-                previousSelectionRange: TextRange?
-            ): TextRange {
-                // Previous selection is null. We start a word based selection.
-                if (previousSelectionRange == null) {
-                    return Word.adjust(
-                        textLayoutResult = textLayoutResult,
-                        newRawSelectionRange = newRawSelectionRange,
-                        previousHandleOffset = previousHandleOffset,
-                        isStartHandle = isStartHandle,
-                        previousSelectionRange = previousSelectionRange
-                    )
-                }
-
-                // The new selection is collapsed, ensure at least one char is selected.
-                if (newRawSelectionRange.collapsed) {
-                    return ensureAtLeastOneChar(
-                        offset = newRawSelectionRange.start,
-                        lastOffset = textLayoutResult.layoutInput.text.lastIndex,
-                        isStartHandle = isStartHandle,
-                        previousHandlesCrossed = previousSelectionRange.reversed
-                    )
-                }
-
-                val start: Int
-                val end: Int
-                if (isStartHandle) {
-                    start = updateSelectionBoundary(
-                        textLayoutResult = textLayoutResult,
-                        newRawOffset = newRawSelectionRange.start,
-                        previousRawOffset = previousHandleOffset,
-                        previousAdjustedOffset = previousSelectionRange.start,
-                        otherBoundaryOffset = newRawSelectionRange.end,
-                        isStart = true,
-                        isReversed = newRawSelectionRange.reversed
-                    )
-                    end = newRawSelectionRange.end
-                } else {
-                    start = newRawSelectionRange.start
-                    end = updateSelectionBoundary(
-                        textLayoutResult = textLayoutResult,
-                        newRawOffset = newRawSelectionRange.end,
-                        previousRawOffset = previousHandleOffset,
-                        previousAdjustedOffset = previousSelectionRange.end,
-                        otherBoundaryOffset = newRawSelectionRange.start,
-                        isStart = false,
-                        isReversed = newRawSelectionRange.reversed
-                    )
-                }
-                return TextRange(start, end)
-            }
-
-            /**
-             * Helper function that updates start or end boundary of the selection. It implements
-             * the "expand by word and shrink by character behavior".
-             *
-             * @param textLayoutResult the text layout result
-             * @param newRawOffset the new raw offset of the selection boundary after the movement.
-             * @param previousRawOffset the raw offset of the updated selection boundary before the
-             * movement. In the case where previousRawOffset invalid(when selection update is
-             * triggered by long-press or click) pass -1 for this parameter.
-             * @param previousAdjustedOffset the previous final/adjusted offset. It's the current
-             * @param otherBoundaryOffset the offset of the other selection boundary. It is used
-             * to avoid empty selection in word based selection mode.
-             * selection boundary.
-             * @param isStart whether it's updating the selection start or end boundary.
-             * @param isReversed whether the selection is reversed or not. We use
-             * this information to determine if the selection is expanding or shrinking.
-             */
-            private fun updateSelectionBoundary(
-                textLayoutResult: TextLayoutResult,
-                newRawOffset: Int,
-                previousRawOffset: Int,
-                previousAdjustedOffset: Int,
-                otherBoundaryOffset: Int,
-                isStart: Boolean,
-                isReversed: Boolean
-            ): Int {
-                // The raw offset didn't change, directly return the previous adjusted start offset.
-                if (newRawOffset == previousRawOffset) {
-                    return previousAdjustedOffset
-                }
-
-                val currentLine = textLayoutResult.getLineForOffset(newRawOffset)
-                val previousLine = textLayoutResult.getLineForOffset(previousAdjustedOffset)
-
-                // The updating selection boundary has crossed a line, use word based selection.
-                if (currentLine != previousLine) {
-                    return snapToWordBoundary(
-                        textLayoutResult = textLayoutResult,
-                        newRawOffset = newRawOffset,
-                        currentLine = currentLine,
-                        otherBoundaryOffset = otherBoundaryOffset,
-                        isStart = isStart,
-                        isReversed = isReversed
-                    )
-                }
-
-                // Check if the start or end selection boundary is expanding. If it's shrinking,
-                // use character based selection.
-                val isExpanding =
-                    isExpanding(newRawOffset, previousRawOffset, isStart, isReversed)
-                if (!isExpanding) {
-                    return newRawOffset
-                }
-
-                // If the previous start/end offset is not at a word boundary, which is indicating
-                // that start/end offset is updating within a word. In this case, it still uses
-                // character based selection.
-                if (!textLayoutResult.isAtWordBoundary(previousAdjustedOffset)) {
-                    return newRawOffset
-                }
-
-                // At this point we know, the updating start/end offset is still in the same line,
-                // it's expanding the selection, and it's not updating within a word. It should
-                // use word based selection.
-                return snapToWordBoundary(
-                    textLayoutResult = textLayoutResult,
-                    newRawOffset = newRawOffset,
-                    currentLine = currentLine,
-                    otherBoundaryOffset = otherBoundaryOffset,
-                    isStart = isStart,
-                    isReversed = isReversed
-                )
-            }
-
-            private fun snapToWordBoundary(
-                textLayoutResult: TextLayoutResult,
-                newRawOffset: Int,
-                currentLine: Int,
-                otherBoundaryOffset: Int,
-                isStart: Boolean,
-                isReversed: Boolean
-            ): Int {
-                val wordBoundary = textLayoutResult.getWordBoundary(newRawOffset)
-
-                // In the case where the target word crosses multiple lines due to hyphenation or
-                // being too long, we use the line start/end to keep the adjusted offset at the
-                // same line.
-                val wordStartLine = textLayoutResult.getLineForOffset(wordBoundary.start)
-                val start = if (wordStartLine == currentLine) {
-                    wordBoundary.start
-                } else {
-                    textLayoutResult.getLineStart(currentLine)
-                }
-
-                val wordEndLine = textLayoutResult.getLineForOffset(wordBoundary.end)
-                val end = if (wordEndLine == currentLine) {
-                    wordBoundary.end
-                } else {
-                    textLayoutResult.getLineEnd(currentLine)
-                }
-
-                // If one of the word boundary is exactly same as the otherBoundaryOffset, we
-                // can't snap to this word boundary since it will result in an empty selection
-                // range.
-                if (start == otherBoundaryOffset) {
-                    return end
-                }
-                if (end == otherBoundaryOffset) {
-                    return start
-                }
-
-                val threshold = (start + end) / 2
-                return if (isStart xor isReversed) {
-                    // In this branch when:
-                    // 1. selection is updating the start offset, and selection is not reversed.
-                    // 2. selection is updating the end offset, and selection is reversed.
-                    if (newRawOffset <= threshold) {
-                        start
-                    } else {
-                        end
-                    }
-                } else {
-                    // In this branch when:
-                    // 1. selection is updating the end offset, and selection is not reversed.
-                    // 2. selection is updating the start offset, and selection is reversed.
-                    if (newRawOffset >= threshold) {
-                        end
-                    } else {
-                        start
-                    }
-                }
-            }
-
-            private fun TextLayoutResult.isAtWordBoundary(offset: Int): Boolean {
-                val wordBoundary = getWordBoundary(offset)
-                return offset == wordBoundary.start || offset == wordBoundary.end
-            }
-
-            private fun isExpanding(
-                newRawOffset: Int,
-                previousRawOffset: Int,
-                isStart: Boolean,
-                previousReversed: Boolean
-            ): Boolean {
-                // -1 is considered as no previous offset, so the selection is expanding.
-                if (previousRawOffset == -1) {
-                    return true
-                }
-                if (newRawOffset == previousRawOffset) {
-                    return false
-                }
-                return if (isStart xor previousReversed) {
-                    newRawOffset < previousRawOffset
-                } else {
-                    newRawOffset > previousRawOffset
-                }
-            }
-        }
-    }
-}
-
-/**
- * This method adjusts the raw start and end offset and bounds the selection to one character. The
- * logic of bounding evaluates the last selection result, which handle is being dragged, and if
- * selection reaches the boundary.
- *
- * @param offset unprocessed start and end offset calculated directly from input position, in
- * this case start and offset equals to each other.
- * @param lastOffset last offset of the text. It's actually the length of the text.
- * @param isStartHandle true if the start handle is being dragged
- * @param previousHandlesCrossed true if the selection handles are crossed in the previous
- * selection. This function will try to maintain the handle cross state. This can help make
- * selection stable.
- *
- * @return the adjusted [TextRange].
- */
-internal fun ensureAtLeastOneChar(
-    offset: Int,
-    lastOffset: Int,
-    isStartHandle: Boolean,
-    previousHandlesCrossed: Boolean
-): TextRange {
-    // When lastOffset is 0, it can only return an empty TextRange.
-    // When previousSelection is null, it won't start a selection and return an empty TextRange.
-    if (lastOffset == 0) return TextRange(offset, offset)
-
-    // When offset is at the boundary, the handle that is not dragged should be at [offset]. Here
-    // the other handle's position is computed accordingly.
-    if (offset == 0) {
-        return if (isStartHandle) {
-            TextRange(1, 0)
-        } else {
-            TextRange(0, 1)
-        }
-    }
-
-    if (offset == lastOffset) {
-        return if (isStartHandle) {
-            TextRange(lastOffset - 1, lastOffset)
-        } else {
-            TextRange(lastOffset, lastOffset - 1)
-        }
-    }
-
-    // In other cases, this function will try to maintain the current cross handle states.
-    // Only in this way the selection can be stable.
-    return if (isStartHandle) {
-        if (!previousHandlesCrossed) {
-            // Handle is NOT crossed, and the start handle is dragged.
-            TextRange(offset - 1, offset)
-        } else {
-            // Handle is crossed, and the start handle is dragged.
-            TextRange(offset + 1, offset)
-        }
-    } else {
-        if (!previousHandlesCrossed) {
-            // Handle is NOT crossed, and the end handle is dragged.
-            TextRange(offset, offset + 1)
-        } else {
-            // Handle is crossed, and the end handle is dragged.
-            TextRange(offset, offset - 1)
-        }
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionContainer.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionContainer.kt
deleted file mode 100644
index 82d53aa..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionContainer.kt
+++ /dev/null
@@ -1,143 +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.foundation.newtext.text.copypasta.selection
-
-import androidx.compose.foundation.newtext.text.copypasta.ContextMenuArea
-import androidx.compose.foundation.newtext.text.copypasta.detectDownAndDragGesturesWithObserver
-import androidx.compose.foundation.newtext.text.copypasta.isInTouchMode
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.DisposableEffect
-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.input.pointer.pointerInput
-import androidx.compose.ui.platform.LocalClipboardManager
-import androidx.compose.ui.platform.LocalHapticFeedback
-import androidx.compose.ui.platform.LocalTextToolbar
-import androidx.compose.ui.util.fastForEach
-
-/**
- * Enables text selection for its direct or indirect children.
- *
- * @sample androidx.compose.foundation.samples.SelectionSample
- */
-@Composable
-fun SelectionContainer(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
-    var selection by remember { mutableStateOf<Selection?>(null) }
-    SelectionContainer(
-        modifier = modifier,
-        selection = selection,
-        onSelectionChange = {
-            selection = it
-        },
-        children = content
-    )
-}
-
-/**
- * Disables text selection for its direct or indirect children. To use this, simply add this
- * to wrap one or more text composables.
- *
- * @sample androidx.compose.foundation.samples.DisableSelectionSample
- */
-@Composable
-fun DisableSelection(content: @Composable () -> Unit) {
-    CompositionLocalProvider(
-        LocalSelectionRegistrar provides null,
-        content = content
-    )
-}
-
-/**
- * Selection Composable.
- *
- * The selection composable wraps composables and let them to be selectable. It paints the selection
- * area with start and end handles.
- */
-@Suppress("ComposableLambdaParameterNaming")
-@Composable
-internal fun SelectionContainer(
-    /** A [Modifier] for SelectionContainer. */
-    modifier: Modifier = Modifier,
-    /** Current Selection status.*/
-    selection: Selection?,
-    /** A function containing customized behaviour when selection changes. */
-    onSelectionChange: (Selection?) -> Unit,
-    children: @Composable () -> Unit
-) {
-    val registrarImpl = remember { SelectionRegistrarImpl() }
-    val manager = remember { SelectionManager(registrarImpl) }
-
-    manager.hapticFeedBack = LocalHapticFeedback.current
-    manager.clipboardManager = LocalClipboardManager.current
-    manager.textToolbar = LocalTextToolbar.current
-    manager.onSelectionChange = onSelectionChange
-    manager.selection = selection
-    manager.touchMode = isInTouchMode
-
-    ContextMenuArea(manager) {
-        CompositionLocalProvider(LocalSelectionRegistrar provides registrarImpl) {
-            // Get the layout coordinates of the selection container. This is for hit test of
-            // cross-composable selection.
-            SimpleLayout(modifier = modifier.then(manager.modifier)) {
-                children()
-                if (isInTouchMode && manager.hasFocus) {
-                    manager.selection?.let {
-                        listOf(true, false).fastForEach { isStartHandle ->
-                            val observer = remember(isStartHandle) {
-                                manager.handleDragObserver(isStartHandle)
-                            }
-                            val position = if (isStartHandle) {
-                                manager.startHandlePosition
-                            } else {
-                                manager.endHandlePosition
-                            }
-
-                            val direction = if (isStartHandle) {
-                                it.start.direction
-                            } else {
-                                it.end.direction
-                            }
-
-                            if (position != null) {
-                                SelectionHandle(
-                                    position = position,
-                                    isStartHandle = isStartHandle,
-                                    direction = direction,
-                                    handlesCrossed = it.handlesCrossed,
-                                    modifier = Modifier.pointerInput(observer) {
-                                        detectDownAndDragGesturesWithObserver(observer)
-                                    },
-                                    content = null
-                                )
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    DisposableEffect(manager) {
-        onDispose {
-            manager.hideSelectionToolbar()
-        }
-    }
-}
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionHandles.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionHandles.kt
deleted file mode 100644
index ddea64a..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionHandles.kt
+++ /dev/null
@@ -1,68 +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.foundation.newtext.text.copypasta.selection
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.semantics.SemanticsPropertyKey
-import androidx.compose.ui.text.style.ResolvedTextDirection
-import androidx.compose.ui.unit.dp
-
-internal val HandleWidth = 25.dp
-internal val HandleHeight = 25.dp
-
-/**
- * [SelectionHandleInfo]s for the nodes representing selection handles. These nodes are in popup
- * windows, and will respond to drag gestures.
- */
-internal val SelectionHandleInfoKey =
-    SemanticsPropertyKey<SelectionHandleInfo>("SelectionHandleInfo")
-
-/**
- * Information about a single selection handle popup.
- *
- * @param position The position that the handle is anchored to relative to the selectable content.
- * This position is not necessarily the position of the popup itself, it's the position that the
- * handle "points" to
- */
-internal data class SelectionHandleInfo(
-    // TODO: This is removed for copypasta because it's never used in basic usage
-    // val handle: Handle,
-    val position: Offset
-)
-
-@Composable
-internal expect fun SelectionHandle(
-    position: Offset,
-    isStartHandle: Boolean,
-    direction: ResolvedTextDirection,
-    handlesCrossed: Boolean,
-    modifier: Modifier,
-    content: @Composable (() -> Unit)?
-)
-
-/**
- * Adjust coordinates for given text offset.
- *
- * Currently [android.text.Layout.getLineBottom] returns y coordinates of the next
- * line's top offset, which is not included in current line's hit area. To be able to
- * hit current line, move up this y coordinates by 1 pixel.
- */
-internal fun getAdjustedCoordinates(position: Offset): Offset {
-    return Offset(position.x, position.y - 1f)
-}
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionMagnifier.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionMagnifier.kt
deleted file mode 100644
index 1f7a00c..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionMagnifier.kt
+++ /dev/null
@@ -1,109 +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.foundation.newtext.text.copypasta.selection
-
-import androidx.compose.animation.core.Animatable
-import androidx.compose.animation.core.AnimationVector2D
-import androidx.compose.animation.core.Spring
-import androidx.compose.animation.core.SpringSpec
-import androidx.compose.animation.core.TwoWayConverter
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.State
-import androidx.compose.runtime.derivedStateOf
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.snapshotFlow
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.isSpecified
-import kotlinx.coroutines.flow.collect
-import kotlinx.coroutines.launch
-
-private val UnspecifiedAnimationVector2D = AnimationVector2D(Float.NaN, Float.NaN)
-
-/** Like `Offset.VectorConverter` but propagates [Offset.Unspecified] values. */
-private val UnspecifiedSafeOffsetVectorConverter = TwoWayConverter<Offset, AnimationVector2D>(
-    convertToVector = {
-        if (it.isSpecified) {
-            AnimationVector2D(it.x, it.y)
-        } else {
-            UnspecifiedAnimationVector2D
-        }
-    },
-    convertFromVector = { Offset(it.v1, it.v2) }
-)
-
-private val OffsetDisplacementThreshold = Offset(
-    Spring.DefaultDisplacementThreshold,
-    Spring.DefaultDisplacementThreshold
-)
-
-private val MagnifierSpringSpec = SpringSpec(visibilityThreshold = OffsetDisplacementThreshold)
-
-/**
- * The text magnifier follows horizontal dragging exactly, but is vertically clamped to the current
- * line, so when it changes lines we animate it.
- */
-internal fun Modifier.animatedSelectionMagnifier(
-    magnifierCenter: () -> Offset,
-    platformMagnifier: (animatedCenter: () -> Offset) -> Modifier
-): Modifier = composed {
-    val animatedCenter by rememberAnimatedMagnifierPosition(targetCalculation = magnifierCenter)
-    return@composed platformMagnifier { animatedCenter }
-}
-
-/**
- * Remembers and returns a [State] that will smoothly animate to the result of [targetCalculation]
- * any time the result of [targetCalculation] changes due to any state values it reads change.
- */
-@Composable
-private fun rememberAnimatedMagnifierPosition(
-    targetCalculation: () -> Offset,
-): State<Offset> {
-    val targetValue by remember { derivedStateOf(targetCalculation) }
-    val animatable = remember {
-        // Can't use Offset.VectorConverter because we need to handle Unspecified specially.
-        Animatable(targetValue, UnspecifiedSafeOffsetVectorConverter, OffsetDisplacementThreshold)
-    }
-    LaunchedEffect(Unit) {
-        val animationScope = this
-        snapshotFlow { targetValue }
-            .collect { targetValue ->
-                // Only animate the position when moving vertically (i.e. jumping between lines),
-                // since horizontal movement in a single line should stay as close to the gesture as
-                // possible and animation would only add unnecessary lag.
-                if (
-                    animatable.value.isSpecified &&
-                    targetValue.isSpecified &&
-                    animatable.value.y != targetValue.y
-                ) {
-                    // Launch the animation, instead of cancelling and re-starting manually via
-                    // collectLatest, so if another animation is started before this one finishes,
-                    // the new one will use the correct velocity, e.g. in order to propagate spring
-                    // inertia.
-                    animationScope.launch {
-                        animatable.animateTo(targetValue, MagnifierSpringSpec)
-                    }
-                } else {
-                    animatable.snapTo(targetValue)
-                }
-            }
-    }
-    return animatable.asState()
-}
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionManager.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionManager.kt
deleted file mode 100644
index 73e0612..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionManager.kt
+++ /dev/null
@@ -1,903 +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.foundation.newtext.text.copypasta.selection
-
-import androidx.compose.foundation.focusable
-import androidx.compose.foundation.gestures.awaitEachGesture
-import androidx.compose.foundation.gestures.waitForUpOrCancellation
-import androidx.compose.foundation.newtext.text.copypasta.TextDragObserver
-import androidx.compose.runtime.MutableState
-import androidx.compose.runtime.State
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focus.focusRequester
-import androidx.compose.ui.focus.onFocusChanged
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.hapticfeedback.HapticFeedback
-import androidx.compose.ui.hapticfeedback.HapticFeedbackType
-import androidx.compose.ui.input.key.KeyEvent
-import androidx.compose.ui.input.key.onKeyEvent
-import androidx.compose.ui.input.pointer.PointerInputScope
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.layout.boundsInWindow
-import androidx.compose.ui.layout.onGloballyPositioned
-import androidx.compose.ui.layout.positionInWindow
-import androidx.compose.ui.platform.ClipboardManager
-import androidx.compose.ui.platform.TextToolbar
-import androidx.compose.ui.platform.TextToolbarStatus
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.unit.IntSize
-import kotlin.math.absoluteValue
-import kotlin.math.max
-import kotlin.math.min
-
-/**
- * A bridge class between user interaction to the text composables for text selection.
- */
-internal class SelectionManager(private val selectionRegistrar: SelectionRegistrarImpl) {
-
-    private val _selection: MutableState<Selection?> = mutableStateOf(null)
-
-    /**
-     * The current selection.
-     */
-    var selection: Selection?
-        get() = _selection.value
-        set(value) {
-            _selection.value = value
-            if (value != null) {
-                updateHandleOffsets()
-            }
-        }
-
-    /**
-     * Is touch mode active
-     */
-    var touchMode: Boolean = true
-
-    /**
-     * The manager will invoke this every time it comes to the conclusion that the selection should
-     * change. The expectation is that this callback will end up causing `setSelection` to get
-     * called. This is what makes this a "controlled component".
-     */
-    var onSelectionChange: (Selection?) -> Unit = {}
-
-    /**
-     * [HapticFeedback] handle to perform haptic feedback.
-     */
-    var hapticFeedBack: HapticFeedback? = null
-
-    /**
-     * [ClipboardManager] to perform clipboard features.
-     */
-    var clipboardManager: ClipboardManager? = null
-
-    /**
-     * [TextToolbar] to show floating toolbar(post-M) or primary toolbar(pre-M).
-     */
-    var textToolbar: TextToolbar? = null
-
-    /**
-     * Focus requester used to request focus when selection becomes active.
-     */
-    var focusRequester: FocusRequester = FocusRequester()
-
-    /**
-     * Return true if the corresponding SelectionContainer is focused.
-     */
-    var hasFocus: Boolean by mutableStateOf(false)
-
-    /**
-     * Modifier for selection container.
-     */
-    val modifier
-        get() = Modifier
-            .onClearSelectionRequested { onRelease() }
-            .onGloballyPositioned { containerLayoutCoordinates = it }
-            .focusRequester(focusRequester)
-            .onFocusChanged { focusState ->
-                if (!focusState.isFocused && hasFocus) {
-                    onRelease()
-                }
-                hasFocus = focusState.isFocused
-            }
-            .focusable()
-            .onKeyEvent {
-                if (isCopyKeyEvent(it)) {
-                    copy()
-                    true
-                } else {
-                    false
-                }
-            }
-            .then(if (shouldShowMagnifier) Modifier.selectionMagnifier(this) else Modifier)
-
-    private var previousPosition: Offset? = null
-
-    /**
-     * Layout Coordinates of the selection container.
-     */
-    var containerLayoutCoordinates: LayoutCoordinates? = null
-        set(value) {
-            field = value
-            if (hasFocus && selection != null) {
-                val positionInWindow = value?.positionInWindow()
-                if (previousPosition != positionInWindow) {
-                    previousPosition = positionInWindow
-                    updateHandleOffsets()
-                    updateSelectionToolbarPosition()
-                }
-            }
-        }
-
-    /**
-     * The beginning position of the drag gesture. Every time a new drag gesture starts, it wil be
-     * recalculated.
-     */
-    internal var dragBeginPosition by mutableStateOf(Offset.Zero)
-        private set
-
-    /**
-     * The total distance being dragged of the drag gesture. Every time a new drag gesture starts,
-     * it will be zeroed out.
-     */
-    internal var dragTotalDistance by mutableStateOf(Offset.Zero)
-        private set
-
-    /**
-     * The calculated position of the start handle in the [SelectionContainer] coordinates. It
-     * is null when handle shouldn't be displayed.
-     * It is a [State] so reading it during the composition will cause recomposition every time
-     * the position has been changed.
-     */
-    var startHandlePosition: Offset? by mutableStateOf(null)
-        private set
-
-    /**
-     * The calculated position of the end handle in the [SelectionContainer] coordinates. It
-     * is null when handle shouldn't be displayed.
-     * It is a [State] so reading it during the composition will cause recomposition every time
-     * the position has been changed.
-     */
-    var endHandlePosition: Offset? by mutableStateOf(null)
-        private set
-
-    /**
-     * The handle that is currently being dragged, or null when no handle is being dragged. To get
-     * the position of the last drag event, use [currentDragPosition].
-     */
-    var draggingHandle: Handle? by mutableStateOf(null)
-        private set
-
-    /**
-     * When a handle is being dragged (i.e. [draggingHandle] is non-null), this is the last position
-     * of the actual drag event. It is not clamped to handle positions. Null when not being dragged.
-     */
-    var currentDragPosition: Offset? by mutableStateOf(null)
-        private set
-
-    private val shouldShowMagnifier get() = draggingHandle != null
-
-    init {
-        selectionRegistrar.onPositionChangeCallback = { selectableId ->
-            if (
-                selectableId == selection?.start?.selectableId ||
-                selectableId == selection?.end?.selectableId
-            ) {
-                updateHandleOffsets()
-                updateSelectionToolbarPosition()
-            }
-        }
-
-        selectionRegistrar.onSelectionUpdateStartCallback =
-            { layoutCoordinates, position, selectionMode ->
-                val positionInContainer = convertToContainerCoordinates(
-                    layoutCoordinates,
-                    position
-                )
-
-                if (positionInContainer != null) {
-                    startSelection(
-                        position = positionInContainer,
-                        isStartHandle = false,
-                        adjustment = selectionMode
-                    )
-
-                    focusRequester.requestFocus()
-                    hideSelectionToolbar()
-                }
-            }
-
-        selectionRegistrar.onSelectionUpdateSelectAll =
-            { selectableId ->
-                val (newSelection, newSubselection) = selectAll(
-                    selectableId = selectableId,
-                    previousSelection = selection,
-                )
-                if (newSelection != selection) {
-                    selectionRegistrar.subselections = newSubselection
-                    onSelectionChange(newSelection)
-                }
-
-                focusRequester.requestFocus()
-                hideSelectionToolbar()
-            }
-
-        selectionRegistrar.onSelectionUpdateCallback =
-            { layoutCoordinates, newPosition, previousPosition, isStartHandle, selectionMode ->
-                val newPositionInContainer =
-                    convertToContainerCoordinates(layoutCoordinates, newPosition)
-                val previousPositionInContainer =
-                    convertToContainerCoordinates(layoutCoordinates, previousPosition)
-
-                updateSelection(
-                    newPosition = newPositionInContainer,
-                    previousPosition = previousPositionInContainer,
-                    isStartHandle = isStartHandle,
-                    adjustment = selectionMode
-                )
-            }
-
-        selectionRegistrar.onSelectionUpdateEndCallback = {
-            showSelectionToolbar()
-            // This property is set by updateSelection while dragging, so we need to clear it after
-            // the original selection drag.
-            draggingHandle = null
-            currentDragPosition = null
-        }
-
-        selectionRegistrar.onSelectableChangeCallback = { selectableKey ->
-            if (selectableKey in selectionRegistrar.subselections) {
-                // clear the selection range of each Selectable.
-                onRelease()
-                selection = null
-            }
-        }
-
-        selectionRegistrar.afterSelectableUnsubscribe = { selectableKey ->
-            if (
-                selectableKey == selection?.start?.selectableId ||
-                selectableKey == selection?.end?.selectableId
-            ) {
-                // The selectable that contains a selection handle just unsubscribed.
-                // Hide selection handles for now
-                startHandlePosition = null
-                endHandlePosition = null
-            }
-        }
-    }
-
-    /**
-     * Returns the [Selectable] responsible for managing the given [Selection.AnchorInfo], or null
-     * if the anchor is not from a currently-registered [Selectable].
-     */
-    internal fun getAnchorSelectable(anchor: Selection.AnchorInfo): Selectable? {
-        return selectionRegistrar.selectableMap[anchor.selectableId]
-    }
-
-    private fun updateHandleOffsets() {
-        val selection = selection
-        val containerCoordinates = containerLayoutCoordinates
-        val startSelectable = selection?.start?.let(::getAnchorSelectable)
-        val endSelectable = selection?.end?.let(::getAnchorSelectable)
-        val startLayoutCoordinates = startSelectable?.getLayoutCoordinates()
-        val endLayoutCoordinates = endSelectable?.getLayoutCoordinates()
-        if (
-            selection == null ||
-            containerCoordinates == null ||
-            !containerCoordinates.isAttached ||
-            startLayoutCoordinates == null ||
-            endLayoutCoordinates == null
-        ) {
-            this.startHandlePosition = null
-            this.endHandlePosition = null
-            return
-        }
-
-        val startHandlePosition = containerCoordinates.localPositionOf(
-            startLayoutCoordinates,
-            startSelectable.getHandlePosition(
-                selection = selection,
-                isStartHandle = true
-            )
-        )
-        val endHandlePosition = containerCoordinates.localPositionOf(
-            endLayoutCoordinates,
-            endSelectable.getHandlePosition(
-                selection = selection,
-                isStartHandle = false
-            )
-        )
-
-        val visibleBounds = containerCoordinates.visibleBounds()
-        this.startHandlePosition =
-            if (visibleBounds.containsInclusive(startHandlePosition)) startHandlePosition else null
-        this.endHandlePosition =
-            if (visibleBounds.containsInclusive(endHandlePosition)) endHandlePosition else null
-    }
-
-    /**
-     * Returns non-nullable [containerLayoutCoordinates].
-     */
-    internal fun requireContainerCoordinates(): LayoutCoordinates {
-        val coordinates = containerLayoutCoordinates
-        require(coordinates != null)
-        require(coordinates.isAttached)
-        return coordinates
-    }
-
-    internal fun selectAll(
-        selectableId: Long,
-        previousSelection: Selection?
-    ): Pair<Selection?, Map<Long, Selection>> {
-        val subselections = mutableMapOf<Long, Selection>()
-        val newSelection = selectionRegistrar.sort(requireContainerCoordinates())
-            .fastFold(null) { mergedSelection: Selection?, selectable: Selectable ->
-                val selection = if (selectable.selectableId == selectableId)
-                    selectable.getSelectAllSelection() else null
-                selection?.let { subselections[selectable.selectableId] = it }
-                merge(mergedSelection, selection)
-            }
-        if (newSelection != previousSelection) {
-            hapticFeedBack?.performHapticFeedback(HapticFeedbackType.TextHandleMove)
-        }
-        return Pair(newSelection, subselections)
-    }
-
-    internal fun getSelectedText(): AnnotatedString? {
-        val selectables = selectionRegistrar.sort(requireContainerCoordinates())
-        var selectedText: AnnotatedString? = null
-
-        selection?.let {
-            for (i in selectables.indices) {
-                val selectable = selectables[i]
-                // Continue if the current selectable is before the selection starts.
-                if (selectable.selectableId != it.start.selectableId &&
-                    selectable.selectableId != it.end.selectableId &&
-                    selectedText == null
-                ) continue
-
-                val currentSelectedText = getCurrentSelectedText(
-                    selectable = selectable,
-                    selection = it
-                )
-                selectedText = selectedText?.plus(currentSelectedText) ?: currentSelectedText
-
-                // Break if the current selectable is the last selected selectable.
-                if (selectable.selectableId == it.end.selectableId && !it.handlesCrossed ||
-                    selectable.selectableId == it.start.selectableId && it.handlesCrossed
-                ) break
-            }
-        }
-        return selectedText
-    }
-
-    internal fun copy() {
-        val selectedText = getSelectedText()
-        selectedText?.let { clipboardManager?.setText(it) }
-    }
-
-    /**
-     * This function get the selected region as a Rectangle region, and pass it to [TextToolbar]
-     * to make the FloatingToolbar show up in the proper place. In addition, this function passes
-     * the copy method as a callback when "copy" is clicked.
-     */
-    internal fun showSelectionToolbar() {
-        if (hasFocus) {
-            selection?.let {
-                textToolbar?.showMenu(
-                    getContentRect(),
-                    onCopyRequested = {
-                        copy()
-                        onRelease()
-                    }
-                )
-            }
-        }
-    }
-
-    internal fun hideSelectionToolbar() {
-        if (hasFocus && textToolbar?.status == TextToolbarStatus.Shown) {
-            textToolbar?.hide()
-        }
-    }
-
-    private fun updateSelectionToolbarPosition() {
-        if (hasFocus && textToolbar?.status == TextToolbarStatus.Shown) {
-            showSelectionToolbar()
-        }
-    }
-
-    /**
-     * Calculate selected region as [Rect]. The top is the top of the first selected
-     * line, and the bottom is the bottom of the last selected line. The left is the leftmost
-     * handle's horizontal coordinates, and the right is the rightmost handle's coordinates.
-     */
-    private fun getContentRect(): Rect {
-        val selection = selection ?: return Rect.Zero
-        val startSelectable = getAnchorSelectable(selection.start)
-        val endSelectable = getAnchorSelectable(selection.end)
-        val startLayoutCoordinates = startSelectable?.getLayoutCoordinates() ?: return Rect.Zero
-        val endLayoutCoordinates = endSelectable?.getLayoutCoordinates() ?: return Rect.Zero
-
-        val localLayoutCoordinates = containerLayoutCoordinates
-        if (localLayoutCoordinates != null && localLayoutCoordinates.isAttached) {
-            var startOffset = localLayoutCoordinates.localPositionOf(
-                startLayoutCoordinates,
-                startSelectable.getHandlePosition(
-                    selection = selection,
-                    isStartHandle = true
-                )
-            )
-            var endOffset = localLayoutCoordinates.localPositionOf(
-                endLayoutCoordinates,
-                endSelectable.getHandlePosition(
-                    selection = selection,
-                    isStartHandle = false
-                )
-            )
-
-            startOffset = localLayoutCoordinates.localToRoot(startOffset)
-            endOffset = localLayoutCoordinates.localToRoot(endOffset)
-
-            val left = min(startOffset.x, endOffset.x)
-            val right = max(startOffset.x, endOffset.x)
-
-            var startTop = localLayoutCoordinates.localPositionOf(
-                startLayoutCoordinates,
-                Offset(
-                    0f,
-                    startSelectable.getBoundingBox(selection.start.offset).top
-                )
-            )
-
-            var endTop = localLayoutCoordinates.localPositionOf(
-                endLayoutCoordinates,
-                Offset(
-                    0.0f,
-                    endSelectable.getBoundingBox(selection.end.offset).top
-                )
-            )
-
-            startTop = localLayoutCoordinates.localToRoot(startTop)
-            endTop = localLayoutCoordinates.localToRoot(endTop)
-
-            val top = min(startTop.y, endTop.y)
-            val bottom = max(startOffset.y, endOffset.y) + (HandleHeight.value * 4.0).toFloat()
-
-            return Rect(
-                left,
-                top,
-                right,
-                bottom
-            )
-        }
-        return Rect.Zero
-    }
-
-    // This is for PressGestureDetector to cancel the selection.
-    fun onRelease() {
-        selectionRegistrar.subselections = emptyMap()
-        hideSelectionToolbar()
-        if (selection != null) {
-            onSelectionChange(null)
-            hapticFeedBack?.performHapticFeedback(HapticFeedbackType.TextHandleMove)
-        }
-    }
-
-    fun handleDragObserver(isStartHandle: Boolean): TextDragObserver = object : TextDragObserver {
-        override fun onDown(point: Offset) {
-            val selection = selection ?: return
-            val anchor = if (isStartHandle) selection.start else selection.end
-            val selectable = getAnchorSelectable(anchor) ?: return
-            // The LayoutCoordinates of the composable where the drag gesture should begin. This
-            // is used to convert the position of the beginning of the drag gesture from the
-            // composable coordinates to selection container coordinates.
-            val beginLayoutCoordinates = selectable.getLayoutCoordinates() ?: return
-
-            // The position of the character where the drag gesture should begin. This is in
-            // the composable coordinates.
-            val beginCoordinates = getAdjustedCoordinates(
-                selectable.getHandlePosition(
-                    selection = selection, isStartHandle = isStartHandle
-                )
-            )
-
-            // Convert the position where drag gesture begins from composable coordinates to
-            // selection container coordinates.
-            currentDragPosition = requireContainerCoordinates().localPositionOf(
-                beginLayoutCoordinates,
-                beginCoordinates
-            )
-            draggingHandle = if (isStartHandle) Handle.SelectionStart else Handle.SelectionEnd
-        }
-
-        override fun onUp() {
-            draggingHandle = null
-            currentDragPosition = null
-        }
-
-        override fun onStart(startPoint: Offset) {
-            hideSelectionToolbar()
-            val selection = selection!!
-            val startSelectable =
-                selectionRegistrar.selectableMap[selection.start.selectableId]
-            val endSelectable =
-                selectionRegistrar.selectableMap[selection.end.selectableId]
-            // The LayoutCoordinates of the composable where the drag gesture should begin. This
-            // is used to convert the position of the beginning of the drag gesture from the
-            // composable coordinates to selection container coordinates.
-            val beginLayoutCoordinates = if (isStartHandle) {
-                startSelectable?.getLayoutCoordinates()!!
-            } else {
-                endSelectable?.getLayoutCoordinates()!!
-            }
-
-            // The position of the character where the drag gesture should begin. This is in
-            // the composable coordinates.
-            val beginCoordinates = getAdjustedCoordinates(
-                if (isStartHandle) {
-                    startSelectable!!.getHandlePosition(
-                        selection = selection, isStartHandle = true
-                    )
-                } else {
-                    endSelectable!!.getHandlePosition(
-                        selection = selection, isStartHandle = false
-                    )
-                }
-            )
-
-            // Convert the position where drag gesture begins from composable coordinates to
-            // selection container coordinates.
-            dragBeginPosition = requireContainerCoordinates().localPositionOf(
-                beginLayoutCoordinates,
-                beginCoordinates
-            )
-
-            // Zero out the total distance that being dragged.
-            dragTotalDistance = Offset.Zero
-        }
-
-        override fun onDrag(delta: Offset) {
-            dragTotalDistance += delta
-            val endPosition = dragBeginPosition + dragTotalDistance
-            val consumed = updateSelection(
-                newPosition = endPosition,
-                previousPosition = dragBeginPosition,
-                isStartHandle = isStartHandle,
-                adjustment = SelectionAdjustment.CharacterWithWordAccelerate
-            )
-            if (consumed) {
-                dragBeginPosition = endPosition
-                dragTotalDistance = Offset.Zero
-            }
-        }
-
-        override fun onStop() {
-            showSelectionToolbar()
-            draggingHandle = null
-            currentDragPosition = null
-        }
-
-        override fun onCancel() {
-            showSelectionToolbar()
-            draggingHandle = null
-            currentDragPosition = null
-        }
-    }
-
-    /**
-     * Detect tap without consuming the up event.
-     */
-    private suspend fun PointerInputScope.detectNonConsumingTap(onTap: (Offset) -> Unit) {
-        awaitEachGesture {
-            waitForUpOrCancellation()?.let {
-                onTap(it.position)
-            }
-        }
-    }
-
-    private fun Modifier.onClearSelectionRequested(block: () -> Unit): Modifier {
-        return if (hasFocus) pointerInput(Unit) { detectNonConsumingTap { block() } } else this
-    }
-
-    private fun convertToContainerCoordinates(
-        layoutCoordinates: LayoutCoordinates,
-        offset: Offset
-    ): Offset? {
-        val coordinates = containerLayoutCoordinates
-        if (coordinates == null || !coordinates.isAttached) return null
-        return requireContainerCoordinates().localPositionOf(layoutCoordinates, offset)
-    }
-
-    /**
-     * Cancel the previous selection and start a new selection at the given [position].
-     * It's used for long-press, double-click, triple-click and so on to start selection.
-     *
-     * @param position initial position of the selection. Both start and end handle is considered
-     * at this position.
-     * @param isStartHandle whether it's considered as the start handle moving. This parameter
-     * will influence the [SelectionAdjustment]'s behavior. For example,
-     * [SelectionAdjustment.Character] only adjust the moving handle.
-     * @param adjustment the selection adjustment.
-     */
-    private fun startSelection(
-        position: Offset,
-        isStartHandle: Boolean,
-        adjustment: SelectionAdjustment
-    ) {
-        updateSelection(
-            startHandlePosition = position,
-            endHandlePosition = position,
-            previousHandlePosition = null,
-            isStartHandle = isStartHandle,
-            adjustment = adjustment
-        )
-    }
-
-    /**
-     * Updates the selection after one of the selection handle moved.
-     *
-     * @param newPosition the new position of the moving selection handle.
-     * @param previousPosition the previous position of the moving selection handle.
-     * @param isStartHandle whether the moving selection handle is the start handle.
-     * @param adjustment the [SelectionAdjustment] used to adjust the raw selection range and
-     * produce the final selection range.
-     *
-     * @return a boolean representing whether the movement is consumed.
-     *
-     * @see SelectionAdjustment
-     */
-    internal fun updateSelection(
-        newPosition: Offset?,
-        previousPosition: Offset?,
-        isStartHandle: Boolean,
-        adjustment: SelectionAdjustment,
-    ): Boolean {
-        if (newPosition == null) return false
-        val otherHandlePosition = selection?.let { selection ->
-            val otherSelectableId = if (isStartHandle) {
-                selection.end.selectableId
-            } else {
-                selection.start.selectableId
-            }
-            val otherSelectable =
-                selectionRegistrar.selectableMap[otherSelectableId] ?: return@let null
-            convertToContainerCoordinates(
-                otherSelectable.getLayoutCoordinates()!!,
-                getAdjustedCoordinates(
-                    otherSelectable.getHandlePosition(selection, !isStartHandle)
-                )
-            )
-        } ?: return false
-
-        val startHandlePosition = if (isStartHandle) newPosition else otherHandlePosition
-        val endHandlePosition = if (isStartHandle) otherHandlePosition else newPosition
-
-        return updateSelection(
-            startHandlePosition = startHandlePosition,
-            endHandlePosition = endHandlePosition,
-            previousHandlePosition = previousPosition,
-            isStartHandle = isStartHandle,
-            adjustment = adjustment
-        )
-    }
-
-    /**
-     * Updates the selection after one of the selection handle moved.
-     *
-     * To make sure that [SelectionAdjustment] works correctly, it's expected that only one
-     * selection handle is updated each time. The only exception is that when a new selection is
-     * started. In this case, [previousHandlePosition] is always null.
-     *
-     * @param startHandlePosition the position of the start selection handle.
-     * @param endHandlePosition the position of the end selection handle.
-     * @param previousHandlePosition the position of the moving handle before the update.
-     * @param isStartHandle whether the moving selection handle is the start handle.
-     * @param adjustment the [SelectionAdjustment] used to adjust the raw selection range and
-     * produce the final selection range.
-     *
-     * @return a boolean representing whether the movement is consumed. It's useful for the case
-     * where a selection handle is updating consecutively. When the return value is true, it's
-     * expected that the caller will update the [startHandlePosition] to be the given
-     * [endHandlePosition] in following calls.
-     *
-     * @see SelectionAdjustment
-     */
-    internal fun updateSelection(
-        startHandlePosition: Offset,
-        endHandlePosition: Offset,
-        previousHandlePosition: Offset?,
-        isStartHandle: Boolean,
-        adjustment: SelectionAdjustment,
-    ): Boolean {
-        draggingHandle = if (isStartHandle) Handle.SelectionStart else Handle.SelectionEnd
-        currentDragPosition = if (isStartHandle) startHandlePosition else endHandlePosition
-        val newSubselections = mutableMapOf<Long, Selection>()
-        var moveConsumed = false
-        val newSelection = selectionRegistrar.sort(requireContainerCoordinates())
-            .fastFold(null) { mergedSelection: Selection?, selectable: Selectable ->
-                val previousSubselection =
-                    selectionRegistrar.subselections[selectable.selectableId]
-                val (selection, consumed) = selectable.updateSelection(
-                    startHandlePosition = startHandlePosition,
-                    endHandlePosition = endHandlePosition,
-                    previousHandlePosition = previousHandlePosition,
-                    isStartHandle = isStartHandle,
-                    containerLayoutCoordinates = requireContainerCoordinates(),
-                    adjustment = adjustment,
-                    previousSelection = previousSubselection,
-                )
-
-                moveConsumed = moveConsumed || consumed
-                selection?.let { newSubselections[selectable.selectableId] = it }
-                merge(mergedSelection, selection)
-            }
-        if (newSelection != selection) {
-            hapticFeedBack?.performHapticFeedback(HapticFeedbackType.TextHandleMove)
-            selectionRegistrar.subselections = newSubselections
-            onSelectionChange(newSelection)
-        }
-        return moveConsumed
-    }
-
-    fun contextMenuOpenAdjustment(position: Offset) {
-        val isEmptySelection = selection?.toTextRange()?.collapsed ?: true
-        // TODO(b/209483184) the logic should be more complex here, it should check that current
-        // selection doesn't include click position
-        if (isEmptySelection) {
-            startSelection(
-                position = position,
-                isStartHandle = true,
-                adjustment = SelectionAdjustment.Word
-            )
-        }
-    }
-}
-
-internal fun merge(lhs: Selection?, rhs: Selection?): Selection? {
-    return lhs?.merge(rhs) ?: rhs
-}
-
-internal expect fun isCopyKeyEvent(keyEvent: KeyEvent): Boolean
-
-internal expect fun Modifier.selectionMagnifier(manager: SelectionManager): Modifier
-
-internal fun calculateSelectionMagnifierCenterAndroid(
-    manager: SelectionManager,
-    magnifierSize: IntSize
-): Offset {
-    fun getMagnifierCenter(anchor: Selection.AnchorInfo, isStartHandle: Boolean): Offset {
-        val selectable = manager.getAnchorSelectable(anchor) ?: return Offset.Unspecified
-        val containerCoordinates = manager.containerLayoutCoordinates ?: return Offset.Unspecified
-        val selectableCoordinates = selectable.getLayoutCoordinates() ?: return Offset.Unspecified
-        // The end offset is exclusive.
-        val offset = if (isStartHandle) anchor.offset else anchor.offset - 1
-
-        // The horizontal position doesn't snap to cursor positions but should directly track the
-        // actual drag.
-        val localDragPosition = selectableCoordinates.localPositionOf(
-            containerCoordinates,
-            manager.currentDragPosition!!
-        )
-        val dragX = localDragPosition.x
-        // But it is constrained by the horizontal bounds of the current line.
-        val centerX = selectable.getRangeOfLineContaining(offset).let { line ->
-            val lineMin = selectable.getBoundingBox(line.min)
-            // line.end is exclusive, but we want the bounding box of the actual last character in
-            // the line.
-            val lineMax = selectable.getBoundingBox((line.max - 1).coerceAtLeast(line.min))
-            val minX = minOf(lineMin.left, lineMax.left)
-            val maxX = maxOf(lineMin.right, lineMax.right)
-            dragX.coerceIn(minX, maxX)
-        }
-
-        // Hide the magnifier when dragged too far (outside the horizontal bounds of how big the
-        // magnifier actually is). See
-        // https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/widget/Editor.java;l=5228-5231;drc=2fdb6bd709be078b72f011334362456bb758922c
-        if ((dragX - centerX).absoluteValue > magnifierSize.width / 2) {
-            return Offset.Unspecified
-        }
-
-        // Let the selectable determine the vertical position of the magnifier, since it should be
-        // clamped to the center of text lines.
-        val anchorBounds = selectable.getBoundingBox(offset)
-        val centerY = anchorBounds.center.y
-
-        return containerCoordinates.localPositionOf(
-            sourceCoordinates = selectableCoordinates,
-            relativeToSource = Offset(centerX, centerY)
-        )
-    }
-
-    val selection = manager.selection ?: return Offset.Unspecified
-    return when (manager.draggingHandle) {
-        null -> return Offset.Unspecified
-        Handle.SelectionStart -> getMagnifierCenter(selection.start, isStartHandle = true)
-        Handle.SelectionEnd -> getMagnifierCenter(selection.end, isStartHandle = false)
-        Handle.Cursor -> error("SelectionContainer does not support cursor")
-    }
-}
-
-internal fun getCurrentSelectedText(
-    selectable: Selectable,
-    selection: Selection
-): AnnotatedString {
-    val currentText = selectable.getText()
-
-    return if (
-        selectable.selectableId != selection.start.selectableId &&
-        selectable.selectableId != selection.end.selectableId
-    ) {
-        // Select the full text content if the current selectable is between the
-        // start and the end selectables.
-        currentText
-    } else if (
-        selectable.selectableId == selection.start.selectableId &&
-        selectable.selectableId == selection.end.selectableId
-    ) {
-        // Select partial text content if the current selectable is the start and
-        // the end selectable.
-        if (selection.handlesCrossed) {
-            currentText.subSequence(selection.end.offset, selection.start.offset)
-        } else {
-            currentText.subSequence(selection.start.offset, selection.end.offset)
-        }
-    } else if (selectable.selectableId == selection.start.selectableId) {
-        // Select partial text content if the current selectable is the start
-        // selectable.
-        if (selection.handlesCrossed) {
-            currentText.subSequence(0, selection.start.offset)
-        } else {
-            currentText.subSequence(selection.start.offset, currentText.length)
-        }
-    } else {
-        // Selectable partial text content if the current selectable is the end
-        // selectable.
-        if (selection.handlesCrossed) {
-            currentText.subSequence(selection.end.offset, currentText.length)
-        } else {
-            currentText.subSequence(0, selection.end.offset)
-        }
-    }
-}
-
-/** Returns the boundary of the visible area in this [LayoutCoordinates]. */
-internal fun LayoutCoordinates.visibleBounds(): Rect {
-    // globalBounds is the global boundaries of this LayoutCoordinates after it's clipped by
-    // parents. We can think it as the global visible bounds of this Layout. Here globalBounds
-    // is convert to local, which is the boundary of the visible area within the LayoutCoordinates.
-    val boundsInWindow = boundsInWindow()
-    return Rect(
-        windowToLocal(boundsInWindow.topLeft),
-        windowToLocal(boundsInWindow.bottomRight)
-    )
-}
-
-internal fun Rect.containsInclusive(offset: Offset): Boolean =
-    offset.x in left..right && offset.y in top..bottom
-
-internal enum class Handle {
-    Cursor,
-    SelectionStart,
-    SelectionEnd
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionMode.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionMode.kt
deleted file mode 100644
index da7e8e0..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionMode.kt
+++ /dev/null
@@ -1,107 +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.foundation.newtext.text.copypasta.selection
-
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Rect
-
-/**
- * The enum class allows user to decide the selection mode.
- */
-internal enum class SelectionMode {
-    /**
-     * When selection handles are dragged across composables, selection extends by row, for example,
-     * when the end selection handle is dragged down, upper rows will be selected first, and the
-     * lower rows.
-     */
-    Vertical {
-        override fun compare(position: Offset, bounds: Rect): Int {
-            if (bounds.contains(position)) return 0
-
-            // When the position of the selection handle is on the top of the composable, and the
-            // not on the right of the composable, it's considered as start.
-            if (position.y < bounds.top) return -1
-
-            // When the position of the selection handle is on the left of the composable, and not
-            // below the bottom of composable, it's considered as start.
-            if (position.x < bounds.left && position.y < bounds.bottom) return -1
-
-            // In all other cases, the selection handle is considered as the end.
-            return 1
-        }
-    },
-
-    /**
-     * When selection handles are dragged across composables, selection extends by column, for example,
-     * when the end selection handle is dragged to the right, left columns will be selected first,
-     * and the right rows.
-     */
-    Horizontal {
-        override fun compare(position: Offset, bounds: Rect): Int {
-            if (bounds.contains(position)) return 0
-
-            // When the end of the selection is on the left of the composable, the composable is
-            // outside of the selection range.
-            if (position.x < bounds.left) return -1
-
-            // When the end of the selection is on the top of the composable, and the not on the
-            // right of the composable, the composable is outside of the selection range.
-            if (position.y < bounds.top && position.x < bounds.right) return -1
-
-            // In all other cases, the selection handle is considered as the end.
-            return 1
-        }
-    };
-
-    /**
-     * A compare a selection handle with a  [Selectable] boundary. This defines whether an out of
-     * boundary selection handle is treated as the start or the end of the Selectable. If the
-     * [Selectable] is a text selectable, then the start is the index 0, and end corresponds to
-     * the text length.
-     *
-     * @param position the position of the selection handle.
-     * @param bounds the boundary of the [Selectable].
-     * @return 0 if the selection handle [position] is within the [bounds]; a negative value if
-     * the selection handle is considered as "start" of the [Selectable]; a positive value if the
-     * selection handle is considered as the "end" of the [Selectable].
-     */
-    internal abstract fun compare(position: Offset, bounds: Rect): Int
-
-    /**
-     * Decides if Composable which has [bounds], should be accepted by the selection and
-     * change its selected state for a selection that starts at [start] and ends at [end].
-     *
-     * @param bounds Composable bounds of the widget to be checked.
-     * @param start The start coordinates of the selection, in SelectionContainer range.
-     * @param end The end coordinates of the selection, in SelectionContainer range.
-     */
-    internal fun isSelected(
-        bounds: Rect,
-        start: Offset,
-        end: Offset
-    ): Boolean {
-        // If either of the start or end is contained by bounds, the composable is selected.
-        if (bounds.contains(start) || bounds.contains(end)) {
-            return true
-        }
-        // Compare the location of start and end to the bound. If both are on the same side, return
-        // false, otherwise return true.
-        val compareStart = compare(start, bounds)
-        val compareEnd = compare(end, bounds)
-        return (compareStart > 0) xor (compareEnd > 0)
-    }
-}
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionRegistrar.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionRegistrar.kt
deleted file mode 100644
index dd1f86c..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionRegistrar.kt
+++ /dev/null
@@ -1,159 +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.foundation.newtext.text.copypasta.selection
-
-import androidx.compose.runtime.MutableState
-import androidx.compose.runtime.compositionLocalOf
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.layout.LayoutCoordinates
-
-/**
- *  An interface allowing a composable to subscribe and unsubscribe to selection changes.
- */
-internal interface SelectionRegistrar {
-    /**
-     * The map stored current selection information on each [Selectable]. A selectable can query
-     * its selected range using its [Selectable.selectableId]. This field is backed by a
-     * [MutableState]. And any composable reading this field will be recomposed once its value
-     * changed.
-     */
-    val subselections: Map<Long, Selection>
-
-    /**
-     * Subscribe to SelectionContainer selection changes.
-     * @param selectable the [Selectable] that is subscribing to this [SelectionRegistrar].
-     */
-    fun subscribe(selectable: Selectable): Selectable
-
-    /**
-     * Unsubscribe from SelectionContainer selection changes.
-     * @param selectable the [Selectable] that is unsubscribing to this [SelectionRegistrar].
-     */
-    fun unsubscribe(selectable: Selectable)
-
-    /**
-     * Return a unique ID for a [Selectable].
-     * @see [Selectable.selectableId]
-     */
-    fun nextSelectableId(): Long
-
-    /**
-     * When the Global Position of a subscribed [Selectable] changes, this method
-     * is called.
-     */
-    fun notifyPositionChange(selectableId: Long)
-
-    /**
-     * Call this method to notify the [SelectionContainer] that the selection has been initiated.
-     * Depends on the input, [notifySelectionUpdate] may be called repeatedly after
-     * [notifySelectionUpdateStart] is called. And [notifySelectionUpdateEnd] should always be
-     * called after selection finished.
-     * For example:
-     *  1. User long pressed the text and then release. [notifySelectionUpdateStart] should be
-     *  called followed by [notifySelectionUpdateEnd] being called once.
-     *  2. User long pressed the text and then drag a distance and then release.
-     *  [notifySelectionUpdateStart] should be called first after the user long press, and then
-     *  [notifySelectionUpdate] is called several times reporting the updates, in the end
-     *  [notifySelectionUpdateEnd] is called to finish the selection.
-     *
-     * @param layoutCoordinates [LayoutCoordinates] of the [Selectable].
-     * @param startPosition coordinates of where the selection is initiated.
-     * @param adjustment selection should be adjusted according to this param
-     *
-     * @see notifySelectionUpdate
-     * @see notifySelectionUpdateEnd
-     */
-    fun notifySelectionUpdateStart(
-        layoutCoordinates: LayoutCoordinates,
-        startPosition: Offset,
-        adjustment: SelectionAdjustment
-    )
-
-    /**
-     * Call this method to notify the [SelectionContainer] that the selection has been initiated
-     * with selectAll [Selection].
-     *
-     * @param selectableId [selectableId] of the [Selectable]
-     */
-    fun notifySelectionUpdateSelectAll(selectableId: Long)
-
-    /**
-     * Call this method to notify the [SelectionContainer] that one of the selection handle has
-     * moved and selection should be updated.
-     * The caller of this method should make sure that [notifySelectionUpdateStart] is always
-     * called once before calling this function. And [notifySelectionUpdateEnd] is always called
-     * once after the all updates finished.
-     *
-     * @param layoutCoordinates [LayoutCoordinates] of the [Selectable].
-     * @param previousPosition coordinates of where the selection starts.
-     * @param newPosition coordinates of where the selection ends.
-     * @param isStartHandle whether the moving selection handle the start handle.
-     * @param adjustment selection should be adjusted according to this parameter
-     *
-     * @return true if the selection handle movement is consumed. This function acts like a
-     * pointer input consumer when a selection handle is dragged. It expects the caller to
-     * accumulate the unconsumed pointer movement:
-     * 1. if it returns true, the caller will zero out the previous movement.
-     * 2. if it returns false, the caller will continue accumulate pointer movement.
-     * @see notifySelectionUpdateStart
-     * @see notifySelectionUpdateEnd
-     */
-    fun notifySelectionUpdate(
-        layoutCoordinates: LayoutCoordinates,
-        newPosition: Offset,
-        previousPosition: Offset,
-        isStartHandle: Boolean,
-        adjustment: SelectionAdjustment
-    ): Boolean
-
-    /**
-     * Call this method to notify the [SelectionContainer] that the selection update has stopped.
-     *
-     * @see notifySelectionUpdateStart
-     * @see notifySelectionUpdate
-     */
-    fun notifySelectionUpdateEnd()
-
-    /**
-     * Call this method to notify the [SelectionContainer] that the content of the passed
-     * selectable has been changed.
-     *
-     * @param selectableId the ID of the selectable whose the content has been updated.
-     */
-    fun notifySelectableChange(selectableId: Long)
-
-    companion object {
-        /**
-         * Representing an invalid ID for [Selectable].
-         */
-        const val InvalidSelectableId = 0L
-    }
-}
-
-/**
- * Helper function that checks if there is a selection on this CoreText.
- */
-internal fun SelectionRegistrar?.hasSelection(selectableId: Long): Boolean {
-    return this?.subselections?.containsKey(selectableId) ?: false
-}
-
-/**
- * SelectionRegistrar CompositionLocal. Composables that implement selection logic can use this
- * CompositionLocal to get a [SelectionRegistrar] in order to subscribe and unsubscribe to
- * [SelectionRegistrar].
- */
-internal val LocalSelectionRegistrar = compositionLocalOf<SelectionRegistrar?> { null }
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionRegistrarImpl.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionRegistrarImpl.kt
deleted file mode 100644
index f08c238..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionRegistrarImpl.kt
+++ /dev/null
@@ -1,205 +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.foundation.newtext.text.copypasta.selection
-
-import androidx.compose.foundation.newtext.text.copypasta.AtomicLong
-import androidx.compose.runtime.setValue
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.layout.LayoutCoordinates
-
-internal class SelectionRegistrarImpl : SelectionRegistrar {
-    /**
-     * A flag to check if the [Selectable]s have already been sorted.
-     */
-    internal var sorted: Boolean = false
-
-    /**
-     * This is essentially the list of registered components that want
-     * to handle text selection that are below the SelectionContainer.
-     */
-    private val _selectables = mutableListOf<Selectable>()
-
-    /**
-     * Getter for handlers that returns a List.
-     */
-    internal val selectables: List<Selectable>
-        get() = _selectables
-
-    private val _selectableMap = mutableMapOf<Long, Selectable>()
-
-    /**
-     * A map from selectable keys to subscribed selectables.
-     */
-    internal val selectableMap: Map<Long, Selectable>
-        get() = _selectableMap
-
-    /**
-     * The incremental id to be assigned to each selectable. It starts from 1 and 0 is used to
-     * denote an invalid id.
-     * @see SelectionRegistrar.InvalidSelectableId
-     */
-    private var incrementId = AtomicLong(1)
-
-    /**
-     * The callback to be invoked when the position change was triggered.
-     */
-    internal var onPositionChangeCallback: ((Long) -> Unit)? = null
-
-    /**
-     * The callback to be invoked when the selection is initiated.
-     */
-    internal var onSelectionUpdateStartCallback:
-        ((LayoutCoordinates, Offset, SelectionAdjustment) -> Unit)? = null
-
-    /**
-     * The callback to be invoked when the selection is initiated with selectAll [Selection].
-     */
-    internal var onSelectionUpdateSelectAll: (
-        (Long) -> Unit
-    )? = null
-
-    /**
-     * The callback to be invoked when the selection is updated.
-     * If the first offset is null it means that the start of selection is unknown for the caller.
-     */
-    internal var onSelectionUpdateCallback:
-        ((LayoutCoordinates, Offset, Offset, Boolean, SelectionAdjustment) -> Boolean)? = null
-
-    /**
-     * The callback to be invoked when selection update finished.
-     */
-    internal var onSelectionUpdateEndCallback: (() -> Unit)? = null
-
-    /**
-     * The callback to be invoked when one of the selectable has changed.
-     */
-    internal var onSelectableChangeCallback: ((Long) -> Unit)? = null
-
-    /**
-     * The callback to be invoked after a selectable is unsubscribed from this [SelectionRegistrar].
-     */
-    internal var afterSelectableUnsubscribe: ((Long) -> Unit)? = null
-
-    override var subselections: Map<Long, Selection> by mutableStateOf(emptyMap())
-
-    override fun subscribe(selectable: Selectable): Selectable {
-        require(selectable.selectableId != SelectionRegistrar.InvalidSelectableId) {
-            "The selectable contains an invalid id: ${selectable.selectableId}"
-        }
-        require(!_selectableMap.containsKey(selectable.selectableId)) {
-            "Another selectable with the id: $selectable.selectableId has already subscribed."
-        }
-        _selectableMap[selectable.selectableId] = selectable
-        _selectables.add(selectable)
-        sorted = false
-        return selectable
-    }
-
-    override fun unsubscribe(selectable: Selectable) {
-        if (!_selectableMap.containsKey(selectable.selectableId)) return
-        _selectables.remove(selectable)
-        _selectableMap.remove(selectable.selectableId)
-        afterSelectableUnsubscribe?.invoke(selectable.selectableId)
-    }
-
-    override fun nextSelectableId(): Long {
-        var id = incrementId.getAndIncrement()
-        while (id == SelectionRegistrar.InvalidSelectableId) {
-            id = incrementId.getAndIncrement()
-        }
-        return id
-    }
-
-    /**
-     * Sort the list of registered [Selectable]s in [SelectionRegistrar]. Currently the order of
-     * selectables is geometric-based.
-     */
-    fun sort(containerLayoutCoordinates: LayoutCoordinates): List<Selectable> {
-        if (!sorted) {
-            // Sort selectables by y-coordinate first, and then x-coordinate, to match English
-            // hand-writing habit.
-            _selectables.sortWith { a: Selectable, b: Selectable ->
-                val layoutCoordinatesA = a.getLayoutCoordinates()
-                val layoutCoordinatesB = b.getLayoutCoordinates()
-
-                val positionA = if (layoutCoordinatesA != null) {
-                    containerLayoutCoordinates.localPositionOf(layoutCoordinatesA, Offset.Zero)
-                } else {
-                    Offset.Zero
-                }
-                val positionB = if (layoutCoordinatesB != null) {
-                    containerLayoutCoordinates.localPositionOf(layoutCoordinatesB, Offset.Zero)
-                } else {
-                    Offset.Zero
-                }
-
-                if (positionA.y == positionB.y) {
-                    compareValues(positionA.x, positionB.x)
-                } else {
-                    compareValues(positionA.y, positionB.y)
-                }
-            }
-            sorted = true
-        }
-        return selectables
-    }
-
-    override fun notifyPositionChange(selectableId: Long) {
-        // Set the variable sorted to be false, when the global position of a registered
-        // selectable changes.
-        sorted = false
-        onPositionChangeCallback?.invoke(selectableId)
-    }
-
-    override fun notifySelectionUpdateStart(
-        layoutCoordinates: LayoutCoordinates,
-        startPosition: Offset,
-        adjustment: SelectionAdjustment
-    ) {
-        onSelectionUpdateStartCallback?.invoke(layoutCoordinates, startPosition, adjustment)
-    }
-
-    override fun notifySelectionUpdateSelectAll(selectableId: Long) {
-        onSelectionUpdateSelectAll?.invoke(selectableId)
-    }
-
-    override fun notifySelectionUpdate(
-        layoutCoordinates: LayoutCoordinates,
-        newPosition: Offset,
-        previousPosition: Offset,
-        isStartHandle: Boolean,
-        adjustment: SelectionAdjustment
-    ): Boolean {
-        return onSelectionUpdateCallback?.invoke(
-            layoutCoordinates,
-            newPosition,
-            previousPosition,
-            isStartHandle,
-            adjustment
-        ) ?: true
-    }
-
-    override fun notifySelectionUpdateEnd() {
-        onSelectionUpdateEndCallback?.invoke()
-    }
-
-    override fun notifySelectableChange(selectableId: Long) {
-        onSelectableChangeCallback?.invoke(selectableId)
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SimpleLayout.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SimpleLayout.kt
deleted file mode 100644
index 9929a2e..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SimpleLayout.kt
+++ /dev/null
@@ -1,65 +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.newtext.text.copypasta.selection
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.util.fastForEach
-import androidx.compose.ui.util.fastMap
-import kotlin.contracts.ExperimentalContracts
-import kotlin.contracts.contract
-import kotlin.math.max
-
-/**
- * Selection is transparent in terms of measurement and layout and passes the same constraints to
- * the children.
- */
-@Composable
-internal fun SimpleLayout(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
-    Layout(modifier = modifier, content = content) { measurables, constraints ->
-        val placeables = measurables.fastMap { measurable ->
-            measurable.measure(constraints)
-        }
-
-        val width = placeables.fastFold(0) { maxWidth, placeable ->
-            max(maxWidth, (placeable.width))
-        }
-
-        val height = placeables.fastFold(0) { minWidth, placeable ->
-            max(minWidth, (placeable.height))
-        }
-
-        layout(width, height) {
-            placeables.fastForEach { placeable ->
-                placeable.place(0, 0)
-            }
-        }
-    }
-}
-
-// copypasta from foundation to compile this copypasta
-@Suppress("BanInlineOptIn") // Treat Kotlin Contracts as non-experimental.
-@OptIn(ExperimentalContracts::class)
-internal inline fun <T, R> List<T>.fastFold(initial: R, operation: (acc: R, T) -> R): R {
-    contract { callsInPlace(operation) }
-    var accumulator = initial
-    fastForEach { e ->
-        accumulator = operation(accumulator, e)
-    }
-    return accumulator
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/TextPreparedSelection.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/TextPreparedSelection.kt
deleted file mode 100644
index a3e5817..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/TextPreparedSelection.kt
+++ /dev/null
@@ -1,432 +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.foundation.newtext.text.copypasta.selection
-
-import androidx.compose.foundation.newtext.text.copypasta.TextLayoutResultProxy
-import androidx.compose.foundation.newtext.text.copypasta.findFollowingBreak
-import androidx.compose.foundation.newtext.text.copypasta.findParagraphEnd
-import androidx.compose.foundation.newtext.text.copypasta.findParagraphStart
-import androidx.compose.foundation.newtext.text.copypasta.findPrecedingBreak
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.TextLayoutResult
-import androidx.compose.ui.text.TextRange
-import androidx.compose.ui.text.input.CommitTextCommand
-import androidx.compose.ui.text.input.EditCommand
-import androidx.compose.ui.text.input.OffsetMapping
-import androidx.compose.ui.text.input.SetSelectionCommand
-import androidx.compose.ui.text.input.TextFieldValue
-import androidx.compose.ui.text.style.ResolvedTextDirection
-
-internal class TextPreparedSelectionState {
-    // it's set at the start of vertical navigation and used as the preferred value to set a new
-    // cursor position.
-    var cachedX: Float? = null
-
-    fun resetCachedX() {
-        cachedX = null
-    }
-}
-
-/**
- * This utility class implements many selection-related operations on text (including basic
- * cursor movements and deletions) and combines them, taking into account how the text was
- * rendered. So, for example, [moveCursorToLineEnd] moves it to the visual line end.
- *
- * For many of these operations, it's particularly important to keep the difference between
- * selection start and selection end. In some systems, they are called "anchor" and "caret"
- * respectively. For example, for selection from scratch, after [moveCursorLeftByWord]
- * [moveCursorRight] will move the left side of the selection, but after [moveCursorRightByWord]
- * the right one.
- *
- * To use it in scope of text fields see [TextFieldPreparedSelection]
- */
-internal abstract class BaseTextPreparedSelection<T : BaseTextPreparedSelection<T>>(
-    val originalText: AnnotatedString,
-    val originalSelection: TextRange,
-    val layoutResult: TextLayoutResult?,
-    val offsetMapping: OffsetMapping,
-    val state: TextPreparedSelectionState
-) {
-    var selection = originalSelection
-
-    var annotatedString = originalText
-    internal val text
-        get() = annotatedString.text
-
-    @Suppress("UNCHECKED_CAST")
-    protected inline fun <U> U.apply(resetCachedX: Boolean = true, block: U.() -> Unit): T {
-        if (resetCachedX) {
-            state.resetCachedX()
-        }
-        if (text.isNotEmpty()) {
-            block()
-        }
-        return this as T
-    }
-
-    protected fun setCursor(offset: Int) {
-        setSelection(offset, offset)
-    }
-
-    protected fun setSelection(start: Int, end: Int) {
-        selection = TextRange(start, end)
-    }
-
-    fun selectAll() = apply {
-        setSelection(0, text.length)
-    }
-
-    fun deselect() = apply {
-        setCursor(selection.end)
-    }
-
-    fun moveCursorLeft() = apply {
-        if (isLtr()) {
-            moveCursorPrev()
-        } else {
-            moveCursorNext()
-        }
-    }
-
-    fun moveCursorRight() = apply {
-        if (isLtr()) {
-            moveCursorNext()
-        } else {
-            moveCursorPrev()
-        }
-    }
-
-    /**
-     * If there is already a selection, collapse it to the left side. Otherwise, execute [or]
-     */
-    fun collapseLeftOr(or: T.() -> Unit) = apply {
-        if (selection.collapsed) {
-            @Suppress("UNCHECKED_CAST")
-            or(this as T)
-        } else {
-            if (isLtr()) {
-                setCursor(selection.min)
-            } else {
-                setCursor(selection.max)
-            }
-        }
-    }
-
-    /**
-     * If there is already a selection, collapse it to the right side. Otherwise, execute [or]
-     */
-    fun collapseRightOr(or: T.() -> Unit) = apply {
-        if (selection.collapsed) {
-            @Suppress("UNCHECKED_CAST")
-            or(this as T)
-        } else {
-            if (isLtr()) {
-                setCursor(selection.max)
-            } else {
-                setCursor(selection.min)
-            }
-        }
-    }
-
-    /**
-     * Returns the index of the character break preceding the end of [selection].
-     */
-    fun getPrecedingCharacterIndex() = annotatedString.text.findPrecedingBreak(selection.end)
-
-    /**
-     * Returns the index of the character break following the end of [selection]. Returns
-     * [NoCharacterFound] if there are no more breaks before the end of the string.
-     */
-    fun getNextCharacterIndex() = annotatedString.text.findFollowingBreak(selection.end)
-
-    private fun moveCursorPrev() = apply {
-        val prev = getPrecedingCharacterIndex()
-        if (prev != -1) setCursor(prev)
-    }
-
-    private fun moveCursorNext() = apply {
-        val next = getNextCharacterIndex()
-        if (next != -1) setCursor(next)
-    }
-
-    fun moveCursorToHome() = apply {
-        setCursor(0)
-    }
-
-    fun moveCursorToEnd() = apply {
-        setCursor(text.length)
-    }
-
-    fun moveCursorLeftByWord() = apply {
-        if (isLtr()) {
-            moveCursorPrevByWord()
-        } else {
-            moveCursorNextByWord()
-        }
-    }
-
-    fun moveCursorRightByWord() = apply {
-        if (isLtr()) {
-            moveCursorNextByWord()
-        } else {
-            moveCursorPrevByWord()
-        }
-    }
-
-    fun getNextWordOffset(): Int? = layoutResult?.getNextWordOffsetForLayout()
-
-    private fun moveCursorNextByWord() = apply {
-        getNextWordOffset()?.let { setCursor(it) }
-    }
-
-    fun getPreviousWordOffset(): Int? = layoutResult?.getPrevWordOffset()
-
-    private fun moveCursorPrevByWord() = apply {
-        getPreviousWordOffset()?.let { setCursor(it) }
-    }
-
-    fun moveCursorPrevByParagraph() = apply {
-        setCursor(getParagraphStart())
-    }
-
-    fun moveCursorNextByParagraph() = apply {
-        setCursor(getParagraphEnd())
-    }
-
-    fun moveCursorUpByLine() = apply(false) {
-        layoutResult?.jumpByLinesOffset(-1)?.let { setCursor(it) }
-    }
-
-    fun moveCursorDownByLine() = apply(false) {
-        layoutResult?.jumpByLinesOffset(1)?.let { setCursor(it) }
-    }
-
-    fun getLineStartByOffset(): Int? = layoutResult?.getLineStartByOffsetForLayout()
-
-    fun moveCursorToLineStart() = apply {
-        getLineStartByOffset()?.let { setCursor(it) }
-    }
-
-    fun getLineEndByOffset(): Int? = layoutResult?.getLineEndByOffsetForLayout()
-
-    fun moveCursorToLineEnd() = apply {
-        getLineEndByOffset()?.let { setCursor(it) }
-    }
-
-    fun moveCursorToLineLeftSide() = apply {
-        if (isLtr()) {
-            moveCursorToLineStart()
-        } else {
-            moveCursorToLineEnd()
-        }
-    }
-
-    fun moveCursorToLineRightSide() = apply {
-        if (isLtr()) {
-            moveCursorToLineEnd()
-        } else {
-            moveCursorToLineStart()
-        }
-    }
-
-    // it selects a text from the original selection start to a current selection end
-    fun selectMovement() = apply(false) {
-        selection = TextRange(originalSelection.start, selection.end)
-    }
-
-    private fun isLtr(): Boolean {
-        val direction = layoutResult?.getParagraphDirection(transformedEndOffset())
-        return direction != ResolvedTextDirection.Rtl
-    }
-
-    private fun TextLayoutResult.getNextWordOffsetForLayout(
-        currentOffset: Int = transformedEndOffset()
-    ): Int {
-        if (currentOffset >= originalText.length) {
-            return originalText.length
-        }
-        val currentWord = getWordBoundary(charOffset(currentOffset))
-        return if (currentWord.end <= currentOffset) {
-            getNextWordOffsetForLayout(currentOffset + 1)
-        } else {
-            offsetMapping.transformedToOriginal(currentWord.end)
-        }
-    }
-
-    private fun TextLayoutResult.getPrevWordOffset(
-        currentOffset: Int = transformedEndOffset()
-    ): Int {
-        if (currentOffset < 0) {
-            return 0
-        }
-        val currentWord = getWordBoundary(charOffset(currentOffset))
-        return if (currentWord.start >= currentOffset) {
-            getPrevWordOffset(currentOffset - 1)
-        } else {
-            offsetMapping.transformedToOriginal(currentWord.start)
-        }
-    }
-
-    private fun TextLayoutResult.getLineStartByOffsetForLayout(
-        currentOffset: Int = transformedMinOffset()
-    ): Int {
-        val currentLine = getLineForOffset(currentOffset)
-        return offsetMapping.transformedToOriginal(getLineStart(currentLine))
-    }
-
-    private fun TextLayoutResult.getLineEndByOffsetForLayout(
-        currentOffset: Int = transformedMaxOffset()
-    ): Int {
-        val currentLine = getLineForOffset(currentOffset)
-        return offsetMapping.transformedToOriginal(getLineEnd(currentLine, true))
-    }
-
-    private fun TextLayoutResult.jumpByLinesOffset(linesAmount: Int): Int {
-        val currentOffset = transformedEndOffset()
-
-        if (state.cachedX == null) {
-            state.cachedX = getCursorRect(currentOffset).left
-        }
-
-        val targetLine = getLineForOffset(currentOffset) + linesAmount
-        when {
-            targetLine < 0 -> {
-                return 0
-            }
-            targetLine >= lineCount -> {
-                return text.length
-            }
-        }
-
-        val y = getLineBottom(targetLine) - 1
-        val x = state.cachedX!!.also {
-            if ((isLtr() && it >= getLineRight(targetLine)) ||
-                (!isLtr() && it <= getLineLeft(targetLine))
-            ) {
-                return getLineEnd(targetLine, true)
-            }
-        }
-
-        val newOffset = getOffsetForPosition(Offset(x, y)).let {
-            offsetMapping.transformedToOriginal(it)
-        }
-
-        return newOffset
-    }
-
-    private fun transformedEndOffset(): Int {
-        return offsetMapping.originalToTransformed(selection.end)
-    }
-
-    private fun transformedMinOffset(): Int {
-        return offsetMapping.originalToTransformed(selection.min)
-    }
-
-    private fun transformedMaxOffset(): Int {
-        return offsetMapping.originalToTransformed(selection.max)
-    }
-
-    private fun charOffset(offset: Int) =
-        offset.coerceAtMost(text.length - 1)
-
-    private fun getParagraphStart() = text.findParagraphStart(selection.min)
-
-    private fun getParagraphEnd() = text.findParagraphEnd(selection.max)
-
-    companion object {
-        /**
-         * Value returned by [getNextCharacterIndex] and [getPrecedingCharacterIndex] when no valid
-         * index could be found, e.g. it would be the end of the string.
-         *
-         * This is equivalent to `BreakIterator.DONE` on JVM/Android.
-         */
-        const val NoCharacterFound = -1
-    }
-}
-
-internal class TextPreparedSelection(
-    originalText: AnnotatedString,
-    originalSelection: TextRange,
-    layoutResult: TextLayoutResult? = null,
-    offsetMapping: OffsetMapping = OffsetMapping.Identity,
-    state: TextPreparedSelectionState = TextPreparedSelectionState()
-) : BaseTextPreparedSelection<TextPreparedSelection>(
-    originalText = originalText,
-    originalSelection = originalSelection,
-    layoutResult = layoutResult,
-    offsetMapping = offsetMapping,
-    state = state
-)
-
-internal class TextFieldPreparedSelection(
-    val currentValue: TextFieldValue,
-    offsetMapping: OffsetMapping = OffsetMapping.Identity,
-    val layoutResultProxy: TextLayoutResultProxy?,
-    state: TextPreparedSelectionState = TextPreparedSelectionState()
-) : BaseTextPreparedSelection<TextFieldPreparedSelection>(
-    originalText = currentValue.annotatedString,
-    originalSelection = currentValue.selection,
-    offsetMapping = offsetMapping,
-    layoutResult = layoutResultProxy?.value,
-    state = state
-) {
-    val value
-        get() = currentValue.copy(
-            annotatedString = annotatedString,
-            selection = selection
-        )
-
-    fun deleteIfSelectedOr(or: TextFieldPreparedSelection.() -> EditCommand?): List<EditCommand>? {
-        return if (selection.collapsed) {
-            or(this)?.let {
-                listOf(it)
-            }
-        } else {
-            listOf(
-                CommitTextCommand("", 0),
-                SetSelectionCommand(selection.min, selection.min)
-            )
-        }
-    }
-
-    fun moveCursorUpByPage() = apply(false) {
-        layoutResultProxy?.jumpByPagesOffset(-1)?.let { setCursor(it) }
-    }
-
-    fun moveCursorDownByPage() = apply(false) {
-        layoutResultProxy?.jumpByPagesOffset(1)?.let { setCursor(it) }
-    }
-
-    /**
-     * Returns a cursor position after jumping back or forth by [pagesAmount] number of pages,
-     * where `page` is the visible amount of space in the text field
-     */
-    private fun TextLayoutResultProxy.jumpByPagesOffset(pagesAmount: Int): Int {
-        val visibleInnerTextFieldRect = innerTextFieldCoordinates?.let { inner ->
-            decorationBoxCoordinates?.localBoundingBoxOf(inner)
-        } ?: Rect.Zero
-        val currentOffset = offsetMapping.originalToTransformed(currentValue.selection.end)
-        val currentPos = value.getCursorRect(currentOffset)
-        val x = currentPos.left
-        val y = currentPos.top + visibleInnerTextFieldRect.size.height * pagesAmount
-        return offsetMapping.transformedToOriginal(
-            value.getOffsetForPosition(Offset(x, y))
-        )
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/TextSelectionColors.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/TextSelectionColors.kt
deleted file mode 100644
index 6cc76ee..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/TextSelectionColors.kt
+++ /dev/null
@@ -1,79 +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.foundation.newtext.text.copypasta.selection
-
-import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.Stable
-import androidx.compose.runtime.compositionLocalOf
-import androidx.compose.ui.graphics.Color
-
-/**
- * Represents the colors used for text selection by text and text field components.
- *
- * See [LocalTextSelectionColors] to provide new values for this throughout the hierarchy.
- *
- * @property handleColor the color used for the selection handles on either side of the
- * selection region.
- * @property backgroundColor the color used to draw the background behind the selected
- * region. This color should have alpha applied to keep the text legible - this alpha is
- * typically 0.4f (40%) but this may need to be reduced in order to meet contrast requirements
- * depending on the color used for text, selection background, and the background behind the
- * selection background.
- */
-@Immutable
-class TextSelectionColors(
-    val handleColor: Color,
-    val backgroundColor: Color
-) {
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        if (other !is TextSelectionColors) return false
-
-        if (handleColor != other.handleColor) return false
-        if (backgroundColor != other.backgroundColor) return false
-
-        return true
-    }
-
-    override fun hashCode(): Int {
-        var result = handleColor.hashCode()
-        result = 31 * result + backgroundColor.hashCode()
-        return result
-    }
-
-    override fun toString(): String {
-        return "SelectionColors(selectionHandleColor=$handleColor, " +
-            "selectionBackgroundColor=$backgroundColor)"
-    }
-}
-
-/**
- * CompositionLocal used to change the [TextSelectionColors] used by text and text field
- * components in the hierarchy.
- */
-val LocalTextSelectionColors = compositionLocalOf { DefaultTextSelectionColors }
-
-/**
- * Default color used is the blue from the Compose logo, b/172679845 for context
- */
-private val DefaultSelectionColor = Color(0xFF4286F4)
-
-@Stable
-private val DefaultTextSelectionColors = TextSelectionColors(
-    handleColor = DefaultSelectionColor,
-    backgroundColor = DefaultSelectionColor.copy(alpha = 0.4f)
-)
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/TextSelectionDelegate.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/TextSelectionDelegate.kt
deleted file mode 100644
index 5a530ec..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/TextSelectionDelegate.kt
+++ /dev/null
@@ -1,62 +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.foundation.newtext.text.copypasta.selection
-
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.text.TextLayoutResult
-import kotlin.math.max
-
-/**
- * This method returns the graphical position where the selection handle should be based on the
- * offset and other information.
- *
- * @param textLayoutResult a result of the text layout.
- * @param offset character offset to be calculated
- * @param isStart true if called for selection start handle
- * @param areHandlesCrossed true if the selection handles are crossed
- *
- * @return the graphical position where the selection handle should be.
- */
-internal fun getSelectionHandleCoordinates(
-    textLayoutResult: TextLayoutResult,
-    offset: Int,
-    isStart: Boolean,
-    areHandlesCrossed: Boolean
-): Offset {
-    val line = textLayoutResult.getLineForOffset(offset)
-    val x = textLayoutResult.getHorizontalPosition(offset, isStart, areHandlesCrossed)
-    val y = textLayoutResult.getLineBottom(line)
-
-    return Offset(x, y)
-}
-
-internal fun TextLayoutResult.getHorizontalPosition(
-    offset: Int,
-    isStart: Boolean,
-    areHandlesCrossed: Boolean
-): Float {
-    val offsetToCheck =
-        if (isStart && !areHandlesCrossed || !isStart && areHandlesCrossed) offset
-        else max(offset - 1, 0)
-    val bidiRunDirection = getBidiRunDirection(offsetToCheck)
-    val paragraphDirection = getParagraphDirection(offset)
-
-    return getHorizontalPosition(
-        offset = offset,
-        usePrimaryDirection = bidiRunDirection == paragraphDirection
-    )
-}
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/TextSelectionMouseDetector.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/TextSelectionMouseDetector.kt
deleted file mode 100644
index 3931bc4..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/TextSelectionMouseDetector.kt
+++ /dev/null
@@ -1,135 +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.foundation.newtext.text.copypasta.selection
-
-import androidx.compose.foundation.gestures.awaitEachGesture
-import androidx.compose.foundation.gestures.drag
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.input.pointer.AwaitPointerEventScope
-import androidx.compose.ui.input.pointer.PointerEvent
-import androidx.compose.ui.input.pointer.PointerEventPass
-import androidx.compose.ui.input.pointer.PointerInputChange
-import androidx.compose.ui.input.pointer.PointerInputScope
-import androidx.compose.ui.input.pointer.PointerType
-import androidx.compose.ui.input.pointer.changedToDown
-import androidx.compose.ui.input.pointer.isPrimaryPressed
-import androidx.compose.ui.input.pointer.isShiftPressed
-import androidx.compose.ui.platform.ViewConfiguration
-import androidx.compose.ui.util.fastAll
-
-// * Without shift it starts the new selection from the scratch.
-// * With shift expand / shrink existed selection.
-// * Click sets start and end of the selection, but shift click only the end of
-// selection.
-// * The specific case of it when selection is collapsed, but the same logic is
-// applied for not collapsed selection too.
-internal interface MouseSelectionObserver {
-    // on start of shift click. if returns true event will be consumed
-    fun onExtend(downPosition: Offset): Boolean
-    // on drag after shift click. if returns true event will be consumed
-    fun onExtendDrag(dragPosition: Offset): Boolean
-
-    // if returns true event will be consumed
-    fun onStart(downPosition: Offset, adjustment: SelectionAdjustment): Boolean
-    fun onDrag(dragPosition: Offset, adjustment: SelectionAdjustment): Boolean
-}
-
-// Distance in pixels between consecutive click positions to be considered them as clicks sequence
-internal const val ClicksSlop = 100.0
-
-private class ClicksCounter(
-    private val viewConfiguration: ViewConfiguration
-) {
-    var clicks = 0
-    var prevClick: PointerInputChange? = null
-    fun update(event: PointerEvent) {
-        val currentPrevClick = prevClick
-        val newClick = event.changes[0]
-        if (currentPrevClick != null &&
-            timeIsTolerable(currentPrevClick, newClick) &&
-            positionIsTolerable(currentPrevClick, newClick)
-        ) {
-            clicks += 1
-        } else {
-            clicks = 1
-        }
-        prevClick = newClick
-    }
-
-    fun timeIsTolerable(prevClick: PointerInputChange, newClick: PointerInputChange): Boolean {
-        val diff = newClick.uptimeMillis - prevClick.uptimeMillis
-        return diff < viewConfiguration.doubleTapTimeoutMillis
-    }
-
-    fun positionIsTolerable(prevClick: PointerInputChange, newClick: PointerInputChange): Boolean {
-        val diff = newClick.position - prevClick.position
-        return diff.getDistance() < ClicksSlop
-    }
-}
-
-internal suspend fun PointerInputScope.mouseSelectionDetector(
-    observer: MouseSelectionObserver
-) {
-    awaitEachGesture {
-        val clicksCounter = ClicksCounter(viewConfiguration)
-        while (true) {
-            val down: PointerEvent = awaitMouseEventDown()
-            clicksCounter.update(down)
-            val downChange = down.changes[0]
-            if (down.keyboardModifiers.isShiftPressed) {
-                val started = observer.onExtend(downChange.position)
-                if (started) {
-                    downChange.consume()
-                    drag(downChange.id) {
-                        if (observer.onExtendDrag(it.position)) {
-                            it.consume()
-                        }
-                    }
-                }
-            } else {
-                val selectionMode = when (clicksCounter.clicks) {
-                    1 -> SelectionAdjustment.None
-                    2 -> SelectionAdjustment.Word
-                    else -> SelectionAdjustment.Paragraph
-                }
-                val started = observer.onStart(downChange.position, selectionMode)
-                if (started) {
-                    downChange.consume()
-                    drag(downChange.id) {
-                        if (observer.onDrag(it.position, selectionMode)) {
-                            it.consume()
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
-private suspend fun AwaitPointerEventScope.awaitMouseEventDown(): PointerEvent {
-    var event: PointerEvent
-    do {
-        event = awaitPointerEvent(PointerEventPass.Main)
-    } while (
-        !(
-            event.buttons.isPrimaryPressed && event.changes.fastAll {
-                it.type == PointerType.Mouse && it.changedToDown()
-            }
-            )
-    )
-    return event
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/MinMaxLinesCoercer.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/MinMaxLinesCoercer.kt
deleted file mode 100644
index c616ac9..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/MinMaxLinesCoercer.kt
+++ /dev/null
@@ -1,152 +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.newtext.text.modifiers
-
-import androidx.compose.ui.text.Paragraph
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.resolveDefaults
-import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.LayoutDirection
-import kotlin.math.roundToInt
-
-internal class MinMaxLinesCoercer private constructor(
-    val layoutDirection: LayoutDirection,
-    val inputTextStyle: TextStyle,
-    val density: Density,
-    val fontFamilyResolver: FontFamily.Resolver
-) {
-    private val resolvedStyle = resolveDefaults(inputTextStyle, layoutDirection)
-    private var lineHeightCache: Float = Float.NaN
-    private var oneLineHeightCache: Float = Float.NaN
-
-    companion object {
-        // LRU cache of one since this tends to be used for similar styles
-        // ... it may be useful to increase this cache if requested by some dev use case
-        private var last: MinMaxLinesCoercer? = null
-
-        /**
-         * Returns a coercer (possibly cached) with these parameters
-         */
-        fun from(
-            minMaxUtil: MinMaxLinesCoercer?,
-            layoutDirection: LayoutDirection,
-            paramStyle: TextStyle,
-            density: Density,
-            fontFamilyResolver: FontFamily.Resolver
-        ): MinMaxLinesCoercer {
-            minMaxUtil?.let {
-                if (layoutDirection == it.layoutDirection &&
-                    paramStyle == it.inputTextStyle &&
-                    density.density == it.density.density &&
-                    fontFamilyResolver === it.fontFamilyResolver) {
-                    return it
-                }
-            }
-            last?.let {
-                if (layoutDirection == it.layoutDirection &&
-                    paramStyle == it.inputTextStyle &&
-                    density.density == it.density.density &&
-                    fontFamilyResolver === it.fontFamilyResolver) {
-                    return it
-                }
-            }
-            return MinMaxLinesCoercer(
-                layoutDirection,
-                resolveDefaults(paramStyle, layoutDirection),
-                density,
-                fontFamilyResolver
-            ).also {
-                last = it
-            }
-        }
-    }
-
-    /**
-     * Coerce inConstraints to have min and max lines applied.
-     *
-     * On first invocation this will cause (2) Paragraph measurements.
-     */
-    internal fun coerceMaxMinLines(
-        inConstraints: Constraints,
-        minLines: Int,
-        maxLines: Int,
-    ): Constraints {
-        var oneLineHeight = oneLineHeightCache
-        var lineHeight = lineHeightCache
-        if (oneLineHeight.isNaN() || lineHeight.isNaN()) {
-            oneLineHeight = Paragraph(
-                text = EmptyTextReplacement,
-                style = resolvedStyle,
-                constraints = Constraints(),
-                density = density,
-                fontFamilyResolver = fontFamilyResolver,
-                maxLines = 1,
-                ellipsis = false
-            ).height
-
-            val twoLineHeight = Paragraph(
-                text = TwoLineTextReplacement,
-                style = resolvedStyle,
-                constraints = Constraints(),
-                density = density,
-                fontFamilyResolver = fontFamilyResolver,
-                maxLines = 2,
-                ellipsis = false
-            ).height
-
-            lineHeight = twoLineHeight - oneLineHeight
-            oneLineHeightCache = oneLineHeight
-            lineHeightCache = lineHeight
-        }
-        val maxHeight = if (maxLines != Int.MAX_VALUE) {
-            (oneLineHeight + (lineHeight * (maxLines - 1)))
-                .roundToInt()
-                .coerceAtLeast(0)
-        } else {
-            inConstraints.maxHeight
-        }
-        val minHeight = if (minLines != 1) {
-            (oneLineHeight + (lineHeight * (minLines - 1)))
-                .roundToInt()
-                .coerceAtLeast(0)
-                .coerceAtMost(maxHeight)
-        } else {
-            inConstraints.minHeight
-        }
-        return Constraints(
-            minHeight = minHeight,
-            maxHeight = maxHeight,
-            minWidth = inConstraints.minWidth,
-            maxWidth = inConstraints.maxWidth,
-        )
-    }
-}
-
-private const val DefaultWidthCharCount = 10 // min width for TextField is 10 chars long
-private val EmptyTextReplacement = "H".repeat(DefaultWidthCharCount) // just a reference character.
-private val TwoLineTextReplacement = EmptyTextReplacement + "\n" + EmptyTextReplacement
-
-internal fun validateMinMaxLines(minLines: Int, maxLines: Int) {
-    require(minLines > 0 && maxLines > 0) {
-        "both minLines $minLines and maxLines $maxLines must be greater than zero"
-    }
-    require(minLines <= maxLines) {
-        "minLines $minLines must be less than or equal to maxLines $maxLines"
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/MultiParagraphLayoutCache.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/MultiParagraphLayoutCache.kt
deleted file mode 100644
index fa494d1..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/MultiParagraphLayoutCache.kt
+++ /dev/null
@@ -1,352 +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.newtext.text.modifiers
-
-import androidx.compose.foundation.newtext.text.DefaultMinLines
-import androidx.compose.foundation.newtext.text.ceilToIntPx
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.MultiParagraph
-import androidx.compose.ui.text.MultiParagraphIntrinsics
-import androidx.compose.ui.text.Placeholder
-import androidx.compose.ui.text.TextLayoutInput
-import androidx.compose.ui.text.TextLayoutResult
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.resolveDefaults
-import androidx.compose.ui.text.style.LineBreak
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.constrain
-
-internal class MultiParagraphLayoutCache(
-    private var text: AnnotatedString,
-    private var style: TextStyle,
-    private var fontFamilyResolver: FontFamily.Resolver,
-    private var overflow: TextOverflow = TextOverflow.Clip,
-    private var softWrap: Boolean = true,
-    private var maxLines: Int = Int.MAX_VALUE,
-    private var minLines: Int = DefaultMinLines,
-    private var placeholders: List<AnnotatedString.Range<Placeholder>>? = null,
-) {
-    private var minMaxLinesCoercer: MinMaxLinesCoercer? = null
-    internal var density: Density? = null
-        set(value) {
-            val localField = field
-            if (value == null || localField == null) {
-                field = value
-                return
-            }
-
-            if (localField.density != value.density || localField.fontScale != value.fontScale) {
-                field = value
-                // none of our results are correct if density changed
-                markDirty()
-            }
-        }
-
-    /*@VisibleForTesting*/
-    // NOTE(text-perf-review): it seems like TextDelegate essentially guarantees that we use
-    // MultiParagraph. Can we have a fast-path that uses just Paragraph in simpler cases (ie,
-    // String)?
-    private var paragraphIntrinsics: MultiParagraphIntrinsics? = null
-
-    private var intrinsicsLayoutDirection: LayoutDirection? = null
-
-    private var layoutCache: TextLayoutResult? = null
-    private var cachedIntrinsicHeight: Pair<Int, Int>? = null
-
-    private val nonNullIntrinsics: MultiParagraphIntrinsics
-        get() = paragraphIntrinsics ?: throw IllegalStateException(
-            "MeasureScope.measure() must be called first to query text intrinsics"
-        )
-
-    /**
-     * The width for text if all soft wrap opportunities were taken.
-     *
-     * Valid only after [layoutWithConstraints] has been called.
-     */
-    val minIntrinsicWidth: Int get() = nonNullIntrinsics.minIntrinsicWidth.ceilToIntPx()
-
-    /**
-     * The width at which increasing the width of the text no lonfger decreases the height.
-     *
-     * Valid only after [layoutWithConstraints] has been called.
-     */
-    val maxIntrinsicWidth: Int get() = nonNullIntrinsics.maxIntrinsicWidth.ceilToIntPx()
-
-    val layout: TextLayoutResult
-        get() = layoutCache
-            ?: throw IllegalStateException("You must call doLayoutInConstraints first")
-
-    val layoutOrNull: TextLayoutResult?
-        get() = layoutCache
-
-    /**
-     * Update layout constraints for this text
-     *
-     * @return true if constraints caused a text layout invalidation
-     */
-    fun layoutWithConstraints(
-        constraints: Constraints,
-        layoutDirection: LayoutDirection
-    ): Boolean {
-        if (!layoutCache.newLayoutWillBeDifferent(constraints, layoutDirection)) {
-            return false
-        }
-        val finalConstraints = if (maxLines != Int.MAX_VALUE || minLines > 1) {
-            val localMinMax = MinMaxLinesCoercer.from(
-                minMaxLinesCoercer,
-                layoutDirection,
-                style,
-                density!!,
-                fontFamilyResolver
-            ).also {
-                minMaxLinesCoercer = it
-            }
-            localMinMax.coerceMaxMinLines(
-                inConstraints = constraints,
-                minLines = minLines,
-                maxLines = maxLines
-            )
-        } else {
-            constraints
-        }
-        val multiParagraph = layoutText(finalConstraints, layoutDirection)
-
-        val size = finalConstraints.constrain(
-            IntSize(
-                multiParagraph.width.ceilToIntPx(),
-                multiParagraph.height.ceilToIntPx()
-            )
-        )
-
-        layoutCache = TextLayoutResult(
-            TextLayoutInput(
-                text,
-                style,
-                placeholders.orEmpty(),
-                maxLines,
-                softWrap,
-                overflow,
-                density!!,
-                layoutDirection,
-                fontFamilyResolver,
-                finalConstraints
-            ),
-            multiParagraph,
-            size
-        )
-        return true
-    }
-
-    fun intrinsicHeightAt(width: Int, layoutDirection: LayoutDirection): Int {
-        cachedIntrinsicHeight?.let { (prevWidth, prevHeight) ->
-            if (width == prevWidth) return prevHeight
-        }
-        val result = layoutText(
-            Constraints(0, width, 0, Constraints.Infinity),
-            layoutDirection
-        ).height.ceilToIntPx()
-
-        cachedIntrinsicHeight = width to result
-        return result
-    }
-
-    fun update(
-        text: AnnotatedString,
-        style: TextStyle,
-        fontFamilyResolver: FontFamily.Resolver,
-        overflow: TextOverflow,
-        softWrap: Boolean,
-        maxLines: Int,
-        minLines: Int,
-        placeholders: List<AnnotatedString.Range<Placeholder>>?
-    ) {
-        this.text = text
-        this.style = style
-        this.fontFamilyResolver = fontFamilyResolver
-        this.overflow = overflow
-        this.softWrap = softWrap
-        this.maxLines = maxLines
-        this.minLines = minLines
-        this.placeholders = placeholders
-        markDirty()
-    }
-
-    private fun setLayoutDirection(layoutDirection: LayoutDirection) {
-        val localIntrinsics = paragraphIntrinsics
-        val intrinsics = if (
-            localIntrinsics == null ||
-            layoutDirection != intrinsicsLayoutDirection ||
-            localIntrinsics.hasStaleResolvedFonts
-        ) {
-            intrinsicsLayoutDirection = layoutDirection
-            MultiParagraphIntrinsics(
-                annotatedString = text,
-                style = resolveDefaults(style, layoutDirection),
-                density = density!!,
-                fontFamilyResolver = fontFamilyResolver,
-                placeholders = placeholders.orEmpty()
-            )
-        } else {
-            localIntrinsics
-        }
-
-        paragraphIntrinsics = intrinsics
-    }
-
-    /**
-     * Computes the visual position of the glyphs for painting the text.
-     *
-     * The text will layout with a width that's as close to its max intrinsic width as possible
-     * while still being greater than or equal to `minWidth` and less than or equal to `maxWidth`.
-     */
-    private fun layoutText(
-        constraints: Constraints,
-        layoutDirection: LayoutDirection
-    ): MultiParagraph {
-        setLayoutDirection(layoutDirection)
-
-        val minWidth = constraints.minWidth
-        val widthMatters = softWrap || overflow == TextOverflow.Ellipsis
-        val maxWidth = if (widthMatters && constraints.hasBoundedWidth) {
-            constraints.maxWidth
-        } else {
-            Constraints.Infinity
-        }
-
-        // This is a fallback behavior because native text layout doesn't support multiple
-        // ellipsis in one text layout.
-        // When softWrap is turned off and overflow is ellipsis, it's expected that each line
-        // that exceeds maxWidth will be ellipsized.
-        // For example,
-        // input text:
-        //     "AAAA\nAAAA"
-        // maxWidth:
-        //     3 * fontSize that only allow 3 characters to be displayed each line.
-        // expected output:
-        //     AA…
-        //     AA…
-        // Here we assume there won't be any '\n' character when softWrap is false. And make
-        // maxLines 1 to implement the similar behavior.
-        val overwriteMaxLines = !softWrap && overflow == TextOverflow.Ellipsis
-        val finalMaxLines = if (overwriteMaxLines) 1 else maxLines.coerceAtLeast(1)
-
-        // if minWidth == maxWidth the width is fixed.
-        //    therefore we can pass that value to our paragraph and use it
-        // if minWidth != maxWidth there is a range
-        //    then we should check if the max intrinsic width is in this range to decide the
-        //    width to be passed to Paragraph
-        //        if max intrinsic width is between minWidth and maxWidth
-        //           we can use it to layout
-        //        else if max intrinsic width is greater than maxWidth, we can only use maxWidth
-        //        else if max intrinsic width is less than minWidth, we should use minWidth
-        val width = if (minWidth == maxWidth) {
-            maxWidth
-        } else {
-            maxIntrinsicWidth.coerceIn(minWidth, maxWidth)
-        }
-
-        return MultiParagraph(
-            intrinsics = nonNullIntrinsics,
-            constraints = Constraints(maxWidth = width, maxHeight = constraints.maxHeight),
-            // This is a fallback behavior for ellipsis. Native
-            maxLines = finalMaxLines,
-            ellipsis = overflow == TextOverflow.Ellipsis
-        )
-    }
-
-    private fun TextLayoutResult?.newLayoutWillBeDifferent(
-        constraints: Constraints,
-        layoutDirection: LayoutDirection
-    ): Boolean {
-        // no layout yet
-        if (this == null) return true
-
-        // async typeface changes
-        if (this.multiParagraph.intrinsics.hasStaleResolvedFonts) return true
-
-        // layout direction changed
-        if (layoutDirection != layoutInput.layoutDirection) return true
-
-        // if we were passed identical constraints just skip more work
-        if (constraints == layoutInput.constraints) return false
-
-        // only be clever if we can predict line break behavior exactly, which is only possible with
-        // simple geometry math for the greedy layout case
-        if (style.lineBreak != LineBreak.Simple) {
-            return true
-        }
-
-        // see if width would produce the same wraps (greedy wraps only)
-        val canWrap = softWrap && maxLines > 1
-        if (canWrap && size.width != multiParagraph.maxIntrinsicWidth.ceilToIntPx()) {
-            // some soft wrapping happened, check to see if we're between the previous measure and
-            // the next wrap
-            val prevActualMaxWidth = maxWidth(layoutInput.constraints)
-            val newMaxWidth = maxWidth(constraints)
-            if (newMaxWidth > prevActualMaxWidth) {
-                // we've grown the potential layout area, and may break longer lines
-                return true
-            }
-            if (newMaxWidth <= size.width) {
-                // it's possible to shrink this text (possible opt: check minIntrinsicWidth
-                return true
-            }
-        }
-
-        // check any constraint width changes for single line text
-        if (!canWrap &&
-            (constraints.maxWidth != layoutInput.constraints.maxWidth ||
-                (constraints.minWidth != layoutInput.constraints.minWidth))) {
-            // no soft wrap and width is different, always invalidate
-            return true
-        }
-
-        // if we get here width won't change, height may be clipped
-        if (constraints.maxHeight < multiParagraph.height) {
-            // vertical clip changes
-            return true
-        }
-
-        // breaks can't change, height can't change
-        return false
-    }
-
-    private fun maxWidth(constraints: Constraints): Int {
-        val minWidth = constraints.minWidth
-        val widthMatters = softWrap || overflow == TextOverflow.Ellipsis
-        val maxWidth = if (widthMatters && constraints.hasBoundedWidth) {
-            constraints.maxWidth
-        } else {
-            Constraints.Infinity
-        }
-        return if (minWidth == maxWidth) {
-            maxWidth
-        } else {
-            maxIntrinsicWidth.coerceIn(minWidth, maxWidth)
-        }
-    }
-
-    private fun markDirty() {
-        paragraphIntrinsics = null
-        layoutCache = null
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/ParagraphLayoutCache.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/ParagraphLayoutCache.kt
deleted file mode 100644
index 45330d0..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/ParagraphLayoutCache.kt
+++ /dev/null
@@ -1,389 +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.newtext.text.modifiers
-
-import androidx.compose.foundation.newtext.text.DefaultMinLines
-import androidx.compose.foundation.newtext.text.ceilToIntPx
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.MultiParagraph
-import androidx.compose.ui.text.MultiParagraphIntrinsics
-import androidx.compose.ui.text.Paragraph
-import androidx.compose.ui.text.ParagraphIntrinsics
-import androidx.compose.ui.text.TextLayoutInput
-import androidx.compose.ui.text.TextLayoutResult
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.resolveDefaults
-import androidx.compose.ui.text.style.LineBreak
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.constrain
-
-internal class ParagraphLayoutCache(
-    private var text: String,
-    private var style: TextStyle,
-    private var fontFamilyResolver: FontFamily.Resolver,
-    private var overflow: TextOverflow = TextOverflow.Clip,
-    private var softWrap: Boolean = true,
-    private var maxLines: Int = Int.MAX_VALUE,
-    private var minLines: Int = DefaultMinLines,
-) {
-    internal var density: Density? = null
-        set(value) {
-            val localField = field
-            if (localField == null) {
-                field = value
-                return
-            }
-
-            if (value == null) {
-                field = value
-                markDirty()
-                return
-            }
-
-            if (localField.density != value.density || localField.fontScale != value.fontScale) {
-                field = value
-                // none of our results are correct if density changed
-                markDirty()
-            }
-        }
-    internal val observeFontChanges: Unit
-        get() {
-            paragraphIntrinsics?.hasStaleResolvedFonts
-        }
-
-    internal var paragraph: Paragraph? = null
-    internal var didOverflow: Boolean = false
-    internal var layoutSize: IntSize = IntSize(0, 0)
-
-    private var minMaxLinesCoercer: MinMaxLinesCoercer? = null
-    private var paragraphIntrinsics: ParagraphIntrinsics? = null
-
-    private var intrinsicsLayoutDirection: LayoutDirection? = null
-    private var prevConstraints: Constraints = Constraints.fixed(0, 0)
-
-    private var cachedIntrinsicWidth: Int = -1
-    private var cachedIntrinsicHeight: Int = -1
-
-    private val nonNullIntrinsics: ParagraphIntrinsics
-        get() = paragraphIntrinsics ?: throw IllegalStateException(
-            "MeasureScope.measure() must be called first to query text intrinsics"
-        )
-
-    /**
-     * The width for text if all soft wrap opportunities were taken.
-     *
-     * Valid only after [layoutWithConstraints] has been called.
-     */
-    val minIntrinsicWidth: Int get() = nonNullIntrinsics.minIntrinsicWidth.ceilToIntPx()
-
-    /**
-     * The width at which increasing the width of the text no lonfger decreases the height.
-     *
-     * Valid only after [layoutWithConstraints] has been called.
-     */
-    val maxIntrinsicWidth: Int get() = nonNullIntrinsics.maxIntrinsicWidth.ceilToIntPx()
-
-    /**
-     * Update layout constraints for this text
-     *
-     * @return true if constraints caused a text layout invalidation
-     */
-    fun layoutWithConstraints(
-        constraints: Constraints,
-        layoutDirection: LayoutDirection
-    ): Boolean {
-        val finalConstraints = if (maxLines != Int.MAX_VALUE || minLines > 1) {
-            val localMinMax = MinMaxLinesCoercer.from(
-                minMaxLinesCoercer,
-                layoutDirection,
-                style,
-                density!!,
-                fontFamilyResolver
-            ).also {
-                minMaxLinesCoercer = it
-            }
-            localMinMax.coerceMaxMinLines(
-                inConstraints = constraints,
-                minLines = minLines,
-                maxLines = maxLines
-            )
-        } else {
-            constraints
-        }
-        if (!newLayoutWillBeDifferent(finalConstraints, layoutDirection)) {
-            return false
-        }
-        paragraph = layoutText(finalConstraints, layoutDirection).also {
-            prevConstraints = finalConstraints
-            val localSize = finalConstraints.constrain(
-                IntSize(
-                    it.width.ceilToIntPx(),
-                    it.height.ceilToIntPx()
-                )
-            )
-            layoutSize = localSize
-            didOverflow = overflow != TextOverflow.Visible &&
-                (localSize.width < it.width || localSize.height < it.height)
-        }
-        return true
-    }
-
-    fun intrinsicHeightAt(width: Int, layoutDirection: LayoutDirection): Int {
-        val localWidth = cachedIntrinsicWidth
-        val localHeght = cachedIntrinsicHeight
-        if (width == localWidth && localWidth != -1) return localHeght
-        val result = layoutText(
-            Constraints(0, width, 0, Constraints.Infinity),
-            layoutDirection
-        ).height.ceilToIntPx()
-
-        cachedIntrinsicWidth = width
-        cachedIntrinsicHeight = result
-        return result
-    }
-
-    fun update(
-        text: String,
-        style: TextStyle,
-        fontFamilyResolver: FontFamily.Resolver,
-        overflow: TextOverflow,
-        softWrap: Boolean,
-        maxLines: Int,
-        minLines: Int
-    ) {
-        this.text = text
-        this.style = style
-        this.fontFamilyResolver = fontFamilyResolver
-        this.overflow = overflow
-        this.softWrap = softWrap
-        this.maxLines = maxLines
-        this.minLines = minLines
-        markDirty()
-    }
-
-    private fun setLayoutDirection(layoutDirection: LayoutDirection) {
-        val localIntrinsics = paragraphIntrinsics
-        val intrinsics = if (
-            localIntrinsics == null ||
-            layoutDirection != intrinsicsLayoutDirection ||
-            localIntrinsics.hasStaleResolvedFonts
-        ) {
-            intrinsicsLayoutDirection = layoutDirection
-            ParagraphIntrinsics(
-                text = text,
-                style = resolveDefaults(style, layoutDirection),
-                density = density!!,
-                fontFamilyResolver = fontFamilyResolver
-            )
-        } else {
-            localIntrinsics
-        }
-        paragraphIntrinsics = intrinsics
-    }
-
-    /**
-     * Computes the visual position of the glyphs for painting the text.
-     *
-     * The text will layout with a width that's as close to its max intrinsic width as possible
-     * while still being greater than or equal to `minWidth` and less than or equal to `maxWidth`.
-     */
-    private fun layoutText(
-        constraints: Constraints,
-        layoutDirection: LayoutDirection
-    ): Paragraph {
-        setLayoutDirection(layoutDirection)
-
-        val minWidth = constraints.minWidth
-        val widthMatters = softWrap || overflow == TextOverflow.Ellipsis
-        val maxWidth = if (widthMatters && constraints.hasBoundedWidth) {
-            constraints.maxWidth
-        } else {
-            Constraints.Infinity
-        }
-
-        // This is a fallback behavior because native text layout doesn't support multiple
-        // ellipsis in one text layout.
-        // When softWrap is turned off and overflow is ellipsis, it's expected that each line
-        // that exceeds maxWidth will be ellipsized.
-        // For example,
-        // input text:
-        //     "AAAA\nAAAA"
-        // maxWidth:
-        //     3 * fontSize that only allow 3 characters to be displayed each line.
-        // expected output:
-        //     AA…
-        //     AA…
-        // Here we assume there won't be any '\n' character when softWrap is false. And make
-        // maxLines 1 to implement the similar behavior.
-        val overwriteMaxLines = !softWrap && overflow == TextOverflow.Ellipsis
-        val finalMaxLines = if (overwriteMaxLines) 1 else maxLines.coerceAtLeast(1)
-
-        // if minWidth == maxWidth the width is fixed.
-        //    therefore we can pass that value to our paragraph and use it
-        // if minWidth != maxWidth there is a range
-        //    then we should check if the max intrinsic width is in this range to decide the
-        //    width to be passed to Paragraph
-        //        if max intrinsic width is between minWidth and maxWidth
-        //           we can use it to layout
-        //        else if max intrinsic width is greater than maxWidth, we can only use maxWidth
-        //        else if max intrinsic width is less than minWidth, we should use minWidth
-        val width = if (minWidth == maxWidth) {
-            maxWidth
-        } else {
-            maxIntrinsicWidth.coerceIn(minWidth, maxWidth)
-        }
-
-        val finalConstraints = Constraints(maxWidth = width, maxHeight = constraints.maxHeight)
-        return Paragraph(
-            paragraphIntrinsics = nonNullIntrinsics,
-            constraints = finalConstraints,
-            // This is a fallback behavior for ellipsis. Native
-            maxLines = finalMaxLines,
-            ellipsis = overflow == TextOverflow.Ellipsis
-        )
-    }
-
-    private fun newLayoutWillBeDifferent(
-        constraints: Constraints,
-        layoutDirection: LayoutDirection
-    ): Boolean {
-        val localParagraph = paragraph ?: return true
-        val localParagraphIntrinsics = paragraphIntrinsics ?: return true
-        // no layout yet
-
-        // async typeface changes
-        if (localParagraphIntrinsics.hasStaleResolvedFonts) return true
-
-        // layout direction changed
-        if (layoutDirection != intrinsicsLayoutDirection) return true
-
-        // if we were passed identical constraints just skip more work
-        if (constraints == prevConstraints) return false
-
-        // only be clever if we can predict line break behavior exactly, which is only possible with
-        // simple geometry math for the greedy layout case
-        if (style.lineBreak != LineBreak.Simple) {
-            return true
-        }
-
-        // see if width would produce the same wraps (greedy wraps only)
-        val canWrap = softWrap && maxLines > 1
-        if (canWrap && layoutSize.width != localParagraph.maxIntrinsicWidth.ceilToIntPx()) {
-            // some soft wrapping happened, check to see if we're between the previous measure and
-            // the next wrap
-            val prevActualMaxWidth = maxWidth(prevConstraints)
-            val newMaxWidth = maxWidth(constraints)
-            if (newMaxWidth > prevActualMaxWidth) {
-                // we've grown the potential layout area, and may break longer lines
-                return true
-            }
-            if (newMaxWidth <= layoutSize.width) {
-                // it's possible to shrink this text (possible opt: check minIntrinsicWidth
-                return true
-            }
-        }
-
-        // check any constraint width changes for single line text
-        if (!canWrap &&
-            (constraints.maxWidth != prevConstraints.maxWidth ||
-                (constraints.minWidth != prevConstraints.minWidth))) {
-            // no soft wrap and width is different, always invalidate
-            return true
-        }
-
-        // if we get here width won't change, height may be clipped
-        if (constraints.maxHeight < localParagraph.height) {
-            // vertical clip changes
-            return true
-        }
-
-        // breaks can't change, height can't change
-        return false
-    }
-
-    private fun maxWidth(constraints: Constraints): Int {
-        val minWidth = constraints.minWidth
-        val widthMatters = softWrap || overflow == TextOverflow.Ellipsis
-        val maxWidth = if (widthMatters && constraints.hasBoundedWidth) {
-            constraints.maxWidth
-        } else {
-            Constraints.Infinity
-        }
-        return if (minWidth == maxWidth) {
-            maxWidth
-        } else {
-            maxIntrinsicWidth.coerceIn(minWidth, maxWidth)
-        }
-    }
-
-    private fun markDirty() {
-        paragraph = null
-        paragraphIntrinsics = null
-        intrinsicsLayoutDirection = null
-        cachedIntrinsicWidth = -1
-        cachedIntrinsicHeight = -1
-        prevConstraints = Constraints.fixed(0, 0)
-        layoutSize = IntSize(0, 0)
-        didOverflow = false
-    }
-
-    /**
-     * This does an entire Text layout to produce the result, it is slow
-     */
-    fun slowCreateTextLayoutResultOrNull(): TextLayoutResult? {
-        // make sure we're in a valid place
-        val localLayoutDirection = intrinsicsLayoutDirection ?: return null
-        val localDensity = density ?: return null
-        val annotatedString = AnnotatedString(text)
-        paragraph ?: return null
-        paragraphIntrinsics ?: return null
-
-        // and redo layout with MultiParagraph
-        return TextLayoutResult(
-            TextLayoutInput(
-                annotatedString,
-                style,
-                emptyList(),
-                maxLines,
-                softWrap,
-                overflow,
-                localDensity,
-                localLayoutDirection,
-                fontFamilyResolver,
-                prevConstraints
-            ),
-            MultiParagraph(
-                MultiParagraphIntrinsics(
-                    annotatedString = annotatedString,
-                    style = style,
-                    placeholders = emptyList(),
-                    density = localDensity,
-                    fontFamilyResolver = fontFamilyResolver
-                ),
-                prevConstraints,
-                maxLines,
-                overflow == TextOverflow.Ellipsis
-            ),
-            layoutSize
-        )
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/SelectableTextAnnotatedStringElement.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/SelectableTextAnnotatedStringElement.kt
deleted file mode 100644
index 0b4fa32..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/SelectableTextAnnotatedStringElement.kt
+++ /dev/null
@@ -1,122 +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.newtext.text.modifiers
-
-import androidx.compose.foundation.newtext.text.DefaultMinLines
-import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.node.ModifierNodeElement
-import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.Placeholder
-import androidx.compose.ui.text.TextLayoutResult
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.style.TextOverflow
-
-@ExperimentalComposeUiApi
-internal data class SelectableTextAnnotatedStringElement(
-    private val text: AnnotatedString,
-    private val style: TextStyle,
-    private val fontFamilyResolver: FontFamily.Resolver,
-    private val onTextLayout: ((TextLayoutResult) -> Unit)? = null,
-    private val overflow: TextOverflow = TextOverflow.Clip,
-    private val softWrap: Boolean = true,
-    private val maxLines: Int = Int.MAX_VALUE,
-    private val minLines: Int = DefaultMinLines,
-    private val placeholders: List<AnnotatedString.Range<Placeholder>>? = null,
-    private val onPlaceholderLayout: ((List<Rect?>) -> Unit)? = null,
-    private val selectionController: SelectionController? = null
-) : ModifierNodeElement<SelectableTextAnnotatedStringNode>() {
-
-    override fun create(): SelectableTextAnnotatedStringNode = SelectableTextAnnotatedStringNode(
-        text,
-        style,
-        fontFamilyResolver,
-        onTextLayout,
-        overflow,
-        softWrap,
-        maxLines,
-        minLines,
-        placeholders,
-        onPlaceholderLayout,
-        selectionController
-    )
-
-    override fun update(
-        node: SelectableTextAnnotatedStringNode
-    ): SelectableTextAnnotatedStringNode {
-        node.update(
-            text = text,
-            style = style,
-            placeholders = placeholders,
-            minLines = minLines,
-            maxLines = maxLines,
-            softWrap = softWrap,
-            fontFamilyResolver = fontFamilyResolver,
-            overflow = overflow,
-            onTextLayout = onTextLayout,
-            onPlaceholderLayout = onPlaceholderLayout,
-            selectionController = selectionController
-        )
-        return node
-    }
-
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-
-        if (other !is SelectableTextAnnotatedStringElement) return false
-
-        // these three are most likely to actually change
-        if (text != other.text) return false
-        if (style != other.style) return false
-        if (placeholders != other.placeholders) return false
-
-        // these are equally unlikely to change
-        if (fontFamilyResolver != other.fontFamilyResolver) return false
-        if (onTextLayout != other.onTextLayout) return false
-        if (overflow != other.overflow) return false
-        if (softWrap != other.softWrap) return false
-        if (maxLines != other.maxLines) return false
-        if (minLines != other.minLines) return false
-
-        // these never change, but check anyway for correctness
-        if (onPlaceholderLayout != other.onPlaceholderLayout) return false
-        if (selectionController != other.selectionController) return false
-
-        return true
-    }
-
-    override fun hashCode(): Int {
-        var result = text.hashCode()
-        result = 31 * result + style.hashCode()
-        result = 31 * result + fontFamilyResolver.hashCode()
-        result = 31 * result + (onTextLayout?.hashCode() ?: 0)
-        result = 31 * result + overflow.hashCode()
-        result = 31 * result + softWrap.hashCode()
-        result = 31 * result + maxLines
-        result = 31 * result + minLines
-        result = 31 * result + (placeholders?.hashCode() ?: 0)
-        result = 31 * result + (onPlaceholderLayout?.hashCode() ?: 0)
-        result = 31 * result + (selectionController?.hashCode() ?: 0)
-        return result
-    }
-
-    override fun InspectorInfo.inspectableProperties() {
-        // Show nothing in the inspector.
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/SelectableTextAnnotatedStringNode.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/SelectableTextAnnotatedStringNode.kt
deleted file mode 100644
index c369c40..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/SelectableTextAnnotatedStringNode.kt
+++ /dev/null
@@ -1,151 +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.newtext.text.modifiers
-
-import androidx.compose.foundation.newtext.text.DefaultMinLines
-import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.graphics.drawscope.ContentDrawScope
-import androidx.compose.ui.layout.IntrinsicMeasurable
-import androidx.compose.ui.layout.IntrinsicMeasureScope
-import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.layout.Measurable
-import androidx.compose.ui.layout.MeasureResult
-import androidx.compose.ui.layout.MeasureScope
-import androidx.compose.ui.node.DelegatingNode
-import androidx.compose.ui.node.DrawModifierNode
-import androidx.compose.ui.node.GlobalPositionAwareModifierNode
-import androidx.compose.ui.node.LayoutModifierNode
-import androidx.compose.ui.node.SemanticsModifierNode
-import androidx.compose.ui.node.invalidateMeasurements
-import androidx.compose.ui.semantics.SemanticsConfiguration
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.Placeholder
-import androidx.compose.ui.text.TextLayoutResult
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.unit.Constraints
-
-@OptIn(ExperimentalComposeUiApi::class)
-internal class SelectableTextAnnotatedStringNode(
-    text: AnnotatedString,
-    style: TextStyle,
-    fontFamilyResolver: FontFamily.Resolver,
-    onTextLayout: ((TextLayoutResult) -> Unit)? = null,
-    overflow: TextOverflow = TextOverflow.Clip,
-    softWrap: Boolean = true,
-    maxLines: Int = Int.MAX_VALUE,
-    minLines: Int = DefaultMinLines,
-    placeholders: List<AnnotatedString.Range<Placeholder>>? = null,
-    onPlaceholderLayout: ((List<Rect?>) -> Unit)? = null,
-    private val selectionController: SelectionController? = null
-) : DelegatingNode(), LayoutModifierNode, DrawModifierNode, GlobalPositionAwareModifierNode,
-    SemanticsModifierNode {
-
-    private val delegate = delegated {
-        TextAnnotatedStringNode(
-            text = text,
-            style = style,
-            fontFamilyResolver = fontFamilyResolver,
-            onTextLayout = onTextLayout,
-            overflow = overflow,
-            softWrap = softWrap,
-            maxLines = maxLines,
-            minLines = minLines,
-            placeholders = placeholders,
-            onPlaceholderLayout = onPlaceholderLayout,
-            selectionController = selectionController
-        )
-    }
-
-    init {
-        requireNotNull(selectionController) {
-            "Do not use SelectionCapableStaticTextModifier unless selectionController != null"
-        }
-    }
-
-    override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
-        selectionController?.updateGlobalPosition(coordinates)
-    }
-
-    override fun ContentDrawScope.draw() = delegate.drawNonExtension(this)
-
-    override fun MeasureScope.measure(
-        measurable: Measurable,
-        constraints: Constraints
-    ): MeasureResult = delegate.measureNonExtension(this, measurable, constraints)
-
-    override val semanticsConfiguration: SemanticsConfiguration
-        get() = delegate.semanticsConfiguration
-
-    override fun IntrinsicMeasureScope.minIntrinsicWidth(
-        measurable: IntrinsicMeasurable,
-        height: Int
-    ): Int = delegate.minIntrinsicWidthNonExtension(this, measurable, height)
-
-    override fun IntrinsicMeasureScope.minIntrinsicHeight(
-        measurable: IntrinsicMeasurable,
-        width: Int
-    ): Int = delegate.minIntrinsicHeightNonExtension(this, measurable, width)
-
-    override fun IntrinsicMeasureScope.maxIntrinsicWidth(
-        measurable: IntrinsicMeasurable,
-        height: Int
-    ): Int = delegate.maxIntrinsicWidthNonExtension(this, measurable, height)
-
-    override fun IntrinsicMeasureScope.maxIntrinsicHeight(
-        measurable: IntrinsicMeasurable,
-        width: Int
-    ): Int = delegate.maxIntrinsicHeightNonExtension(this, measurable, width)
-
-    fun update(
-        text: AnnotatedString,
-        style: TextStyle,
-        placeholders: List<AnnotatedString.Range<Placeholder>>?,
-        minLines: Int,
-        maxLines: Int,
-        softWrap: Boolean,
-        fontFamilyResolver: FontFamily.Resolver,
-        overflow: TextOverflow,
-        onTextLayout: ((TextLayoutResult) -> Unit)?,
-        onPlaceholderLayout: ((List<Rect?>) -> Unit)?,
-        selectionController: SelectionController?
-    ) {
-        delegate.doInvalidations(
-            textChanged = delegate.updateText(
-                text = text
-            ),
-            layoutChanged = delegate.updateLayoutRelatedArgs(
-                style = style,
-                placeholders = placeholders,
-                minLines = minLines,
-                maxLines = maxLines,
-                softWrap = softWrap,
-                fontFamilyResolver = fontFamilyResolver,
-                overflow = overflow
-            ),
-            callbacksChanged = delegate.updateCallbacks(
-                onTextLayout = onTextLayout,
-                onPlaceholderLayout = onPlaceholderLayout,
-                selectionController = selectionController
-            )
-        )
-        // we always relayout when we're selectable
-        invalidateMeasurements()
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/SelectionController.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/SelectionController.kt
deleted file mode 100644
index ed21488..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/SelectionController.kt
+++ /dev/null
@@ -1,327 +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.newtext.text.modifiers
-
-import androidx.compose.foundation.newtext.text.copypasta.TextDragObserver
-import androidx.compose.foundation.newtext.text.copypasta.detectDragGesturesAfterLongPressWithObserver
-import androidx.compose.foundation.newtext.text.copypasta.selection.MouseSelectionObserver
-import androidx.compose.foundation.newtext.text.copypasta.selection.MultiWidgetSelectionDelegate
-import androidx.compose.foundation.newtext.text.copypasta.selection.Selectable
-import androidx.compose.foundation.newtext.text.copypasta.selection.SelectionAdjustment
-import androidx.compose.foundation.newtext.text.copypasta.selection.SelectionRegistrar
-import androidx.compose.foundation.newtext.text.copypasta.selection.hasSelection
-import androidx.compose.foundation.newtext.text.copypasta.selection.mouseSelectionDetector
-import androidx.compose.foundation.newtext.text.copypasta.textPointerIcon
-import androidx.compose.runtime.RememberObserver
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.drawscope.ContentDrawScope
-import androidx.compose.ui.input.pointer.pointerHoverIcon
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.text.TextLayoutResult
-
-internal data class StaticTextSelectionParams(
-    val layoutCoordinates: LayoutCoordinates?,
-    val textLayoutResult: TextLayoutResult?
-) {
-    companion object {
-        val Empty = StaticTextSelectionParams(null, null)
-    }
-}
-
-// This is _basically_ a Modifier.Node but moved into remember because we need to do pointerInput
-// TODO: Refactor when Modifier.pointerInput is available for delegation
-internal class SelectionController(
-    private val selectionRegistrar: SelectionRegistrar,
-    private val backgroundSelectionColor: Color
-) : RememberObserver {
-    private var selectable: Selectable? = null
-    private val selectableId = selectionRegistrar.nextSelectableId()
-    // TODO: Move these into Modifer.element eventually
-    private var params: StaticTextSelectionParams = StaticTextSelectionParams.Empty
-
-    val modifier: Modifier = selectionRegistrar.makeSelectionModifier(
-        selectableId = selectableId,
-        layoutCoordinates = { params.layoutCoordinates },
-        textLayoutResult = { params.textLayoutResult },
-        // TODO: Use real isInTouchMode on merge
-        isInTouchMode = true /* fake it to android hardcode */
-    )
-
-    override fun onRemembered() {
-        selectable = selectionRegistrar.subscribe(
-            MultiWidgetSelectionDelegate(
-                selectableId = selectableId,
-                coordinatesCallback = { params.layoutCoordinates },
-                layoutResultCallback = { params.textLayoutResult }
-            )
-        )
-    }
-
-    override fun onForgotten() {
-        val localSelectable = selectable
-        if (localSelectable != null) {
-            selectionRegistrar.unsubscribe(localSelectable)
-            selectable = null
-        }
-    }
-
-    override fun onAbandoned() {
-        val localSelectable = selectable
-        if (localSelectable != null) {
-            selectionRegistrar.unsubscribe(localSelectable)
-            selectable = null
-        }
-    }
-
-    fun updateTextLayout(textLayoutResult: TextLayoutResult) {
-        params = params.copy(textLayoutResult = textLayoutResult)
-    }
-
-    fun updateGlobalPosition(coordinates: LayoutCoordinates) {
-        params = params.copy(layoutCoordinates = coordinates)
-    }
-
-    fun draw(contentDrawScope: ContentDrawScope) {
-        val layoutResult = params.textLayoutResult ?: return
-        val selection = selectionRegistrar.subselections[selectableId]
-
-        if (selection != null) {
-            val start = if (!selection.handlesCrossed) {
-                selection.start.offset
-            } else {
-                selection.end.offset
-            }
-            val end = if (!selection.handlesCrossed) {
-                selection.end.offset
-            } else {
-                selection.start.offset
-            }
-
-            if (start != end) {
-                val selectionPath = layoutResult.multiParagraph.getPathForRange(start, end)
-                with(contentDrawScope) {
-                    drawPath(selectionPath, backgroundSelectionColor)
-                }
-            }
-        }
-    }
-}
-
-// this is not chained, but is a standalone factory
-@Suppress("ModifierFactoryExtensionFunction")
-private fun SelectionRegistrar.makeSelectionModifier(
-    selectableId: Long,
-    layoutCoordinates: () -> LayoutCoordinates?,
-    textLayoutResult: () -> TextLayoutResult?,
-    isInTouchMode: Boolean
-): Modifier {
-    return if (isInTouchMode) {
-        val longPressDragObserver = object : TextDragObserver {
-            /**
-             * The beginning position of the drag gesture. Every time a new drag gesture starts, it wil be
-             * recalculated.
-             */
-            var lastPosition = Offset.Zero
-
-            /**
-             * The total distance being dragged of the drag gesture. Every time a new drag gesture starts,
-             * it will be zeroed out.
-             */
-            var dragTotalDistance = Offset.Zero
-
-            override fun onDown(point: Offset) {
-                // Not supported for long-press-drag.
-            }
-
-            override fun onUp() {
-                // Nothing to do.
-            }
-
-            override fun onStart(startPoint: Offset) {
-                layoutCoordinates()?.let {
-                    if (!it.isAttached) return
-
-                    if (textLayoutResult().outOfBoundary(startPoint, startPoint)) {
-                        notifySelectionUpdateSelectAll(
-                            selectableId = selectableId
-                        )
-                    } else {
-                        notifySelectionUpdateStart(
-                            layoutCoordinates = it,
-                            startPosition = startPoint,
-                            adjustment = SelectionAdjustment.Word
-                        )
-                    }
-
-                    lastPosition = startPoint
-                }
-                // selection never started
-                if (!hasSelection(selectableId)) return
-                // Zero out the total distance that being dragged.
-                dragTotalDistance = Offset.Zero
-            }
-
-            override fun onDrag(delta: Offset) {
-                layoutCoordinates()?.let {
-                    if (!it.isAttached) return
-                    // selection never started, did not consume any drag
-                    if (!hasSelection(selectableId)) return
-
-                    dragTotalDistance += delta
-                    val newPosition = lastPosition + dragTotalDistance
-
-                    if (!textLayoutResult().outOfBoundary(lastPosition, newPosition)) {
-                        // Notice that only the end position needs to be updated here.
-                        // Start position is left unchanged. This is typically important when
-                        // long-press is using SelectionAdjustment.WORD or
-                        // SelectionAdjustment.PARAGRAPH that updates the start handle position from
-                        // the dragBeginPosition.
-                        val consumed = notifySelectionUpdate(
-                            layoutCoordinates = it,
-                            previousPosition = lastPosition,
-                            newPosition = newPosition,
-                            isStartHandle = false,
-                            adjustment = SelectionAdjustment.CharacterWithWordAccelerate
-                        )
-                        if (consumed) {
-                            lastPosition = newPosition
-                            dragTotalDistance = Offset.Zero
-                        }
-                    }
-                }
-            }
-
-            override fun onStop() {
-                if (hasSelection(selectableId)) {
-                    notifySelectionUpdateEnd()
-                }
-            }
-
-            override fun onCancel() {
-                if (hasSelection(selectableId)) {
-                    notifySelectionUpdateEnd()
-                }
-            }
-        }
-        Modifier.pointerInput(longPressDragObserver) {
-            detectDragGesturesAfterLongPressWithObserver(
-                longPressDragObserver
-            )
-        }
-    } else {
-        val mouseSelectionObserver = object : MouseSelectionObserver {
-            var lastPosition = Offset.Zero
-
-            override fun onExtend(downPosition: Offset): Boolean {
-                layoutCoordinates()?.let { layoutCoordinates ->
-                    if (!layoutCoordinates.isAttached) return false
-                    val consumed = notifySelectionUpdate(
-                        layoutCoordinates = layoutCoordinates,
-                        newPosition = downPosition,
-                        previousPosition = lastPosition,
-                        isStartHandle = false,
-                        adjustment = SelectionAdjustment.None
-                    )
-                    if (consumed) {
-                        lastPosition = downPosition
-                    }
-                    return hasSelection(selectableId)
-                }
-                return false
-            }
-
-            override fun onExtendDrag(dragPosition: Offset): Boolean {
-                layoutCoordinates()?.let { layoutCoordinates ->
-                    if (!layoutCoordinates.isAttached) return false
-                    if (!hasSelection(selectableId)) return false
-
-                    val consumed = notifySelectionUpdate(
-                        layoutCoordinates = layoutCoordinates,
-                        newPosition = dragPosition,
-                        previousPosition = lastPosition,
-                        isStartHandle = false,
-                        adjustment = SelectionAdjustment.None
-                    )
-
-                    if (consumed) {
-                        lastPosition = dragPosition
-                    }
-                }
-                return true
-            }
-
-            override fun onStart(
-                downPosition: Offset,
-                adjustment: SelectionAdjustment
-            ): Boolean {
-                layoutCoordinates()?.let {
-                    if (!it.isAttached) return false
-
-                    notifySelectionUpdateStart(
-                        layoutCoordinates = it,
-                        startPosition = downPosition,
-                        adjustment = adjustment
-                    )
-
-                    lastPosition = downPosition
-                    return hasSelection(selectableId)
-                }
-
-                return false
-            }
-
-            override fun onDrag(
-                dragPosition: Offset,
-                adjustment: SelectionAdjustment
-            ): Boolean {
-                layoutCoordinates()?.let {
-                    if (!it.isAttached) return false
-                    if (!hasSelection(selectableId)) return false
-
-                    val consumed = notifySelectionUpdate(
-                        layoutCoordinates = it,
-                        previousPosition = lastPosition,
-                        newPosition = dragPosition,
-                        isStartHandle = false,
-                        adjustment = adjustment
-                    )
-                    if (consumed) {
-                        lastPosition = dragPosition
-                    }
-                }
-                return true
-            }
-        }
-        Modifier.pointerInput(mouseSelectionObserver) {
-            mouseSelectionDetector(mouseSelectionObserver)
-        }.pointerHoverIcon(textPointerIcon)
-    }
-}
-
-private fun TextLayoutResult?.outOfBoundary(start: Offset, end: Offset): Boolean {
-    this ?: return false
-
-    val lastOffset = layoutInput.text.text.length
-    val rawStartOffset = getOffsetForPosition(start)
-    val rawEndOffset = getOffsetForPosition(end)
-
-    return rawStartOffset >= lastOffset - 1 && rawEndOffset >= lastOffset - 1 ||
-        rawStartOffset < 0 && rawEndOffset < 0
-}
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/TextAnnotatedStringElement.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/TextAnnotatedStringElement.kt
deleted file mode 100644
index e25f48a..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/TextAnnotatedStringElement.kt
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.newtext.text.modifiers
-
-import androidx.compose.foundation.newtext.text.DefaultMinLines
-import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.node.ModifierNodeElement
-import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.Placeholder
-import androidx.compose.ui.text.TextLayoutResult
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.style.TextOverflow
-
-@ExperimentalComposeUiApi
-internal class TextAnnotatedStringElement(
-    private val text: AnnotatedString,
-    private val style: TextStyle,
-    private val fontFamilyResolver: FontFamily.Resolver,
-    private val onTextLayout: ((TextLayoutResult) -> Unit)? = null,
-    private val overflow: TextOverflow = TextOverflow.Clip,
-    private val softWrap: Boolean = true,
-    private val maxLines: Int = Int.MAX_VALUE,
-    private val minLines: Int = DefaultMinLines,
-    private val placeholders: List<AnnotatedString.Range<Placeholder>>? = null,
-    private val onPlaceholderLayout: ((List<Rect?>) -> Unit)? = null,
-    private val selectionController: SelectionController? = null
-) : ModifierNodeElement<TextAnnotatedStringNode>() {
-
-    override fun create(): TextAnnotatedStringNode = TextAnnotatedStringNode(
-        text,
-        style,
-        fontFamilyResolver,
-        onTextLayout,
-        overflow,
-        softWrap,
-        maxLines,
-        minLines,
-        placeholders,
-        onPlaceholderLayout,
-        selectionController
-    )
-
-    override fun update(node: TextAnnotatedStringNode): TextAnnotatedStringNode {
-        node.doInvalidations(
-            textChanged = node.updateText(
-                text = text
-            ),
-            layoutChanged = node.updateLayoutRelatedArgs(
-                style = style,
-                placeholders = placeholders,
-                minLines = minLines,
-                maxLines = maxLines,
-                softWrap = softWrap,
-                fontFamilyResolver = fontFamilyResolver,
-                overflow = overflow
-            ),
-            callbacksChanged = node.updateCallbacks(
-                onTextLayout = onTextLayout,
-                onPlaceholderLayout = onPlaceholderLayout,
-                selectionController = selectionController
-            )
-        )
-        return node
-    }
-
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-
-        if (other !is TextAnnotatedStringElement) return false
-
-        // these three are most likely to actually change
-        if (text != other.text) return false
-        if (style != other.style) return false
-        if (placeholders != other.placeholders) return false
-
-        // these are equally unlikely to change
-        if (fontFamilyResolver != other.fontFamilyResolver) return false
-        if (onTextLayout != other.onTextLayout) return false
-        if (overflow != other.overflow) return false
-        if (softWrap != other.softWrap) return false
-        if (maxLines != other.maxLines) return false
-        if (minLines != other.minLines) return false
-
-        // these never change, but check anyway for correctness
-        if (onPlaceholderLayout != other.onPlaceholderLayout) return false
-        if (selectionController != other.selectionController) return false
-
-        return true
-    }
-
-    override fun hashCode(): Int {
-        var result = text.hashCode()
-        result = 31 * result + style.hashCode()
-        result = 31 * result + fontFamilyResolver.hashCode()
-        result = 31 * result + (onTextLayout?.hashCode() ?: 0)
-        result = 31 * result + overflow.hashCode()
-        result = 31 * result + softWrap.hashCode()
-        result = 31 * result + maxLines
-        result = 31 * result + minLines
-        result = 31 * result + (placeholders?.hashCode() ?: 0)
-        result = 31 * result + (onPlaceholderLayout?.hashCode() ?: 0)
-        result = 31 * result + (selectionController?.hashCode() ?: 0)
-        return result
-    }
-
-    override fun InspectorInfo.inspectableProperties() {
-        // Show nothing in the inspector.
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/TextAnnotatedStringNode.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/TextAnnotatedStringNode.kt
deleted file mode 100644
index 164c4fb..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/TextAnnotatedStringNode.kt
+++ /dev/null
@@ -1,344 +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.newtext.text.modifiers
-
-import androidx.compose.foundation.newtext.text.DefaultMinLines
-import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.graphics.drawscope.ContentDrawScope
-import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
-import androidx.compose.ui.layout.AlignmentLine
-import androidx.compose.ui.layout.FirstBaseline
-import androidx.compose.ui.layout.IntrinsicMeasurable
-import androidx.compose.ui.layout.IntrinsicMeasureScope
-import androidx.compose.ui.layout.LastBaseline
-import androidx.compose.ui.layout.Measurable
-import androidx.compose.ui.layout.MeasureResult
-import androidx.compose.ui.layout.MeasureScope
-import androidx.compose.ui.node.DrawModifierNode
-import androidx.compose.ui.node.LayoutModifierNode
-import androidx.compose.ui.node.SemanticsModifierNode
-import androidx.compose.ui.node.invalidateDraw
-import androidx.compose.ui.node.invalidateLayer
-import androidx.compose.ui.node.invalidateMeasurements
-import androidx.compose.ui.node.invalidateSemantics
-import androidx.compose.ui.semantics.SemanticsConfiguration
-import androidx.compose.ui.semantics.getTextLayoutResult
-import androidx.compose.ui.semantics.text
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.Placeholder
-import androidx.compose.ui.text.TextLayoutResult
-import androidx.compose.ui.text.TextPainter
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.Density
-import kotlin.math.roundToInt
-
-@OptIn(ExperimentalComposeUiApi::class)
-internal class TextAnnotatedStringNode(
-    private var text: AnnotatedString,
-    private var style: TextStyle,
-    private var fontFamilyResolver: FontFamily.Resolver,
-    private var onTextLayout: ((TextLayoutResult) -> Unit)? = null,
-    private var overflow: TextOverflow = TextOverflow.Clip,
-    private var softWrap: Boolean = true,
-    private var maxLines: Int = Int.MAX_VALUE,
-    private var minLines: Int = DefaultMinLines,
-    private var placeholders: List<AnnotatedString.Range<Placeholder>>? = null,
-    private var onPlaceholderLayout: ((List<Rect?>) -> Unit)? = null,
-    private var selectionController: SelectionController? = null
-) : Modifier.Node(), LayoutModifierNode, DrawModifierNode, SemanticsModifierNode {
-    private var baselineCache: Map<AlignmentLine, Int>? = null
-
-    private var _layoutCache: MultiParagraphLayoutCache? = null
-    private val layoutCache: MultiParagraphLayoutCache
-        get() {
-            if (_layoutCache == null) {
-                _layoutCache = MultiParagraphLayoutCache(
-                    text,
-                    style,
-                    fontFamilyResolver,
-                    overflow,
-                    softWrap,
-                    maxLines,
-                    minLines,
-                    placeholders
-                )
-            }
-            return _layoutCache!!
-        }
-
-    private fun getLayoutCache(density: Density): MultiParagraphLayoutCache {
-        return layoutCache.also { it.density = density }
-    }
-
-    fun updateText(text: AnnotatedString): Boolean {
-        if (this.text == text) return false
-        this.text = text
-        return true
-    }
-
-    fun updateLayoutRelatedArgs(
-        style: TextStyle,
-        placeholders: List<AnnotatedString.Range<Placeholder>>?,
-        minLines: Int,
-        maxLines: Int,
-        softWrap: Boolean,
-        fontFamilyResolver: FontFamily.Resolver,
-        overflow: TextOverflow
-    ): Boolean {
-        var changed = false
-        if (this.style != style) {
-            this.style = style
-            changed = true
-        }
-        if (this.placeholders != placeholders) {
-            this.placeholders = placeholders
-            changed = true
-        }
-
-        if (this.minLines != minLines) {
-            this.minLines = minLines
-            changed = true
-        }
-
-        if (this.maxLines != maxLines) {
-            this.maxLines != maxLines
-            changed = true
-        }
-
-        if (this.softWrap != softWrap) {
-            this.softWrap = softWrap
-            changed = true
-        }
-
-        if (this.fontFamilyResolver != fontFamilyResolver) {
-            this.fontFamilyResolver = fontFamilyResolver
-            changed = true
-        }
-
-        if (this.overflow != overflow) {
-            this.overflow = overflow
-            changed = true
-        }
-
-        return changed
-    }
-
-    fun updateCallbacks(
-        onTextLayout: ((TextLayoutResult) -> Unit)?,
-        onPlaceholderLayout: ((List<Rect?>) -> Unit)?,
-        selectionController: SelectionController?
-    ): Boolean {
-        var changed = false
-
-        if (this.onTextLayout != onTextLayout) {
-            this.onTextLayout = onTextLayout
-            changed = true
-        }
-
-        if (this.onPlaceholderLayout != onPlaceholderLayout) {
-            this.onPlaceholderLayout = onPlaceholderLayout
-            changed = true
-        }
-
-        if (this.selectionController != selectionController) {
-            this.selectionController = selectionController
-            changed = true
-        }
-        return changed
-    }
-
-    fun doInvalidations(
-        textChanged: Boolean,
-        layoutChanged: Boolean,
-        callbacksChanged: Boolean
-    ) {
-        if (textChanged) {
-            _semanticsConfiguration = null
-            invalidateSemantics()
-        }
-
-        if (textChanged || layoutChanged || callbacksChanged) {
-            layoutCache.update(
-                text = text,
-                style = style,
-                fontFamilyResolver = fontFamilyResolver,
-                overflow = overflow,
-                softWrap = softWrap,
-                maxLines = maxLines,
-                minLines = minLines,
-                placeholders = placeholders
-            )
-            invalidateMeasurements()
-            invalidateDraw()
-        }
-    }
-
-    private var _semanticsConfiguration: SemanticsConfiguration? = null
-
-    private var semanticsTextLayoutResult: ((MutableList<TextLayoutResult>) -> Boolean)? = null
-
-    private fun generateSemantics(text: AnnotatedString): SemanticsConfiguration {
-        var localSemanticsTextLayoutResult = semanticsTextLayoutResult
-        if (localSemanticsTextLayoutResult == null) {
-            localSemanticsTextLayoutResult = { textLayoutResult ->
-                val layout = layoutCache.layoutOrNull?.also {
-                    textLayoutResult.add(it)
-                }
-                layout != null
-            }
-            semanticsTextLayoutResult = localSemanticsTextLayoutResult
-        }
-        return SemanticsConfiguration().also {
-            it.isMergingSemanticsOfDescendants = false
-            it.isClearingSemantics = false
-            it.text = text
-            it.getTextLayoutResult(action = localSemanticsTextLayoutResult)
-        }
-    }
-
-    override val semanticsConfiguration: SemanticsConfiguration
-        get() {
-            var localSemantics = _semanticsConfiguration
-            if (localSemantics == null) {
-                localSemantics = generateSemantics(text)
-                _semanticsConfiguration = localSemantics
-            }
-            return localSemantics
-        }
-
-    fun measureNonExtension(
-        measureScope: MeasureScope,
-        measurable: Measurable,
-        constraints: Constraints
-    ): MeasureResult {
-        return measureScope.measure(measurable, constraints)
-    }
-
-    override fun MeasureScope.measure(
-        measurable: Measurable,
-        constraints: Constraints
-    ): MeasureResult {
-        val layoutCache = getLayoutCache(this)
-
-        val didChangeLayout = layoutCache.layoutWithConstraints(constraints, layoutDirection)
-        val textLayoutResult = layoutCache.layout
-
-        // ensure measure restarts when hasStaleResolvedFonts by reading in measure
-        textLayoutResult.multiParagraph.intrinsics.hasStaleResolvedFonts
-
-        if (didChangeLayout) {
-            invalidateLayer()
-            onTextLayout?.invoke(textLayoutResult)
-            selectionController?.updateTextLayout(textLayoutResult)
-            baselineCache = mapOf(
-                FirstBaseline to textLayoutResult.firstBaseline.roundToInt(),
-                LastBaseline to textLayoutResult.lastBaseline.roundToInt()
-            )
-        }
-
-        // first share the placeholders
-        onPlaceholderLayout?.invoke(textLayoutResult.placeholderRects)
-
-        // then allow children to measure _inside_ our final box, with the above placeholders
-        val placeable = measurable.measure(
-            Constraints.fixed(
-                textLayoutResult.size.width,
-                textLayoutResult.size.height
-            )
-        )
-
-        return layout(
-            textLayoutResult.size.width,
-            textLayoutResult.size.height,
-            baselineCache!!
-        ) {
-            // this is basically a graphicsLayer
-            placeable.place(0, 0)
-        }
-    }
-
-    fun minIntrinsicWidthNonExtension(
-        intrinsicMeasureScope: IntrinsicMeasureScope,
-        measurable: IntrinsicMeasurable,
-        height: Int
-    ): Int {
-        return intrinsicMeasureScope.minIntrinsicWidth(measurable, height)
-    }
-
-    override fun IntrinsicMeasureScope.minIntrinsicWidth(
-        measurable: IntrinsicMeasurable,
-        height: Int
-    ): Int {
-        return getLayoutCache(this).minIntrinsicWidth
-    }
-
-    fun minIntrinsicHeightNonExtension(
-        intrinsicMeasureScope: IntrinsicMeasureScope,
-        measurable: IntrinsicMeasurable,
-        width: Int
-    ): Int {
-        return intrinsicMeasureScope.minIntrinsicHeight(measurable, width)
-    }
-
-    override fun IntrinsicMeasureScope.minIntrinsicHeight(
-        measurable: IntrinsicMeasurable,
-        width: Int
-    ): Int = getLayoutCache(this).intrinsicHeightAt(width, layoutDirection)
-
-    fun maxIntrinsicWidthNonExtension(
-        intrinsicMeasureScope: IntrinsicMeasureScope,
-        measurable: IntrinsicMeasurable,
-        height: Int
-    ): Int = intrinsicMeasureScope.maxIntrinsicWidth(measurable, height)
-
-    override fun IntrinsicMeasureScope.maxIntrinsicWidth(
-        measurable: IntrinsicMeasurable,
-        height: Int
-    ): Int = getLayoutCache(this).maxIntrinsicWidth
-
-    fun maxIntrinsicHeightNonExtension(
-        intrinsicMeasureScope: IntrinsicMeasureScope,
-        measurable: IntrinsicMeasurable,
-        width: Int
-    ): Int = intrinsicMeasureScope.maxIntrinsicHeight(measurable, width)
-
-    override fun IntrinsicMeasureScope.maxIntrinsicHeight(
-        measurable: IntrinsicMeasurable,
-        width: Int
-    ): Int = getLayoutCache(this).intrinsicHeightAt(width, layoutDirection)
-
-    fun drawNonExtension(
-        contentDrawScope: ContentDrawScope
-    ) {
-        return contentDrawScope.draw()
-    }
-
-    override fun ContentDrawScope.draw() {
-        selectionController?.draw(this)
-        drawIntoCanvas { canvas ->
-            TextPainter.paint(canvas, requireNotNull(layoutCache.layout))
-        }
-        if (!placeholders.isNullOrEmpty()) {
-            drawContent()
-        }
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/TextStringSimpleElement.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/TextStringSimpleElement.kt
deleted file mode 100644
index 797e399..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/TextStringSimpleElement.kt
+++ /dev/null
@@ -1,98 +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.newtext.text.modifiers
-
-import androidx.compose.foundation.newtext.text.DefaultMinLines
-import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.node.ModifierNodeElement
-import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.style.TextOverflow
-
-@ExperimentalComposeUiApi
-internal class TextStringSimpleElement(
-    private val text: String,
-    private val style: TextStyle,
-    private val fontFamilyResolver: FontFamily.Resolver,
-    private val overflow: TextOverflow = TextOverflow.Clip,
-    private val softWrap: Boolean = true,
-    private val maxLines: Int = Int.MAX_VALUE,
-    private val minLines: Int = DefaultMinLines,
-) : ModifierNodeElement<TextStringSimpleNode>() {
-
-    override fun create(): TextStringSimpleNode = TextStringSimpleNode(
-        text,
-        style,
-        fontFamilyResolver,
-        overflow,
-        softWrap,
-        maxLines,
-        minLines
-    )
-
-    override fun update(node: TextStringSimpleNode): TextStringSimpleNode {
-        node.doInvalidations(
-            textChanged = node.updateText(
-                text = text
-            ),
-            layoutChanged = node.updateLayoutRelatedArgs(
-                style = style,
-                minLines = minLines,
-                maxLines = maxLines,
-                softWrap = softWrap,
-                fontFamilyResolver = fontFamilyResolver,
-                overflow = overflow
-            )
-        )
-        return node
-    }
-
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-
-        if (other !is TextStringSimpleElement) return false
-
-        // these three are most likely to actually change
-        if (text != other.text) return false
-        if (style != other.style) return false
-
-        // these are equally unlikely to change
-        if (fontFamilyResolver != other.fontFamilyResolver) return false
-        if (overflow != other.overflow) return false
-        if (softWrap != other.softWrap) return false
-        if (maxLines != other.maxLines) return false
-        if (minLines != other.minLines) return false
-
-        return true
-    }
-
-    override fun hashCode(): Int {
-        var result = text.hashCode()
-        result = 31 * result + style.hashCode()
-        result = 31 * result + fontFamilyResolver.hashCode()
-        result = 31 * result + overflow.hashCode()
-        result = 31 * result + softWrap.hashCode()
-        result = 31 * result + maxLines
-        result = 31 * result + minLines
-        return result
-    }
-
-    override fun InspectorInfo.inspectableProperties() {
-        // Show nothing in the inspector.
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/TextStringSimpleNode.kt b/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/TextStringSimpleNode.kt
deleted file mode 100644
index a90ff59..0000000
--- a/compose/foundation/foundation-newtext/src/commonMain/kotlin/androidx/compose/foundation/newtext/text/modifiers/TextStringSimpleNode.kt
+++ /dev/null
@@ -1,301 +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.newtext.text.modifiers
-
-import androidx.compose.foundation.newtext.text.DefaultMinLines
-import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.graphics.Shadow
-import androidx.compose.ui.graphics.drawscope.ContentDrawScope
-import androidx.compose.ui.graphics.drawscope.Fill
-import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
-import androidx.compose.ui.layout.AlignmentLine
-import androidx.compose.ui.layout.FirstBaseline
-import androidx.compose.ui.layout.IntrinsicMeasurable
-import androidx.compose.ui.layout.IntrinsicMeasureScope
-import androidx.compose.ui.layout.LastBaseline
-import androidx.compose.ui.layout.Measurable
-import androidx.compose.ui.layout.MeasureResult
-import androidx.compose.ui.layout.MeasureScope
-import androidx.compose.ui.node.DrawModifierNode
-import androidx.compose.ui.node.LayoutModifierNode
-import androidx.compose.ui.node.SemanticsModifierNode
-import androidx.compose.ui.node.invalidateDraw
-import androidx.compose.ui.node.invalidateLayer
-import androidx.compose.ui.node.invalidateMeasurements
-import androidx.compose.ui.node.invalidateSemantics
-import androidx.compose.ui.semantics.SemanticsConfiguration
-import androidx.compose.ui.semantics.getTextLayoutResult
-import androidx.compose.ui.semantics.text
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.text.TextLayoutResult
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.style.TextDecoration
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.Density
-import kotlin.math.roundToInt
-
-@OptIn(ExperimentalComposeUiApi::class)
-internal class TextStringSimpleNode(
-    private var text: String,
-    private var style: TextStyle,
-    private var fontFamilyResolver: FontFamily.Resolver,
-    private var overflow: TextOverflow = TextOverflow.Clip,
-    private var softWrap: Boolean = true,
-    private var maxLines: Int = Int.MAX_VALUE,
-    private var minLines: Int = DefaultMinLines
-) : Modifier.Node(), LayoutModifierNode, DrawModifierNode, SemanticsModifierNode {
-    private var baselineCache: Map<AlignmentLine, Int>? = null
-
-    private var _layoutCache: ParagraphLayoutCache? = null
-    private val layoutCache: ParagraphLayoutCache
-        get() {
-            if (_layoutCache == null) {
-                _layoutCache = ParagraphLayoutCache(
-                    text,
-                    style,
-                    fontFamilyResolver,
-                    overflow,
-                    softWrap,
-                    maxLines,
-                    minLines,
-                )
-            }
-            return _layoutCache!!
-        }
-
-    private fun getLayoutCache(density: Density): ParagraphLayoutCache {
-        return layoutCache.also { it.density = density }
-    }
-
-    fun updateText(text: String): Boolean {
-        if (this.text == text) return false
-        this.text = text
-        return true
-    }
-
-    fun updateLayoutRelatedArgs(
-        style: TextStyle,
-        minLines: Int,
-        maxLines: Int,
-        softWrap: Boolean,
-        fontFamilyResolver: FontFamily.Resolver,
-        overflow: TextOverflow
-    ): Boolean {
-        var changed = false
-        if (this.style != style) {
-            this.style = style
-            changed = true
-        }
-
-        if (this.minLines != minLines) {
-            this.minLines = minLines
-            changed = true
-        }
-
-        if (this.maxLines != maxLines) {
-            this.maxLines != maxLines
-            changed = true
-        }
-
-        if (this.softWrap != softWrap) {
-            this.softWrap = softWrap
-            changed = true
-        }
-
-        if (this.fontFamilyResolver != fontFamilyResolver) {
-            this.fontFamilyResolver = fontFamilyResolver
-            changed = true
-        }
-
-        if (this.overflow != overflow) {
-            this.overflow = overflow
-            changed = true
-        }
-
-        return changed
-    }
-
-    fun doInvalidations(
-        textChanged: Boolean,
-        layoutChanged: Boolean
-    ) {
-        if (textChanged) {
-            _semanticsConfiguration = null
-            invalidateSemantics()
-        }
-
-        if (textChanged || layoutChanged) {
-            layoutCache.update(
-                text = text,
-                style = style,
-                fontFamilyResolver = fontFamilyResolver,
-                overflow = overflow,
-                softWrap = softWrap,
-                maxLines = maxLines,
-                minLines = minLines
-            )
-            invalidateMeasurements()
-            invalidateDraw()
-        }
-    }
-
-    private var _semanticsConfiguration: SemanticsConfiguration? = null
-
-    private var semanticsTextLayoutResult: ((MutableList<TextLayoutResult>) -> Boolean)? = null
-
-    private fun generateSemantics(text: String): SemanticsConfiguration {
-        var localSemanticsTextLayoutResult = semanticsTextLayoutResult
-        if (localSemanticsTextLayoutResult == null) {
-            localSemanticsTextLayoutResult = { textLayoutResult ->
-                val layout = layoutCache.slowCreateTextLayoutResultOrNull()?.also {
-                    textLayoutResult.add(it)
-                }
-                layout != null
-                false
-            }
-            semanticsTextLayoutResult = localSemanticsTextLayoutResult
-        }
-        return SemanticsConfiguration().also {
-            it.isMergingSemanticsOfDescendants = false
-            it.isClearingSemantics = false
-            it.text = AnnotatedString(text)
-            it.getTextLayoutResult(action = localSemanticsTextLayoutResult)
-        }
-    }
-
-    override val semanticsConfiguration: SemanticsConfiguration
-        get() {
-            var localSemantics = _semanticsConfiguration
-            if (localSemantics == null) {
-                localSemantics = generateSemantics(text)
-                _semanticsConfiguration = localSemantics
-            }
-            return localSemantics
-        }
-
-    override fun MeasureScope.measure(
-        measurable: Measurable,
-        constraints: Constraints
-    ): MeasureResult {
-        val layoutCache = getLayoutCache(this)
-
-        val didChangeLayout = layoutCache.layoutWithConstraints(constraints, layoutDirection)
-        // ensure measure restarts when hasStaleResolvedFonts by reading in measure
-        layoutCache.observeFontChanges
-        val paragraph = layoutCache.paragraph!!
-        val layoutSize = layoutCache.layoutSize
-
-        if (didChangeLayout) {
-            invalidateLayer()
-            baselineCache = mapOf(
-                FirstBaseline to paragraph.firstBaseline.roundToInt(),
-                LastBaseline to paragraph.lastBaseline.roundToInt()
-            )
-        }
-
-        // then allow children to measure _inside_ our final box, with the above placeholders
-        val placeable = measurable.measure(
-            Constraints.fixed(
-                layoutSize.width,
-                layoutSize.height
-            )
-        )
-
-        return layout(
-            layoutSize.width,
-            layoutSize.height,
-            baselineCache!!
-        ) {
-            // this is basically a graphicsLayer
-            placeable.place(0, 0)
-        }
-    }
-
-    override fun IntrinsicMeasureScope.minIntrinsicWidth(
-        measurable: IntrinsicMeasurable,
-        height: Int
-    ): Int {
-        return getLayoutCache(this).minIntrinsicWidth
-    }
-
-    override fun IntrinsicMeasureScope.minIntrinsicHeight(
-        measurable: IntrinsicMeasurable,
-        width: Int
-    ): Int = getLayoutCache(this).intrinsicHeightAt(width, layoutDirection)
-
-    override fun IntrinsicMeasureScope.maxIntrinsicWidth(
-        measurable: IntrinsicMeasurable,
-        height: Int
-    ): Int = getLayoutCache(this).maxIntrinsicWidth
-
-    override fun IntrinsicMeasureScope.maxIntrinsicHeight(
-        measurable: IntrinsicMeasurable,
-        width: Int
-    ): Int = getLayoutCache(this).intrinsicHeightAt(width, layoutDirection)
-
-    @OptIn(ExperimentalTextApi::class)
-    override fun ContentDrawScope.draw() {
-        val localParagraph = requireNotNull(layoutCache.paragraph)
-        drawIntoCanvas { canvas ->
-            val willClip = layoutCache.didOverflow
-            if (willClip) {
-                val width = layoutCache.layoutSize.width.toFloat()
-                val height = layoutCache.layoutSize.height.toFloat()
-                val bounds = Rect(Offset.Zero, Size(width, height))
-                canvas.save()
-                canvas.clipRect(bounds)
-            }
-            try {
-                val textDecoration = style.textDecoration ?: TextDecoration.None
-                val shadow = style.shadow ?: Shadow.None
-                val drawStyle = style.drawStyle ?: Fill
-                val brush = style.brush
-                if (brush != null) {
-                    val alpha = style.alpha
-                    localParagraph.paint(
-                        canvas = canvas,
-                        brush = brush,
-                        alpha = alpha,
-                        shadow = shadow,
-                        drawStyle = drawStyle,
-                        textDecoration = textDecoration
-                    )
-                } else {
-                    val color = style.color
-                    localParagraph.paint(
-                        canvas = canvas,
-                        color = color,
-                        shadow = shadow,
-                        textDecoration = textDecoration,
-                        drawStyle = drawStyle
-                    )
-                }
-            } finally {
-                if (willClip) {
-                    canvas.restore()
-                }
-            }
-        }
-    }
-}
diff --git a/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/BasicContextMenuRepresentation.desktop.kt b/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/BasicContextMenuRepresentation.desktop.kt
deleted file mode 100644
index 86ddbd5..0000000
--- a/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/BasicContextMenuRepresentation.desktop.kt
+++ /dev/null
@@ -1,144 +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
-
-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.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.sizeIn
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.text.BasicText
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.shadow
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.input.pointer.PointerEventType
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.window.Popup
-import androidx.compose.ui.window.rememberCursorPositionProvider
-
-// Design of basic represenation is from Material specs:
-// https://material.io/design/interaction/states.html#hover
-// https://material.io/components/menus#specs
-
-val LightDefaultContextMenuRepresentation = DefaultContextMenuRepresentation(
-    backgroundColor = Color.White,
-    textColor = Color.Black,
-    itemHoverColor = Color.Black.copy(alpha = 0.04f)
-)
-
-val DarkDefaultContextMenuRepresentation = DefaultContextMenuRepresentation(
-    backgroundColor = Color(0xFF121212), // like surface in darkColors
-    textColor = Color.White,
-    itemHoverColor = Color.White.copy(alpha = 0.04f)
-)
-
-class DefaultContextMenuRepresentation(
-    private val backgroundColor: Color,
-    private val textColor: Color,
-    private val itemHoverColor: Color
-) : ContextMenuRepresentation {
-    @Composable
-    override fun Representation(state: ContextMenuState, items: List<ContextMenuItem>) {
-        val isOpen = state.status is ContextMenuState.Status.Open
-        if (isOpen) {
-            Popup(
-                focusable = true,
-                onDismissRequest = { state.status = ContextMenuState.Status.Closed },
-                popupPositionProvider = rememberCursorPositionProvider()
-            ) {
-                Column(
-                    modifier = Modifier
-                        .shadow(8.dp)
-                        .background(backgroundColor)
-                        .padding(vertical = 4.dp)
-                        .width(IntrinsicSize.Max)
-                        .verticalScroll(rememberScrollState())
-
-                ) {
-                    items.distinctBy { it.label }.forEach { item ->
-                        MenuItemContent(
-                            itemHoverColor = itemHoverColor,
-                            onClick = {
-                                state.status = ContextMenuState.Status.Closed
-                                item.onClick()
-                            }
-                        ) {
-                            BasicText(text = item.label, style = TextStyle(color = textColor))
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
-@Composable
-private fun MenuItemContent(
-    itemHoverColor: Color,
-    onClick: () -> Unit,
-    content: @Composable RowScope.() -> Unit
-) {
-    var hovered by remember { mutableStateOf(false) }
-    Row(
-        modifier = Modifier
-            .clickable(
-                onClick = onClick,
-            )
-            .onHover { hovered = it }
-            .background(if (hovered) itemHoverColor else Color.Transparent)
-            .fillMaxWidth()
-            // Preferred min and max width used during the intrinsic measurement.
-            .sizeIn(
-                minWidth = 112.dp,
-                maxWidth = 280.dp,
-                minHeight = 32.dp
-            )
-            .padding(
-                PaddingValues(
-                    horizontal = 16.dp,
-                    vertical = 0.dp
-                )
-            ),
-        verticalAlignment = Alignment.CenterVertically
-    ) {
-        content()
-    }
-}
-
-private fun Modifier.onHover(onHover: (Boolean) -> Unit) = pointerInput(Unit) {
-    awaitPointerEventScope {
-        while (true) {
-            val event = awaitPointerEvent()
-            when (event.type) {
-                PointerEventType.Enter -> onHover(true)
-                PointerEventType.Exit -> onHover(false)
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/ContextMenuProvider.desktop.kt b/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/ContextMenuProvider.desktop.kt
deleted file mode 100644
index 2a7fa89..0000000
--- a/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/ContextMenuProvider.desktop.kt
+++ /dev/null
@@ -1,259 +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
-
-import androidx.compose.foundation.gestures.awaitEachGesture
-import androidx.compose.foundation.layout.Box
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.ProvidableCompositionLocal
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.runtime.staticCompositionLocalOf
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.input.pointer.AwaitPointerEventScope
-import androidx.compose.ui.input.pointer.PointerEvent
-import androidx.compose.ui.input.pointer.changedToDown
-import androidx.compose.ui.input.pointer.isSecondaryPressed
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.util.fastAll
-
-/**
- * Defines a container where context menu is available. Menu is triggered by right mouse clicks.
- * Representation of menu is defined by [LocalContextMenuRepresentation]`
- *
- * @param items List of context menu items. Final context menu contains all items from descendant
- * [ContextMenuArea] and [ContextMenuDataProvider].
- * @param state [ContextMenuState] of menu controlled by this area.
- * @param enabled If false then gesture detector is disabled.
- * @param content The content of the [ContextMenuArea].
- */
-@Composable
-fun ContextMenuArea(
-    items: () -> List<ContextMenuItem>,
-    state: ContextMenuState = remember { ContextMenuState() },
-    enabled: Boolean = true,
-    content: @Composable () -> Unit
-) {
-    val data = ContextMenuData(items, LocalContextMenuData.current)
-
-    ContextMenuDataProvider(data) {
-        Box(Modifier.contextMenuDetector(state, enabled), propagateMinConstraints = true) {
-            content()
-        }
-        LocalContextMenuRepresentation.current.Representation(state, data.allItems)
-    }
-}
-
-/**
- * Adds items to the hierarchy of context menu items. Can be used, for example, to customize
- * context menu of text fields.
- *
- * @param items List of context menu items. Final context menu contains all items from descendant
- * [ContextMenuArea] and [ContextMenuDataProvider].
- * @param content The content of the [ContextMenuDataProvider].
- *
- * @see [[ContextMenuArea]]
- */
-@Composable
-fun ContextMenuDataProvider(
-    items: () -> List<ContextMenuItem>,
-    content: @Composable () -> Unit
-) {
-    ContextMenuDataProvider(
-        ContextMenuData(items, LocalContextMenuData.current),
-        content
-    )
-}
-
-@Composable
-internal fun ContextMenuDataProvider(
-    data: ContextMenuData,
-    content: @Composable () -> Unit
-) {
-    CompositionLocalProvider(
-        LocalContextMenuData provides data
-    ) {
-        content()
-    }
-}
-
-private val LocalContextMenuData = staticCompositionLocalOf<ContextMenuData?> {
-    null
-}
-
-private fun Modifier.contextMenuDetector(
-    state: ContextMenuState,
-    enabled: Boolean = true
-): Modifier {
-    return if (
-        enabled && state.status == ContextMenuState.Status.Closed
-    ) {
-        this.pointerInput(state) {
-            awaitEachGesture {
-                val event = awaitEventFirstDown()
-                if (event.buttons.isSecondaryPressed) {
-                    event.changes.forEach { it.consume() }
-                    state.status =
-                        ContextMenuState.Status.Open(Rect(event.changes[0].position, 0f))
-                }
-            }
-        }
-    } else {
-        Modifier
-    }
-}
-
-private suspend fun AwaitPointerEventScope.awaitEventFirstDown(): PointerEvent {
-    var event: PointerEvent
-    do {
-        event = awaitPointerEvent()
-    } while (
-        !event.changes.fastAll { it.changedToDown() }
-    )
-    return event
-}
-
-/**
- * Individual element of context menu.
- *
- * @param label The text to be displayed as a context menu item.
- * @param onClick The action to be executed after click on the item.
- */
-class ContextMenuItem(
-    val label: String,
-    val onClick: () -> Unit
-) {
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        if (other == null || this::class != other::class) return false
-
-        other as ContextMenuItem
-
-        if (label != other.label) return false
-        if (onClick != other.onClick) return false
-
-        return true
-    }
-
-    override fun hashCode(): Int {
-        var result = label.hashCode()
-        result = 31 * result + onClick.hashCode()
-        return result
-    }
-
-    override fun toString(): String {
-        return "ContextMenuItem(label='$label')"
-    }
-}
-
-/**
- * Data container contains all [ContextMenuItem]s were defined previously in the hierarchy.
- * [ContextMenuRepresentation] uses it to display context menu.
- */
-class ContextMenuData(
-    val items: () -> List<ContextMenuItem>,
-    val next: ContextMenuData?
-) {
-
-    internal val allItems: List<ContextMenuItem> by lazy {
-        allItemsSeq.toList()
-    }
-
-    internal val allItemsSeq: Sequence<ContextMenuItem>
-        get() = sequence {
-            yieldAll(items())
-            next?.let { yieldAll(it.allItemsSeq) }
-        }
-
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        if (other == null || this::class != other::class) return false
-
-        other as ContextMenuData
-
-        if (items != other.items) return false
-        if (next != other.next) return false
-
-        return true
-    }
-
-    override fun hashCode(): Int {
-        var result = items.hashCode()
-        result = 31 * result + (next?.hashCode() ?: 0)
-        return result
-    }
-}
-
-/**
- * Represents a state of context menu in [ContextMenuArea]. [status] is implemented
- * via [androidx.compose.runtime.MutableState] so it's possible to track it inside @Composable
- * functions.
- */
-class ContextMenuState {
-    sealed class Status {
-        class Open(
-            val rect: Rect
-        ) : Status() {
-            override fun equals(other: Any?): Boolean {
-                if (this === other) return true
-                if (other == null || this::class != other::class) return false
-
-                other as Open
-
-                if (rect != other.rect) return false
-
-                return true
-            }
-
-            override fun hashCode(): Int {
-                return rect.hashCode()
-            }
-
-            override fun toString(): String {
-                return "Open(rect=$rect)"
-            }
-        }
-
-        object Closed : Status()
-    }
-
-    var status: Status by mutableStateOf(Status.Closed)
-}
-
-/**
- * Implementations of this interface are responsible for displaying context menus. There are two
- * implementations out of the box: [LightDefaultContextMenuRepresentation] and
- * [DarkDefaultContextMenuRepresentation].
- * To change currently used representation, different value for [LocalContextMenuRepresentation]
- * could be provided.
- */
-interface ContextMenuRepresentation {
-    @Composable
-    fun Representation(state: ContextMenuState, items: List<ContextMenuItem>)
-}
-
-/**
- * Composition local that keeps [ContextMenuRepresentation] which is used by [ContextMenuArea]s.
- */
-val LocalContextMenuRepresentation:
-    ProvidableCompositionLocal<ContextMenuRepresentation> = staticCompositionLocalOf {
-    LightDefaultContextMenuRepresentation
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/ContextMenu.desktop.kt b/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/ContextMenu.desktop.kt
deleted file mode 100644
index dcea9d1..0000000
--- a/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/ContextMenu.desktop.kt
+++ /dev/null
@@ -1,52 +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.newtext.text.copypasta
-
-import androidx.compose.foundation.ContextMenuItem
-import androidx.compose.foundation.ContextMenuState
-import androidx.compose.foundation.newtext.text.copypasta.selection.SelectionManager
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.snapshotFlow
-import androidx.compose.ui.geometry.Offset
-import kotlinx.coroutines.flow.collect
-
-@Composable
-internal actual fun ContextMenuArea(
-    manager: SelectionManager,
-    content: @Composable () -> Unit
-) {
-    /* noop*/
-}
-
-@Composable
-internal fun OpenMenuAdjuster(state: ContextMenuState, adjustAction: (Offset) -> Unit) {
-    LaunchedEffect(state) {
-        snapshotFlow { state.status }.collect { status ->
-            if (status is ContextMenuState.Status.Open) {
-                adjustAction(status.rect.center)
-            }
-        }
-    }
-}
-
-@Composable
-internal fun SelectionManager.contextMenuItems(): () -> List<ContextMenuItem> {
-    return {
-        listOf()
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/StringHelpers.desktop.kt b/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/StringHelpers.desktop.kt
deleted file mode 100644
index 58ac4c9..0000000
--- a/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/StringHelpers.desktop.kt
+++ /dev/null
@@ -1,31 +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.newtext.text.copypasta
-
-import org.jetbrains.skia.BreakIterator
-
-internal actual fun String.findPrecedingBreak(index: Int): Int {
-    val it = BreakIterator.makeCharacterInstance()
-    it.setText(this)
-    return it.preceding(index)
-}
-
-internal actual fun String.findFollowingBreak(index: Int): Int {
-    val it = BreakIterator.makeCharacterInstance()
-    it.setText(this)
-    return it.following(index)
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/TextPointerIcon.desktop.kt b/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/TextPointerIcon.desktop.kt
deleted file mode 100644
index ff55d6d..0000000
--- a/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/TextPointerIcon.desktop.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.newtext.text.copypasta
-
-import androidx.compose.ui.input.pointer.PointerIcon
-import java.awt.Cursor
-
-internal actual val textPointerIcon: PointerIcon =
-    PointerIcon(Cursor(Cursor.TEXT_CURSOR))
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/DesktopSelectionHandles.desktop.kt b/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/DesktopSelectionHandles.desktop.kt
deleted file mode 100644
index c509e01..0000000
--- a/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/DesktopSelectionHandles.desktop.kt
+++ /dev/null
@@ -1,34 +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.newtext.text.copypasta.selection
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.text.style.ResolvedTextDirection
-
-@Composable
-internal actual fun SelectionHandle(
-    position: Offset,
-    isStartHandle: Boolean,
-    direction: ResolvedTextDirection,
-    handlesCrossed: Boolean,
-    modifier: Modifier,
-    content: (@Composable () -> Unit)?
-) {
-    // TODO
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionManager.desktop.kt b/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionManager.desktop.kt
deleted file mode 100644
index a03574a..0000000
--- a/compose/foundation/foundation-newtext/src/desktopMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/selection/SelectionManager.desktop.kt
+++ /dev/null
@@ -1,30 +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.newtext.text.copypasta.selection
-
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.input.key.KeyEvent
-
-// this doesn't sounds very sustainable
-// it would end up being a function for any conceptual keyevent (selectall, cut, copy, paste)
-// TODO(b/1564937)
-internal actual fun isCopyKeyEvent(keyEvent: KeyEvent) = true
-
-/**
- * Magnification is not supported on desktop.
- */
-internal actual fun Modifier.selectionMagnifier(manager: SelectionManager): Modifier = this
diff --git a/compose/foundation/foundation-newtext/src/jvmMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/AtomicLong.kt b/compose/foundation/foundation-newtext/src/jvmMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/AtomicLong.kt
deleted file mode 100644
index 8f2bd49..0000000
--- a/compose/foundation/foundation-newtext/src/jvmMain/kotlin/androidx/compose/foundation/newtext/text/copypasta/AtomicLong.kt
+++ /dev/null
@@ -1,21 +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.
- */
-
-// ktlint-disable filename
-
-package androidx.compose.foundation.newtext.text.copypasta
-
-internal actual typealias AtomicLong = java.util.concurrent.atomic.AtomicLong
\ No newline at end of file
diff --git a/compose/foundation/foundation-newtext/src/test/kotlin/androidx/compose/foundation/newtext/text/modifiers/MultiParagraphLayoutCacheTest.kt b/compose/foundation/foundation-newtext/src/test/kotlin/androidx/compose/foundation/newtext/text/modifiers/MultiParagraphLayoutCacheTest.kt
deleted file mode 100644
index 32ef628..0000000
--- a/compose/foundation/foundation-newtext/src/test/kotlin/androidx/compose/foundation/newtext/text/modifiers/MultiParagraphLayoutCacheTest.kt
+++ /dev/null
@@ -1,58 +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.foundation.newtext.text.modifiers
-
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.unit.Density
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.kotlin.mock
-
-@RunWith(JUnit4::class)
-class MultiParagraphLayoutCacheTest {
-    private val density = Density(density = 1f)
-    private val fontFamilyResolver = mock<FontFamily.Resolver>()
-
-    @Test(expected = IllegalStateException::class)
-    fun whenMinInstrinsicWidth_withoutLayout_throws() {
-        val textDelegate = MultiParagraphLayoutCache(
-            text = AnnotatedString(""),
-            style = TextStyle.Default,
-            fontFamilyResolver = fontFamilyResolver
-        ).also {
-            it.density = density
-        }
-
-        textDelegate.minIntrinsicWidth
-    }
-
-    @Test(expected = IllegalStateException::class)
-    fun whenMaxIntrinsicWidth_withoutLayout_throws() {
-        val textDelegate = MultiParagraphLayoutCache(
-            text = AnnotatedString(""),
-            style = TextStyle.Default,
-            fontFamilyResolver = fontFamilyResolver
-        ).also {
-            it.density = density
-        }
-
-        textDelegate.maxIntrinsicWidth
-    }
-}
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index ade8b1a..97bb9ba 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -740,10 +740,13 @@
   }
 
   public final class BasicTextKt {
-    method @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines);
-    method @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, 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);
-    method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines);
-    method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent);
+    method @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines);
+    method @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, 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);
+    method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines);
+    method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent);
+    method @Deprecated public static boolean getNewTextRendering1_5();
+    method @Deprecated public static void setNewTextRendering1_5(boolean);
+    property @Deprecated public static final boolean NewTextRendering1_5;
   }
 
   public final class ClickableTextKt {
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index a57c9d4..df15145 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -920,17 +920,22 @@
   }
 
   @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public interface StaggeredGridCells {
-    method public java.util.List<java.lang.Integer> calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
+    method public int[] calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
   }
 
   public static final class StaggeredGridCells.Adaptive implements androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells {
     ctor public StaggeredGridCells.Adaptive(float minSize);
-    method public java.util.List<java.lang.Integer> calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
+    method public int[] calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
   }
 
   public static final class StaggeredGridCells.Fixed implements androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells {
     ctor public StaggeredGridCells.Fixed(int count);
-    method public java.util.List<java.lang.Integer> calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
+    method public int[] calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
+  }
+
+  public static final class StaggeredGridCells.FixedSize implements androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells {
+    ctor public StaggeredGridCells.FixedSize(float size);
+    method public int[] calculateCrossAxisCellSizes(androidx.compose.ui.unit.Density, int availableSize, int spacing);
   }
 
   @androidx.compose.foundation.ExperimentalFoundationApi public final class StaggeredGridItemSpan {
@@ -1177,10 +1182,13 @@
   }
 
   public final class BasicTextKt {
-    method @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines);
-    method @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, 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);
-    method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines);
-    method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent);
+    method @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines);
+    method @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, 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);
+    method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines);
+    method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent);
+    method @Deprecated public static boolean getNewTextRendering1_5();
+    method @Deprecated public static void setNewTextRendering1_5(boolean);
+    property @Deprecated public static final boolean NewTextRendering1_5;
   }
 
   public final class ClickableTextKt {
@@ -1279,7 +1287,7 @@
 package androidx.compose.foundation.text2 {
 
   public final class BasicTextField2Kt {
-    method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void BasicTextField2(androidx.compose.foundation.text2.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional int minLines, optional int maxLines, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout);
+    method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void BasicTextField2(androidx.compose.foundation.text2.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional int minLines, optional int maxLines, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
   }
 
   @androidx.compose.foundation.ExperimentalFoundationApi public fun interface TextEditFilter {
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index ade8b1a..97bb9ba 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -740,10 +740,13 @@
   }
 
   public final class BasicTextKt {
-    method @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines);
-    method @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, 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);
-    method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines);
-    method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent);
+    method @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines);
+    method @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit>? onTextLayout, 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);
+    method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(String text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines);
+    method @Deprecated @androidx.compose.runtime.Composable public static void BasicText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.text.TextStyle style, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit>? onTextLayout, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent);
+    method @Deprecated public static boolean getNewTextRendering1_5();
+    method @Deprecated public static void setNewTextRendering1_5(boolean);
+    property @Deprecated public static final boolean NewTextRendering1_5;
   }
 
   public final class ClickableTextKt {
diff --git a/compose/foundation/foundation/benchmark/build.gradle b/compose/foundation/foundation/benchmark/build.gradle
index 905f5b4..8a2d505 100644
--- a/compose/foundation/foundation/benchmark/build.gradle
+++ b/compose/foundation/foundation/benchmark/build.gradle
@@ -31,7 +31,6 @@
     androidTestImplementation project(":compose:ui:ui-text:ui-text-benchmark")
     androidTestImplementation project(":compose:foundation:foundation-layout")
     androidTestImplementation project(":compose:foundation:foundation")
-    androidTestImplementation project(":compose:foundation:foundation-do-not-ship-newtext")
     androidTestImplementation project(":compose:material:material")
     androidTestImplementation project(":compose:benchmark-utils")
     androidTestImplementation(libs.testRules)
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/TextDelegateBenchmark.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/TextDelegateBenchmark.kt
index 8d45456..9bb4429 100644
--- a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/TextDelegateBenchmark.kt
+++ b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/TextDelegateBenchmark.kt
@@ -24,7 +24,6 @@
 import androidx.compose.foundation.text.TextDelegate
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.ImageBitmap
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.benchmark.RandomTextGenerator
 import androidx.compose.ui.text.benchmark.TextBenchmarkTestRule
@@ -85,7 +84,6 @@
         ).roundToInt()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     private fun textDelegate(textGenerator: RandomTextGenerator): TextDelegate {
         val text = textGenerator.nextAnnotatedString(
             length = textLength,
@@ -101,7 +99,6 @@
         )
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun constructor() {
         textBenchmarkTestRule.generator { textGenerator ->
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/EmpiricalBench.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/EmpiricalBench.kt
index a3d29f3..9c3951d 100644
--- a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/EmpiricalBench.kt
+++ b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/EmpiricalBench.kt
@@ -16,12 +16,18 @@
 
 package androidx.compose.foundation.benchmark.text.empirical
 
+import androidx.compose.foundation.text.InlineTextContent
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
 import androidx.compose.testutils.LayeredComposeTestCase
 import androidx.compose.testutils.ToggleableTestCase
 import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
 import androidx.compose.testutils.benchmark.toggleStateBenchmarkComposeMeasureLayout
 import androidx.compose.testutils.benchmark.toggleStateBenchmarkDraw
 import androidx.compose.testutils.benchmark.toggleStateBenchmarkRecompose
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.TextStyle
 import org.junit.Rule
 import org.junit.Test
 
@@ -47,3 +53,27 @@
         benchmarkRule.toggleStateBenchmarkDraw(caseFactory)
     }
 }
+
+@Composable
+fun Subject(text: String, style: TextStyle) {
+    Text(text, style = style)
+}
+
+@Composable
+fun Subject(text: String, modifier: Modifier, style: TextStyle) {
+    Text(text, modifier, style = style)
+}
+
+@Composable
+fun Subject(text: AnnotatedString, style: TextStyle) {
+    Text(text, style = style)
+}
+
+@Composable
+fun Subject(
+    text: AnnotatedString,
+    style: TextStyle,
+    inlineContent: Map<String, InlineTextContent>
+) {
+    Text(text, style = style, inlineContent = inlineContent)
+}
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/IfNotEmptyCallText.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/IfNotEmptyCallText.kt
index 9e4a39e1..38e6250 100644
--- a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/IfNotEmptyCallText.kt
+++ b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/IfNotEmptyCallText.kt
@@ -17,7 +17,7 @@
 package androidx.compose.foundation.benchmark.text.empirical
 
 import androidx.compose.foundation.benchmark.text.DoFullBenchmark
-import androidx.compose.foundation.text.BasicText
+import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.testutils.LayeredComposeTestCase
@@ -42,7 +42,7 @@
     @Composable
     override fun MeasuredContent() {
         if (toggleText.value.isNotEmpty()) {
-            BasicText(
+            Text(
                 toggleText.value,
                 style = style
             )
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/IfNotEmptyCallTextWithSpans.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/IfNotEmptyCallTextWithSpans.kt
index 2de260d..d37ba36 100644
--- a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/IfNotEmptyCallTextWithSpans.kt
+++ b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/IfNotEmptyCallTextWithSpans.kt
@@ -17,12 +17,12 @@
 package androidx.compose.foundation.benchmark.text.empirical
 
 import androidx.compose.foundation.benchmark.text.DoFullBenchmark
-import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.testutils.LayeredComposeTestCase
 import androidx.compose.testutils.ToggleableTestCase
 import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.font.FontFamily
 import androidx.test.filters.LargeTest
 import org.junit.Assume
@@ -49,9 +49,11 @@
 ) : LayeredComposeTestCase(), ToggleableTestCase {
     private var toggleText = mutableStateOf(AnnotatedString(""))
 
+    private val style = TextStyle.Default.copy(fontFamily = FontFamily.Monospace)
+
     @Composable
     override fun MeasuredContent() {
-        Text(toggleText.value, fontFamily = FontFamily.Monospace)
+        Subject(toggleText.value, style = style)
     }
 
     override fun toggleState() {
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/ModifierIfNotEmptyCallText.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/ModifierIfNotEmptyCallText.kt
deleted file mode 100644
index 153b285..0000000
--- a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/ModifierIfNotEmptyCallText.kt
+++ /dev/null
@@ -1,114 +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.foundation.benchmark.text.empirical
-
-import androidx.compose.foundation.benchmark.text.DoFullBenchmark
-import androidx.compose.foundation.newtext.text.TextUsingModifier
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.testutils.LayeredComposeTestCase
-import androidx.compose.testutils.ToggleableTestCase
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontFamily
-import androidx.test.filters.LargeTest
-import org.junit.Assume
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-
-/**
- * Toggle between missing Text and Text("aaa..") to simulate backend text loading.
- *
- * This intentionally hits as many text caches as possible, to isolate compose setText behavior.
- */
-@OptIn(ExperimentalTextApi::class)
-class ModifierIfNotEmptyCallText(
-    private val text: String
-) : LayeredComposeTestCase(), ToggleableTestCase {
-    private var toggleText = mutableStateOf("")
-
-    private val style = TextStyle.Default.copy(fontFamily = FontFamily.Monospace)
-
-    @Composable
-    override fun MeasuredContent() {
-        if (toggleText.value.isNotEmpty()) {
-            TextUsingModifier(
-                text = toggleText.value,
-                style = style
-            )
-        }
-    }
-
-    override fun toggleState() {
-        if (toggleText.value == "") {
-            toggleText.value = text
-        } else {
-            toggleText.value = ""
-        }
-    }
-}
-
-@LargeTest
-@RunWith(Parameterized::class)
-open class ModifierIfNotEmptyParent(
-    private val size: Int
-) : EmpiricalBench<ModifierIfNotEmptyCallText>() {
-
-    override val caseFactory = {
-        val text = generateCacheableStringOf(size)
-        ModifierIfNotEmptyCallText(text)
-    }
-
-    companion object {
-        @JvmStatic
-        @Parameterized.Parameters(name = "size={0}")
-        fun initParameters(): Array<Any> = arrayOf()
-    }
-}
-
-/**
- * Metrics determined from all apps
- */
-@LargeTest
-@RunWith(Parameterized::class)
-class ModifierAllAppsIfNotEmptyCallText(size: Int) : ModifierIfNotEmptyParent(size) {
-    companion object {
-        @JvmStatic
-        @Parameterized.Parameters(name = "size={0}")
-        fun initParameters(): Array<Any> = AllApps.TextLengths
-    }
-}
-
-/**
- * Metrics for Chat-like apps.
- *
- * These apps typically have more longer strings, due to user generated content.
- */
-@LargeTest
-@RunWith(Parameterized::class)
-class ModifierChatAppIfNotEmptyCallText(size: Int) : ModifierIfNotEmptyParent(size) {
-    companion object {
-        @JvmStatic
-        @Parameterized.Parameters(name = "size={0}")
-        fun initParameters(): Array<Any> = ChatApps.TextLengths
-    }
-
-    init {
-        // we only need this for full reporting
-        Assume.assumeTrue(DoFullBenchmark)
-    }
-}
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/ModifierSetText.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/ModifierSetText.kt
deleted file mode 100644
index 0542cf5..0000000
--- a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/ModifierSetText.kt
+++ /dev/null
@@ -1,109 +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.foundation.benchmark.text.empirical
-
-import androidx.compose.foundation.benchmark.text.DoFullBenchmark
-import androidx.compose.foundation.newtext.text.TextUsingModifier
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.testutils.LayeredComposeTestCase
-import androidx.compose.testutils.ToggleableTestCase
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontFamily
-import androidx.test.filters.LargeTest
-import org.junit.Assume
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-
-/**
- * Toggle between "" and "aaaa..." to simulate backend text loading.
- *
- * This intentionally hits as many text caches as possible, to isolate compose setText behavior.
- */
-class ModifierSetText(private val text: String) : LayeredComposeTestCase(), ToggleableTestCase {
-    private val toggleText = mutableStateOf("")
-
-    private val style = TextStyle.Default.copy(fontFamily = FontFamily.Monospace)
-
-    @OptIn(ExperimentalTextApi::class)
-    @Composable
-    override fun MeasuredContent() {
-        TextUsingModifier(
-            toggleText.value,
-            style = style
-        )
-    }
-
-    override fun toggleState() {
-        if (toggleText.value == "") {
-            toggleText.value = text
-        } else {
-            toggleText.value = ""
-        }
-    }
-}
-
-@LargeTest
-@RunWith(Parameterized::class)
-open class ModifierSetTextParent(
-    private val size: Int
-) : EmpiricalBench<ModifierSetText>() {
-    override val caseFactory = {
-        val text = generateCacheableStringOf(size)
-        ModifierSetText(text)
-    }
-
-    companion object {
-        @JvmStatic
-        @Parameterized.Parameters(name = "size={0}")
-        fun initParameters(): Array<Any> = arrayOf()
-    }
-}
-
-/**
- * Metrics determined from all apps
- */
-@LargeTest
-@RunWith(Parameterized::class)
-class ModifierAllAppsSetText(size: Int) : ModifierSetTextParent(size) {
-    companion object {
-        @JvmStatic
-        @Parameterized.Parameters(name = "size={0}")
-        fun initParameters(): Array<Any> = AllApps.TextLengths
-    }
-}
-
-/**
- * Metrics for Chat-like apps.
- *
- * These apps typically have more longer strings, due to user generated content.
- */
-@LargeTest
-@RunWith(Parameterized::class)
-class ModifierChatAppSetText(size: Int) : ModifierSetTextParent(size) {
-    companion object {
-        @JvmStatic
-        @Parameterized.Parameters(name = "size={0}")
-        fun initParameters(): Array<Any> = ChatApps.TextLengths
-    }
-
-    init {
-        // we only need this for full reporting
-        Assume.assumeTrue(DoFullBenchmark)
-    }
-}
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/SetText.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/SetText.kt
index 2b1d49d..845e473 100644
--- a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/SetText.kt
+++ b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/SetText.kt
@@ -17,7 +17,6 @@
 package androidx.compose.foundation.benchmark.text.empirical
 
 import androidx.compose.foundation.benchmark.text.DoFullBenchmark
-import androidx.compose.foundation.text.BasicText
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.testutils.LayeredComposeTestCase
@@ -41,7 +40,7 @@
 
     @Composable
     override fun MeasuredContent() {
-        BasicText(
+        Subject(
             toggleText.value,
             style = style
         )
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/SetTextFillMaxWidth.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/SetTextFillMaxWidth.kt
index f64c74c..60f2d14 100644
--- a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/SetTextFillMaxWidth.kt
+++ b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/SetTextFillMaxWidth.kt
@@ -18,12 +18,12 @@
 
 import androidx.compose.foundation.benchmark.text.DoFullBenchmark
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.testutils.LayeredComposeTestCase
 import androidx.compose.testutils.ToggleableTestCase
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.font.FontFamily
 import androidx.test.filters.LargeTest
 import org.junit.Assume
@@ -40,12 +40,14 @@
 class SetTextFillMaxWidth(private val text: String) : LayeredComposeTestCase(), ToggleableTestCase {
     private var toggleText = mutableStateOf("")
 
+    private val style = TextStyle.Default.copy(fontFamily = FontFamily.Monospace)
+
     @Composable
     override fun MeasuredContent() {
-        Text(
+        Subject(
             toggleText.value,
             modifier = Modifier.fillMaxWidth(),
-            fontFamily = FontFamily.Monospace
+            style = style
         )
     }
 
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/SetTextWithInlineContent.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/SetTextWithInlineContent.kt
index a157190..4916430 100644
--- a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/SetTextWithInlineContent.kt
+++ b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/SetTextWithInlineContent.kt
@@ -20,7 +20,6 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.text.InlineTextContent
-import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.testutils.LayeredComposeTestCase
@@ -29,6 +28,7 @@
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.Placeholder
 import androidx.compose.ui.text.PlaceholderVerticalAlign
+import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.font.FontFamily
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
@@ -49,10 +49,12 @@
 ) : LayeredComposeTestCase(), ToggleableTestCase {
     private var toggleText = mutableStateOf(AnnotatedString(""))
 
+    private val style = TextStyle.Default.copy(fontFamily = FontFamily.Monospace)
+
     @Composable
     override fun MeasuredContent() {
-        Text(toggleText.value,
-            fontFamily = FontFamily.Monospace,
+        Subject(toggleText.value,
+            style = style,
             inlineContent = mapOf(
                 BenchmarkInlineContentId to InlineTextContent(
                     Placeholder(12.sp, 12.sp, PlaceholderVerticalAlign.Center)
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/SetTextWithSpans.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/SetTextWithSpans.kt
index 13c3962..bf8d216 100644
--- a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/SetTextWithSpans.kt
+++ b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/SetTextWithSpans.kt
@@ -17,12 +17,12 @@
 package androidx.compose.foundation.benchmark.text.empirical
 
 import androidx.compose.foundation.benchmark.text.DoFullBenchmark
-import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.testutils.LayeredComposeTestCase
 import androidx.compose.testutils.ToggleableTestCase
 import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.font.FontFamily
 import androidx.test.filters.LargeTest
 import org.junit.Assume.assumeTrue
@@ -49,9 +49,11 @@
 ) : LayeredComposeTestCase(), ToggleableTestCase {
     private var toggleText = mutableStateOf(AnnotatedString(""))
 
+    private val style = TextStyle.Default.copy(fontFamily = FontFamily.Monospace)
+
     @Composable
     override fun MeasuredContent() {
-        Text(toggleText.value, fontFamily = FontFamily.Monospace)
+        Subject(toggleText.value, style = style)
     }
 
     override fun toggleState() {
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/baselines/StaticLayoutBaseline.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/baselines/StaticLayoutBaseline.kt
index 0ab85a3..4e3cd87 100644
--- a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/baselines/StaticLayoutBaseline.kt
+++ b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/empirical/baselines/StaticLayoutBaseline.kt
@@ -72,6 +72,9 @@
                 isEmpty = !isEmpty
                 if (isEmpty) "" else text
             }
+            // measure requires this in typical cases
+            Layout.getDesiredWidth(measureText, textPaint)
+            // paint requires this in all cases
             val layout = makeStaticLayout(measureText, textPaint)
             layout.draw(canvas)
         }
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
index 27d2e77..cd800a7 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
@@ -986,11 +986,11 @@
         val layoutDirection = if (rtl) LayoutDirection.Rtl else LayoutDirection.Ltr
         CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) {
             LazyVerticalStaggeredGrid(
-                columns = StaggeredGridCells.Fixed(3),
+                columns = StaggeredGridCells.FixedSize(100.dp),
                 modifier = Modifier.fillMaxSize(),
                 state = state,
                 contentPadding = PaddingValues(vertical = 30.dp, horizontal = 20.dp),
-                horizontalArrangement = Arrangement.spacedBy(10.dp),
+                horizontalArrangement = Arrangement.End,
                 verticalItemSpacing = 10.dp,
                 reverseLayout = reverseLayout,
                 content = {
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeTextAccessibility.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeTextAccessibility.kt
index e353496..3f36fd9 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeTextAccessibility.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeTextAccessibility.kt
@@ -76,9 +76,9 @@
             text = buildAnnotatedString {
                 append("This word is a link: ")
                 withAnnotation(UrlAnnotation("https://google.com")) {
-                    append("Google\n")
+                    append("Google")
                 }
-                append("This word is not a link: google.com")
+                append("\nThis word is not a link: google.com")
             },
             style = TextStyle(fontSize = fontSize8)
         )
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/PlatformTextInputAdapterDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/PlatformTextInputAdapterDemo.kt
index 193fd9d..778644a 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/PlatformTextInputAdapterDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/PlatformTextInputAdapterDemo.kt
@@ -60,7 +60,6 @@
 import androidx.compose.ui.text.input.PlatformTextInput
 import androidx.compose.ui.text.input.PlatformTextInputAdapter
 import androidx.compose.ui.text.input.PlatformTextInputPlugin
-import androidx.compose.ui.text.input.TextInputForTests
 import androidx.compose.ui.unit.dp
 import androidx.core.view.inputmethod.EditorInfoCompat
 import kotlinx.coroutines.launch
@@ -174,9 +173,6 @@
     private var currentSession: WackyTextState? = null
     private var currentConnection: WackyInputConnection? = null
 
-    override val inputForTests: TextInputForTests
-        get() = currentConnection ?: error("WackyTextInputService is not active")
-
     fun startInput(state: WackyTextState) {
         Log.d(TAG, "starting input for $state")
         platformTextInput.requestInputFocus()
@@ -220,7 +216,7 @@
      */
     private inner class WackyInputConnection(
         private val state: WackyTextState
-    ) : BaseInputConnection(view, false), TextInputForTests {
+    ) : BaseInputConnection(view, false) {
         private var selection: TextRange = TextRange(state.buffer.length)
         private var composition: TextRange? = null
 
@@ -339,14 +335,5 @@
         }
 
         // endregion
-        // region TextInputForTests
-
-        override fun inputTextForTest(text: String) {
-            beginBatchEdit()
-            commitText(text, 0)
-            endBatchEdit()
-        }
-
-        // endregion
     }
 }
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 72d075c..1702e47 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
@@ -17,6 +17,7 @@
 package androidx.compose.foundation.demos.text
 
 import androidx.compose.foundation.demos.text2.BasicTextField2Demos
+import androidx.compose.foundation.demos.text2.DecorationBoxDemos
 import androidx.compose.foundation.demos.text2.KeyboardOptionsDemos
 import androidx.compose.integration.demos.common.ComposableDemo
 import androidx.compose.integration.demos.common.DemoCategory
@@ -124,6 +125,9 @@
                 },
                 ComposableDemo("Keyboard Options") {
                     KeyboardOptionsDemos()
+                },
+                ComposableDemo("Decoration Box") {
+                    DecorationBoxDemos()
                 }
             )
         ),
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/BasicTextField2Demos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/BasicTextField2Demos.kt
index aaeb580..9e4bb72 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/BasicTextField2Demos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/BasicTextField2Demos.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalFoundationApi::class)
-
 package androidx.compose.foundation.demos.text2
 
 import androidx.compose.foundation.ExperimentalFoundationApi
@@ -40,6 +38,9 @@
         TagLine(tag = "Plain BasicTextField2")
         PlainBasicTextField2()
 
+        TagLine(tag = "Single Line BasicTextField2")
+        SingleLineBasicTextField2()
+
         TagLine(tag = "State toggling BasicTextField2")
         StateTogglingBasicTextField2()
 
@@ -48,12 +49,26 @@
     }
 }
 
+@OptIn(ExperimentalFoundationApi::class)
 @Composable
 fun PlainBasicTextField2() {
     val state = remember { TextFieldState() }
-    BasicTextField2(state, Modifier, textStyle = LocalTextStyle.current)
+    BasicTextField2(state, demoTextFieldModifiers, textStyle = LocalTextStyle.current)
 }
 
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+fun SingleLineBasicTextField2() {
+    val state = remember { TextFieldState() }
+    BasicTextField2(
+        state = state,
+        modifier = demoTextFieldModifiers,
+        textStyle = LocalTextStyle.current,
+        maxLines = 1
+    )
+}
+
+@OptIn(ExperimentalFoundationApi::class)
 @Composable
 fun StateTogglingBasicTextField2() {
     var counter by remember { mutableStateOf(0) }
@@ -64,9 +79,10 @@
         counter %= 2
     })
 
-    BasicTextField2(state, Modifier, textStyle = LocalTextStyle.current)
+    BasicTextField2(state, demoTextFieldModifiers, textStyle = LocalTextStyle.current)
 }
 
+@OptIn(ExperimentalFoundationApi::class)
 @Composable
 fun DigitsOnlyBasicTextField2() {
     val state = remember {
@@ -74,5 +90,5 @@
             if (new.text.isDigitsOnly()) new else old
         }
     }
-    BasicTextField2(state, Modifier, textStyle = LocalTextStyle.current)
+    BasicTextField2(state, demoTextFieldModifiers, textStyle = LocalTextStyle.current)
 }
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/DecorationBoxDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/DecorationBoxDemos.kt
new file mode 100644
index 0000000..84e7cfe
--- /dev/null
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/DecorationBoxDemos.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalFoundationApi::class, ExperimentalMaterialApi::class)
+
+package androidx.compose.foundation.demos.text2
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.demos.text.TagLine
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.text2.BasicTextField2
+import androidx.compose.foundation.text2.TextFieldState
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.LocalTextStyle
+import androidx.compose.material.TextFieldDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.input.VisualTransformation
+
+@Composable
+fun DecorationBoxDemos() {
+    Column {
+        TagLine(tag = "OutlinedTextField")
+        OutlinedBasicTextField2()
+    }
+}
+
+@Composable
+fun OutlinedBasicTextField2() {
+    val state = remember { TextFieldState() }
+    BasicTextField2(
+        state = state,
+        modifier = Modifier,
+        textStyle = LocalTextStyle.current,
+        decorationBox = @Composable {
+            TextFieldDefaults.OutlinedTextFieldDecorationBox(
+                value = state.value.text,
+                visualTransformation = VisualTransformation.None,
+                innerTextField = it,
+                placeholder = null,
+                label = null,
+                leadingIcon = null,
+                trailingIcon = null,
+                singleLine = true,
+                enabled = true,
+                isError = false,
+                interactionSource = remember { MutableInteractionSource() },
+                colors = TextFieldDefaults.outlinedTextFieldColors()
+            )
+        }
+    )
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/TransformableSample.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/TransformableSample.kt
index b0f117c..0443df8 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/TransformableSample.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/TransformableSample.kt
@@ -92,11 +92,11 @@
                 "\uD83C\uDF55",
                 fontSize = 32.sp,
                 // apply other transformations like rotation and zoom on the pizza slice emoji
-                modifier = Modifier.graphicsLayer(
-                    scaleX = scale,
-                    scaleY = scale,
+                modifier = Modifier.graphicsLayer {
+                    scaleX = scale
+                    scaleY = scale
                     rotationZ = rotation
-                )
+                }
             )
         }
     }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/HoverableTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/HoverableTest.kt
index 634b442..3b480d2 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/HoverableTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/HoverableTest.kt
@@ -42,7 +42,6 @@
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.launch
 import org.junit.After
 import org.junit.Before
@@ -77,10 +76,7 @@
             Truth.assertThat(modifier.nameFallback).isEqualTo("hoverable")
             Truth.assertThat(modifier.valueOverride).isNull()
             Truth.assertThat(modifier.inspectableElements.map { it.name }.asIterable())
-                .containsExactly(
-                    "interactionSource",
-                    "enabled",
-                )
+                .containsExactly("interactionSource")
         }
     }
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/AwaitEachGestureTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/AwaitEachGestureTest.kt
index ef7040b..592c0df 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/AwaitEachGestureTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/AwaitEachGestureTest.kt
@@ -25,6 +25,7 @@
 import androidx.compose.ui.input.pointer.PointerEventType
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.click
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.performTouchInput
@@ -48,12 +49,14 @@
     @get:Rule
     val rule = createComposeRule()
 
+    private val tag = "pointerInputTag"
+
     @Test
     fun awaitEachGestureInternalCancellation() {
         val inputLatch = CountDownLatch(1)
         rule.setContent {
             Box(
-                Modifier.pointerInput(Unit) {
+                Modifier.testTag(tag).pointerInput(Unit) {
                     try {
                         var count = 0
                         coroutineScope {
@@ -79,6 +82,7 @@
             )
         }
         rule.waitForIdle()
+        rule.onNodeWithTag(tag).performTouchInput { click(Offset.Zero) }
         assertThat(inputLatch.await(1, TimeUnit.SECONDS)).isTrue()
     }
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/ForEachGestureTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/ForEachGestureTest.kt
index 2fcb00d..5660223 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/ForEachGestureTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/ForEachGestureTest.kt
@@ -20,8 +20,13 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.size
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.click
 import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -45,16 +50,20 @@
     @get:Rule
     val rule = createComposeRule()
 
+    private val tag = "pointerInputTag"
+
     /**
-     * Make sure that an empty `forEachGesture` block does not cause a crash.
+     * Make sure that a single `forEachGesture` block does not cause a crash.
+     * Note: Is is no longer possible for an empty gesture since pointerInput() is started lazily.
      */
+    // TODO (jjw): Check with George that this test is needed anymore.
     @Test
-    fun testEmptyForEachGesture() {
+    fun testSingleTapForEachGesture() {
         val latch1 = CountDownLatch(2)
         val latch2 = CountDownLatch(1)
         rule.setContent {
             Box(
-                Modifier.pointerInput(Unit) {
+                Modifier.testTag(tag).pointerInput(Unit) {
                     forEachGesture {
                         if (latch1.count == 0L) {
                             // forEachGesture will loop infinitely with nothing in the middle
@@ -65,12 +74,17 @@
                     }
                 }.pointerInput(Unit) {
                     awaitPointerEventScope {
-                        assertTrue(currentEvent.changes.isEmpty())
+                        // there is no awaitPointerEvent() / loop here, so it will only
+                        // execute once.
+                        assertTrue(currentEvent.changes.size == 1)
                         latch2.countDown()
                     }
                 }.size(10.dp)
             )
         }
+        rule.waitForIdle()
+        rule.onNodeWithTag(tag).performTouchInput { click(Offset.Zero) }
+
         assertTrue(latch1.await(1, TimeUnit.SECONDS))
         assertTrue(latch2.await(1, TimeUnit.SECONDS))
     }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/TapGestureDetectorTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/TapGestureDetectorTest.kt
new file mode 100644
index 0000000..8c4d47f
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/TapGestureDetectorTest.kt
@@ -0,0 +1,905 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.gesture
+
+import androidx.compose.foundation.gestures.GestureCancellationException
+import androidx.compose.foundation.gestures.detectTapAndPress
+import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.testutils.TestViewConfiguration
+import androidx.compose.ui.AbsoluteAlignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.PointerInputScope
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.TouchInjectionScope
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.DpSize
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+private const val TargetTag = "TargetLayout"
+
+@RunWith(JUnit4::class)
+class TapGestureDetectorTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    private var pressed = false
+    private var released = false
+    private var canceled = false
+    private var tapped = false
+    private var doubleTapped = false
+    private var longPressed = false
+
+    /** The time before a long press gesture attempts to win. */
+    private val LongPressTimeoutMillis: Long = 500L
+
+    /**
+     * The maximum time from the start of the first tap to the start of the second
+     * tap in a double-tap gesture.
+     */
+    // TODO(shepshapard): In Android, this is actually the time from the first's up event
+    // to the second's down event, according to the ViewConfiguration docs.
+    private val DoubleTapTimeoutMillis: Long = 300L
+
+    private val util = layoutWithGestureDetector {
+        detectTapGestures(
+            onPress = {
+                pressed = true
+                if (tryAwaitRelease()) {
+                    released = true
+                } else {
+                    canceled = true
+                }
+            },
+            onTap = {
+                tapped = true
+            }
+        )
+    }
+
+    private val utilWithShortcut = layoutWithGestureDetector {
+        detectTapAndPress(
+            onPress = {
+                pressed = true
+                if (tryAwaitRelease()) {
+                    released = true
+                } else {
+                    canceled = true
+                }
+            },
+            onTap = {
+                tapped = true
+            }
+        )
+    }
+
+    private val allGestures = layoutWithGestureDetector {
+        detectTapGestures(
+            onPress = {
+                pressed = true
+                try {
+                    awaitRelease()
+                    released = true
+                } catch (_: GestureCancellationException) {
+                    canceled = true
+                }
+            },
+            onTap = { tapped = true },
+            onLongPress = { longPressed = true },
+            onDoubleTap = { doubleTapped = true }
+        )
+    }
+
+    private val nothingHandler: PointerInputChange.() -> Unit = {}
+
+    private var initialPass: PointerInputChange.() -> Unit = nothingHandler
+    private var finalPass: PointerInputChange.() -> Unit = nothingHandler
+
+    @Before
+    fun setup() {
+        pressed = false
+        released = false
+        canceled = false
+        tapped = false
+        doubleTapped = false
+        longPressed = false
+    }
+
+    private fun layoutWithGestureDetector(
+        gestureDetector: suspend PointerInputScope.() -> Unit,
+    ): @Composable () -> Unit = {
+        CompositionLocalProvider(
+            LocalDensity provides Density(1f),
+            LocalViewConfiguration provides TestViewConfiguration(
+                minimumTouchTargetSize = DpSize.Zero
+            )
+        ) {
+            with(LocalDensity.current) {
+                Box(
+                    Modifier
+                        .fillMaxSize()
+                        // Some tests execute a lambda before the initial and final passes
+                        // so they are called here, higher up the chain, so that the
+                        // calls happen prior to the gestureDetector below. The lambdas
+                        // do things like consume events on the initial pass or validate
+                        // consumption on the final pass.
+                        .pointerInput(Unit) {
+                            awaitPointerEventScope {
+                                while (true) {
+                                    val event = awaitPointerEvent(PointerEventPass.Initial)
+                                    event.changes.forEach {
+                                        initialPass(it)
+                                    }
+                                    awaitPointerEvent(PointerEventPass.Final)
+                                    event.changes.forEach {
+                                        finalPass(it)
+                                    }
+                                }
+                            }
+                        }
+                        .wrapContentSize(AbsoluteAlignment.TopLeft)
+                        .size(10.toDp())
+                        .pointerInput(gestureDetector, gestureDetector)
+                        .testTag(TargetTag)
+                )
+            }
+        }
+    }
+
+    private fun performTouch(
+        initialPass: PointerInputChange.() -> Unit = nothingHandler,
+        finalPass: PointerInputChange.() -> Unit = nothingHandler,
+        block: TouchInjectionScope.() -> Unit
+    ) {
+        this.initialPass = initialPass
+        this.finalPass = finalPass
+        rule.onNodeWithTag(TargetTag).performTouchInput(block)
+        rule.waitForIdle()
+        this.initialPass = nothingHandler
+        this.finalPass = nothingHandler
+    }
+
+    /**
+     * Clicking in the region should result in the callback being invoked.
+     */
+    @Test
+    fun normalTap() {
+        rule.setContent(util)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            down(0, Offset(5f, 5f))
+        }
+
+        assertTrue(pressed)
+        assertFalse(tapped)
+        assertFalse(released)
+
+        rule.mainClock.advanceTimeBy(50)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            up(0)
+        }
+
+        assertTrue(tapped)
+        assertTrue(released)
+        assertFalse(canceled)
+    }
+
+    /**
+     * Clicking in the region should result in the callback being invoked.
+     */
+    @Test
+    fun normalTap_withShortcut() {
+        rule.setContent(utilWithShortcut)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            down(0, Offset(5f, 5f))
+        }
+
+        assertTrue(pressed)
+        assertFalse(tapped)
+        assertFalse(released)
+
+        rule.mainClock.advanceTimeBy(50)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            up(0)
+        }
+
+        assertTrue(tapped)
+        assertTrue(released)
+        assertFalse(canceled)
+    }
+
+    /**
+     * Clicking in the region should result in the callback being invoked.
+     */
+    @Test
+    fun normalTapWithAllGestures() {
+        rule.setContent(allGestures)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            down(0, Offset(5f, 5f))
+        }
+
+        assertTrue(pressed)
+
+        rule.mainClock.advanceTimeBy(50)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            up(0)
+        }
+
+        assertTrue(released)
+
+        // we have to wait for the double-tap timeout before we receive an event
+
+        assertFalse(tapped)
+        assertFalse(doubleTapped)
+
+        rule.mainClock.advanceTimeBy(DoubleTapTimeoutMillis + 10)
+
+        assertTrue(tapped)
+        assertFalse(doubleTapped)
+    }
+
+    /**
+     * Clicking in the region should result in the callback being invoked.
+     */
+    @Test
+    fun normalDoubleTap() {
+        rule.setContent(allGestures)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+        }
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            up(0)
+        }
+
+        assertTrue(pressed)
+        assertTrue(released)
+        assertFalse(tapped)
+        assertFalse(doubleTapped)
+
+        pressed = false
+        released = false
+
+        rule.mainClock.advanceTimeBy(50)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+        }
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            up(0)
+        }
+
+        assertFalse(tapped)
+        assertTrue(doubleTapped)
+        assertTrue(pressed)
+        assertTrue(released)
+    }
+
+    /**
+     * Long press in the region should result in the callback being invoked.
+     */
+    @Test
+    fun normalLongPress() {
+        rule.setContent(allGestures)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            down(0, Offset(5f, 5f))
+        }
+
+        assertTrue(pressed)
+
+        rule.mainClock.advanceTimeBy(LongPressTimeoutMillis + 10)
+
+        assertTrue(longPressed)
+
+        rule.mainClock.advanceTimeBy(500)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            up(0)
+        }
+
+        assertFalse(tapped)
+        assertFalse(doubleTapped)
+        assertTrue(released)
+        assertFalse(canceled)
+    }
+
+    /**
+     * Pressing in the region, sliding out and then lifting should result in
+     * the callback not being invoked
+     */
+    @Test
+    fun tapMiss() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+            moveTo(0, Offset(15f, 15f))
+        }
+
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            up(0)
+        }
+
+        assertTrue(pressed)
+        assertTrue(canceled)
+        assertFalse(released)
+        assertFalse(tapped)
+    }
+
+    /**
+     * Pressing in the region, sliding out and then lifting should result in
+     * the callback not being invoked
+     */
+    @Test
+    fun tapMiss_withShortcut() {
+        rule.setContent(utilWithShortcut)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+            moveTo(0, Offset(15f, 15f))
+        }
+
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            up(0)
+        }
+
+        assertTrue(pressed)
+        assertTrue(canceled)
+        assertFalse(released)
+        assertFalse(tapped)
+    }
+
+    /**
+     * Pressing in the region, sliding out and then lifting should result in
+     * the callback not being invoked
+     */
+    @Test
+    fun longPressMiss() {
+        rule.setContent(allGestures)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+            moveTo(0, Offset(15f, 15f))
+        }
+
+        rule.mainClock.advanceTimeBy(LongPressTimeoutMillis + 10)
+
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            up(0)
+        }
+
+        assertTrue(pressed)
+        assertFalse(released)
+        assertTrue(canceled)
+        assertFalse(tapped)
+        assertFalse(longPressed)
+        assertFalse(doubleTapped)
+    }
+
+    /**
+     * Pressing in the region, sliding out and then lifting should result in
+     * the callback not being invoked for double-tap
+     */
+    @Test
+    fun doubleTapMiss() {
+        rule.setContent(allGestures)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            down(0, Offset(5f, 5f))
+            up(0)
+        }
+
+        assertTrue(pressed)
+        assertTrue(released)
+        assertFalse(canceled)
+
+        pressed = false
+        released = false
+
+        rule.mainClock.advanceTimeBy(50)
+
+        performTouch {
+            down(1, Offset(5f, 5f))
+            moveTo(1, Offset(15f, 15f))
+        }
+
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            up(1)
+        }
+
+        assertTrue(pressed)
+        assertFalse(released)
+        assertTrue(canceled)
+        assertTrue(tapped)
+        assertFalse(longPressed)
+        assertFalse(doubleTapped)
+    }
+
+    /**
+     * Pressing in the region, sliding out, then back in, then lifting
+     * should result the gesture being canceled.
+     */
+    @Test
+    fun tapOutAndIn() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+            moveTo(0, Offset(15f, 15f))
+            moveTo(0, Offset(6f, 6f))
+        }
+
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            up(0)
+        }
+
+        assertFalse(tapped)
+        assertTrue(pressed)
+        assertFalse(released)
+        assertTrue(canceled)
+    }
+
+    /**
+     * Pressing in the region, sliding out, then back in, then lifting
+     * should result the gesture being canceled.
+     */
+    @Test
+    fun tapOutAndIn_withShortcut() {
+        rule.setContent(utilWithShortcut)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+            moveTo(0, Offset(15f, 15f))
+            moveTo(0, Offset(6f, 6f))
+        }
+
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            up(0)
+        }
+
+        assertFalse(tapped)
+        assertTrue(pressed)
+        assertFalse(released)
+        assertTrue(canceled)
+    }
+
+    /**
+     * After a first tap, a second tap should also be detected.
+     */
+    @Test
+    fun secondTap() {
+        rule.setContent(util)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            down(0, Offset(5f, 5f))
+            up(0)
+        }
+
+        assertTrue(pressed)
+        assertTrue(released)
+        assertFalse(canceled)
+
+        tapped = false
+        pressed = false
+        released = false
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            down(1, Offset(4f, 4f))
+            up(1)
+        }
+
+        assertTrue(tapped)
+        assertTrue(pressed)
+        assertTrue(released)
+        assertFalse(canceled)
+    }
+
+    /**
+     * After a first tap, a second tap should also be detected.
+     */
+    @Test
+    fun secondTap_withShortcut() {
+        rule.setContent(utilWithShortcut)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+            up(0)
+        }
+
+        assertTrue(pressed)
+        assertTrue(released)
+        assertFalse(canceled)
+
+        tapped = false
+        pressed = false
+        released = false
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            down(0, Offset(5f, 5f))
+            up(0)
+        }
+
+        assertTrue(tapped)
+        assertTrue(pressed)
+        assertTrue(released)
+        assertFalse(canceled)
+    }
+
+    /**
+     * Clicking in the region with the up already consumed should result in the callback not
+     * being invoked.
+     */
+    @Test
+    fun consumedUpTap() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+        }
+
+        assertFalse(tapped)
+        assertTrue(pressed)
+
+        performTouch(initialPass = { if (pressed != previousPressed) consume() }) {
+            up(0)
+        }
+
+        assertFalse(tapped)
+        assertFalse(released)
+        assertTrue(canceled)
+    }
+
+    /**
+     * Clicking in the region with the up already consumed should result in the callback not
+     * being invoked.
+     */
+    @Test
+    fun consumedUpTap_withShortcut() {
+        rule.setContent(utilWithShortcut)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+        }
+
+        assertFalse(tapped)
+        assertTrue(pressed)
+
+        performTouch(initialPass = { if (pressed != previousPressed) consume() }) {
+            up(0)
+        }
+
+        assertFalse(tapped)
+        assertFalse(released)
+        assertTrue(canceled)
+    }
+
+    /**
+     * Clicking in the region with the motion consumed should result in the callback not
+     * being invoked.
+     */
+    @Test
+    fun consumedMotionTap() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+        }
+
+        performTouch(initialPass = { consume() }) {
+            moveTo(0, Offset(6f, 2f))
+        }
+
+        rule.mainClock.advanceTimeBy(50)
+
+        performTouch {
+            up(0)
+        }
+
+        assertFalse(tapped)
+        assertTrue(pressed)
+        assertFalse(released)
+        assertTrue(canceled)
+    }
+
+    /**
+     * Clicking in the region with the motion consumed should result in the callback not
+     * being invoked.
+     */
+    @Test
+    fun consumedMotionTap_withShortcut() {
+        rule.setContent(utilWithShortcut)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+        }
+
+        performTouch(initialPass = { consume() }) {
+            moveTo(0, Offset(6f, 2f))
+        }
+
+        rule.mainClock.advanceTimeBy(50)
+
+        performTouch {
+            up(0)
+        }
+
+        assertFalse(tapped)
+        assertTrue(pressed)
+        assertFalse(released)
+        assertTrue(canceled)
+    }
+
+    @Test
+    fun consumedChange_MotionTap() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+        }
+
+        performTouch(initialPass = { consume() }) {
+            moveTo(0, Offset(6f, 2f))
+        }
+
+        rule.mainClock.advanceTimeBy(50)
+
+        performTouch {
+            up(0)
+        }
+
+        assertFalse(tapped)
+        assertTrue(pressed)
+        assertFalse(released)
+        assertTrue(canceled)
+    }
+
+    /**
+     * Clicking in the region with the up already consumed should result in the callback not
+     * being invoked.
+     */
+    @Test
+    fun consumedChange_upTap() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+        }
+
+        assertFalse(tapped)
+        assertTrue(pressed)
+
+        performTouch(initialPass = { consume() }) {
+            up(0)
+        }
+
+        assertFalse(tapped)
+        assertFalse(released)
+        assertTrue(canceled)
+    }
+
+    /**
+     * Ensure that two-finger taps work.
+     */
+    @Test
+    fun twoFingerTap() {
+        rule.setContent(util)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            down(0, Offset(1f, 1f))
+        }
+
+        assertTrue(pressed)
+        pressed = false
+
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            down(1, Offset(9f, 5f))
+        }
+
+        assertFalse(pressed)
+
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            up(0)
+        }
+
+        assertFalse(tapped)
+        assertFalse(released)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            up(1)
+        }
+
+        assertTrue(tapped)
+        assertTrue(released)
+        assertFalse(canceled)
+    }
+
+    /**
+     * Ensure that two-finger taps work.
+     */
+    @Test
+    fun twoFingerTap_withShortcut() {
+        rule.setContent(utilWithShortcut)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            down(0, Offset(1f, 1f))
+        }
+
+        assertTrue(pressed)
+        pressed = false
+
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            down(1, Offset(9f, 5f))
+        }
+
+        assertFalse(pressed)
+
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            up(0)
+        }
+
+        assertFalse(tapped)
+        assertFalse(released)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            up(1)
+        }
+
+        assertTrue(tapped)
+        assertTrue(released)
+        assertFalse(canceled)
+    }
+
+    /**
+     * A position change consumption on any finger should cause tap to cancel.
+     */
+    @Test
+    fun twoFingerTapCancel() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(1f, 1f))
+        }
+        assertTrue(pressed)
+
+        performTouch {
+            down(1, Offset(9f, 5f))
+        }
+
+        performTouch(initialPass = { consume() }) {
+            moveTo(0, Offset(5f, 5f))
+        }
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            up(0)
+        }
+
+        assertFalse(tapped)
+        assertTrue(canceled)
+
+        rule.mainClock.advanceTimeBy(50)
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            up(1)
+        }
+
+        assertFalse(tapped)
+        assertFalse(released)
+    }
+
+    /**
+     * A position change consumption on any finger should cause tap to cancel.
+     */
+    @Test
+    fun twoFingerTapCancel_withShortcut() {
+        rule.setContent(utilWithShortcut)
+        performTouch {
+            down(0, Offset(1f, 1f))
+        }
+
+        assertTrue(pressed)
+
+        performTouch {
+            down(1, Offset(9f, 5f))
+        }
+
+        performTouch(initialPass = { consume() }) {
+            moveTo(0, Offset(5f, 5f))
+        }
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            up(0)
+        }
+
+        assertFalse(tapped)
+        assertTrue(canceled)
+
+        rule.mainClock.advanceTimeBy(50)
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            up(1)
+        }
+
+        assertFalse(tapped)
+        assertFalse(released)
+    }
+
+    /**
+     * Detect the second tap as long press.
+     */
+    @Test
+    fun secondTapLongPress() {
+        rule.setContent(allGestures)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+            up(0)
+        }
+
+        assertTrue(pressed)
+        assertTrue(released)
+        assertFalse(canceled)
+        assertFalse(tapped)
+        assertFalse(doubleTapped)
+        assertFalse(longPressed)
+
+        pressed = false
+        released = false
+
+        rule.mainClock.advanceTimeBy(50)
+        performTouch {
+            down(1, Offset(5f, 5f))
+        }
+
+        assertTrue(pressed)
+
+        rule.mainClock.advanceTimeBy(LongPressTimeoutMillis + 10)
+
+        assertTrue(tapped)
+        assertTrue(longPressed)
+        assertFalse(released)
+        assertFalse(canceled)
+
+        rule.mainClock.advanceTimeBy(500)
+        performTouch {
+            up(1)
+        }
+        assertTrue(released)
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/TransformGestureDetectorTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/TransformGestureDetectorTest.kt
new file mode 100644
index 0000000..44a51df
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/TransformGestureDetectorTest.kt
@@ -0,0 +1,606 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.gesture
+
+import androidx.compose.foundation.gestures.detectTransformGestures
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.testutils.TestViewConfiguration
+import androidx.compose.ui.AbsoluteAlignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.PointerId
+import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.PointerInputScope
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.TouchInjectionScope
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.DpSize
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+private const val TargetTag = "TargetLayout"
+
+@RunWith(Parameterized::class)
+class TransformGestureDetectorTest(val panZoomLock: Boolean) {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters
+        fun parameters() = arrayOf(false, true)
+    }
+
+    private var centroid = Offset.Zero
+    private var panned = false
+    private var panAmount = Offset.Zero
+    private var rotated = false
+    private var rotateAmount = 0f
+    private var zoomed = false
+    private var zoomAmount = 1f
+
+    private val util = layoutWithGestureDetector {
+        detectTransformGestures(
+            panZoomLock = panZoomLock
+        ) { c, pan, gestureZoom, gestureAngle ->
+            centroid = c
+            if (gestureAngle != 0f) {
+                rotated = true
+                rotateAmount += gestureAngle
+            }
+            if (gestureZoom != 1f) {
+                zoomed = true
+                zoomAmount *= gestureZoom
+            }
+            if (pan != Offset.Zero) {
+                panned = true
+                panAmount += pan
+            }
+        }
+    }
+
+    private val nothingHandler: PointerInputChange.() -> Unit = {}
+
+    private var initialPass: PointerInputChange.() -> Unit = nothingHandler
+    private var finalPass: PointerInputChange.() -> Unit = nothingHandler
+
+    @Before
+    fun setup() {
+        panned = false
+        panAmount = Offset.Zero
+        rotated = false
+        rotateAmount = 0f
+        zoomed = false
+        zoomAmount = 1f
+    }
+
+    private fun layoutWithGestureDetector(
+        gestureDetector: suspend PointerInputScope.() -> Unit,
+    ): @Composable () -> Unit = {
+        CompositionLocalProvider(
+            LocalDensity provides Density(1f),
+            LocalViewConfiguration provides TestViewConfiguration(
+                minimumTouchTargetSize = DpSize.Zero
+            )
+        ) {
+            with(LocalDensity.current) {
+                Box(
+                    Modifier
+                        .fillMaxSize()
+                        // Some tests execute a lambda before the initial and final passes
+                        // so they are called here, higher up the chain, so that the
+                        // calls happen prior to the gestureDetector below. The lambdas
+                        // do things like consume events on the initial pass or validate
+                        // consumption on the final pass.
+                        .pointerInput(Unit) {
+                            awaitPointerEventScope {
+                                while (true) {
+                                    val event = awaitPointerEvent(PointerEventPass.Initial)
+                                    event.changes.forEach {
+                                        initialPass(it)
+                                    }
+                                    awaitPointerEvent(PointerEventPass.Final)
+                                    event.changes.forEach {
+                                        finalPass(it)
+                                    }
+                                }
+                            }
+                        }
+                        .wrapContentSize(AbsoluteAlignment.TopLeft)
+                        .size(1600.toDp())
+                        .pointerInput(gestureDetector, gestureDetector)
+                        .testTag(TargetTag)
+                )
+            }
+        }
+    }
+
+    private fun performTouch(
+        initialPass: PointerInputChange.() -> Unit = nothingHandler,
+        finalPass: PointerInputChange.() -> Unit = nothingHandler,
+        block: TouchInjectionScope.() -> Unit
+    ) {
+        this.initialPass = initialPass
+        this.finalPass = finalPass
+        rule.onNodeWithTag(TargetTag).performTouchInput(block)
+        rule.waitForIdle()
+        this.initialPass = nothingHandler
+        this.finalPass = nothingHandler
+    }
+
+    /**
+     * Single finger pan.
+     */
+    @Test
+    fun singleFingerPan() {
+        rule.setContent(util)
+
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            down(0, Offset(5f, 5f))
+        }
+
+        assertFalse(panned)
+
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            moveBy(0, Offset(12.7f, 12.7f))
+        }
+
+        assertFalse(panned)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            moveBy(0, Offset(0.1f, 0.1f))
+        }
+
+        assertEquals(17.7f, centroid.x, 0.1f)
+        assertEquals(17.7f, centroid.y, 0.1f)
+        assertTrue(panned)
+        assertFalse(zoomed)
+        assertFalse(rotated)
+
+        assertTrue(panAmount.getDistance() < 1f)
+
+        panAmount = Offset.Zero
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            moveBy(0, Offset(1f, 0f))
+        }
+
+        assertEquals(Offset(1f, 0f), panAmount)
+
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            up(0)
+        }
+
+        assertFalse(rotated)
+        assertFalse(zoomed)
+    }
+
+    /**
+     * Multi-finger pan
+     */
+    @Test
+    fun multiFingerPanZoom() {
+        rule.setContent(util)
+
+        // [PointerId] needed later to assert whether or not a particular pointer id was consumed.
+        var pointerId0: PointerId? = null
+        var pointerId1: PointerId? = null
+
+        performTouch(
+            finalPass = {
+                pointerId0 = id
+                assertFalse(isConsumed)
+            }
+        ) {
+            down(0, Offset(5f, 5f))
+        }
+
+        performTouch(
+            finalPass = {
+                if (id != pointerId0) {
+                    pointerId1 = id
+                }
+                assertFalse(isConsumed)
+            }
+        ) {
+            down(1, Offset(25f, 25f))
+        }
+
+        assertFalse(panned)
+
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            moveBy(0, Offset(13f, 13f))
+        }
+
+        // With the move below, we've now averaged enough movement (touchSlop is around 18.0)
+        performTouch(
+            finalPass = {
+                if (id == pointerId1) {
+                    assertTrue(isConsumed)
+                }
+            }
+        ) {
+            moveBy(1, Offset(13f, 13f))
+        }
+
+        assertEquals((5f + 25f + 13f) / 2f, centroid.x, 0.1f)
+        assertEquals((5f + 25f + 13f) / 2f, centroid.y, 0.1f)
+        assertTrue(panned)
+        assertTrue(zoomed)
+        assertFalse(rotated)
+
+        assertEquals(6.4f, panAmount.x, 0.1f)
+        assertEquals(6.4f, panAmount.y, 0.1f)
+
+        performTouch {
+            up(0)
+            up(1)
+        }
+    }
+
+    /**
+     * 2-pointer zoom
+     */
+    @Test
+    fun zoom2Pointer() {
+        rule.setContent(util)
+
+        // [PointerId] needed later to assert whether or not a particular pointer id was consumed.
+        var pointerId0: PointerId? = null
+        var pointerId1: PointerId? = null
+
+        performTouch(
+            finalPass = {
+                pointerId0 = id
+                assertFalse(isConsumed)
+            }
+        ) {
+            down(0, Offset(5f, 5f))
+        }
+
+        performTouch(
+            finalPass = {
+                if (id != pointerId0) {
+                    pointerId1 = id
+                    assertFalse(isConsumed)
+                }
+            }
+        ) {
+            down(1, Offset(25f, 5f))
+        }
+
+        performTouch(
+            finalPass = {
+                if (id == pointerId1) {
+                    assertFalse(isConsumed)
+                }
+            }
+        ) {
+            moveBy(1, Offset(35.95f, 0f))
+        }
+
+        performTouch(
+            finalPass = {
+                if (id == pointerId1) {
+                    assertTrue(isConsumed)
+                }
+            }
+        ) {
+            moveBy(1, Offset(0.1f, 0f))
+        }
+
+        assertTrue(panned)
+        assertTrue(zoomed)
+        assertFalse(rotated)
+
+        // both should be small movements
+        assertTrue(panAmount.getDistance() < 1f)
+        assertTrue(zoomAmount in 1f..1.1f)
+
+        zoomAmount = 1f
+        panAmount = Offset.Zero
+
+        performTouch(
+            finalPass = {
+                if (id == pointerId0) {
+                    assertTrue(isConsumed)
+                }
+            }
+        ) {
+            moveBy(0, Offset(-1f, 0f))
+        }
+
+        performTouch(
+            finalPass = {
+                if (id == pointerId1) {
+                    assertTrue(isConsumed)
+                }
+            }
+        ) {
+            moveBy(1, Offset(1f, 0f))
+        }
+
+        assertEquals(0f, panAmount.x, 0.01f)
+        assertEquals(0f, panAmount.y, 0.01f)
+
+        assertEquals(48f / 46f, zoomAmount, 0.01f)
+
+        performTouch {
+            up(0)
+            up(1)
+        }
+    }
+
+    /**
+     * 4-pointer zoom
+     */
+    @Test
+    fun zoom4Pointer() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(0f, 50f))
+        }
+
+        // just get past the touch slop
+        performTouch {
+            moveBy(0, Offset(-1000f, 0f))
+            moveBy(0, Offset(1000f, 0f))
+        }
+
+        panned = false
+        panAmount = Offset.Zero
+
+        performTouch {
+            down(1, Offset(100f, 50f))
+            down(2, Offset(50f, 0f))
+            down(3, Offset(50f, 100f))
+        }
+
+        performTouch {
+            moveBy(0, Offset(-50f, 0f))
+            moveBy(1, Offset(50f, 0f))
+        }
+
+        assertTrue(zoomed)
+        assertTrue(panned)
+
+        assertEquals(0f, panAmount.x, 0.1f)
+        assertEquals(0f, panAmount.y, 0.1f)
+        assertEquals(1.5f, zoomAmount, 0.1f)
+
+        performTouch {
+            moveBy(2, Offset(0f, -50f))
+            moveBy(3, Offset(0f, 50f))
+        }
+
+        assertEquals(0f, panAmount.x, 0.1f)
+        assertEquals(0f, panAmount.y, 0.1f)
+        assertEquals(2f, zoomAmount, 0.1f)
+
+        performTouch {
+            up(0)
+            up(1)
+            up(2)
+            up(3)
+        }
+    }
+
+    /**
+     * 2 pointer rotation.
+     */
+    @Test
+    fun rotation2Pointer() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(0f, 50f))
+            down(1, Offset(100f, 50f))
+
+            // Move
+            moveBy(0, Offset(50f, -50f))
+            moveBy(1, Offset(-50f, 50f))
+        }
+
+        // assume some of the above was touch slop
+        assertTrue(rotated)
+        rotateAmount = 0f
+        rotated = false
+        zoomAmount = 1f
+        panAmount = Offset.Zero
+
+        // now do the real rotation:
+        performTouch {
+            moveBy(0, Offset(-50f, 50f))
+            moveBy(1, Offset(50f, -50f))
+        }
+
+        performTouch {
+            up(0)
+            up(1)
+        }
+
+        assertTrue(rotated)
+        assertEquals(-90f, rotateAmount, 0.01f)
+        assertEquals(0f, panAmount.x, 0.1f)
+        assertEquals(0f, panAmount.y, 0.1f)
+        assertEquals(1f, zoomAmount, 0.1f)
+    }
+
+    /**
+     * 2 pointer rotation, with early panning.
+     */
+    @Test
+    fun rotation2PointerLock() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(0f, 50f))
+        }
+
+        // just get past the touch slop with panning
+        performTouch {
+            moveBy(0, Offset(-1000f, 0f))
+            moveBy(0, Offset(1000f, 0f))
+        }
+
+        performTouch {
+            down(1, Offset(100f, 50f))
+        }
+
+        // now do the rotation:
+        performTouch {
+            moveBy(0, Offset(50f, -50f))
+            moveBy(1, Offset(-50f, 50f))
+        }
+
+        performTouch {
+            up(0)
+            up(1)
+        }
+
+        if (panZoomLock) {
+            assertFalse(rotated)
+        } else {
+            assertTrue(rotated)
+            assertEquals(90f, rotateAmount, 0.01f)
+        }
+        assertEquals(0f, panAmount.x, 0.1f)
+        assertEquals(0f, panAmount.y, 0.1f)
+        assertEquals(1f, zoomAmount, 0.1f)
+    }
+
+    /**
+     * Adding or removing a pointer won't change the current values
+     */
+    @Test
+    fun noChangeOnPointerDownUp() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(0f, 50f))
+            down(1, Offset(100f, 50f))
+
+            moveBy(0, Offset(50f, -50f))
+            moveBy(1, Offset(-50f, 50f))
+        }
+
+        // now we've gotten past the touch slop
+        rotated = false
+        panned = false
+        zoomed = false
+
+        performTouch {
+            down(2, Offset(0f, 50f))
+        }
+
+        assertFalse(rotated)
+        assertFalse(panned)
+        assertFalse(zoomed)
+
+        performTouch {
+            down(3, Offset(100f, 50f))
+        }
+
+        assertFalse(rotated)
+        assertFalse(panned)
+        assertFalse(zoomed)
+
+        performTouch {
+            up(0)
+            up(1)
+            up(2)
+            up(3)
+        }
+
+        assertFalse(rotated)
+        assertFalse(panned)
+        assertFalse(zoomed)
+    }
+
+    /**
+     * Consuming position during touch slop will cancel the current gesture.
+     */
+    @Test
+    fun touchSlopCancel() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+        }
+
+        performTouch(initialPass = { consume() }) {
+            moveBy(0, Offset(50f, 0f))
+        }
+
+        performTouch {
+            up(0)
+        }
+
+        assertFalse(panned)
+        assertFalse(zoomed)
+        assertFalse(rotated)
+    }
+
+    /**
+     * Consuming position after touch slop will cancel the current gesture.
+     */
+    @Test
+    fun afterTouchSlopCancel() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+        }
+
+        performTouch {
+            moveBy(0, Offset(50f, 0f))
+        }
+
+        performTouch(initialPass = { consume() }) {
+            moveBy(0, Offset(50f, 0f))
+        }
+
+        performTouch {
+            up(0)
+        }
+
+        assertTrue(panned)
+        assertFalse(zoomed)
+        assertFalse(rotated)
+        assertEquals(50f, panAmount.x, 0.1f)
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridArrangementsTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridArrangementsTest.kt
index ea976eb..74e519c 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridArrangementsTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridArrangementsTest.kt
@@ -55,6 +55,7 @@
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.text.BasicText
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.onNodeWithTag
@@ -248,4 +249,60 @@
             Truth.assertThat(state.firstVisibleItemScrollOffset).isEqualTo(0)
         }
     }
+
+    @Test
+    fun nonStartCrossAxisArrangement() {
+        val state = LazyStaggeredGridState()
+        rule.setContent {
+            LazyStaggeredGrid(
+                cells = StaggeredGridCells.FixedSize(itemSizeDp * 2),
+                modifier = Modifier.axisSize(crossAxis = itemSizeDp * 5, mainAxis = itemSizeDp * 5),
+                crossAxisArrangement = Arrangement.Center,
+                state = state
+            ) {
+                items(10) { index ->
+                    Box(Modifier.size(itemSizeDp).testTag(index.toString()))
+                }
+            }
+        }
+
+        rule.onNodeWithTag("0")
+            .assertCrossAxisStartPositionInRootIsEqualTo(itemSizeDp * 0.5f)
+            .assertCrossAxisSizeIsEqualTo(itemSizeDp * 2)
+        rule.onNodeWithTag("1")
+            .assertCrossAxisStartPositionInRootIsEqualTo(itemSizeDp * 2.5f)
+            .assertCrossAxisSizeIsEqualTo(itemSizeDp * 2)
+    }
+
+    @Test
+    fun spacedByWithAlignment() {
+        val state = LazyStaggeredGridState()
+        rule.setContent {
+            LazyStaggeredGrid(
+                cells = StaggeredGridCells.FixedSize(itemSizeDp * 2),
+                modifier = Modifier.axisSize(crossAxis = itemSizeDp * 5, mainAxis = itemSizeDp * 5),
+                crossAxisArrangement = object : Arrangement.HorizontalOrVertical,
+                    Arrangement.Horizontal by Arrangement.spacedBy(
+                        itemSizeDp * 0.5f, Alignment.End
+                    ),
+                    Arrangement.Vertical by Arrangement.spacedBy(
+                        itemSizeDp * 0.5f, Alignment.Bottom
+                    ) {
+                        override val spacing: Dp = itemSizeDp * 0.5f
+                    },
+                state = state
+            ) {
+                items(10) { index ->
+                    Box(Modifier.size(itemSizeDp).testTag(index.toString()))
+                }
+            }
+        }
+
+        rule.onNodeWithTag("0")
+            .assertCrossAxisStartPositionInRootIsEqualTo(itemSizeDp * 0.5f)
+            .assertCrossAxisSizeIsEqualTo(itemSizeDp * 2)
+        rule.onNodeWithTag("1")
+            .assertCrossAxisStartPositionInRootIsEqualTo(itemSizeDp * 3f)
+            .assertCrossAxisSizeIsEqualTo(itemSizeDp * 2)
+    }
 }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt
index 6da4790..9ad452d 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt
@@ -1888,4 +1888,27 @@
             assertThat(state.firstVisibleItemIndex).isEqualTo(10)
         }
     }
+
+    @Test
+    fun fixedSizeCell_forcesFixedSize() {
+        val state = LazyStaggeredGridState()
+        rule.setContent {
+            LazyStaggeredGrid(
+                cells = StaggeredGridCells.FixedSize(itemSizeDp * 2),
+                modifier = Modifier.axisSize(crossAxis = itemSizeDp * 5, mainAxis = itemSizeDp * 5),
+                state = state
+            ) {
+                items(10) { index ->
+                    Box(Modifier.size(itemSizeDp).testTag(index.toString()))
+                }
+            }
+        }
+
+        rule.onNodeWithTag("0")
+            .assertCrossAxisStartPositionInRootIsEqualTo(0.dp)
+            .assertCrossAxisSizeIsEqualTo(itemSizeDp * 2)
+        rule.onNodeWithTag("1")
+            .assertCrossAxisStartPositionInRootIsEqualTo(itemSizeDp * 2f)
+            .assertCrossAxisSizeIsEqualTo(itemSizeDp * 2)
+    }
 }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt
index b0a59e3..514a0b9 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt
@@ -17,6 +17,7 @@
 package androidx.compose.foundation.pager
 
 import android.view.View
+import androidx.compose.foundation.BaseLazyLayoutTestWithOrientation
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.LocalOverscrollConfiguration
 import androidx.compose.foundation.background
@@ -25,13 +26,12 @@
 import androidx.compose.foundation.gestures.snapping.SnapFlingBehavior
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.text.BasicText
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.Stable
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -51,21 +51,17 @@
 import androidx.compose.ui.test.SemanticsNodeInteraction
 import androidx.compose.ui.test.TouchInjectionScope
 import androidx.compose.ui.test.assertPositionInRootIsEqualTo
-import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.swipeWithVelocity
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import kotlinx.coroutines.CoroutineScope
-import org.junit.Rule
 
 @OptIn(ExperimentalFoundationApi::class)
-internal open class BasePagerTest(private val config: ParamConfig) {
-    @get:Rule
-    val rule = createComposeRule()
+open class BasePagerTest(private val config: ParamConfig) :
+    BaseLazyLayoutTestWithOrientation(config.orientation) {
 
-    val isVertical = config.orientation == Orientation.Vertical
     lateinit var scope: CoroutineScope
     var pagerSize: Int = 0
     var placed = mutableSetOf<Int>()
@@ -74,19 +70,11 @@
     lateinit var firstItemFocusRequester: FocusRequester
     var composeView: View? = null
 
-    @Stable
-    fun Modifier.crossAxisSize(size: Dp) =
-        if (isVertical) {
-            this.width(size)
-        } else {
-            this.height(size)
-        }
-
     fun TouchInjectionScope.swipeWithVelocityAcrossMainAxis(velocity: Float, delta: Float? = null) {
         val end = if (delta == null) {
             layoutEnd
         } else {
-            if (isVertical) {
+            if (vertical) {
                 layoutStart.copy(y = layoutStart.y + delta)
             } else {
                 layoutStart.copy(x = layoutStart.x + delta)
@@ -102,7 +90,7 @@
         val end = if (delta == null) {
             layoutEnd
         } else {
-            if (isVertical) {
+            if (vertical) {
                 layoutStart.copy(x = layoutStart.x + delta)
             } else {
                 layoutStart.copy(y = layoutStart.y + delta)
@@ -111,18 +99,30 @@
         swipeWithVelocity(layoutStart, end, velocity)
     }
 
+    fun Modifier.fillMaxCrossAxis() =
+        if (vertical) {
+            this.fillMaxWidth()
+        } else {
+            this.fillMaxHeight()
+        }
+
     internal fun createPager(
         state: PagerState,
         modifier: Modifier = Modifier,
         pageCount: () -> Int = { DefaultPageCount },
-        offscreenPageLimit: Int = 0,
-        pageSize: PageSize = PageSize.Fill,
+        offscreenPageLimit: Int = config.beyondBoundsPageCount,
+        pageSize: () -> PageSize = { PageSize.Fill },
         userScrollEnabled: Boolean = true,
         snappingPage: PagerSnapDistance = PagerSnapDistance.atMost(1),
         nestedScrollConnection: NestedScrollConnection = object : NestedScrollConnection {},
-        effects: @Composable () -> Unit = {},
+        additionalContent: @Composable () -> Unit = {},
+        contentPadding: PaddingValues = config.mainAxisContentPadding,
+        pageSpacing: Dp = config.pageSpacing,
+        reverseLayout: Boolean = config.reverseLayout,
+        key: ((index: Int) -> Any)? = null,
         pageContent: @Composable (page: Int) -> Unit = { Page(index = it) }
     ) {
+
         rule.setContent {
             composeView = LocalView.current
             focusManager = LocalFocusManager.current
@@ -147,18 +147,19 @@
                         beyondBoundsPageCount = offscreenPageLimit,
                         modifier = modifier
                             .testTag(PagerTestTag)
-                            .onSizeChanged { pagerSize = if (isVertical) it.height else it.width },
-                        pageSize = pageSize,
+                            .onSizeChanged { pagerSize = if (vertical) it.height else it.width },
+                        pageSize = pageSize(),
                         userScrollEnabled = userScrollEnabled,
-                        reverseLayout = config.reverseLayout,
+                        reverseLayout = reverseLayout,
                         flingBehavior = flingBehavior,
-                        pageSpacing = config.pageSpacing,
-                        contentPadding = config.mainAxisContentPadding,
-                        pageContent = pageContent
+                        pageSpacing = pageSpacing,
+                        contentPadding = contentPadding,
+                        pageContent = pageContent,
+                        key = key
                     )
                 }
             }
-            effects()
+            additionalContent()
         }
     }
 
@@ -171,7 +172,7 @@
             .focusRequester(focusRequester)
             .onPlaced {
                 placed.add(index)
-                pageSize = if (isVertical) it.size.height else it.size.width
+                pageSize = if (vertical) it.size.height else it.size.width
             }
             .fillMaxSize()
             .background(Color.Blue)
@@ -187,7 +188,7 @@
     }
 
     internal val scrollForwardSign: Int
-        get() = if (isVertical) {
+        get() = if (vertical) {
             if (config.reverseLayout && config.layoutDirection == LayoutDirection.Rtl) {
                 1
             } else if (!config.reverseLayout && config.layoutDirection == LayoutDirection.Rtl) {
@@ -210,7 +211,7 @@
         }
 
     internal val TouchInjectionScope.layoutStart: Offset
-        get() = if (isVertical) {
+        get() = if (vertical) {
             if (config.reverseLayout && config.layoutDirection == LayoutDirection.Rtl) {
                 topCenter
             } else if (!config.reverseLayout && config.layoutDirection == LayoutDirection.Rtl) {
@@ -233,7 +234,7 @@
         }
 
     internal val TouchInjectionScope.layoutEnd: Offset
-        get() = if (isVertical) {
+        get() = if (vertical) {
             if (config.reverseLayout && config.layoutDirection == LayoutDirection.Rtl) {
                 bottomCenter
             } else if (!config.reverseLayout && config.layoutDirection == LayoutDirection.Rtl) {
@@ -268,9 +269,10 @@
         pageSize: PageSize = PageSize.Fill,
         flingBehavior: SnapFlingBehavior = PagerDefaults.flingBehavior(state = state),
         pageSpacing: Dp = 0.dp,
+        key: ((index: Int) -> Any)? = null,
         pageContent: @Composable (pager: Int) -> Unit
     ) {
-        if (isVertical) {
+        if (vertical) {
             VerticalPager(
                 pageCount = pageCount,
                 state = state,
@@ -282,6 +284,7 @@
                 pageSize = pageSize,
                 flingBehavior = flingBehavior,
                 pageSpacing = pageSpacing,
+                key = key,
                 pageContent = pageContent
             )
         } else {
@@ -296,6 +299,7 @@
                 pageSize = pageSize,
                 flingBehavior = flingBehavior,
                 pageSpacing = pageSpacing,
+                key = key,
                 pageContent = pageContent
             )
         }
@@ -317,7 +321,7 @@
             val position = pageToVerifyPosition * (pageSize + spacings) - initialPageOffset
             val positionWithOffset =
                 position + (pageSize + spacings) * pageOffset * scrollForwardSign
-            if (isVertical) {
+            if (vertical) {
                 0.dp to positionWithOffset.toDp()
             } else {
                 positionWithOffset.toDp() to 0.dp
@@ -328,19 +332,21 @@
     }
 }
 
-internal class ParamConfig(
+class ParamConfig(
     val orientation: Orientation,
     val reverseLayout: Boolean = false,
     val layoutDirection: LayoutDirection = LayoutDirection.Ltr,
     val pageSpacing: Dp = 0.dp,
-    val mainAxisContentPadding: PaddingValues = PaddingValues(0.dp)
+    val mainAxisContentPadding: PaddingValues = PaddingValues(0.dp),
+    val beyondBoundsPageCount: Int = 0
 ) {
     override fun toString(): String {
         return "orientation=$orientation " +
             "reverseLayout=$reverseLayout " +
             "layoutDirection=$layoutDirection " +
             "pageSpacing=$pageSpacing " +
-            "mainAxisContentPadding=$mainAxisContentPadding"
+            "mainAxisContentPadding=$mainAxisContentPadding " +
+            "beyondBoundsPageCount=$beyondBoundsPageCount"
     }
 }
 
@@ -348,6 +354,11 @@
 internal const val DefaultPageCount = 20
 internal const val DefaultAnimationRepetition = 3
 internal val TestOrientation = listOf(Orientation.Vertical, Orientation.Horizontal)
+internal val AllOrientationsParams = mutableListOf<ParamConfig>().apply {
+    for (orientation in TestOrientation) {
+        add(ParamConfig(orientation = orientation))
+    }
+}
 internal val TestReverseLayout = listOf(false, true)
 internal val TestLayoutDirection = listOf(LayoutDirection.Rtl, LayoutDirection.Ltr)
 internal val TestPageSpacing = listOf(0.dp, 8.dp)
@@ -359,3 +370,7 @@
     PaddingValues(start = 16.dp),
     PaddingValues(end = 16.dp)
 )
+
+open class SingleOrientationPagerTest(
+    orientation: Orientation
+) : BasePagerTest(ParamConfig(orientation))
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/EmptyPagerTests.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/EmptyPagerTests.kt
index 874b208..1680009 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/EmptyPagerTests.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/EmptyPagerTests.kt
@@ -28,7 +28,7 @@
 @OptIn(ExperimentalFoundationApi::class)
 @LargeTest
 @RunWith(Parameterized::class)
-internal class EmptyPagerTests(val config: ParamConfig) : BasePagerTest(config) {
+class EmptyPagerTests(val config: ParamConfig) : BasePagerTest(config) {
 
     @Test
     fun checkNoPagesArePlaced() {
@@ -45,10 +45,6 @@
     companion object {
         @JvmStatic
         @Parameterized.Parameters(name = "{0}")
-        fun params() = mutableListOf<ParamConfig>().apply {
-            for (orientation in TestOrientation) {
-                add(ParamConfig(orientation = orientation))
-            }
-        }
+        fun params() = AllOrientationsParams
     }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageLayoutPositionOnScrollingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageLayoutPositionOnScrollingTest.kt
index 8d25cb7..bd96983 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageLayoutPositionOnScrollingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageLayoutPositionOnScrollingTest.kt
@@ -31,7 +31,7 @@
 @OptIn(ExperimentalFoundationApi::class)
 @LargeTest
 @RunWith(Parameterized::class)
-internal class PageLayoutPositionOnScrollingTest(
+class PageLayoutPositionOnScrollingTest(
     val config: ParamConfig
 ) : BasePagerTest(config) {
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageSizeTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageSizeTest.kt
index e290ab9..eebddc6 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageSizeTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageSizeTest.kt
@@ -32,7 +32,7 @@
 @OptIn(ExperimentalFoundationApi::class)
 @LargeTest
 @RunWith(Parameterized::class)
-internal class PageSizeTest(val config: ParamConfig) : BasePagerTest(config) {
+class PageSizeTest(val config: ParamConfig) : BasePagerTest(config) {
     @Test
     fun pageSizeFill_onlySnappedItemIsDisplayed() {
         // Arrange
@@ -66,19 +66,19 @@
             state = state,
             modifier = Modifier.crossAxisSize(200.dp),
             offscreenPageLimit = 0,
-            pageSize = pagerMode
+            pageSize = { pagerMode }
         )
 
         // Assert
         rule.runOnIdle {
-            val visibleItems = state.layoutInfo.visibleItemsInfo.size
+            val visibleItems = state.layoutInfo.visiblePagesInfo.size
             val pageCount = with(rule.density) {
                 (pagerSize / (pageSize + config.pageSpacing.roundToPx()))
             } + 1
             Truth.assertThat(visibleItems).isEqualTo(pageCount)
         }
 
-        for (pageIndex in 5 until state.layoutInfo.visibleItemsInfo.size + 4) {
+        for (pageIndex in 5 until state.layoutInfo.visiblePagesInfo.size + 4) {
             confirmPageIsInCorrectPosition(5, pageIndex)
         }
     }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerAccessibilityTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerAccessibilityTest.kt
index 8f0c10d..ff8f42c 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerAccessibilityTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerAccessibilityTest.kt
@@ -38,7 +38,7 @@
 @OptIn(ExperimentalFoundationApi::class)
 @LargeTest
 @RunWith(Parameterized::class)
-internal class PagerAccessibilityTest(config: ParamConfig) : BasePagerTest(config = config) {
+class PagerAccessibilityTest(config: ParamConfig) : BasePagerTest(config = config) {
 
     private val accessibilityNodeProvider: AccessibilityNodeProvider
         get() = checkNotNull(composeView) {
@@ -71,7 +71,7 @@
 
         rule.runOnIdle { assertThat(state.currentPage).isEqualTo(5) }
 
-        val actionBackward = if (isVertical) {
+        val actionBackward = if (vertical) {
             android.R.id.accessibilityActionPageUp
         } else {
             android.R.id.accessibilityActionPageLeft
@@ -89,7 +89,7 @@
         rule.runOnIdle { assertThat(state.currentPage).isEqualTo(4) }
         rule.runOnIdle { assertThat(state.currentPageOffsetFraction).isEqualTo(0.0f) }
 
-        val actionForward = if (isVertical) {
+        val actionForward = if (vertical) {
             android.R.id.accessibilityActionPageDown
         } else {
             android.R.id.accessibilityActionPageRight
@@ -155,10 +155,6 @@
     companion object {
         @JvmStatic
         @Parameterized.Parameters(name = "{0}")
-        fun params() = mutableListOf<ParamConfig>().apply {
-            for (orientation in TestOrientation) {
-                add(ParamConfig(orientation = orientation))
-            }
-        }
+        fun params() = AllOrientationsParams
     }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerContentPaddingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerContentPaddingTest.kt
new file mode 100644
index 0000000..66adc7b
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerContentPaddingTest.kt
@@ -0,0 +1,1026 @@
+/*
+ * 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.foundation.pager
+
+import androidx.compose.animation.core.snap
+import androidx.compose.foundation.AutoTestFrameClock
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.foundation.gestures.animateScrollBy
+import androidx.compose.foundation.gestures.scrollBy
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.assertWidthIsEqualTo
+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.filters.LargeTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@OptIn(ExperimentalFoundationApi::class)
+@LargeTest
+@RunWith(Parameterized::class)
+internal class PagerContentPaddingTest(paramConfig: ParamConfig) : BasePagerTest(paramConfig) {
+
+    private val PagerTag = "Pager"
+    private val PageTag = "page"
+    private val ContainerTag = "container"
+
+    private var pageTotalSize: Dp = Dp.Infinity
+    private var smallPaddingSize: Dp = Dp.Infinity
+    private var pageTotalSizePx = 50f
+    private var smallPaddingSizePx = 12f
+
+    @Before
+    fun before() {
+        with(rule.density) {
+            pageTotalSize = pageTotalSizePx.toDp()
+            smallPaddingSize = smallPaddingSizePx.toDp()
+        }
+    }
+
+    @Test
+    fun contentPaddingIsApplied() {
+        val state = PagerState()
+        val containerSize = pageTotalSize * 2
+        val largePaddingSize = pageTotalSize
+
+        createPager(
+            state = state,
+            modifier = Modifier
+                .requiredSize(containerSize)
+                .testTag(PagerTag),
+            contentPadding = PaddingValues(
+                mainAxis = largePaddingSize,
+                crossAxis = smallPaddingSize
+            ),
+            pageCount = { 1 },
+            pageSize = { PageSize.Fixed(pageTotalSize) }
+        ) {
+            Spacer(
+                Modifier
+                    .fillMaxCrossAxis()
+                    .mainAxisSize(pageTotalSize)
+                    .testTag(PageTag)
+            )
+        }
+
+        rule.onNodeWithTag(PageTag)
+            .assertCrossAxisStartPositionInRootIsEqualTo(smallPaddingSize)
+            .assertStartPositionInRootIsEqualTo(largePaddingSize)
+            .assertCrossAxisSizeIsEqualTo(containerSize - smallPaddingSize * 2)
+            .assertMainAxisSizeIsEqualTo(pageTotalSize)
+
+        state.scrollBy(largePaddingSize)
+
+        rule.onNodeWithTag(PageTag)
+            .assertStartPositionInRootIsEqualTo(0.dp)
+            .assertMainAxisSizeIsEqualTo(pageTotalSize)
+    }
+
+    @Test
+    fun contentPaddingIsNotAffectingScrollPosition() {
+        val state = PagerState()
+
+        createPager(
+            state = state,
+            modifier = Modifier
+                .requiredSize(pageTotalSize * 2)
+                .testTag(PagerTag),
+            contentPadding = PaddingValues(mainAxis = pageTotalSize),
+            pageCount = { 1 },
+            pageSize = { PageSize.Fixed(pageTotalSize) }
+        ) {
+            Spacer(
+                Modifier
+                    .fillMaxCrossAxis()
+                    .mainAxisSize(pageTotalSize)
+                    .testTag(PageTag)
+            )
+        }
+
+        state.assertScrollPosition(0, 0.dp)
+
+        state.scrollBy(pageTotalSize)
+
+        state.assertScrollPosition(0, pageTotalSize)
+    }
+
+    @Test
+    fun scrollForwardItemWithinStartPaddingDisplayed() {
+        val state = PagerState()
+        val padding = pageTotalSize * 1.5f
+        createPager(
+            state = state,
+            modifier = Modifier
+                .requiredSize(padding * 2 + pageTotalSize)
+                .testTag(PagerTag),
+            contentPadding = PaddingValues(mainAxis = padding),
+            pageCount = { 4 },
+            pageSize = { PageSize.Fixed(pageTotalSize) }
+        ) {
+            Spacer(
+                Modifier
+                    .requiredSize(pageTotalSize)
+                    .testTag(it.toString())
+            )
+        }
+
+        rule.onNodeWithTag("0")
+            .assertStartPositionInRootIsEqualTo(padding)
+        rule.onNodeWithTag("1")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize + padding)
+        rule.onNodeWithTag("2")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize * 2 + padding)
+
+        state.scrollBy(padding)
+
+        state.assertScrollPosition(1, padding - pageTotalSize)
+
+        rule.onNodeWithTag("0")
+            .assertStartPositionInRootIsEqualTo(0.dp)
+        rule.onNodeWithTag("1")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize)
+        rule.onNodeWithTag("2")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize * 2)
+        rule.onNodeWithTag("3")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize * 3)
+    }
+
+    @Test
+    fun scrollBackwardItemWithinStartPaddingDisplayed() {
+        val state = PagerState()
+        val padding = pageTotalSize * 1.5f
+        createPager(
+            state = state,
+            modifier = Modifier
+                .requiredSize(padding * 2 + pageTotalSize)
+                .testTag(PagerTag),
+            contentPadding = PaddingValues(mainAxis = padding),
+            pageCount = { 4 },
+            pageSize = { PageSize.Fixed(pageTotalSize) }
+        ) {
+            Spacer(
+                Modifier
+                    .requiredSize(pageTotalSize)
+                    .testTag(it.toString())
+            )
+        }
+
+        state.scrollBy(pageTotalSize * 3)
+        state.scrollBy(-pageTotalSize * 1.5f)
+
+        state.assertScrollPosition(1, pageTotalSize * 0.5f)
+
+        rule.onNodeWithTag("0")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize * 1.5f - padding)
+        rule.onNodeWithTag("1")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize * 2.5f - padding)
+        rule.onNodeWithTag("2")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize * 3.5f - padding)
+        rule.onNodeWithTag("3")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize * 4.5f - padding)
+    }
+
+    @Test
+    fun scrollForwardTillTheEnd() {
+        val state = PagerState()
+        val padding = pageTotalSize * 1.5f
+        createPager(
+            state = state,
+            modifier = Modifier
+                .requiredSize(padding * 2 + pageTotalSize)
+                .testTag(PagerTag),
+            contentPadding = PaddingValues(mainAxis = padding),
+            pageCount = { 4 },
+            pageSize = { PageSize.Fixed(pageTotalSize) }
+        ) {
+            Spacer(
+                Modifier
+                    .requiredSize(pageTotalSize)
+                    .testTag(it.toString())
+            )
+        }
+
+        state.scrollBy(pageTotalSize * 3)
+
+        state.assertScrollPosition(3, 0.dp)
+
+        rule.onNodeWithTag("1")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize - padding)
+        rule.onNodeWithTag("2")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize * 2 - padding)
+        rule.onNodeWithTag("3")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize * 3 - padding)
+
+        // there are no space to scroll anymore, so it should change nothing
+        state.scrollBy(10.dp)
+
+        state.assertScrollPosition(3, 0.dp)
+
+        rule.onNodeWithTag("1")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize - padding)
+        rule.onNodeWithTag("2")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize * 2 - padding)
+        rule.onNodeWithTag("3")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize * 3 - padding)
+    }
+
+    @Test
+    fun scrollForwardTillTheEndAndABitBack() {
+        val state = PagerState()
+        val padding = pageTotalSize * 1.5f
+        createPager(
+            state = state,
+            modifier = Modifier
+                .requiredSize(padding * 2 + pageTotalSize)
+                .testTag(PagerTag),
+            contentPadding = PaddingValues(mainAxis = padding),
+            pageCount = { 4 },
+            pageSize = { PageSize.Fixed(pageTotalSize) }
+        ) {
+            Spacer(
+                Modifier
+                    .requiredSize(pageTotalSize)
+                    .testTag(it.toString())
+            )
+        }
+
+        state.scrollBy(pageTotalSize * 3)
+        state.scrollBy(-pageTotalSize / 2)
+
+        state.assertScrollPosition(2, pageTotalSize / 2)
+
+        rule.onNodeWithTag("1")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize * 1.5f - padding)
+        rule.onNodeWithTag("2")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize * 2.5f - padding)
+        rule.onNodeWithTag("3")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize * 3.5f - padding)
+    }
+
+    @Test
+    fun contentPaddingAndWrapContent() {
+        rule.setContent {
+            Box(modifier = Modifier.testTag(ContainerTag)) {
+                HorizontalOrVerticalPager(
+                    contentPadding = PaddingValues(
+                        beforeContentCrossAxis = 2.dp,
+                        beforeContent = 4.dp,
+                        afterContentCrossAxis = 6.dp,
+                        afterContent = 8.dp
+                    ),
+                    pageCount = 1,
+                    pageSize = PageSize.Fixed(pageTotalSize)
+                ) {
+                    Spacer(
+                        Modifier
+                            .requiredSize(pageTotalSize)
+                            .testTag(PageTag)
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag(PageTag)
+            .assertCrossAxisStartPositionInRootIsEqualTo(2.dp)
+            .assertStartPositionInRootIsEqualTo(4.dp)
+            .assertCrossAxisSizeIsEqualTo(pageTotalSize)
+            .assertMainAxisSizeIsEqualTo(pageTotalSize)
+
+        rule.onNodeWithTag(ContainerTag)
+            .assertCrossAxisStartPositionInRootIsEqualTo(0.dp)
+            .assertStartPositionInRootIsEqualTo(0.dp)
+            .assertCrossAxisSizeIsEqualTo(pageTotalSize + 2.dp + 6.dp)
+            .assertMainAxisSizeIsEqualTo(pageTotalSize + 4.dp + 8.dp)
+    }
+
+    @Test
+    fun contentPaddingAndNoContent() {
+        rule.setContent {
+            Box(modifier = Modifier.testTag(ContainerTag)) {
+                HorizontalOrVerticalPager(
+                    contentPadding = PaddingValues(
+                        beforeContentCrossAxis = 2.dp,
+                        beforeContent = 4.dp,
+                        afterContentCrossAxis = 6.dp,
+                        afterContent = 8.dp
+                    ),
+                    pageCount = 0,
+                    pageSize = PageSize.Fixed(pageTotalSize)
+                ) { }
+            }
+        }
+
+        rule.onNodeWithTag(ContainerTag)
+            .assertCrossAxisStartPositionInRootIsEqualTo(0.dp)
+            .assertStartPositionInRootIsEqualTo(0.dp)
+            .assertCrossAxisSizeIsEqualTo(8.dp)
+            .assertMainAxisSizeIsEqualTo(12.dp)
+    }
+
+    @Test
+    fun contentPaddingAndZeroSizedItem() {
+        rule.setContent {
+            Box(modifier = Modifier.testTag(ContainerTag)) {
+                HorizontalOrVerticalPager(
+                    contentPadding = PaddingValues(
+                        beforeContentCrossAxis = 2.dp,
+                        beforeContent = 4.dp,
+                        afterContentCrossAxis = 6.dp,
+                        afterContent = 8.dp
+                    ),
+                    pageCount = 1,
+                    pageSize = PageSize.Fixed(0.dp)
+                ) {
+                    Box { }
+                }
+            }
+        }
+
+        rule.onNodeWithTag(ContainerTag)
+            .assertCrossAxisStartPositionInRootIsEqualTo(0.dp)
+            .assertStartPositionInRootIsEqualTo(0.dp)
+            .assertCrossAxisSizeIsEqualTo(8.dp)
+            .assertMainAxisSizeIsEqualTo(12.dp)
+    }
+
+    @Test
+    fun contentPaddingAndReverseLayout() {
+        val topPadding = pageTotalSize * 2
+        val bottomPadding = pageTotalSize / 2
+        val listSize = pageTotalSize * 3
+        val state = PagerState()
+        createPager(
+            reverseLayout = true,
+            state = state,
+            modifier = Modifier.requiredSize(listSize),
+            contentPadding = PaddingValues(
+                beforeContent = topPadding,
+                afterContent = bottomPadding
+            ),
+            pageSize = { PageSize.Fixed(pageTotalSize) },
+            pageCount = { 3 }
+        ) { page ->
+            Box(
+                Modifier
+                    .requiredSize(pageTotalSize)
+                    .testTag("$page")
+            )
+        }
+
+        rule.onNodeWithTag("0")
+            .assertStartPositionInRootIsEqualTo(listSize - bottomPadding - pageTotalSize)
+        rule.onNodeWithTag("1")
+            .assertStartPositionInRootIsEqualTo(listSize - bottomPadding - pageTotalSize * 2)
+        // Partially visible.
+        rule.onNodeWithTag("2")
+            .assertStartPositionInRootIsEqualTo(-pageTotalSize / 2)
+
+        // Scroll to the top.
+        state.scrollBy(pageTotalSize * 2.5f)
+
+        rule.onNodeWithTag("2").assertStartPositionInRootIsEqualTo(topPadding)
+        // Shouldn't be visible
+        rule.onNodeWithTag("1").assertIsNotDisplayed()
+        rule.onNodeWithTag("0").assertIsNotDisplayed()
+    }
+
+    @Test
+    fun contentLargePaddingAndReverseLayout() {
+        val topPadding = pageTotalSize * 2
+        val bottomPadding = pageTotalSize * 2
+        val listSize = pageTotalSize * 3
+        val state = PagerState()
+        createPager(
+            reverseLayout = true,
+            state = state,
+            modifier = Modifier.requiredSize(listSize),
+            contentPadding = PaddingValues(
+                beforeContent = topPadding,
+                afterContent = bottomPadding
+            ),
+            pageSize = { PageSize.Fixed(pageTotalSize) },
+            pageCount = { 3 }
+        ) { page ->
+            Box(
+                Modifier
+                    .requiredSize(pageTotalSize)
+                    .testTag("$page")
+            )
+        }
+
+        rule.onNodeWithTag("0")
+            .assertStartPositionInRootIsEqualTo(0.dp)
+        // Shouldn't be visible
+        rule.onNodeWithTag("1").assertDoesNotExist()
+
+        // Scroll to the top.
+        state.scrollBy(pageTotalSize * 5f)
+
+        rule.onNodeWithTag("2").assertStartPositionInRootIsEqualTo(topPadding)
+        // Shouldn't be visible
+        rule.onNodeWithTag("1").assertIsNotDisplayed()
+        rule.onNodeWithTag("0").assertIsNotDisplayed()
+    }
+
+    @Test
+    fun overscrollWithContentPadding() {
+        lateinit var state: PagerState
+        rule.setContent {
+            state = rememberPagerState()
+            Box(
+                modifier = Modifier
+                    .testTag(ContainerTag)
+                    .size(pageTotalSize + smallPaddingSize * 2)
+            ) {
+                HorizontalOrVerticalPager(
+                    state = state,
+                    contentPadding = PaddingValues(mainAxis = smallPaddingSize),
+                    pageCount = 2,
+                    pageSize = PageSize.Fixed(pageTotalSize)
+                ) {
+                    Box(
+                        Modifier
+                            .testTag("$it")
+                            .fillMaxSize()
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag("0")
+            .assertStartPositionInRootIsEqualTo(smallPaddingSize)
+            .assertMainAxisSizeIsEqualTo(pageTotalSize)
+
+        rule.onNodeWithTag("1")
+            .assertStartPositionInRootIsEqualTo(smallPaddingSize + pageTotalSize)
+            .assertMainAxisSizeIsEqualTo(pageTotalSize)
+
+        rule.runOnIdle {
+            runBlocking {
+                // pageSizePx is the maximum offset, plus if we overscroll the content padding
+                // the layout mechanism will decide the page 0 is not needed until we start
+                // filling the over scrolled gap.
+                (state as ScrollableState).scrollBy(pageTotalSizePx + smallPaddingSizePx * 1.5f)
+            }
+        }
+
+        rule.onNodeWithTag("1")
+            .assertStartPositionInRootIsEqualTo(smallPaddingSize)
+            .assertMainAxisSizeIsEqualTo(pageTotalSize)
+
+        rule.onNodeWithTag("0")
+            .assertStartPositionInRootIsEqualTo(smallPaddingSize - pageTotalSize)
+            .assertMainAxisSizeIsEqualTo(pageTotalSize)
+    }
+
+    @Test
+    fun totalPaddingLargerParentSize_initialState() {
+        lateinit var state: PagerState
+        rule.setContent {
+            state = rememberPagerState()
+            Box(
+                modifier = Modifier
+                    .testTag(ContainerTag)
+                    .size(pageTotalSize * 1.5f)
+            ) {
+                HorizontalOrVerticalPager(
+                    state = state,
+                    contentPadding = PaddingValues(mainAxis = pageTotalSize),
+                    pageCount = 4,
+                    pageSize = PageSize.Fixed(pageTotalSize)
+                ) {
+                    Box(
+                        Modifier
+                            .testTag("$it")
+                            .size(pageTotalSize)
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag("0")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize)
+
+        rule.onNodeWithTag("1")
+            .assertDoesNotExist()
+
+        rule.runOnIdle {
+            state.assertScrollPosition(0, 0.dp)
+            state.assertVisibleItems(0 to 0.dp)
+            state.assertLayoutInfoOffsetRange(-pageTotalSize, pageTotalSize * 0.5f)
+        }
+    }
+
+    @Test
+    fun totalPaddingLargerParentSize_scrollByPadding() {
+        lateinit var state: PagerState
+        rule.setContent {
+            state = rememberPagerState()
+            Box(
+                modifier = Modifier
+                    .testTag(ContainerTag)
+                    .size(pageTotalSize * 1.5f)
+            ) {
+                HorizontalOrVerticalPager(
+                    state = state,
+                    contentPadding = PaddingValues(mainAxis = pageTotalSize),
+                    pageCount = 4,
+                    pageSize = PageSize.Fixed(pageTotalSize)
+                ) {
+                    Box(
+                        Modifier
+                            .testTag("$it")
+                            .size(pageTotalSize)
+                    )
+                }
+            }
+        }
+
+        state.scrollBy(pageTotalSize)
+
+        rule.onNodeWithTag("0")
+            .assertStartPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithTag("1")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize)
+
+        rule.onNodeWithTag("2")
+            .assertIsNotDisplayed()
+
+        rule.runOnIdle {
+            state.assertScrollPosition(1, 0.dp)
+            state.assertVisibleItems(0 to -pageTotalSize, 1 to 0.dp)
+        }
+    }
+
+    @Test
+    fun totalPaddingLargerParentSize_scrollToLastItem() {
+        lateinit var state: PagerState
+        rule.setContent {
+            state = rememberPagerState()
+            Box(
+                modifier = Modifier
+                    .testTag(ContainerTag)
+                    .size(pageTotalSize * 1.5f)
+            ) {
+                HorizontalOrVerticalPager(
+                    state = state,
+                    contentPadding = PaddingValues(mainAxis = pageTotalSize),
+                    pageCount = 4,
+                    pageSize = PageSize.Fixed(pageTotalSize)
+                ) {
+                    Box(
+                        Modifier
+                            .testTag("$it")
+                            .size(pageTotalSize)
+                    )
+                }
+            }
+        }
+
+        state.runScrollToPage(3)
+
+        rule.onNodeWithTag("1")
+            .assertDoesNotExist()
+
+        rule.onNodeWithTag("2")
+            .assertStartPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithTag("3")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize)
+
+        rule.runOnIdle {
+            state.assertScrollPosition(3, 0.dp)
+            state.assertVisibleItems(2 to -pageTotalSize, 3 to 0.dp)
+        }
+    }
+
+    @Test
+    fun totalPaddingLargerParentSize_scrollToLastItemByDelta() {
+        lateinit var state: PagerState
+        rule.setContent {
+            state = rememberPagerState()
+            Box(
+                modifier = Modifier
+                    .testTag(ContainerTag)
+                    .size(pageTotalSize * 1.5f)
+            ) {
+                HorizontalOrVerticalPager(
+                    state = state,
+                    contentPadding = PaddingValues(mainAxis = pageTotalSize),
+                    pageSize = PageSize.Fixed(pageTotalSize),
+                    pageCount = 4
+                ) {
+                    Box(
+                        Modifier
+                            .testTag("$it")
+                            .size(pageTotalSize)
+                    )
+                }
+            }
+        }
+
+        state.scrollBy(pageTotalSize * 3)
+
+        rule.onNodeWithTag("1")
+            .assertIsNotDisplayed()
+
+        rule.onNodeWithTag("2")
+            .assertStartPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithTag("3")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize)
+
+        rule.runOnIdle {
+            state.assertScrollPosition(3, 0.dp)
+            state.assertVisibleItems(2 to -pageTotalSize, 3 to 0.dp)
+        }
+    }
+
+    @Test
+    fun totalPaddingLargerParentSize_scrollTillTheEnd() {
+        // the whole end content padding is displayed
+        lateinit var state: PagerState
+        rule.setContent {
+            state = rememberPagerState()
+            Box(
+                modifier = Modifier
+                    .testTag(ContainerTag)
+                    .size(pageTotalSize * 1.5f)
+            ) {
+                HorizontalOrVerticalPager(
+                    state = state,
+                    contentPadding = PaddingValues(mainAxis = pageTotalSize),
+                    pageSize = PageSize.Fixed(pageTotalSize),
+                    pageCount = 4
+                ) {
+                    Box(
+                        Modifier
+                            .testTag("$it")
+                            .size(pageTotalSize)
+                    )
+                }
+            }
+        }
+
+        state.scrollBy(pageTotalSize * 4.5f)
+
+        rule.onNodeWithTag("2")
+            .assertIsNotDisplayed()
+
+        rule.onNodeWithTag("3")
+            .assertStartPositionInRootIsEqualTo(-pageTotalSize * 0.5f)
+
+        rule.runOnIdle {
+            state.assertScrollPosition(3, pageTotalSize * 1.5f)
+            state.assertVisibleItems(3 to -pageTotalSize * 1.5f)
+        }
+    }
+
+    @Test
+    fun eachPaddingLargerParentSize_initialState() {
+        lateinit var state: PagerState
+        rule.setContent {
+            state = rememberPagerState()
+            Box(
+                modifier = Modifier
+                    .testTag(ContainerTag)
+                    .size(pageTotalSize * 1.5f)
+            ) {
+                HorizontalOrVerticalPager(
+                    state = state,
+                    contentPadding = PaddingValues(mainAxis = pageTotalSize * 2),
+                    pageSize = PageSize.Fixed(pageTotalSize),
+                    pageCount = 4
+                ) {
+                    Box(
+                        Modifier
+                            .testTag("$it")
+                            .size(pageTotalSize)
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag("0")
+            .assertIsNotDisplayed()
+
+        rule.runOnIdle {
+            state.assertScrollPosition(0, 0.dp)
+            state.assertVisibleItems(0 to 0.dp)
+            state.assertLayoutInfoOffsetRange(-pageTotalSize * 2, -pageTotalSize * 0.5f)
+        }
+    }
+
+    @Test
+    fun eachPaddingLargerParentSize_scrollByPadding() {
+        lateinit var state: PagerState
+        rule.setContent {
+            state = rememberPagerState()
+            Box(
+                modifier = Modifier
+                    .testTag(ContainerTag)
+                    .size(pageTotalSize * 1.5f)
+            ) {
+                HorizontalOrVerticalPager(
+                    state = state,
+                    contentPadding = PaddingValues(mainAxis = pageTotalSize * 2),
+                    pageSize = PageSize.Fixed(pageTotalSize),
+                    pageCount = 4
+                ) {
+                    Box(
+                        Modifier
+                            .testTag("$it")
+                            .size(pageTotalSize)
+                    )
+                }
+            }
+        }
+
+        state.scrollBy(pageTotalSize * 2)
+
+        rule.onNodeWithTag("0")
+            .assertStartPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithTag("1")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize)
+
+        rule.onNodeWithTag("2")
+            .assertIsNotDisplayed()
+
+        rule.runOnIdle {
+            state.assertScrollPosition(2, 0.dp)
+            state.assertVisibleItems(0 to -pageTotalSize * 2, 1 to -pageTotalSize, 2 to 0.dp)
+        }
+    }
+
+    @Test
+    fun eachPaddingLargerParentSize_scrollToLastItem() {
+        lateinit var state: PagerState
+        rule.setContent {
+            state = rememberPagerState()
+            Box(
+                modifier = Modifier
+                    .testTag(ContainerTag)
+                    .size(pageTotalSize * 1.5f)
+            ) {
+                HorizontalOrVerticalPager(
+                    state = state,
+                    contentPadding = PaddingValues(mainAxis = pageTotalSize * 2),
+                    pageSize = PageSize.Fixed(pageTotalSize),
+                    pageCount = 4
+                ) {
+                    Box(
+                        Modifier
+                            .testTag("$it")
+                            .size(pageTotalSize)
+                    )
+                }
+            }
+        }
+
+        state.runScrollToPage(3)
+
+        rule.onNodeWithTag("0")
+            .assertIsNotDisplayed()
+
+        rule.onNodeWithTag("1")
+            .assertStartPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithTag("2")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize)
+
+        rule.onNodeWithTag("3")
+            .assertIsNotDisplayed()
+
+        rule.runOnIdle {
+            state.assertScrollPosition(3, 0.dp)
+            state.assertVisibleItems(1 to -pageTotalSize * 2, 2 to -pageTotalSize, 3 to 0.dp)
+        }
+    }
+
+    @Test
+    fun eachPaddingLargerParentSize_scrollToLastItemByDelta() {
+        lateinit var state: PagerState
+        rule.setContent {
+            state = rememberPagerState()
+            Box(
+                modifier = Modifier
+                    .testTag(ContainerTag)
+                    .size(pageTotalSize * 1.5f)
+            ) {
+                HorizontalOrVerticalPager(
+                    state = state,
+                    contentPadding = PaddingValues(mainAxis = pageTotalSize * 2),
+                    pageSize = PageSize.Fixed(pageTotalSize),
+                    pageCount = 4
+                ) {
+                    Box(
+                        Modifier
+                            .testTag("$it")
+                            .size(pageTotalSize)
+                    )
+                }
+            }
+        }
+
+        state.scrollBy(pageTotalSize * 3)
+
+        rule.onNodeWithTag("0")
+            .assertIsNotDisplayed()
+
+        rule.onNodeWithTag("1")
+            .assertStartPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithTag("2")
+            .assertStartPositionInRootIsEqualTo(pageTotalSize)
+
+        rule.onNodeWithTag("3")
+            .assertIsNotDisplayed()
+
+        rule.runOnIdle {
+            state.assertScrollPosition(3, 0.dp)
+            state.assertVisibleItems(1 to -pageTotalSize * 2, 2 to -pageTotalSize, 3 to 0.dp)
+        }
+    }
+
+    @Test
+    fun eachPaddingLargerParentSize_scrollTillTheEnd() {
+        // only the end content padding is displayed
+        lateinit var state: PagerState
+        rule.setContent {
+            state = rememberPagerState()
+            Box(
+                modifier = Modifier
+                    .testTag(ContainerTag)
+                    .size(pageTotalSize * 1.5f)
+            ) {
+                HorizontalOrVerticalPager(
+                    state = state,
+                    contentPadding = PaddingValues(mainAxis = pageTotalSize * 2),
+                    pageSize = PageSize.Fixed(pageTotalSize),
+                    pageCount = 4
+                ) {
+                    Box(
+                        Modifier
+                            .testTag("$it")
+                            .size(pageTotalSize)
+                    )
+                }
+            }
+        }
+
+        state.scrollBy(
+            pageTotalSize * 1.5f + // container size
+                pageTotalSize * 2 + // start padding
+                pageTotalSize * 3 // all pages
+        )
+
+        rule.onNodeWithTag("3")
+            .assertIsNotDisplayed()
+
+        rule.runOnIdle {
+            state.assertScrollPosition(3, pageTotalSize * 3.5f)
+            state.assertVisibleItems(3 to -pageTotalSize * 3.5f)
+        }
+    }
+
+    @Test
+    fun unevenPaddingWithRtl() {
+        val padding = PaddingValues(start = 20.dp, end = 8.dp)
+        lateinit var state: PagerState
+        rule.setContent {
+            state = rememberPagerState()
+            CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+                HorizontalOrVerticalPager(
+                    modifier = Modifier
+                        .testTag("list")
+                        .mainAxisSize(pageTotalSize * 2),
+                    state = state,
+                    contentPadding = padding,
+                    pageCount = 4,
+                    pageSize = PageSize.Fixed(pageTotalSize)
+                ) {
+                    Box(
+                        Modifier
+                            .testTag("$it")
+                            .background(Color.Red)
+                            .size(pageTotalSize)
+                    ) {
+                        BasicText("$it")
+                    }
+                }
+            }
+        }
+
+        if (vertical) {
+            rule.onNodeWithTag("0")
+                .assertStartPositionInRootIsEqualTo(0.dp)
+                .assertCrossAxisStartPositionInRootIsEqualTo(
+                    padding.calculateLeftPadding(LayoutDirection.Rtl)
+                )
+
+            rule.onNodeWithTag("list")
+                .assertWidthIsEqualTo(28.dp + pageTotalSize)
+        } else {
+            rule.onNodeWithTag("0")
+                .assertCrossAxisStartPositionInRootIsEqualTo(0.dp)
+                .assertStartPositionInRootIsEqualTo(
+                    // list width - pageSize - padding
+                    pageTotalSize * 2 - pageTotalSize -
+                        padding.calculateRightPadding(LayoutDirection.Rtl)
+                )
+        }
+
+        state.scrollBy(pageTotalSize * 4)
+
+        if (vertical) {
+            rule.onNodeWithTag("3")
+                .assertStartPositionInRootIsEqualTo(pageTotalSize)
+                .assertCrossAxisStartPositionInRootIsEqualTo(
+                    padding.calculateLeftPadding(LayoutDirection.Rtl)
+                )
+        } else {
+            rule.onNodeWithTag("3")
+                .assertCrossAxisStartPositionInRootIsEqualTo(0.dp)
+                .assertStartPositionInRootIsEqualTo(
+                    padding.calculateLeftPadding(LayoutDirection.Rtl)
+                )
+        }
+    }
+
+    private fun PagerState.scrollBy(offset: Dp) {
+        runBlocking(Dispatchers.Main + AutoTestFrameClock()) {
+            animateScrollBy(with(rule.density) { offset.roundToPx().toFloat() }, snap())
+        }
+    }
+
+    private fun PagerState.scrollBy(offset: Float) {
+        runBlocking(Dispatchers.Main + AutoTestFrameClock()) {
+            animateScrollBy(offset, snap())
+        }
+    }
+
+    private fun PagerState.runScrollToPage(page: Int) {
+        runBlocking(Dispatchers.Main + AutoTestFrameClock()) {
+            scrollToPage(page)
+        }
+    }
+
+    private fun PagerState.assertScrollPosition(index: Int, offset: Dp) = with(rule.density) {
+        assertThat(firstVisiblePage).isEqualTo(index)
+        assertThat(firstVisiblePageOffset.toDp().value).isWithin(0.5f).of(offset.value)
+    }
+
+    private fun PagerState.assertLayoutInfoOffsetRange(from: Dp, to: Dp) = with(rule.density) {
+        assertThat(layoutInfo.viewportStartOffset to layoutInfo.viewportEndOffset)
+            .isEqualTo(from.roundToPx() to to.roundToPx())
+    }
+
+    private fun PagerState.assertVisibleItems(vararg expected: Pair<Int, Dp>) =
+        with(rule.density) {
+            assertThat(layoutInfo.visiblePagesInfo.map { it.index to it.offset })
+                .isEqualTo(expected.map { it.first to it.second.roundToPx() })
+        }
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun params() = AllOrientationsParams
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerContentTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerContentTest.kt
new file mode 100644
index 0000000..2d7e101
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerContentTest.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.pager
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.test.swipe
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.zIndex
+import kotlin.test.assertContentEquals
+import kotlin.test.assertTrue
+import org.junit.Test
+
+class PagerContentTest : SingleOrientationPagerTest(Orientation.Horizontal) {
+
+    @OptIn(ExperimentalFoundationApi::class)
+    @Test
+    fun pageContent_makeSureContainerOwnsOutsideModifiers() {
+        // Arrange
+        val state = PagerState()
+        rule.setContent {
+            HorizontalOrVerticalPager(
+                pageCount = 10,
+                state = state,
+                contentPadding = PaddingValues(horizontal = 32.dp),
+                pageSpacing = 4.dp,
+                modifier = Modifier
+                    .fillMaxSize()
+                    .testTag("pager"),
+                pageSize = PageSize.Fill
+            ) { page ->
+                Box(
+                    modifier = Modifier
+                        .background(Color.Black)
+                        .size(100.dp)
+                        .testTag(page.toString())
+                )
+            }
+        }
+
+        rule.onNodeWithTag("pager").performTouchInput {
+            swipe(bottomRight, bottomLeft) // swipe outside bounds of pages
+        }
+
+        rule.runOnIdle {
+            assertTrue { state.currentPage != 0 }
+        }
+    }
+
+    @OptIn(ExperimentalFoundationApi::class)
+    @Test
+    fun pageContent_makeSureInnerModifiersAreAppliedToPages() {
+        // Arrange
+        val state = PagerState()
+        val drawingList = mutableListOf<Int>()
+        rule.setContent {
+            HorizontalOrVerticalPager(
+                pageCount = 10,
+                state = state,
+                modifier = Modifier
+                    .width(100.dp)
+                    .testTag("pager"),
+                pageSize = PageSize.Fixed(10.dp)
+            ) { page ->
+                Box(
+                    modifier = Modifier
+                        .background(Color.Black)
+                        .size(100.dp)
+                        .zIndex(if (page % 2 == 0) 100f else 50f)
+                        .drawWithContent {
+                            drawingList.add(page)
+                        }
+                        .testTag(page.toString())
+                )
+            }
+        }
+
+        rule.runOnIdle {
+            assertContentEquals(drawingList, listOf(1, 3, 5, 7, 9, 0, 2, 4, 6, 8))
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerCrossAxisTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerCrossAxisTest.kt
index 9f13bea..33cc710 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerCrossAxisTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerCrossAxisTest.kt
@@ -46,7 +46,7 @@
 @OptIn(ExperimentalFoundationApi::class)
 @LargeTest
 @RunWith(Parameterized::class)
-internal class PagerCrossAxisTest(val config: ParamConfig) : BasePagerTest(config) {
+class PagerCrossAxisTest(val config: ParamConfig) : BasePagerTest(config) {
 
     @Test
     fun pagerOnInfiniteCrossAxisLayout_shouldWrapContentSize() {
@@ -61,7 +61,7 @@
                         .fillMaxWidth()
                         .testTag(PagerTestTag),
                 ) {
-                    val fillModifier = if (isVertical) {
+                    val fillModifier = if (vertical) {
                         Modifier
                             .fillMaxHeight()
                             .width(200.dp)
@@ -79,7 +79,7 @@
         val rootBounds = rule.onRoot().getUnclippedBoundsInRoot()
 
         // Assert: Max Cross Axis size is handled well by wrapping content
-        if (isVertical) {
+        if (vertical) {
             rule.onNodeWithTag(PagerTestTag)
                 .assertHeightIsEqualTo(rootBounds.height)
                 .assertWidthIsEqualTo(200.dp)
@@ -92,7 +92,7 @@
 
     @Composable
     private fun InfiniteAxisRootComposable(content: @Composable () -> Unit) {
-        if (isVertical) {
+        if (vertical) {
             Row(Modifier.horizontalScroll(rememberScrollState())) {
                 content()
             }
@@ -106,10 +106,6 @@
     companion object {
         @JvmStatic
         @Parameterized.Parameters(name = "{0}")
-        fun params() = mutableListOf<ParamConfig>().apply {
-            for (orientation in TestOrientation) {
-                add(ParamConfig(orientation = orientation))
-            }
-        }
+        fun params() = AllOrientationsParams
     }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerLayoutInfoTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerLayoutInfoTest.kt
new file mode 100644
index 0000000..a2b44b83
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerLayoutInfoTest.kt
@@ -0,0 +1,456 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.pager
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.scrollBy
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@OptIn(ExperimentalFoundationApi::class)
+@MediumTest
+@RunWith(Parameterized::class)
+class PagerLayoutInfoTest(private val param: ParamConfig) : BasePagerTest(param) {
+
+    private var pageSizeDp: Dp = 200.dp
+    private var pageSizePx: Int = 0
+
+    @Before
+    fun setUp() {
+        pageSizePx = with(rule.density) { pageSizeDp.roundToPx() }
+    }
+
+    @Test
+    fun visiblePagesAreCorrect() {
+        val state = PagerState()
+
+        createPager(
+            state,
+            modifier = Modifier.requiredSize(pageSizeDp * 3.5f),
+            pageCount = { 5 },
+            pageSize = { PageSize.Fixed(pageSizeDp) }
+        ) {
+            Box(Modifier.requiredSize(pageSizeDp))
+        }
+        rule.runOnIdle {
+            state.layoutInfo.assertVisiblePages(count = 4)
+        }
+    }
+
+    @Test
+    fun visiblePagesAreCorrectAfterScroll() {
+        val state = PagerState()
+
+        createPager(
+            state,
+            modifier = Modifier.requiredSize(pageSizeDp * 3.5f),
+            pageCount = { 5 },
+            pageSize = { PageSize.Fixed(pageSizeDp) }
+        ) {
+            Box(Modifier.requiredSize(pageSizeDp))
+        }
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollToPage(1)
+                state.scrollBy(10f)
+            }
+
+            state.layoutInfo.assertVisiblePages(
+                count = 4,
+                startIndex = 1,
+                startOffset = -10
+            )
+        }
+    }
+
+    @Test
+    fun visiblePagesAreCorrectWithSpacing() {
+        val state = PagerState()
+
+        createPager(
+            state,
+            modifier = Modifier.requiredSize(pageSizeDp * 3.5f),
+            pageCount = { 5 },
+            pageSpacing = pageSizeDp,
+            pageSize = { PageSize.Fixed(pageSizeDp) }
+        ) {
+            Box(Modifier.requiredSize(pageSizeDp))
+        }
+
+        rule.runOnIdle {
+            state.layoutInfo.assertVisiblePages(count = 2, spacing = pageSizePx)
+        }
+    }
+
+    @Test
+    fun visiblePagesAreObservableWhenWeScroll() {
+        val state = PagerState()
+        val currentInfo = StableRef<PagerLayoutInfo?>(null)
+        createPager(
+            state,
+            modifier = Modifier.requiredSize(pageSizeDp * 3.5f),
+            pageCount = { 5 },
+            pageSize = { PageSize.Fixed(pageSizeDp) },
+            additionalContent = {
+                LaunchedEffect(key1 = state) {
+                    snapshotFlow { state.layoutInfo }.collect {
+                        currentInfo.value = it
+                    }
+                }
+            }
+        ) {
+            Box(Modifier.requiredSize(pageSizeDp))
+        }
+
+        rule.runOnIdle {
+            // empty it here and scrolling should invoke observingFun again
+            currentInfo.value = null
+            runBlocking {
+                state.scrollToPage(1)
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(currentInfo.value).isNotNull()
+            currentInfo.value!!.assertVisiblePages(count = 4, startIndex = 1)
+        }
+    }
+
+    @Test
+    fun visiblePagesAreObservableWhenResize() {
+        val state = PagerState()
+        var pageSize by mutableStateOf(PageSize.Fixed(pageSizeDp * 2))
+        var currentInfo: PagerLayoutInfo? = null
+
+        @Composable
+        fun observingFun() {
+            currentInfo = state.layoutInfo
+        }
+
+        createPager(
+            state,
+            pageCount = { 1 },
+            pageSize = { pageSize },
+            additionalContent = { observingFun() }
+        ) {
+            Box(Modifier.requiredSize(pageSizeDp * 2))
+        }
+
+        rule.runOnIdle {
+            assertThat(currentInfo).isNotNull()
+            currentInfo!!.assertVisiblePages(
+                count = 1,
+                pageSize = with(rule.density) { pageSizeDp.roundToPx() * 2 })
+            currentInfo = null
+            pageSize = PageSize.Fixed(pageSizeDp)
+        }
+
+        rule.runOnIdle {
+            assertThat(currentInfo).isNotNull()
+            currentInfo!!.assertVisiblePages(count = 1, pageSize = pageSizePx)
+        }
+    }
+
+    @Test
+    fun totalCountIsCorrect() {
+        var count by mutableStateOf(10)
+        val state = PagerState()
+
+        createPager(
+            state,
+            pageCount = { count },
+            pageSize = { PageSize.Fixed(10.dp) }
+        ) {
+            Box(Modifier.requiredSize(10.dp))
+        }
+
+        rule.runOnIdle {
+            assertThat(state.layoutInfo.pagesCount).isEqualTo(10)
+            count = 20
+        }
+
+        rule.runOnIdle {
+            assertThat(state.layoutInfo.pagesCount).isEqualTo(20)
+        }
+    }
+
+    @Test
+    fun viewportOffsetsAndSizeAreCorrect() {
+        val sizePx = 45
+        val sizeDp = with(rule.density) { sizePx.toDp() }
+        val state = PagerState()
+
+        createPager(
+            state,
+            modifier = Modifier
+                .mainAxisSize(sizeDp)
+                .crossAxisSize(sizeDp * 2),
+            pageCount = { 3 },
+            pageSize = { PageSize.Fixed(sizeDp) }
+        ) {
+            Box(Modifier.requiredSize(sizeDp))
+        }
+
+        rule.runOnIdle {
+            assertThat(state.layoutInfo.viewportStartOffset).isEqualTo(0)
+            assertThat(state.layoutInfo.viewportEndOffset).isEqualTo(sizePx)
+            assertThat(state.layoutInfo.viewportSize).isEqualTo(
+                if (vertical) IntSize(sizePx * 2, sizePx) else IntSize(sizePx, sizePx * 2)
+            )
+        }
+    }
+
+    @Test
+    fun viewportOffsetsAndSizeAreCorrectWithContentPadding() {
+        val reverseLayout = param.reverseLayout
+        val sizePx = 45
+        val startPaddingPx = 10
+        val endPaddingPx = 15
+        val sizeDp = with(rule.density) { sizePx.toDp() }
+        val beforeContentPaddingDp = with(rule.density) {
+            if (!reverseLayout) startPaddingPx.toDp() else endPaddingPx.toDp()
+        }
+        val afterContentPaddingDp = with(rule.density) {
+            if (!reverseLayout) endPaddingPx.toDp() else startPaddingPx.toDp()
+        }
+        val state = PagerState()
+
+        createPager(
+            state,
+            modifier = Modifier
+                .mainAxisSize(sizeDp)
+                .crossAxisSize(sizeDp * 2),
+            pageCount = { 3 },
+            pageSize = { PageSize.Fixed(sizeDp) },
+            contentPadding = PaddingValues(
+                beforeContent = beforeContentPaddingDp,
+                afterContent = afterContentPaddingDp,
+                beforeContentCrossAxis = 2.dp,
+                afterContentCrossAxis = 2.dp
+            )
+        ) {
+            Box(Modifier.requiredSize(sizeDp))
+        }
+
+        rule.runOnIdle {
+            assertThat(state.layoutInfo.viewportStartOffset).isEqualTo(-startPaddingPx)
+            assertThat(state.layoutInfo.viewportEndOffset).isEqualTo(sizePx - startPaddingPx)
+            assertThat(state.layoutInfo.afterContentPadding).isEqualTo(endPaddingPx)
+            assertThat(state.layoutInfo.viewportSize).isEqualTo(
+                if (vertical) IntSize(sizePx * 2, sizePx) else IntSize(sizePx, sizePx * 2)
+            )
+        }
+    }
+
+    @Test
+    fun emptyPagesInVisiblePagesInfo() {
+        val state = PagerState()
+
+        createPager(
+            state,
+            pageCount = { 2 },
+            pageSize = { PageSize.Fixed(pageSizeDp) }
+        ) {
+            Box(Modifier)
+        }
+
+        rule.runOnIdle {
+            assertThat(state.layoutInfo.visiblePagesInfo.size).isEqualTo(2)
+            assertThat(state.layoutInfo.visiblePagesInfo.first().index).isEqualTo(0)
+            assertThat(state.layoutInfo.visiblePagesInfo.last().index).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun emptyContent() {
+        val reverseLayout = param.reverseLayout
+        val sizePx = 45
+        val startPaddingPx = 10
+        val endPaddingPx = 15
+        val sizeDp = with(rule.density) { sizePx.toDp() }
+        val beforeContentPaddingDp = with(rule.density) {
+            if (!reverseLayout) startPaddingPx.toDp() else endPaddingPx.toDp()
+        }
+        val afterContentPaddingDp = with(rule.density) {
+            if (!reverseLayout) endPaddingPx.toDp() else startPaddingPx.toDp()
+        }
+        val state = PagerState()
+
+        createPager(
+            state,
+            modifier = Modifier
+                .mainAxisSize(sizeDp)
+                .crossAxisSize(sizeDp * 2),
+            pageCount = { 0 },
+            pageSize = { PageSize.Fixed(sizeDp) },
+            contentPadding = PaddingValues(
+                beforeContent = beforeContentPaddingDp,
+                afterContent = afterContentPaddingDp,
+                beforeContentCrossAxis = 2.dp,
+                afterContentCrossAxis = 2.dp
+            )
+        ) {}
+
+        rule.runOnIdle {
+            assertThat(state.layoutInfo.viewportStartOffset).isEqualTo(-startPaddingPx)
+            assertThat(state.layoutInfo.viewportEndOffset).isEqualTo(sizePx - startPaddingPx)
+            assertThat(state.layoutInfo.beforeContentPadding).isEqualTo(startPaddingPx)
+            assertThat(state.layoutInfo.afterContentPadding).isEqualTo(endPaddingPx)
+            assertThat(state.layoutInfo.viewportSize).isEqualTo(
+                if (vertical) IntSize(sizePx * 2, sizePx) else IntSize(sizePx, sizePx * 2)
+            )
+        }
+    }
+
+    @Test
+    fun viewportIsLargerThenTheContent() {
+        val reverseLayout = param.reverseLayout
+        val sizePx = 45
+        val startPaddingPx = 10
+        val endPaddingPx = 15
+        val sizeDp = with(rule.density) { sizePx.toDp() }
+        val beforeContentPaddingDp = with(rule.density) {
+            if (!reverseLayout) startPaddingPx.toDp() else endPaddingPx.toDp()
+        }
+        val afterContentPaddingDp = with(rule.density) {
+            if (!reverseLayout) endPaddingPx.toDp() else startPaddingPx.toDp()
+        }
+        val state = PagerState()
+
+        createPager(
+            state,
+            modifier = Modifier
+                .mainAxisSize(sizeDp)
+                .crossAxisSize(sizeDp * 2),
+            pageCount = { 1 },
+            pageSize = { PageSize.Fixed(sizeDp) },
+            contentPadding = PaddingValues(
+                beforeContent = beforeContentPaddingDp,
+                afterContent = afterContentPaddingDp,
+                beforeContentCrossAxis = 2.dp,
+                afterContentCrossAxis = 2.dp
+            )
+        ) {
+            Box(Modifier.size(sizeDp / 2))
+        }
+
+        rule.runOnIdle {
+            assertThat(state.layoutInfo.viewportStartOffset).isEqualTo(-startPaddingPx)
+            assertThat(state.layoutInfo.viewportEndOffset).isEqualTo(sizePx - startPaddingPx)
+            assertThat(state.layoutInfo.beforeContentPadding).isEqualTo(startPaddingPx)
+            assertThat(state.layoutInfo.afterContentPadding).isEqualTo(endPaddingPx)
+            assertThat(state.layoutInfo.viewportSize).isEqualTo(
+                if (vertical) IntSize(sizePx * 2, sizePx) else IntSize(sizePx, sizePx * 2)
+            )
+        }
+    }
+
+    @Test
+    fun reverseLayoutIsCorrect() {
+        val state = PagerState()
+
+        createPager(
+            state,
+            modifier = Modifier.requiredSize(pageSizeDp * 3.5f),
+            pageCount = { 5 },
+            pageSize = { PageSize.Fixed(pageSizeDp) }
+        ) {
+            Box(Modifier.requiredSize(pageSizeDp))
+        }
+
+        rule.runOnIdle {
+            assertThat(state.layoutInfo.reverseLayout).isEqualTo(param.reverseLayout)
+        }
+    }
+
+    @Test
+    fun orientationIsCorrect() {
+
+        val state = PagerState()
+
+        createPager(
+            state,
+            modifier = Modifier.requiredSize(pageSizeDp * 3.5f),
+            pageCount = { 5 },
+            pageSize = { PageSize.Fixed(pageSizeDp) }
+        ) {
+            Box(Modifier.requiredSize(pageSizeDp))
+        }
+
+        rule.runOnIdle {
+            assertThat(state.layoutInfo.orientation)
+                .isEqualTo(if (vertical) Orientation.Vertical else Orientation.Horizontal)
+        }
+    }
+
+    private fun PagerLayoutInfo.assertVisiblePages(
+        count: Int,
+        startIndex: Int = 0,
+        startOffset: Int = 0,
+        spacing: Int = 0,
+        pageSize: Int = pageSizePx
+    ) {
+        assertThat(this.pageSize).isEqualTo(pageSize)
+        assertThat(visiblePagesInfo.size).isEqualTo(count)
+        var currentIndex = startIndex
+        var currentOffset = startOffset
+        visiblePagesInfo.forEach {
+            assertThat(it.index).isEqualTo(currentIndex)
+            assertWithMessage("Offset of page $currentIndex").that(it.offset)
+                .isEqualTo(currentOffset)
+            currentIndex++
+            currentOffset += pageSize + spacing
+        }
+    }
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun params() = mutableListOf<ParamConfig>().apply {
+            for (orientation in TestOrientation) {
+                for (reverseLayout in TestReverseLayout) {
+                    add(ParamConfig(orientation = orientation, reverseLayout = reverseLayout))
+                }
+            }
+        }
+    }
+}
+
+@Stable
+class StableRef<T>(var value: T)
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerNestedScrollContentTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerNestedScrollContentTest.kt
index 1e6ee8d..ffa4aed 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerNestedScrollContentTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerNestedScrollContentTest.kt
@@ -46,7 +46,7 @@
 @OptIn(ExperimentalFoundationApi::class)
 @LargeTest
 @RunWith(Parameterized::class)
-internal class PagerNestedScrollContentTest(
+class PagerNestedScrollContentTest(
     config: ParamConfig
 ) : BasePagerTest(config = config) {
 
@@ -60,7 +60,7 @@
                 modifier = Modifier.fillMaxSize(),
                 contentPadding = PaddingValues(0.dp),
                 flingBehavior = ScrollableDefaults.flingBehavior(),
-                isVertical = isVertical, // scrollable content on the same direction as pager
+                isVertical = vertical, // scrollable content on the same direction as pager
                 reverseLayout = false,
                 state = rememberLazyListState(),
                 userScrollEnabled = true,
@@ -106,7 +106,7 @@
                 modifier = Modifier.fillMaxSize(),
                 contentPadding = PaddingValues(0.dp),
                 flingBehavior = ScrollableDefaults.flingBehavior(),
-                isVertical = !isVertical, // scrollable content on the cross direction of pager
+                isVertical = !vertical, // scrollable content on the cross direction of pager
                 reverseLayout = false,
                 state = rememberLazyListState(),
                 userScrollEnabled = true,
@@ -131,8 +131,8 @@
         rule.waitForIdle()
 
         // Assert
-        val mainAxisVelocity = if (isVertical) postFlingVelocity.y else postFlingVelocity.x
-        val crossAxisVelocity = if (isVertical) postFlingVelocity.x else postFlingVelocity.y
+        val mainAxisVelocity = if (vertical) postFlingVelocity.y else postFlingVelocity.x
+        val crossAxisVelocity = if (vertical) postFlingVelocity.x else postFlingVelocity.y
         assertThat(mainAxisVelocity.absoluteValue).isEqualTo(0f)
         assertThat(crossAxisVelocity.absoluteValue).isNotEqualTo(0f)
     }
@@ -148,7 +148,7 @@
                 modifier = Modifier.fillMaxSize(),
                 contentPadding = PaddingValues(0.dp),
                 flingBehavior = ScrollableDefaults.flingBehavior(),
-                isVertical = isVertical, // scrollable content on the same direction as pager
+                isVertical = vertical, // scrollable content on the same direction as pager
                 reverseLayout = false,
                 state = lazyListState,
                 userScrollEnabled = true,
@@ -198,11 +198,7 @@
     companion object {
         @JvmStatic
         @Parameterized.Parameters(name = "{0}")
-        fun params() = mutableListOf<ParamConfig>().apply {
-            for (orientation in TestOrientation) {
-                add(ParamConfig(orientation = orientation))
-            }
-        }
+        fun params() = AllOrientationsParams
     }
 }
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerOffscreenPageLimitPlacingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerOffscreenPageLimitPlacingTest.kt
index a8dbeb67..4e93b07 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerOffscreenPageLimitPlacingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerOffscreenPageLimitPlacingTest.kt
@@ -29,7 +29,7 @@
 @OptIn(ExperimentalFoundationApi::class)
 @LargeTest
 @RunWith(Parameterized::class)
-internal class PagerOffscreenPageLimitPlacingTest(
+class PagerOffscreenPageLimitPlacingTest(
     val config: ParamConfig
 ) : BasePagerTest(config) {
 
@@ -67,8 +67,8 @@
 
         // Act
         createPager(state = state, modifier = Modifier.fillMaxSize(), offscreenPageLimit = 2)
-        val firstVisible = state.layoutInfo.visibleItemsInfo.first().index
-        val lastVisible = state.layoutInfo.visibleItemsInfo.last().index
+        val firstVisible = state.layoutInfo.visiblePagesInfo.first().index
+        val lastVisible = state.layoutInfo.visiblePagesInfo.last().index
         // Assert
         rule.runOnIdle {
             Truth.assertThat(placed).contains(firstVisible - 2)
@@ -92,8 +92,8 @@
 
         // Assert
         rule.waitForIdle()
-        val firstVisible = state.layoutInfo.visibleItemsInfo.first().index
-        val lastVisible = state.layoutInfo.visibleItemsInfo.last().index
+        val firstVisible = state.layoutInfo.visiblePagesInfo.first().index
+        val lastVisible = state.layoutInfo.visiblePagesInfo.last().index
         Truth.assertThat(placed).doesNotContain(firstVisible - 1)
         Truth.assertThat(placed).contains(5)
         Truth.assertThat(placed).doesNotContain(lastVisible + 1)
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerPinnableContainerTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerPinnableContainerTest.kt
new file mode 100644
index 0000000..a56661e
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerPinnableContainerTest.kt
@@ -0,0 +1,655 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.pager
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.list.assertIsNotPlaced
+import androidx.compose.foundation.lazy.list.assertIsPlaced
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.LocalPinnableContainer
+import androidx.compose.ui.layout.PinnableContainer
+import androidx.compose.ui.layout.PinnableContainer.PinnedHandle
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+
+@OptIn(ExperimentalFoundationApi::class)
+@MediumTest
+class PagerPinnableContainerTest :
+    SingleOrientationPagerTest(orientation = Orientation.Horizontal) {
+
+    private var pinnableContainer: PinnableContainer? = null
+
+    private var pageSizeDp = Dp.Unspecified
+
+    private val composed = mutableSetOf<Int>()
+
+    @Before
+    fun setup() {
+        pageSizeDp = with(rule.density) { 10.toDp() }
+    }
+
+    @Composable
+    fun PageWithEffect(index: Int) {
+        Box(
+            Modifier
+                .size(pageSizeDp)
+                .padding(2.dp)
+                .background(Color.Black)
+                .testTag("$index")
+        )
+        DisposableEffect(index) {
+            composed.add(index)
+            onDispose {
+                composed.remove(index)
+            }
+        }
+    }
+
+    @Test
+    fun pinnedPageIsComposedAndPlacedWhenScrolledOut() {
+        val state = PagerState()
+        // Arrange.
+        rule.setContent {
+            HorizontalOrVerticalPager(
+                state = state,
+                modifier = Modifier.size(pageSizeDp * 2),
+                pageCount = 100,
+                pageSize = PageSize.Fixed(pageSizeDp)
+            ) { page ->
+                if (page == 1) {
+                    pinnableContainer = LocalPinnableContainer.current
+                }
+                PageWithEffect(page)
+            }
+        }
+
+        rule.runOnIdle {
+            requireNotNull(pinnableContainer).pin()
+        }
+
+        rule.runOnIdle {
+            assertThat(composed).contains(1)
+            runBlocking {
+                state.scrollToPage(3)
+            }
+        }
+
+        rule.waitUntil {
+            // not visible pages were disposed
+            !composed.contains(0)
+        }
+
+        rule.runOnIdle {
+            // page 1 is still pinned
+            assertThat(composed).contains(1)
+        }
+
+        rule.onNodeWithTag("1")
+            .assertExists()
+            .assertIsNotDisplayed()
+            .assertIsPlaced()
+    }
+
+    @Test
+    fun pagesBetweenPinnedAndCurrentVisibleAreNotComposed() {
+        val state = PagerState()
+        // Arrange.
+        rule.setContent {
+            HorizontalOrVerticalPager(
+                state = state,
+                modifier = Modifier.size(pageSizeDp * 2),
+                pageCount = 100,
+                pageSize = PageSize.Fixed(pageSizeDp)
+            ) { page ->
+                if (page == 1) {
+                    pinnableContainer = LocalPinnableContainer.current
+                }
+                PageWithEffect(page)
+            }
+        }
+
+        rule.runOnIdle {
+            requireNotNull(pinnableContainer).pin()
+        }
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollToPage(4)
+            }
+        }
+
+        rule.waitUntil {
+            // not visible pages were disposed
+            !composed.contains(0)
+        }
+
+        rule.runOnIdle {
+            assertThat(composed).doesNotContain(0)
+            assertThat(composed).contains(1)
+            assertThat(composed).doesNotContain(2)
+            assertThat(composed).doesNotContain(3)
+            assertThat(composed).contains(4)
+        }
+    }
+
+    @Test
+    fun pinnedPageAfterVisibleOnesIsComposedAndPlacedWhenScrolledOut() {
+        val state = PagerState()
+        // Arrange.
+        rule.setContent {
+            HorizontalOrVerticalPager(
+                state = state,
+                modifier = Modifier.size(pageSizeDp * 2),
+                pageCount = 100,
+                pageSize = PageSize.Fixed(pageSizeDp)
+            ) { page ->
+                if (page == 4) {
+                    pinnableContainer = LocalPinnableContainer.current
+                }
+                PageWithEffect(page)
+            }
+        }
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollToPage(4)
+            }
+        }
+
+        rule.waitUntil {
+            // wait for not visible pages to be disposed
+            !composed.contains(1)
+        }
+
+        rule.runOnIdle {
+            requireNotNull(pinnableContainer).pin()
+            assertThat(composed).contains(5)
+        }
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollToPage(0)
+            }
+        }
+
+        rule.waitUntil {
+            // wait for not visible pages to be disposed
+            !composed.contains(5)
+        }
+
+        rule.runOnIdle {
+            assertThat(composed).contains(0)
+            assertThat(composed).contains(1)
+            assertThat(composed).doesNotContain(2)
+            assertThat(composed).doesNotContain(3)
+            assertThat(composed).contains(4)
+            assertThat(composed).doesNotContain(5)
+        }
+    }
+
+    @Test
+    fun pinnedPageCanBeUnpinned() {
+        val state = PagerState()
+        // Arrange.
+        rule.setContent {
+            HorizontalOrVerticalPager(
+                state = state,
+                modifier = Modifier.size(pageSizeDp * 2),
+                pageCount = 100,
+                pageSize = PageSize.Fixed(pageSizeDp)
+            ) { page ->
+                if (page == 1) {
+                    pinnableContainer = LocalPinnableContainer.current
+                }
+                PageWithEffect(page)
+            }
+        }
+
+        val handle = rule.runOnIdle {
+            requireNotNull(pinnableContainer).pin()
+        }
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollToPage(3)
+            }
+        }
+
+        rule.waitUntil {
+            // wait for not visible pages to be disposed
+            !composed.contains(0)
+        }
+
+        rule.runOnIdle {
+            handle.release()
+        }
+
+        rule.waitUntil {
+            // wait for unpinned page to be disposed
+            !composed.contains(1)
+        }
+
+        rule.onNodeWithTag("1")
+            .assertIsNotPlaced()
+    }
+
+    @Test
+    fun pinnedPageIsStillPinnedWhenReorderedAndNotVisibleAnymore() {
+        val state = PagerState()
+        var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
+        // Arrange.
+        rule.setContent {
+            Pager(state, list, 2, 3)
+        }
+
+        rule.runOnIdle {
+            assertThat(composed).containsExactly(0, 1, 2)
+            requireNotNull(pinnableContainer).pin()
+        }
+
+        rule.runOnIdle {
+            list = listOf(0, 3, 4, 1, 2)
+        }
+
+        rule.waitUntil {
+            // wait for not visible page to be disposed
+            !composed.contains(1)
+        }
+
+        rule.runOnIdle {
+            assertThat(composed).containsExactly(0, 3, 4, 2) // 2 is pinned
+        }
+
+        rule.onNodeWithTag("2")
+            .assertIsPlaced()
+    }
+
+    @Composable
+    fun Pager(state: PagerState, dataset: List<Int>, pinnedPage: Int, visiblePages: Int) {
+        HorizontalOrVerticalPager(
+            state = state,
+            modifier = Modifier.mainAxisSize(pageSizeDp * visiblePages),
+            pageCount = dataset.size,
+            pageSize = PageSize.Fixed(pageSizeDp),
+            key = { dataset[it] }
+        ) { page ->
+            if (dataset[page] == pinnedPage) {
+                pinnableContainer = LocalPinnableContainer.current
+            }
+            PageWithEffect(dataset[page])
+        }
+    }
+
+    @Test
+    fun unpinnedWhenPagerStateChanges() {
+        var state by mutableStateOf(PagerState(initialPage = 2))
+        // Arrange.
+        rule.setContent {
+            HorizontalOrVerticalPager(
+                state = state,
+                modifier = Modifier.size(pageSizeDp * 2),
+                pageCount = 100,
+                pageSize = PageSize.Fixed(pageSizeDp)
+            ) { page ->
+                if (page == 2) {
+                    pinnableContainer = LocalPinnableContainer.current
+                }
+                PageWithEffect(page)
+            }
+        }
+
+        rule.runOnIdle {
+            requireNotNull(pinnableContainer).pin()
+        }
+
+        rule.runOnIdle {
+            assertThat(composed).contains(3)
+            runBlocking {
+                state.scrollToPage(0)
+            }
+        }
+
+        rule.waitUntil {
+            // wait for not visible page to be disposed
+            !composed.contains(3)
+        }
+
+        rule.runOnIdle {
+            assertThat(composed).contains(2)
+            state = PagerState()
+        }
+
+        rule.waitUntil {
+            // wait for pinned page to be disposed
+            !composed.contains(2)
+        }
+
+        rule.onNodeWithTag("2")
+            .assertIsNotPlaced()
+    }
+
+    @Test
+    fun pinAfterPagerStateChange() {
+        var state by mutableStateOf(PagerState())
+        // Arrange.
+        rule.setContent {
+            HorizontalOrVerticalPager(
+                state = state,
+                modifier = Modifier.size(pageSizeDp * 2),
+                pageCount = 100,
+                pageSize = PageSize.Fixed(pageSizeDp)
+            ) { page ->
+                if (page == 0) {
+                    pinnableContainer = LocalPinnableContainer.current
+                }
+                PageWithEffect(page)
+            }
+        }
+
+        rule.runOnIdle {
+            state = PagerState()
+        }
+
+        rule.runOnIdle {
+            requireNotNull(pinnableContainer).pin()
+        }
+
+        rule.runOnIdle {
+            assertThat(composed).contains(1)
+            runBlocking {
+                state.scrollToPage(2)
+            }
+        }
+
+        rule.waitUntil {
+            // wait for not visible page to be disposed
+            !composed.contains(1)
+        }
+
+        rule.runOnIdle {
+            assertThat(composed).contains(0)
+        }
+    }
+
+    @Test
+    fun pagesArePinnedBasedOnGlobalIndexes() {
+        val state = PagerState(initialPage = 3)
+        // Arrange.
+        rule.setContent {
+            HorizontalOrVerticalPager(
+                state = state,
+                modifier = Modifier.size(pageSizeDp * 2),
+                pageCount = 100,
+                pageSize = PageSize.Fixed(pageSizeDp)
+            ) { page ->
+                if (page == 3) {
+                    pinnableContainer = LocalPinnableContainer.current
+                }
+                PageWithEffect(page)
+            }
+        }
+
+        rule.runOnIdle {
+            requireNotNull(pinnableContainer).pin()
+        }
+
+        rule.runOnIdle {
+            assertThat(composed).contains(4)
+            runBlocking {
+                state.scrollToPage(6)
+            }
+        }
+
+        rule.waitUntil {
+            // wait for not visible page to be disposed
+            !composed.contains(4)
+        }
+
+        rule.runOnIdle {
+            assertThat(composed).contains(3)
+        }
+
+        rule.onNodeWithTag("3")
+            .assertExists()
+            .assertIsNotDisplayed()
+            .assertIsPlaced()
+    }
+
+    @Test
+    fun pinnedPageIsRemovedWhenNotVisible() {
+        val state = PagerState(initialPage = 3)
+        var pageCount by mutableStateOf(10)
+        // Arrange.
+        rule.setContent {
+            HorizontalOrVerticalPager(
+                state = state,
+                modifier = Modifier.size(pageSizeDp * 2),
+                pageCount = pageCount,
+                pageSize = PageSize.Fixed(pageSizeDp)
+            ) { page ->
+                if (page == 3) {
+                    pinnableContainer = LocalPinnableContainer.current
+                }
+                PageWithEffect(page)
+            }
+        }
+
+        rule.runOnIdle {
+            requireNotNull(pinnableContainer).pin()
+            assertThat(composed).contains(4)
+            runBlocking {
+                state.scrollToPage(0)
+            }
+        }
+
+        rule.waitUntil {
+            // wait for not visible page to be disposed
+            !composed.contains(4)
+        }
+
+        rule.runOnIdle {
+            pageCount = 3
+        }
+
+        rule.waitUntil {
+            // wait for pinned page to be disposed
+            !composed.contains(3)
+        }
+
+        rule.onNodeWithTag("3")
+            .assertIsNotPlaced()
+    }
+
+    @Test
+    fun pinnedPageIsRemovedWhenVisible() {
+        val state = PagerState()
+        var pages by mutableStateOf(listOf(0, 1, 2))
+        // Arrange.
+        rule.setContent {
+            Pager(state = state, dataset = pages, pinnedPage = 1, visiblePages = 2)
+        }
+
+        rule.runOnIdle {
+            requireNotNull(pinnableContainer).pin()
+        }
+
+        rule.runOnIdle {
+            pages = listOf(0, 2)
+        }
+
+        rule.waitUntil {
+            // wait for pinned page to be disposed
+            !composed.contains(1)
+        }
+
+        rule.onNodeWithTag("1")
+            .assertIsNotPlaced()
+    }
+
+    @Test
+    fun pinnedMultipleTimes() {
+        val state = PagerState(0)
+        // Arrange.
+        rule.setContent {
+            HorizontalOrVerticalPager(
+                state = state,
+                modifier = Modifier.size(pageSizeDp * 2),
+                pageCount = 100,
+                pageSize = PageSize.Fixed(pageSizeDp)
+            ) { page ->
+                if (page == 1) {
+                    pinnableContainer = LocalPinnableContainer.current
+                }
+                PageWithEffect(page)
+            }
+        }
+
+        val handles = mutableListOf<PinnedHandle>()
+        rule.runOnIdle {
+            handles.add(requireNotNull(pinnableContainer).pin())
+            handles.add(requireNotNull(pinnableContainer).pin())
+        }
+
+        rule.runOnIdle {
+            // pinned 3 times in total
+            handles.add(requireNotNull(pinnableContainer).pin())
+            assertThat(composed).contains(0)
+            runBlocking {
+                state.scrollToPage(3)
+            }
+        }
+
+        rule.waitUntil {
+            // wait for not visible page to be disposed
+            !composed.contains(0)
+        }
+
+        while (handles.isNotEmpty()) {
+            rule.runOnIdle {
+                assertThat(composed).contains(1)
+                handles.removeFirst().release()
+            }
+        }
+
+        rule.waitUntil {
+            // wait for pinned page to be disposed
+            !composed.contains(1)
+        }
+    }
+
+    @Test
+    fun pinningIsPropagatedToParentContainer() {
+        var parentPinned = false
+        val parentContainer = object : PinnableContainer {
+            override fun pin(): PinnedHandle {
+                parentPinned = true
+                return PinnedHandle { parentPinned = false }
+            }
+        }
+        // Arrange.
+        rule.setContent {
+            CompositionLocalProvider(LocalPinnableContainer provides parentContainer) {
+                HorizontalOrVerticalPager(
+                    pageCount = 1,
+                    pageSize = PageSize.Fixed(pageSizeDp)
+                ) {
+                    pinnableContainer = LocalPinnableContainer.current
+                    Box(Modifier.size(pageSizeDp))
+                }
+            }
+        }
+
+        val handle = rule.runOnIdle {
+            requireNotNull(pinnableContainer).pin()
+        }
+
+        rule.runOnIdle {
+            assertThat(parentPinned).isTrue()
+            handle.release()
+        }
+
+        rule.runOnIdle {
+            assertThat(parentPinned).isFalse()
+        }
+    }
+
+    @Test
+    fun parentContainerChange_pinningIsMaintained() {
+        var parent1Pinned = false
+        val parent1Container = object : PinnableContainer {
+            override fun pin(): PinnedHandle {
+                parent1Pinned = true
+                return PinnedHandle { parent1Pinned = false }
+            }
+        }
+        var parent2Pinned = false
+        val parent2Container = object : PinnableContainer {
+            override fun pin(): PinnedHandle {
+                parent2Pinned = true
+                return PinnedHandle { parent2Pinned = false }
+            }
+        }
+        var parentContainer by mutableStateOf<PinnableContainer>(parent1Container)
+        // Arrange.
+        rule.setContent {
+            CompositionLocalProvider(LocalPinnableContainer provides parentContainer) {
+                HorizontalOrVerticalPager(
+                    pageCount = 1,
+                    pageSize = PageSize.Fixed(pageSizeDp)
+                ) {
+                    pinnableContainer = LocalPinnableContainer.current
+                    Box(Modifier.size(pageSizeDp))
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            requireNotNull(pinnableContainer).pin()
+        }
+
+        rule.runOnIdle {
+            assertThat(parent1Pinned).isTrue()
+            assertThat(parent2Pinned).isFalse()
+            parentContainer = parent2Container
+        }
+
+        rule.runOnIdle {
+            assertThat(parent1Pinned).isFalse()
+            assertThat(parent2Pinned).isTrue()
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerPrefetcherTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerPrefetcherTest.kt
new file mode 100644
index 0000000..cedcfd6
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerPrefetcherTest.kt
@@ -0,0 +1,498 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.pager
+
+import androidx.compose.foundation.AutoTestFrameClock
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.foundation.gestures.scrollBy
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.Remeasurement
+import androidx.compose.ui.layout.RemeasurementModifier
+import androidx.compose.ui.layout.SubcomposeLayout
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@OptIn(ExperimentalFoundationApi::class)
+@LargeTest
+@RunWith(Parameterized::class)
+class PagerPrefetcherTest(
+    private val paramConfig: ParamConfig
+) : BasePagerTest(paramConfig) {
+
+    var pageSizePx = 30
+    val pageSizeDp = with(rule.density) { pageSizePx.toDp() }
+    lateinit var state: PagerState
+
+    @Test
+    fun notPrefetchingForwardInitially() {
+        composePager()
+
+        rule.onNodeWithTag("${paramConfig.beyondBoundsPageCount + 2}")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun notPrefetchingBackwardInitially() {
+        composePager(initialPage = 2)
+
+        rule.onNodeWithTag("0")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun prefetchingForwardAfterSmallScroll() {
+        composePager()
+        val preFetchIndex = 2
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(5f)
+            }
+        }
+
+        waitForPrefetch(preFetchIndex)
+
+        rule.onNodeWithTag("$preFetchIndex")
+            .assertExists()
+        rule.onNodeWithTag("${paramConfig.beyondBoundsPageCount + preFetchIndex + 1}")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun prefetchingBackwardAfterSmallScroll() {
+        composePager(initialPage = 2, initialPageOffsetFraction = 10 / pageSizePx.toFloat())
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(-5f)
+            }
+        }
+
+        waitForPrefetch(1)
+
+        rule.onNodeWithTag("1")
+            .assertExists()
+        rule.onNodeWithTag("0")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun prefetchingForwardAndBackward() {
+        val initialIndex = 5
+        composePager(initialPage = initialIndex)
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(5f)
+            }
+        }
+        var prefetchIndex = initialIndex + 2
+        waitForPrefetch(prefetchIndex)
+
+        rule.onNodeWithTag("$prefetchIndex")
+            .assertExists()
+        rule.onNodeWithTag("${prefetchIndex - paramConfig.beyondBoundsPageCount - 3}")
+            .assertDoesNotExist()
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(-2f)
+                state.scrollBy(-1f)
+            }
+        }
+
+        prefetchIndex -= 3
+        waitForPrefetch(prefetchIndex)
+
+        rule.onNodeWithTag("$prefetchIndex")
+            .assertExists()
+        rule.onNodeWithTag("${prefetchIndex + paramConfig.beyondBoundsPageCount + 3}")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun prefetchingForwardTwice() {
+        composePager()
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(5f)
+            }
+        }
+
+        waitForPrefetch(2)
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(pageSizePx / 2f)
+                state.scrollBy(pageSizePx / 2f)
+            }
+        }
+
+        val prefetchIndex = 3
+
+        waitForPrefetch(prefetchIndex)
+
+        rule.onNodeWithTag("${prefetchIndex - 1}")
+            .assertIsDisplayed()
+        rule.onNodeWithTag("$prefetchIndex")
+            .assertExists()
+        rule.onNodeWithTag("${prefetchIndex + paramConfig.beyondBoundsPageCount + 1}")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun prefetchingBackwardTwice() {
+        composePager(initialPage = 4)
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(-5f)
+            }
+        }
+
+        waitForPrefetch(2)
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(-pageSizePx / 2f)
+                state.scrollBy(-pageSizePx / 2f)
+            }
+        }
+
+        waitForPrefetch(1)
+
+        rule.onNodeWithTag("2")
+            .assertIsDisplayed()
+        rule.onNodeWithTag("1")
+            .assertExists()
+        rule.onNodeWithTag("0")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun prefetchingForwardAndBackwardReverseLayout() {
+        val initialIndex = 5
+        composePager(initialPage = initialIndex, reverseLayout = true)
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(5f)
+            }
+        }
+
+        var prefetchIndex = initialIndex + 2
+
+        waitForPrefetch(prefetchIndex)
+
+        rule.onNodeWithTag("$prefetchIndex")
+            .assertExists()
+        rule.onNodeWithTag("${prefetchIndex - paramConfig.beyondBoundsPageCount - 3}")
+            .assertDoesNotExist()
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(-2f)
+                state.scrollBy(-1f)
+            }
+        }
+
+        prefetchIndex -= 3
+        waitForPrefetch(prefetchIndex)
+
+        rule.onNodeWithTag("$prefetchIndex")
+            .assertExists()
+        rule.onNodeWithTag("${prefetchIndex + paramConfig.beyondBoundsPageCount + 3}")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun prefetchingForwardAndBackwardWithContentPadding() {
+        val halfItemSize = pageSizeDp / 2f
+        val initialIndex = 5
+        composePager(
+            initialPage = initialIndex,
+            initialPageOffsetFraction = 5 / pageSizePx.toFloat(),
+            contentPadding = PaddingValues(mainAxis = halfItemSize)
+        )
+
+        rule.onNodeWithTag("${initialIndex - 1}")
+            .assertIsDisplayed()
+        rule.onNodeWithTag("$initialIndex")
+            .assertIsDisplayed()
+        rule.onNodeWithTag("${initialIndex + 1}")
+            .assertIsDisplayed()
+        rule.onNodeWithTag("${initialIndex - paramConfig.beyondBoundsPageCount - 2}")
+            .assertDoesNotExist()
+        rule.onNodeWithTag("${initialIndex + paramConfig.beyondBoundsPageCount + 2}")
+            .assertDoesNotExist()
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(5f)
+            }
+        }
+
+        var prefetchIndex = initialIndex + 1
+        waitForPrefetch(prefetchIndex)
+
+        rule.onNodeWithTag("${prefetchIndex + 1}")
+            .assertExists()
+        rule.onNodeWithTag("${prefetchIndex - paramConfig.beyondBoundsPageCount - 3}")
+            .assertDoesNotExist()
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(-2f)
+            }
+        }
+
+        prefetchIndex -= 3
+        waitForPrefetch(prefetchIndex)
+
+        rule.onNodeWithTag("$prefetchIndex")
+            .assertExists()
+    }
+
+    @Test
+    fun disposingWhilePrefetchingScheduled() {
+        var emit = true
+        lateinit var remeasure: Remeasurement
+        rule.setContent {
+            SubcomposeLayout(
+                modifier = object : RemeasurementModifier {
+                    override fun onRemeasurementAvailable(remeasurement: Remeasurement) {
+                        remeasure = remeasurement
+                    }
+                }
+            ) { constraints ->
+                val placeable = if (emit) {
+                    subcompose(Unit) {
+                        state = rememberPagerState()
+                        HorizontalOrVerticalPager(
+                            modifier = Modifier.mainAxisSize(pageSizeDp * 1.5f),
+                            state = state,
+                            pageCount = 1000
+                        ) {
+                            Spacer(
+                                Modifier
+                                    .mainAxisSize(pageSizeDp)
+                                    .then(
+                                        if (vertical)
+                                            Modifier.fillMaxWidth()
+                                        else
+                                            Modifier.fillMaxHeight()
+                                    )
+                            )
+                        }
+                    }.first().measure(constraints)
+                } else {
+                    null
+                }
+                layout(constraints.maxWidth, constraints.maxHeight) {
+                    placeable?.place(0, 0)
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            // this will schedule the prefetching
+            runBlocking(AutoTestFrameClock()) {
+                state.scrollBy(pageSize.toFloat())
+            }
+            // then we synchronously dispose LazyColumn
+            emit = false
+            remeasure.forceRemeasure()
+        }
+
+        rule.waitForIdle()
+    }
+
+    @Test
+    fun snappingToOtherPositionWhilePrefetchIsScheduled() {
+        val composedItems = mutableListOf<Int>()
+        rule.setContent {
+            state = rememberPagerState()
+            HorizontalOrVerticalPager(
+                modifier = Modifier.mainAxisSize(pageSizeDp * 1.5f),
+                state = state,
+                pageCount = 1000
+            ) {
+                composedItems.add(it)
+                Spacer(
+                    Modifier
+                        .mainAxisSize(pageSizeDp)
+                        .then(
+                            if (vertical)
+                                Modifier.fillMaxWidth()
+                            else
+                                Modifier.fillMaxHeight()
+                        )
+                )
+            }
+        }
+
+        rule.runOnIdle {
+            // now we have pages 0 and 1 visible
+            runBlocking(AutoTestFrameClock()) {
+                // this will move the viewport so pages 1 and 2 are visible
+                // and schedule a prefetching for 3
+                state.scrollBy(pageSize.toFloat())
+                // then we move so that pages 100 and 101 are visible.
+                // this should cancel the prefetch for 3
+                state.scrollToPage(100)
+            }
+        }
+
+        // wait a few frames to make sure prefetch happens if was scheduled
+        rule.waitForIdle()
+        rule.waitForIdle()
+        rule.waitForIdle()
+
+        rule.runOnIdle {
+            assertThat(composedItems).doesNotContain(3)
+        }
+    }
+
+    @Test
+    fun scrollingByListSizeCancelsPreviousPrefetch() {
+        composePager()
+
+        // now we have pages 0-1 visible
+        rule.runOnIdle {
+            runBlocking(AutoTestFrameClock()) {
+                // this will move the viewport so pages 1-2 are visible
+                // and schedule a prefetching for 3
+                state.scrollBy(pageSizePx.toFloat())
+
+                // move viewport by screen size to pages 4-5, so page 3 is just behind
+                // the first visible page
+                state.scrollBy(pageSizePx * 3f)
+
+                // move scroll further to pages 5-6, so page 3 is reused
+                state.scrollBy(pageSizePx.toFloat())
+            }
+        }
+
+        waitForPrefetch(7)
+
+        rule.runOnIdle {
+            runBlocking(AutoTestFrameClock()) {
+                // scroll again to ensure page 3 was dropped
+                state.scrollBy(pageSizePx * 100f)
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(activeNodes).doesNotContain(3)
+        }
+    }
+
+    private suspend fun PagerState.scrollBy(delta: Float): Float {
+        val consumed = (this as ScrollableState).scrollBy(delta)
+        scroll { } // cancel fling animation
+        return consumed
+    }
+
+    private fun waitForPrefetch(index: Int) {
+        rule.waitUntil {
+            activeNodes.contains(index) && activeMeasuredNodes.contains(index)
+        }
+    }
+
+    private val activeNodes = mutableSetOf<Int>()
+    private val activeMeasuredNodes = mutableSetOf<Int>()
+
+    private fun composePager(
+        initialPage: Int = 0,
+        initialPageOffsetFraction: Float = 0f,
+        reverseLayout: Boolean = false,
+        contentPadding: PaddingValues = PaddingValues(0.dp)
+    ) {
+        state = PagerState(
+            initialPage = initialPage,
+            initialPageOffsetFraction = initialPageOffsetFraction
+        )
+        createPager(
+            state = state,
+            modifier = Modifier.mainAxisSize(pageSizeDp * 1.5f),
+            reverseLayout = reverseLayout,
+            contentPadding = contentPadding,
+            offscreenPageLimit = paramConfig.beyondBoundsPageCount,
+            pageCount = { 100 },
+            pageSize = {
+                object : PageSize {
+                    override fun Density.calculateMainAxisPageSize(
+                        availableSpace: Int,
+                        pageSpacing: Int
+                    ): Int {
+                        return pageSizePx
+                    }
+                }
+            }
+        ) {
+            DisposableEffect(it) {
+                activeNodes.add(it)
+                onDispose {
+                    activeNodes.remove(it)
+                    activeMeasuredNodes.remove(it)
+                }
+            }
+
+            Spacer(
+                Modifier
+                    .mainAxisSize(pageSizeDp)
+                    .fillMaxCrossAxis()
+                    .testTag("$it")
+                    .layout { measurable, constraints ->
+                        val placeable = measurable.measure(constraints)
+                        activeMeasuredNodes.add(it)
+                        layout(placeable.width, placeable.height) {
+                            placeable.place(0, 0)
+                        }
+                    }
+            )
+        }
+    }
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun params(): Array<Any> = arrayOf(
+            ParamConfig(Orientation.Vertical, beyondBoundsPageCount = 0),
+            ParamConfig(Orientation.Vertical, beyondBoundsPageCount = 1),
+            ParamConfig(Orientation.Horizontal, beyondBoundsPageCount = 0),
+            ParamConfig(Orientation.Horizontal, beyondBoundsPageCount = 1)
+        )
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerScrollingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerScrollingTest.kt
index 6d85e72..14eabb4 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerScrollingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerScrollingTest.kt
@@ -34,7 +34,7 @@
 @OptIn(ExperimentalFoundationApi::class)
 @LargeTest
 @RunWith(Parameterized::class)
-internal class PagerScrollingTest(
+class PagerScrollingTest(
     val config: ParamConfig
 ) : BasePagerTest(config) {
 
@@ -179,7 +179,7 @@
         var initialPage = 1
         val state = PagerState(initialPage)
         createPager(
-            pageSize = PageSize.Fixed(200.dp),
+            pageSize = { PageSize.Fixed(200.dp) },
             state = state,
             modifier = Modifier.fillMaxSize(),
             pageCount = { 100 },
@@ -229,7 +229,7 @@
         var initialPage = 90
         val state = PagerState(initialPage)
         createPager(
-            pageSize = PageSize.Fixed(200.dp),
+            pageSize = { PageSize.Fixed(200.dp) },
             state = state,
             modifier = Modifier.fillMaxSize(),
             pageCount = { 100 },
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateTest.kt
index 8aa3c7a..0483c28 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateTest.kt
@@ -34,6 +34,7 @@
 import androidx.test.filters.SdkSuppress
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.assertFalse
+import kotlin.test.assertTrue
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
@@ -45,7 +46,7 @@
 @OptIn(ExperimentalFoundationApi::class)
 @LargeTest
 @RunWith(Parameterized::class)
-internal class PagerStateTest(val config: ParamConfig) : BasePagerTest(config) {
+class PagerStateTest(val config: ParamConfig) : BasePagerTest(config) {
 
     @Test
     fun pagerStateNotAttached_shouldReturnDefaultValues_andChangeAfterAttached() = runBlocking {
@@ -262,7 +263,7 @@
     @Test
     fun scrollToPage_usingLaunchedEffect() {
         val state = PagerState()
-        createPager(state, effects = {
+        createPager(state, additionalContent = {
             LaunchedEffect(state) {
                 state.scrollToPage(10)
             }
@@ -275,7 +276,7 @@
     @Test
     fun scrollToPageWithOffset_usingLaunchedEffect() {
         val state = PagerState()
-        createPager(state, effects = {
+        createPager(state, additionalContent = {
             LaunchedEffect(state) {
                 state.scrollToPage(10, 0.4f)
             }
@@ -332,7 +333,7 @@
     @Test
     fun animatedScrollToPage_usingLaunchedEffect() {
         val state = PagerState()
-        createPager(state, effects = {
+        createPager(state, additionalContent = {
             LaunchedEffect(state) {
                 state.animateScrollToPage(10)
             }
@@ -345,7 +346,7 @@
     @Test
     fun animatedScrollToPageWithOffset_usingLaunchedEffect() {
         val state = PagerState()
-        createPager(state, effects = {
+        createPager(state, additionalContent = {
             LaunchedEffect(state) {
                 state.animateScrollToPage(10, 0.4f)
             }
@@ -358,7 +359,7 @@
     @Test
     fun animatedScrollToPage_viewPortNumberOfPages_usingLaunchedEffect_shouldNotPlaceALlPages() {
         val state = PagerState()
-        createPager(state, effects = {
+        createPager(state, additionalContent = {
             LaunchedEffect(state) {
                 state.animateScrollToPage(DefaultPageCount - 1)
             }
@@ -386,7 +387,7 @@
         val firstDelta = (pagerSize * 0.4f) * scrollForwardSign
         onPager().performTouchInput {
             down(layoutStart)
-            if (isVertical) {
+            if (vertical) {
                 moveBy(Offset(0f, firstDelta))
             } else {
                 moveBy(Offset(firstDelta, 0f))
@@ -412,7 +413,7 @@
         // Act
         onPager().performTouchInput {
             down(layoutStart)
-            if (isVertical) {
+            if (vertical) {
                 moveBy(Offset(0f, secondDelta))
             } else {
                 moveBy(Offset(secondDelta, 0f))
@@ -464,7 +465,7 @@
         // Act
         // Moving more than threshold
         val backwardDelta = scrollForwardSign.toFloat() * with(rule.density) {
-                -DefaultPositionThreshold.toPx() / 2
+            -DefaultPositionThreshold.toPx() / 2
         }
 
         previousTargetPage = state.targetPage
@@ -548,7 +549,7 @@
             swipeWithVelocityAcrossMainAxis(20000f, forwardDelta)
         }
         rule.mainClock.advanceTimeUntil { state.targetPage != previousTarget }
-        var flingOriginIndex = state.firstVisiblePage?.index ?: 0
+        var flingOriginIndex = state.firstVisiblePageInfo?.index ?: 0
         // Assert
         assertThat(state.targetPage).isEqualTo(flingOriginIndex + 3)
         assertThat(state.targetPage).isNotEqualTo(state.currentPage)
@@ -566,7 +567,7 @@
         rule.mainClock.advanceTimeUntil { state.targetPage != previousTarget }
 
         // Assert
-        flingOriginIndex = (state.firstVisiblePage?.index ?: 0) + 1
+        flingOriginIndex = (state.firstVisiblePageInfo?.index ?: 0) + 1
         assertThat(state.targetPage).isEqualTo(flingOriginIndex - 3)
         assertThat(state.targetPage).isNotEqualTo(state.currentPage)
 
@@ -622,6 +623,89 @@
     }
 
     @Test
+    fun settledPage_onAnimationScroll_shouldChangeOnScrollFinishedOnly() {
+        // Arrange
+        val state = PagerState()
+        var settledPageChanges = 0
+        createPager(
+            state = state,
+            modifier = Modifier.fillMaxSize(),
+            additionalContent = {
+                LaunchedEffect(key1 = state.settledPage) {
+                    settledPageChanges++
+                }
+            }
+        )
+
+        // Settle page changed once for first composition
+        rule.runOnIdle {
+            assertThat(state.settledPage).isEqualTo(state.currentPage)
+            assertTrue { settledPageChanges == 1 }
+        }
+
+        settledPageChanges = 0
+        val previousSettled = state.settledPage
+        rule.mainClock.autoAdvance = false
+        // Act
+        // Moving forward
+        rule.runOnIdle {
+            scope.launch {
+                state.animateScrollToPage(DefaultPageCount - 1)
+            }
+        }
+
+        // Settled page shouldn't change whilst scroll is in progress.
+        assertTrue { state.isScrollInProgress }
+        assertTrue { settledPageChanges == 0 }
+        assertThat(state.settledPage).isEqualTo(previousSettled)
+
+        rule.mainClock.advanceTimeUntil { settledPageChanges != 0 }
+
+        rule.runOnIdle {
+            assertTrue { !state.isScrollInProgress }
+            assertThat(state.settledPage).isEqualTo(state.currentPage)
+        }
+    }
+
+    @Test
+    fun settledPage_onGestureScroll_shouldChangeOnScrollFinishedOnly() {
+        // Arrange
+        val state = PagerState()
+        var settledPageChanges = 0
+        createPager(
+            state = state,
+            modifier = Modifier.fillMaxSize(),
+            additionalContent = {
+                LaunchedEffect(key1 = state.settledPage) {
+                    settledPageChanges++
+                }
+            }
+        )
+
+        settledPageChanges = 0
+        val previousSettled = state.settledPage
+        rule.mainClock.autoAdvance = false
+        // Act
+        // Moving forward
+        val forwardDelta = pagerSize / 2f * scrollForwardSign.toFloat()
+        rule.onNodeWithTag(PagerTestTag).performTouchInput {
+            swipeWithVelocityAcrossMainAxis(10000f, forwardDelta)
+        }
+
+        // Settled page shouldn't change whilst scroll is in progress.
+        assertTrue { state.isScrollInProgress }
+        assertTrue { settledPageChanges == 0 }
+        assertThat(state.settledPage).isEqualTo(previousSettled)
+
+        rule.mainClock.advanceTimeUntil { settledPageChanges != 0 }
+
+        rule.runOnIdle {
+            assertTrue { !state.isScrollInProgress }
+            assertThat(state.settledPage).isEqualTo(state.currentPage)
+        }
+    }
+
+    @Test
     fun currentPageOffset_shouldReflectScrollingOfCurrentPage() {
         // Arrange
         val state = PagerState(DefaultPageCount / 2)
@@ -636,7 +720,7 @@
         // Moving forward
         onPager().performTouchInput {
             down(layoutStart)
-            if (isVertical) {
+            if (vertical) {
                 moveBy(Offset(0f, scrollForwardSign * pagerSize / 4f))
             } else {
                 moveBy(Offset(scrollForwardSign * pagerSize / 4f, 0f))
@@ -666,7 +750,7 @@
         // Moving backward
         onPager().performTouchInput {
             down(layoutStart)
-            if (isVertical) {
+            if (vertical) {
                 moveBy(Offset(0f, -scrollForwardSign * pagerSize / 4f))
             } else {
                 moveBy(Offset(-scrollForwardSign * pagerSize / 4f, 0f))
@@ -740,7 +824,7 @@
         rule.mainClock.autoAdvance = false
 
         // Act
-        createPager(state = state, modifier = Modifier.fillMaxSize(), effects = {
+        createPager(state = state, modifier = Modifier.fillMaxSize(), additionalContent = {
             LaunchedEffect(state) {
                 state.scrollToPage(5)
             }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerSwipeEdgeTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerSwipeEdgeTest.kt
index 5dd952a..7a5a1bc 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerSwipeEdgeTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerSwipeEdgeTest.kt
@@ -31,7 +31,7 @@
 @OptIn(ExperimentalFoundationApi::class)
 @LargeTest
 @RunWith(Parameterized::class)
-internal class PagerSwipeEdgeTest(
+class PagerSwipeEdgeTest(
     val config: ParamConfig
 ) : BasePagerTest(config) {
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerTest.kt
index 6024528..24b1450 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerTest.kt
@@ -34,7 +34,7 @@
 @OptIn(ExperimentalFoundationApi::class)
 @LargeTest
 @RunWith(Parameterized::class)
-internal class PagerTest(val config: ParamConfig) : BasePagerTest(config) {
+class PagerTest(val config: ParamConfig) : BasePagerTest(config) {
 
     @Before
     fun setUp() {
@@ -161,10 +161,6 @@
     companion object {
         @JvmStatic
         @Parameterized.Parameters(name = "{0}")
-        fun params() = mutableListOf<ParamConfig>().apply {
-            for (orientation in TestOrientation) {
-                add(ParamConfig(orientation = orientation))
-            }
-        }
+        fun params() = AllOrientationsParams
     }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/selection/SelectableTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/selection/SelectableTest.kt
index 99af6f7..5b384af 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/selection/SelectableTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/selection/SelectableTest.kt
@@ -77,7 +77,6 @@
 import kotlinx.coroutines.launch
 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
@@ -677,7 +676,6 @@
         rule.runOnIdle { assertThat(counter).isEqualTo(1) }
     }
 
-    @Ignore // b/270412977
     @Test
     @OptIn(ExperimentalTestApi::class, ExperimentalComposeUiApi::class)
     fun selectableTest_clickWithNumPadEnterKey() {
@@ -901,7 +899,6 @@
         }
     }
 
-    @Ignore // b/270755858
     @Test
     @OptIn(ExperimentalComposeUiApi::class, ExperimentalTestApi::class)
     fun selectableTest_otherKey_doesNotEmitIndication() {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/PointerMoveDetectorTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/PointerMoveDetectorTest.kt
new file mode 100644
index 0000000..a7b9fc5
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/PointerMoveDetectorTest.kt
@@ -0,0 +1,231 @@
+/*
+ * 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
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.testutils.TestViewConfiguration
+import androidx.compose.ui.AbsoluteAlignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.PointerInputScope
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.TouchInjectionScope
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.DpSize
+import com.google.common.truth.Correspondence
+import com.google.common.truth.IterableSubject
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+private const val TargetTag = "TargetLayout"
+
+@RunWith(JUnit4::class)
+class PointerMoveDetectorTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    private val actualMoves = mutableListOf<Offset>()
+
+    private val util = layoutWithGestureDetector {
+        detectMoves { actualMoves.add(it) }
+    }
+
+    private val nothingHandler: PointerInputChange.() -> Unit = {}
+
+    private var initialPass: PointerInputChange.() -> Unit = nothingHandler
+    private var finalPass: PointerInputChange.() -> Unit = nothingHandler
+
+    @Before
+    fun setup() {
+        actualMoves.clear()
+    }
+
+    private fun layoutWithGestureDetector(
+        gestureDetector: suspend PointerInputScope.() -> Unit,
+    ): @Composable () -> Unit = {
+        CompositionLocalProvider(
+            LocalDensity provides Density(1f),
+            LocalViewConfiguration provides TestViewConfiguration(
+                minimumTouchTargetSize = DpSize.Zero
+            )
+        ) {
+            with(LocalDensity.current) {
+                Box(
+                    Modifier
+                        .fillMaxSize()
+                        // Some tests execute a lambda before the initial and final passes
+                        // so they are called here, higher up the chain, so that the
+                        // calls happen prior to the gestureDetector below. The lambdas
+                        // do things like consume events on the initial pass or validate
+                        // consumption on the final pass.
+                        .pointerInput(Unit) {
+                            awaitPointerEventScope {
+                                while (true) {
+                                    val event = awaitPointerEvent(PointerEventPass.Initial)
+                                    event.changes.forEach {
+                                        initialPass(it)
+                                    }
+                                    awaitPointerEvent(PointerEventPass.Final)
+                                    event.changes.forEach {
+                                        finalPass(it)
+                                    }
+                                }
+                            }
+                        }
+                        .wrapContentSize(AbsoluteAlignment.TopLeft)
+                        .size(100.toDp())
+                        .pointerInput(gestureDetector, gestureDetector)
+                        .testTag(TargetTag)
+                )
+            }
+        }
+    }
+
+    private fun performTouch(
+        initialPass: PointerInputChange.() -> Unit = nothingHandler,
+        finalPass: PointerInputChange.() -> Unit = nothingHandler,
+        block: TouchInjectionScope.() -> Unit
+    ) {
+        this.initialPass = initialPass
+        this.finalPass = finalPass
+        rule.onNodeWithTag(TargetTag).performTouchInput(block)
+        rule.waitForIdle()
+        this.initialPass = nothingHandler
+        this.finalPass = nothingHandler
+    }
+
+    @Test
+    fun whenSimpleMovement_allMovesAreReported() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+
+            moveTo(0, Offset(4f, 4f))
+            moveTo(0, Offset(3f, 3f))
+            moveTo(0, Offset(2f, 2f))
+            moveTo(0, Offset(1f, 1f))
+
+            up(0)
+        }
+
+        assertThat(actualMoves).hasEqualOffsets(
+            listOf(
+                Offset(4f, 4f),
+                Offset(3f, 3f),
+                Offset(2f, 2f),
+                Offset(1f, 1f),
+            )
+        )
+    }
+
+    @Test
+    fun whenMultiplePointers_onlyUseFirst() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+            down(1, Offset(6f, 6f))
+
+            moveTo(0, Offset(4f, 4f))
+            moveTo(1, Offset(7f, 7f))
+
+            moveTo(0, Offset(3f, 3f))
+            moveTo(1, Offset(8f, 8f))
+
+            moveTo(0, Offset(2f, 2f))
+            moveTo(1, Offset(9f, 9f))
+
+            moveTo(0, Offset(1f, 1f))
+            moveTo(1, Offset(10f, 10f))
+
+            up(0)
+            up(1)
+        }
+
+        assertThat(actualMoves).hasEqualOffsets(
+            listOf(
+                Offset(4f, 4f),
+                Offset(3f, 3f),
+                Offset(2f, 2f),
+                Offset(1f, 1f),
+            )
+        )
+    }
+
+    @Test
+    fun whenMultiplePointers_thenFirstReleases_handOffToNextPointer() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(5f, 5f)) // ignored because not a move
+
+            moveTo(0, Offset(4f, 4f)) // used
+            moveTo(0, Offset(3f, 3f)) // used
+
+            down(1, Offset(4f, 4f)) // ignored because still tracking pointer id 0
+
+            moveTo(0, Offset(2f, 2f)) // used
+            moveTo(1, Offset(3f, 3f)) // ignored because still tracking pointer id 0
+
+            up(0) // ignored because not a move
+
+            moveTo(1, Offset(2f, 2f)) // ignored b/c equal to the previous used move
+            moveTo(1, Offset(1f, 1f)) // used
+
+            up(1) // ignored because not a move
+        }
+
+        assertThat(actualMoves).hasEqualOffsets(
+            listOf(
+                Offset(4f, 4f),
+                Offset(3f, 3f),
+                Offset(2f, 2f),
+                Offset(1f, 1f),
+            )
+        )
+    }
+
+    private fun IterableSubject.hasEqualOffsets(expectedMoves: List<Offset>) {
+        comparingElementsUsing(offsetCorrespondence)
+            .containsExactly(*expectedMoves.toTypedArray())
+            .inOrder()
+    }
+
+    private val offsetCorrespondence: Correspondence<Offset, Offset> = Correspondence.from(
+        { o1, o2 -> o1!!.x == o2!!.x && o1.y == o2.y },
+        "has the offset of",
+    )
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextLayoutTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextLayoutTest.kt
index f6ea83a..0479889 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextLayoutTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextLayoutTest.kt
@@ -108,6 +108,9 @@
                         measurables: List<Measurable>,
                         constraints: Constraints
                     ): MeasureResult {
+                        measurables.forEach {
+                            it.measure(constraints)
+                        }
                         textMeasurable = measurables.first()
                         return layout(0, 0) {}
                     }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextUsingModifierMinMaxLinesTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextUsingModifierMinMaxLinesTest.kt
new file mode 100644
index 0000000..adb2098
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextUsingModifierMinMaxLinesTest.kt
@@ -0,0 +1,152 @@
+/*
+ * 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.text
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.width
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.unit.IntSize
+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
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class TextUsingModifierMinMaxLinesTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun whenMaxLines_isBoundOnLineHeight() {
+        val text = "H\n".repeat(1000)
+        val sizes: Array<IntSize?> = Array(5) { null }
+        val layouts: Array<TextLayoutResult?> = Array(5) { null }
+        var unboundedTextSize: IntSize? = null
+
+        val textStyle = TextStyle(fontFamily = TEST_FONT_FAMILY)
+        rule.setContent {
+            for (i in sizes.indices) {
+                Box(
+                    Modifier
+                        .onSizeChanged { sizes[i] = it }
+                        .width(5.dp)) {
+                    TextUsingModifier(
+                        text,
+                        style = textStyle,
+                        maxLines = i + 1,
+                        onTextLayout = {
+                            layouts[i] = it
+                        }
+                    )
+                }
+            }
+
+            Box(
+                Modifier
+                    .onSizeChanged { unboundedTextSize = it }
+                    .width(5.dp)) {
+                TextUsingModifier(
+                    text,
+                    style = textStyle,
+                    maxLines = Int.MAX_VALUE
+                )
+            }
+        }
+        rule.runOnIdle {
+            for (i in 1 until sizes.size) {
+                assertThat(sizes[i]?.height).isGreaterThan(sizes[i - 1]?.height)
+                assertThat(layouts[i]?.lineCount).isEqualTo(i + 1)
+                // just ensure this is less than the unbounded size too
+                assertThat(unboundedTextSize?.height).isGreaterThan(sizes[i]?.height)
+            }
+        }
+    }
+
+    @Test
+    fun whenMinLines_setsLineHeight() {
+        val text = ""
+        val sizes: Array<IntSize?> = Array(5) { null }
+        val layouts: Array<TextLayoutResult?> = Array(5) { null }
+        var unboundedTextSize: IntSize? = null
+
+        val textStyle = TextStyle(fontFamily = TEST_FONT_FAMILY)
+        rule.setContent {
+            for (i in sizes.indices) {
+                Box(
+                    Modifier
+                        .onSizeChanged { sizes[i] = it }
+                        .width(5.dp)) {
+                    TextUsingModifier(
+                        text,
+                        style = textStyle,
+                        minLines = i + 1,
+                        onTextLayout = {
+                            layouts[i] = it
+                        }
+                    )
+                }
+            }
+
+            Box(
+                Modifier
+                    .onSizeChanged { unboundedTextSize = it }
+                    .width(5.dp)) {
+                TextUsingModifier(
+                    text,
+                    style = textStyle
+                )
+            }
+        }
+        rule.runOnIdle {
+            for (i in 1 until sizes.size) {
+                assertThat(sizes[i]?.height).isGreaterThan(sizes[i - 1]?.height)
+                assertThat(layouts[i]?.lineCount).isEqualTo(1)
+                // just ensure this is less than the unbounded size too
+                assertThat(unboundedTextSize?.height).isLessThan(sizes[i]?.height)
+            }
+        }
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun negativeMinLines_throws() {
+        rule.setContent {
+            TextUsingModifier(text = "", minLines = -1)
+        }
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun negativeMaxLines_throws() {
+        rule.setContent {
+            TextUsingModifier(text = "", maxLines = -1)
+        }
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun crossedMinMaxLines_throws() {
+        rule.setContent {
+            TextUsingModifier(text = "", minLines = 10, maxLines = 5)
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/BasicTextSemanticsTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/BasicTextSemanticsTest.kt
new file mode 100644
index 0000000..8ea3136
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/BasicTextSemanticsTest.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.modifiers
+
+import androidx.compose.foundation.text.TextUsingModifier
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.text.AnnotatedString
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class BasicTextSemanticsTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun semanticsTextChanges_String() {
+        var text by mutableStateOf("before")
+        rule.setContent {
+            TextUsingModifier(text)
+        }
+        rule.onNodeWithText("before").assertExists()
+        text = "after"
+        rule.onNodeWithText("after").assertExists()
+    }
+
+    @Test
+    fun semanticsTextChanges_AnnotatedString() {
+        var text by mutableStateOf("before")
+        rule.setContent {
+            TextUsingModifier(AnnotatedString(text))
+        }
+        rule.onNodeWithText("before").assertExists()
+        text = "after"
+        rule.onNodeWithText("after").assertExists()
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/LayoutUtilsKtTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/LayoutUtilsKtTest.kt
new file mode 100644
index 0000000..f7b269f
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/LayoutUtilsKtTest.kt
@@ -0,0 +1,196 @@
+/*
+ * 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.modifiers
+
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Constraints
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class LayoutUtilsKtTest {
+
+    @Test
+    fun finalConstraints_returnsTightWidth() {
+        val subject = finalConstraints(
+            Constraints(500, 500, 0, 50),
+            true,
+            TextOverflow.Ellipsis,
+            42f
+        )
+        assertThat(subject.maxWidth).isEqualTo(500)
+    }
+
+    @Test
+    fun finalConstraints_returnsMaxIntrinsicWhenUnbound() {
+        val subject = finalConstraints(
+            Constraints(500, 500, 0, 50),
+            false,
+            TextOverflow.Clip,
+            1234.1f
+        )
+        assertThat(subject.maxWidth).isEqualTo(1235)
+    }
+
+    @Test
+    fun finalMaxWidth_returnsTightWidth() {
+        val subject = finalMaxWidth(
+            Constraints(500, 500, 0, 50),
+            true,
+            TextOverflow.Ellipsis,
+            42f
+        )
+        assertThat(subject).isEqualTo(500)
+    }
+
+    @Test
+    fun finalMaxWidth_returnsMaxIntrinsicWhenUnbound() {
+        val subject = finalMaxWidth(
+            Constraints(500, 500, 0, 50),
+            false,
+            TextOverflow.Clip,
+            1234.1f
+        )
+        assertThat(subject).isEqualTo(1235)
+    }
+
+    @Test
+    fun finalMaxLines_negative() {
+        val subject = finalMaxLines(true, TextOverflow.Clip, -1)
+        assertThat(subject).isEqualTo(1)
+    }
+
+    @Test
+    fun finalMaxLines_positive_noOverride() {
+        val subject = finalMaxLines(true, TextOverflow.Clip, 4)
+        assertThat(subject).isEqualTo(4)
+    }
+
+    @Test
+    fun finalMaxLines_overrideOn_TextOverflowEllipsis_andSoftwrapFalse() {
+        val subject = finalMaxLines(false, TextOverflow.Ellipsis, 4)
+        assertThat(subject).isEqualTo(1)
+    }
+
+    @Test
+    fun canChangeBreak_canWrap_false() {
+        val subject = canChangeBreaks(
+            canWrap = false,
+            newConstraints = Constraints(0),
+            oldConstraints = Constraints(0),
+            maxIntrinsicWidth = 42f,
+            softWrap = true,
+            overflow = TextOverflow.Ellipsis
+        )
+        assertThat(subject).isFalse()
+    }
+
+    @Test
+    fun canChangeBreak_sameWidth() {
+        val subject = canChangeBreaks(
+            canWrap = true,
+            newConstraints = Constraints.fixedWidth(50),
+            oldConstraints = Constraints.fixedWidth(50),
+            maxIntrinsicWidth = 1234f,
+            softWrap = true,
+            overflow = TextOverflow.Ellipsis
+        )
+        assertThat(subject).isFalse()
+    }
+
+    @Test
+    fun canChangeBreak_textSmallerThanConstraints() {
+        val subject = canChangeBreaks(
+            canWrap = true,
+            newConstraints = Constraints.fixedWidth(50),
+            oldConstraints = Constraints.fixedWidth(40),
+            maxIntrinsicWidth = 12f,
+            softWrap = true,
+            overflow = TextOverflow.Ellipsis
+        )
+        assertThat(subject).isFalse()
+    }
+
+    @Test
+    fun canChangeBreak_textBiggerThanConstraints() {
+        val subject = canChangeBreaks(
+            canWrap = true,
+            newConstraints = Constraints.fixedWidth(100),
+            oldConstraints = Constraints.fixedWidth(200),
+            maxIntrinsicWidth = 300f,
+            softWrap = true,
+            overflow = TextOverflow.Ellipsis
+        )
+        assertThat(subject).isTrue()
+    }
+
+    @Test
+    fun canChangeBreak_shrinking_textSmallerThanNewConstraints() {
+        val subject = canChangeBreaks(
+            canWrap = true,
+            newConstraints = Constraints.fixedWidth(50),
+            oldConstraints = Constraints.fixedWidth(60),
+            maxIntrinsicWidth = 45f,
+            softWrap = true,
+            overflow = TextOverflow.Ellipsis
+        )
+        assertThat(subject).isFalse()
+    }
+
+    @Test
+    fun canChangeBreak_shrinking_textBiggerThanNewConstraints() {
+        val subject = canChangeBreaks(
+            canWrap = true,
+            newConstraints = Constraints.fixedWidth(50),
+            oldConstraints = Constraints.fixedWidth(60),
+            maxIntrinsicWidth = 59f,
+            softWrap = true,
+            overflow = TextOverflow.Ellipsis
+        )
+        assertThat(subject).isTrue()
+    }
+
+    @Test
+    fun canChangeBreak_growing_textSmallerThanNewConstraints() {
+        val subject = canChangeBreaks(
+            canWrap = true,
+            newConstraints = Constraints.fixedWidth(60),
+            oldConstraints = Constraints.fixedWidth(50),
+            maxIntrinsicWidth = 45f,
+            softWrap = true,
+            overflow = TextOverflow.Ellipsis
+        )
+        assertThat(subject).isFalse()
+    }
+
+    @Test
+    fun canChangeBreak_growing_textBiggerThanNewConstraints() {
+        val subject = canChangeBreaks(
+            canWrap = true,
+            newConstraints = Constraints.fixedWidth(60),
+            oldConstraints = Constraints.fixedWidth(50),
+            maxIntrinsicWidth = 59f,
+            softWrap = true,
+            overflow = TextOverflow.Ellipsis
+        )
+        assertThat(subject).isTrue()
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/MultiParagraphLayoutCacheTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/MultiParagraphLayoutCacheTest.kt
new file mode 100644
index 0000000..605f1bf
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/MultiParagraphLayoutCacheTest.kt
@@ -0,0 +1,253 @@
+/*
+ * 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.text.modifiers
+
+import androidx.compose.foundation.text.TEST_FONT_FAMILY
+import androidx.compose.foundation.text.toIntPx
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.Paragraph
+import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.text.font.createFontFamilyResolver
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.sp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import kotlin.math.roundToInt
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class MultiParagraphLayoutCacheTest {
+
+    private val fontFamily = TEST_FONT_FAMILY
+    private val density = Density(density = 1f)
+    private val context = InstrumentationRegistry.getInstrumentation().context
+    private val fontFamilyResolver = createFontFamilyResolver(context)
+
+    @Test
+    fun minIntrinsicWidth_getter() {
+        with(density) {
+            val fontSize = 20.sp
+            val text = "Hello"
+            val spanStyle = SpanStyle(fontSize = fontSize, fontFamily = fontFamily)
+            val annotatedString = AnnotatedString(text, spanStyle)
+            val textDelegate = MultiParagraphLayoutCache(
+                text = annotatedString,
+                style = TextStyle.Default,
+                fontFamilyResolver = fontFamilyResolver,
+            ).also {
+                it.density = this
+            }
+
+            textDelegate.layoutWithConstraints(Constraints.fixed(0, 0), LayoutDirection.Ltr)
+
+            assertThat(textDelegate.minIntrinsicWidth(LayoutDirection.Ltr))
+                .isEqualTo((fontSize.toPx() * text.length).toIntPx())
+        }
+    }
+
+    @Test
+    fun maxIntrinsicWidth_getter() {
+        with(density) {
+            val fontSize = 20.sp
+            val text = "Hello"
+            val spanStyle = SpanStyle(fontSize = fontSize, fontFamily = fontFamily)
+            val annotatedString = AnnotatedString(text, spanStyle)
+            val textDelegate = MultiParagraphLayoutCache(
+                text = annotatedString,
+                style = TextStyle.Default,
+                fontFamilyResolver = fontFamilyResolver,
+            ).also {
+                it.density = this
+            }
+
+            textDelegate.layoutWithConstraints(Constraints.fixed(0, 0), LayoutDirection.Ltr)
+
+            assertThat(textDelegate.maxIntrinsicWidth(LayoutDirection.Ltr))
+                .isEqualTo((fontSize.toPx() * text.length).toIntPx())
+        }
+    }
+
+    @Test
+    fun TextLayoutInput_reLayout_withDifferentHeight() {
+        val textDelegate = MultiParagraphLayoutCache(
+            text = AnnotatedString("Hello World"),
+            style = TextStyle.Default,
+            fontFamilyResolver = fontFamilyResolver,
+        ).also {
+            it.density = density
+        }
+        val width = 200
+        val heightFirstLayout = 100
+        val heightSecondLayout = 200
+
+        val constraintsFirstLayout = Constraints.fixed(width, heightFirstLayout)
+        textDelegate.layoutWithConstraints(constraintsFirstLayout, LayoutDirection.Ltr)
+        val resultFirstLayout = textDelegate.textLayoutResult
+        assertThat(resultFirstLayout.layoutInput.constraints).isEqualTo(constraintsFirstLayout)
+
+        val constraintsSecondLayout = Constraints.fixed(width, heightSecondLayout)
+        textDelegate.layoutWithConstraints(
+            constraintsSecondLayout,
+            LayoutDirection.Ltr
+        )
+        val resultSecondLayout = textDelegate.textLayoutResult
+        assertThat(resultSecondLayout.layoutInput.constraints).isEqualTo(constraintsSecondLayout)
+    }
+
+    @Test
+    fun TextLayoutResult_reLayout_withDifferentHeight() {
+        val textDelegate = MultiParagraphLayoutCache(
+            text = AnnotatedString("Hello World"),
+            style = TextStyle.Default,
+            fontFamilyResolver = fontFamilyResolver,
+        ).also {
+            it.density = density
+        }
+        val width = 200
+        val heightFirstLayout = 100
+        val heightSecondLayout = 200
+
+        val constraintsFirstLayout = Constraints.fixed(width, heightFirstLayout)
+        textDelegate.layoutWithConstraints(constraintsFirstLayout, LayoutDirection.Ltr)
+        val resultFirstLayout = textDelegate.textLayoutResult
+        assertThat(resultFirstLayout.size.height).isEqualTo(heightFirstLayout)
+
+        val constraintsSecondLayout = Constraints.fixed(width, heightSecondLayout)
+        textDelegate.layoutWithConstraints(
+            constraintsSecondLayout,
+            LayoutDirection.Ltr
+        )
+        val resultSecondLayout = textDelegate.textLayoutResult
+        assertThat(resultSecondLayout.size.height).isEqualTo(heightSecondLayout)
+    }
+
+    @Test
+    fun TextLayoutResult_layout_withEllipsis_withoutSoftWrap() {
+        val fontSize = 20f
+        val text = AnnotatedString(text = "Hello World! Hello World! Hello World! Hello World!")
+        val textDelegate = MultiParagraphLayoutCache(
+            text = text,
+            style = TextStyle(fontSize = fontSize.sp),
+            fontFamilyResolver = fontFamilyResolver,
+            softWrap = false,
+            overflow = TextOverflow.Ellipsis,
+        ).also {
+            it.density = density
+        }
+
+        textDelegate.layoutWithConstraints(Constraints.fixed(0, 0), LayoutDirection.Ltr)
+        // Makes width smaller than needed.
+        val width = textDelegate.maxIntrinsicWidth(LayoutDirection.Ltr) / 2
+        val constraints = Constraints(maxWidth = width)
+        textDelegate.layoutWithConstraints(constraints, LayoutDirection.Ltr)
+        val layoutResult = textDelegate.textLayoutResult
+
+        assertThat(layoutResult.lineCount).isEqualTo(1)
+        assertThat(layoutResult.isLineEllipsized(0)).isTrue()
+    }
+
+    @Test
+    fun TextLayoutResult_layoutWithLimitedHeight_withEllipsis() {
+        val fontSize = 20f
+        val text = AnnotatedString(text = "Hello World! Hello World! Hello World! Hello World!")
+
+        val textDelegate = MultiParagraphLayoutCache(
+            text = text,
+            style = TextStyle(fontSize = fontSize.sp),
+            fontFamilyResolver = fontFamilyResolver,
+            overflow = TextOverflow.Ellipsis,
+        ).also {
+            it.density = density
+        }
+        textDelegate.layoutWithConstraints(Constraints.fixed(0, 0), LayoutDirection.Ltr)
+        val constraints = Constraints(
+            maxWidth = textDelegate.maxIntrinsicWidth(LayoutDirection.Ltr) / 4,
+            maxHeight = (fontSize * 2.7).roundToInt() // fully fits at most 2 lines
+        )
+        textDelegate.layoutWithConstraints(constraints, LayoutDirection.Ltr)
+        val layoutResult = textDelegate.textLayoutResult
+
+        assertThat(layoutResult.lineCount).isEqualTo(2)
+        assertThat(layoutResult.isLineEllipsized(1)).isTrue()
+    }
+
+    @Test
+    fun TextLayoutResult_sameWidth_inRtlAndLtr_withLetterSpacing() {
+        val fontSize = 20f
+        val text = AnnotatedString(text = "Hello World")
+
+        val textDelegate = MultiParagraphLayoutCache(
+            text = text,
+            style = TextStyle(fontSize = fontSize.sp, letterSpacing = 0.5.sp),
+            fontFamilyResolver = fontFamilyResolver,
+            overflow = TextOverflow.Ellipsis,
+        ).also {
+            it.density = density
+        }
+
+        textDelegate.layoutWithConstraints(Constraints(), LayoutDirection.Ltr)
+        val layoutResultLtr = textDelegate.textLayoutResult
+        textDelegate.layoutWithConstraints(Constraints(), LayoutDirection.Rtl)
+        val layoutResultRtl = textDelegate.textLayoutResult
+
+        assertThat(layoutResultLtr.size.width).isEqualTo(layoutResultRtl.size.width)
+    }
+
+    @Test
+    fun maxHeight_hasSameHeight_asParagraph() {
+        val text = buildAnnotatedString {
+            for (i in 1..100 step 10) {
+                pushStyle(SpanStyle(fontSize = i.sp))
+                append("$i.sp\n")
+                pop()
+            }
+        }
+
+        val textDelegate = MultiParagraphLayoutCache(
+            text = text,
+            style = TextStyle(fontSize = 1.sp),
+            fontFamilyResolver = fontFamilyResolver,
+            overflow = TextOverflow.Ellipsis,
+            maxLines = 5
+        ).also {
+            it.density = density
+        }
+        textDelegate.layoutWithConstraints(Constraints(), LayoutDirection.Ltr)
+        val actual = textDelegate.textLayoutResult.multiParagraph
+
+        val expected = Paragraph(
+            text.text,
+            TextStyle(fontSize = 1.sp),
+            Constraints(),
+            density,
+            fontFamilyResolver,
+            text.spanStyles,
+            maxLines = 5,
+            ellipsis = true
+        )
+        assertThat(actual.height).isEqualTo(expected.height)
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/MultiParagraphLayoutCacheWidthWithLetterSpacingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/MultiParagraphLayoutCacheWidthWithLetterSpacingTest.kt
new file mode 100644
index 0000000..4bcbfc4
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/MultiParagraphLayoutCacheWidthWithLetterSpacingTest.kt
@@ -0,0 +1,91 @@
+/*
+ * 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.foundation.text.modifiers
+
+import androidx.compose.foundation.text.TEST_FONT_FAMILY
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.createFontFamilyResolver
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.sp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class MultiParagraphLayoutCacheWidthWithLetterSpacingTest {
+    private val fontFamily = TEST_FONT_FAMILY
+
+    /**
+     * values are exact values for the repro case (on Pixel4, Android 11)
+     */
+    private val density = Density(3.051f, 1.15f)
+    private val letterSpacing = 0.4.sp
+    private val lineHeight = 16.sp
+    private val fontSize = 12.sp
+    private val context = InstrumentationRegistry.getInstrumentation().context
+    @OptIn(ExperimentalTextApi::class)
+    private val fontFamilyResolver = createFontFamilyResolver(context)
+
+    @Test
+    fun letterSpacing_and_lineHeight() {
+        assertLineCount(
+            TextStyle(letterSpacing = letterSpacing, lineHeight = lineHeight)
+        )
+    }
+
+    @Test
+    fun only_letterSpacing() {
+        assertLineCount(TextStyle(letterSpacing = letterSpacing))
+    }
+
+    @Test
+    fun only_lineHeight() {
+        assertLineCount(TextStyle(lineHeight = lineHeight))
+    }
+
+    @Test
+    fun no_lineHeight_or_letterSpacing() {
+        assertLineCount(TextStyle())
+    }
+
+    private fun assertLineCount(style: TextStyle) {
+        val textDelegate = MultiParagraphLayoutCache(
+            text = AnnotatedString(text = "This is a callout message"),
+            style = style.copy(
+                fontFamily = fontFamily,
+                fontSize = fontSize
+            ),
+            fontFamilyResolver = fontFamilyResolver,
+            softWrap = true,
+            overflow = TextOverflow.Clip
+        ).also {
+            it.density = density
+        }
+        textDelegate.layoutWithConstraints(Constraints(), LayoutDirection.Ltr)
+        val layoutResult = textDelegate.textLayoutResult
+        assertThat(layoutResult.lineCount).isEqualTo(1)
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/ParagraphLayoutCacheTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/ParagraphLayoutCacheTest.kt
new file mode 100644
index 0000000..a7cb8c6
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/ParagraphLayoutCacheTest.kt
@@ -0,0 +1,232 @@
+/*
+ * 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.text.modifiers
+
+import androidx.compose.foundation.text.TEST_FONT_FAMILY
+import androidx.compose.foundation.text.toIntPx
+import androidx.compose.ui.text.Paragraph
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.createFontFamilyResolver
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.sp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import kotlin.math.roundToInt
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class ParagraphLayoutCacheTest {
+
+    private val fontFamily = TEST_FONT_FAMILY
+    private val density = Density(density = 1f)
+    private val context = InstrumentationRegistry.getInstrumentation().context
+    private val fontFamilyResolver = createFontFamilyResolver(context)
+
+    @Test
+    fun minIntrinsicWidth_getter() {
+        with(density) {
+            val fontSize = 20.sp
+            val text = "Hello"
+            val textDelegate = ParagraphLayoutCache(
+                text = text,
+                style = TextStyle(fontSize = fontSize, fontFamily = fontFamily),
+                fontFamilyResolver = fontFamilyResolver,
+            ).also {
+                it.density = this
+            }
+
+            assertThat(textDelegate.minIntrinsicWidth(LayoutDirection.Ltr))
+                .isEqualTo((fontSize.toPx() * text.length).toIntPx())
+        }
+    }
+
+    @Test
+    fun maxIntrinsicWidth_getter() {
+        with(density) {
+            val fontSize = 20.sp
+            val text = "Hello"
+            val textDelegate = ParagraphLayoutCache(
+                text = text,
+                style = TextStyle(fontSize = fontSize, fontFamily = fontFamily),
+                fontFamilyResolver = fontFamilyResolver,
+            ).also {
+                it.density = this
+            }
+
+            assertThat(textDelegate.maxIntrinsicWidth(LayoutDirection.Ltr))
+                .isEqualTo((fontSize.toPx() * text.length).toIntPx())
+        }
+    }
+
+    @Test
+    fun TextLayoutInput_reLayout_withDifferentHeight() {
+        val textDelegate = ParagraphLayoutCache(
+            text = "Hello World",
+            style = TextStyle.Default,
+            fontFamilyResolver = fontFamilyResolver,
+        ).also {
+            it.density = density
+        }
+        val width = 200
+        val heightFirstLayout = 100
+        val heightSecondLayout = 200
+
+        val constraintsFirstLayout = Constraints.fixed(width, heightFirstLayout)
+        textDelegate.layoutWithConstraints(constraintsFirstLayout, LayoutDirection.Ltr)
+        val resultFirstLayout = textDelegate.layoutSize
+
+        val constraintsSecondLayout = Constraints.fixed(width, heightSecondLayout)
+        textDelegate.layoutWithConstraints(
+            constraintsSecondLayout,
+            LayoutDirection.Ltr
+        )
+        val resultSecondLayout = textDelegate.layoutSize
+
+        assertThat(resultFirstLayout.height).isLessThan(resultSecondLayout.height)
+    }
+
+    @Test
+    fun TextLayoutResult_reLayout_withDifferentHeight() {
+        val textDelegate = ParagraphLayoutCache(
+            text = "Hello World",
+            style = TextStyle.Default,
+            fontFamilyResolver = fontFamilyResolver,
+        ).also {
+            it.density = density
+        }
+        val width = 200
+        val heightFirstLayout = 100
+        val heightSecondLayout = 200
+
+        val constraintsFirstLayout = Constraints.fixed(width, heightFirstLayout)
+        textDelegate.layoutWithConstraints(constraintsFirstLayout, LayoutDirection.Ltr)
+        val resultFirstLayout = textDelegate.layoutSize
+        assertThat(resultFirstLayout.height).isEqualTo(heightFirstLayout)
+
+        val constraintsSecondLayout = Constraints.fixed(width, heightSecondLayout)
+        textDelegate.layoutWithConstraints(
+            constraintsSecondLayout,
+            LayoutDirection.Ltr
+        )
+        val resultSecondLayout = textDelegate.layoutSize
+        assertThat(resultSecondLayout.height).isEqualTo(heightSecondLayout)
+    }
+
+    @Test
+    fun TextLayoutResult_layout_withEllipsis_withoutSoftWrap() {
+        val fontSize = 20f
+        val textDelegate = ParagraphLayoutCache(
+            text = "Hello World! Hello World! Hello World! Hello World!",
+            style = TextStyle(fontSize = fontSize.sp),
+            fontFamilyResolver = fontFamilyResolver,
+            softWrap = false,
+            overflow = TextOverflow.Ellipsis,
+        ).also {
+            it.density = density
+        }
+
+        textDelegate.layoutWithConstraints(Constraints.fixed(0, 0), LayoutDirection.Ltr)
+        // Makes width smaller than needed.
+        val width = textDelegate.maxIntrinsicWidth(LayoutDirection.Ltr) / 2
+        val constraints = Constraints(maxWidth = width)
+        textDelegate.layoutWithConstraints(constraints, LayoutDirection.Ltr)
+        val layoutResult = textDelegate.paragraph!!
+
+        assertThat(layoutResult.lineCount).isEqualTo(1)
+        assertThat(layoutResult.isLineEllipsized(0)).isTrue()
+    }
+
+    @Test
+    fun TextLayoutResult_layoutWithLimitedHeight_withEllipsis() {
+        val fontSize = 20f
+
+        val textDelegate = ParagraphLayoutCache(
+            text = "Hello World! Hello World! Hello World! Hello World!",
+            style = TextStyle(fontSize = fontSize.sp),
+            fontFamilyResolver = fontFamilyResolver,
+            overflow = TextOverflow.Ellipsis,
+        ).also {
+            it.density = density
+        }
+        textDelegate.layoutWithConstraints(Constraints.fixed(0, 0), LayoutDirection.Ltr)
+        val constraints = Constraints(
+            maxWidth = textDelegate.maxIntrinsicWidth(LayoutDirection.Ltr) / 4,
+            maxHeight = (fontSize * 2.7).roundToInt() // fully fits at most 2 lines
+        )
+        textDelegate.layoutWithConstraints(constraints, LayoutDirection.Ltr)
+        val layoutResult = textDelegate.paragraph!!
+
+        assertThat(layoutResult.lineCount).isEqualTo(2)
+        assertThat(layoutResult.isLineEllipsized(1)).isTrue()
+    }
+
+    @Test
+    fun TextLayoutResult_sameWidth_inRtlAndLtr_withLetterSpacing() {
+        val fontSize = 20f
+
+        val textDelegate = ParagraphLayoutCache(
+            text = "Hello World",
+            style = TextStyle(fontSize = fontSize.sp, letterSpacing = 0.5.sp),
+            fontFamilyResolver = fontFamilyResolver,
+            overflow = TextOverflow.Ellipsis,
+        ).also {
+            it.density = density
+        }
+
+        textDelegate.layoutWithConstraints(Constraints(), LayoutDirection.Ltr)
+        val layoutResultLtr = textDelegate.layoutSize
+        textDelegate.layoutWithConstraints(Constraints(), LayoutDirection.Rtl)
+        val layoutResultRtl = textDelegate.layoutSize
+
+        assertThat(layoutResultLtr.width).isEqualTo(layoutResultRtl.width)
+    }
+
+    @Test
+    fun maxHeight_hasSameHeight_asParagraph() {
+        val text = "a\n".repeat(20)
+        val textDelegate = ParagraphLayoutCache(
+            text = text,
+            style = TextStyle(fontSize = 1.sp),
+            fontFamilyResolver = fontFamilyResolver,
+            overflow = TextOverflow.Ellipsis,
+            maxLines = 5
+        ).also {
+            it.density = density
+        }
+        textDelegate.layoutWithConstraints(Constraints(), LayoutDirection.Ltr)
+        val actual = textDelegate.paragraph!!
+
+        val expected = Paragraph(
+            text,
+            TextStyle(fontSize = 1.sp),
+            Constraints(),
+            density,
+            fontFamilyResolver,
+            emptyList(),
+            maxLines = 5,
+            ellipsis = true
+        )
+        assertThat(actual.height).isEqualTo(expected.height)
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/SelectionControllerTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/SelectionControllerTest.kt
new file mode 100644
index 0000000..c76de6b3
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/SelectionControllerTest.kt
@@ -0,0 +1,266 @@
+/*
+ * 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.modifiers
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.text.ceilToIntPx
+import androidx.compose.foundation.text.selection.Selectable
+import androidx.compose.foundation.text.selection.Selection
+import androidx.compose.foundation.text.selection.Selection.AnchorInfo
+import androidx.compose.foundation.text.selection.SelectionAdjustment
+import androidx.compose.foundation.text.selection.SelectionRegistrar
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.asAndroidBitmap
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.style.ResolvedTextDirection
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class SelectionControllerTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    @SdkSuppress(minSdkVersion = 26)
+    fun drawWithClip_doesClip() {
+        val canvasSize = 10.dp
+        val pathSize = 10_000f
+        val path = Path().also {
+            it.addRect(Rect(0f, 0f, pathSize, pathSize))
+        }
+
+        val subject = SelectionController(
+            FixedSelectionFake(0, 1000, 200),
+            Color.White,
+            params = FakeParams(
+                path, true
+            )
+        )
+        var size: Size? = null
+
+        rule.setContent {
+            Box(Modifier.fillMaxSize().drawBehind { drawRect(Color.Black) }) {
+                Canvas(Modifier.size(canvasSize)) {
+                    size = this.size
+                    subject.draw(this)
+                }
+            }
+        }
+
+        rule.waitForIdle()
+        assertClipped(size!!, true)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 26)
+    fun drawWithOut_doesNotClip() {
+        val canvasSize = 10.dp
+        val pathSize = 10_000f
+        val path = Path().also {
+            it.addRect(Rect(0f, 0f, pathSize, pathSize))
+        }
+
+        val subject = SelectionController(
+            FixedSelectionFake(0, 1000, 200),
+            Color.White,
+            params = FakeParams(
+                path, false
+            )
+        )
+        var size: Size? = null
+
+        rule.setContent {
+            Box(Modifier.fillMaxSize().drawBehind { drawRect(Color.Black) }) {
+                Canvas(Modifier.size(canvasSize)) {
+                    size = this.size
+                    drawRect(Color.Black)
+                    subject.draw(this)
+                }
+            }
+        }
+
+        rule.waitForIdle()
+        assertClipped(size!!, false)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    private fun assertClipped(size: Size, isClipped: Boolean) {
+        val expectedColor = if (isClipped) { Color.Black } else { Color.White }
+        rule.onRoot().captureToImage().asAndroidBitmap().apply {
+            Assert.assertEquals(
+                expectedColor.toArgb(),
+                getPixel(
+                    size.width.ceilToIntPx() + 5,
+                    size.height.ceilToIntPx() + 5
+                )
+            )
+        }
+    }
+}
+
+/**
+ * Fake that always has selection
+ */
+private class FixedSelectionFake(
+    val start: Int,
+    val end: Int,
+    val lastVisible: Int
+) : SelectionRegistrar {
+
+    var selectableId = 0L
+    var allSelectables = mutableListOf<Long>()
+
+    override val subselections: Map<Long, Selection>
+        get() = allSelectables.associateWith { selectionId ->
+            Selection(
+                AnchorInfo(ResolvedTextDirection.Ltr, start, selectionId),
+                AnchorInfo(ResolvedTextDirection.Ltr, end, selectionId)
+            )
+        }
+
+    override fun subscribe(selectable: Selectable): Selectable {
+        return FakeSelectableWithLastVisibleOffset(selectable.selectableId, lastVisible)
+    }
+
+    override fun unsubscribe(selectable: Selectable) {
+        // nothing
+    }
+
+    override fun nextSelectableId(): Long {
+        return selectableId++.also {
+            allSelectables.add(it)
+        }
+    }
+
+    override fun notifyPositionChange(selectableId: Long) {
+        FAKE("Not yet implemented")
+    }
+
+    override fun notifySelectionUpdateStart(
+        layoutCoordinates: LayoutCoordinates,
+        startPosition: Offset,
+        adjustment: SelectionAdjustment
+    ) {
+        FAKE("Selection not editable")
+    }
+
+    override fun notifySelectionUpdateSelectAll(selectableId: Long) {
+        FAKE()
+    }
+
+    override fun notifySelectionUpdate(
+        layoutCoordinates: LayoutCoordinates,
+        newPosition: Offset,
+        previousPosition: Offset,
+        isStartHandle: Boolean,
+        adjustment: SelectionAdjustment
+    ): Boolean {
+        FAKE("Selection not editable")
+    }
+
+    override fun notifySelectionUpdateEnd() {
+        FAKE("Selection not editable")
+    }
+
+    override fun notifySelectableChange(selectableId: Long) {
+        FAKE("Selection not editable")
+    }
+}
+
+private class FakeSelectableWithLastVisibleOffset(
+    override val selectableId: Long,
+    private val lastVisible: Int
+) : Selectable {
+    override fun updateSelection(
+        startHandlePosition: Offset,
+        endHandlePosition: Offset,
+        previousHandlePosition: Offset?,
+        isStartHandle: Boolean,
+        containerLayoutCoordinates: LayoutCoordinates,
+        adjustment: SelectionAdjustment,
+        previousSelection: Selection?
+    ): Pair<Selection?, Boolean> {
+        FAKE()
+    }
+
+    override fun getSelectAllSelection(): Selection? {
+        FAKE()
+    }
+
+    override fun getHandlePosition(selection: Selection, isStartHandle: Boolean): Offset {
+        FAKE()
+    }
+
+    override fun getLayoutCoordinates(): LayoutCoordinates? {
+        FAKE()
+    }
+
+    override fun getText(): AnnotatedString {
+        FAKE()
+    }
+
+    override fun getBoundingBox(offset: Int): Rect {
+        FAKE()
+    }
+
+    override fun getRangeOfLineContaining(offset: Int): TextRange {
+        FAKE()
+    }
+
+    override fun getLastVisibleOffset(): Int {
+        return lastVisible
+    }
+}
+
+private class FakeParams(
+    val path: Path,
+    override val shouldClip: Boolean
+) : StaticTextSelectionParams(null, null) {
+
+    override fun getPathForRange(start: Int, end: Int): Path? {
+        return path
+    }
+}
+
+private fun FAKE(reason: String = "Unsupported fake method on fake"): Nothing =
+    throw NotImplementedError("No support in fake: $reason")
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/TextLayoutResultIntegrationTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/TextLayoutResultIntegrationTest.kt
new file mode 100644
index 0000000..1735715
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/modifiers/TextLayoutResultIntegrationTest.kt
@@ -0,0 +1,330 @@
+/*
+ * 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.foundation.text.modifiers
+
+import androidx.compose.foundation.text.TEST_FONT_FAMILY
+import androidx.compose.foundation.text.toIntPx
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Canvas
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.TextPainter
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.createFontFamilyResolver
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.sp
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.IntegerSubject
+import kotlin.math.floor
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class TextLayoutResultIntegrationTest {
+
+    private val fontFamily = TEST_FONT_FAMILY
+    private val density = Density(density = 1f)
+    private val context = InstrumentationRegistry.getInstrumentation().context
+    private val fontFamilyResolver = createFontFamilyResolver(context)
+    private val layoutDirection = LayoutDirection.Ltr
+
+    @Test
+    fun width_getter() {
+        with(density) {
+            val fontSize = 20.sp
+            val text = "Hello"
+            val spanStyle = SpanStyle(fontSize = fontSize, fontFamily = fontFamily)
+            val annotatedString = AnnotatedString(text, spanStyle)
+            val textDelegate = MultiParagraphLayoutCache(
+                text = annotatedString,
+                style = TextStyle.Default,
+                fontFamilyResolver = fontFamilyResolver
+            ).also {
+                it.density = this
+            }
+
+            textDelegate.layoutWithConstraints(Constraints(0, 200), layoutDirection)
+            val layoutResult = textDelegate.textLayoutResult
+
+            assertThat(layoutResult.size.width).isEqualTo(
+                (fontSize.toPx() * text.length).toIntPx()
+            )
+        }
+    }
+
+    @Test
+    fun width_getter_with_small_width() {
+        val text = "Hello"
+        val width = 80
+        val spanStyle = SpanStyle(fontSize = 20.sp, fontFamily = fontFamily)
+        val annotatedString = AnnotatedString(text, spanStyle)
+        val textDelegate = MultiParagraphLayoutCache(
+            text = annotatedString,
+            style = TextStyle.Default,
+            fontFamilyResolver = fontFamilyResolver
+        ).also {
+            it.density = density
+        }
+
+        textDelegate.layoutWithConstraints(Constraints(maxWidth = width), layoutDirection)
+        val layoutResult = textDelegate.textLayoutResult
+
+        assertThat(layoutResult.size.width).isEqualTo(width)
+    }
+
+    @Test
+    fun height_getter() {
+        with(density) {
+            val fontSize = 20.sp
+            val spanStyle = SpanStyle(fontSize = fontSize, fontFamily = fontFamily)
+            val text = "hello"
+            val annotatedString = AnnotatedString(text, spanStyle)
+            val textDelegate = MultiParagraphLayoutCache(
+                text = annotatedString,
+                style = TextStyle.Default,
+                fontFamilyResolver = fontFamilyResolver
+            ).also {
+                it.density = this
+            }
+
+            textDelegate.layoutWithConstraints(Constraints(), layoutDirection)
+            val layoutResult = textDelegate.textLayoutResult
+
+            assertThat(layoutResult.size.height).isEqualTo((fontSize.toPx()).toIntPx())
+        }
+    }
+
+    @Test
+    fun layout_build_layoutResult() {
+        val textDelegate = MultiParagraphLayoutCache(
+            text = AnnotatedString("hello"),
+            style = TextStyle.Default,
+            fontFamilyResolver = fontFamilyResolver
+        ).also {
+            it.density = density
+        }
+
+        textDelegate.layoutWithConstraints(Constraints(0, 20), layoutDirection)
+        val layoutResult = textDelegate.textLayoutResult
+
+        assertThat(layoutResult).isNotNull()
+    }
+
+    private fun IntegerSubject.isZero() {
+        this.isEqualTo(0)
+    }
+
+    @Test
+    fun getPositionForOffset_First_Character() {
+        val text = "Hello"
+        val annotatedString = AnnotatedString(
+            text,
+            SpanStyle(fontSize = 20.sp, fontFamily = fontFamily)
+        )
+
+        val textDelegate = MultiParagraphLayoutCache(
+            text = annotatedString,
+            style = TextStyle.Default,
+            fontFamilyResolver = fontFamilyResolver
+        ).also {
+            it.density = density
+        }
+        textDelegate.layoutWithConstraints(Constraints(), layoutDirection)
+        val layoutResult = textDelegate.textLayoutResult
+
+        val selection = layoutResult.getOffsetForPosition(Offset.Zero)
+
+        assertThat(selection).isZero()
+    }
+
+    @Test
+    fun getPositionForOffset_other_Character() {
+        with(density) {
+            val fontSize = 20.sp
+            val characterIndex = 2 // Start from 0.
+            val text = "Hello"
+
+            val annotatedString = AnnotatedString(
+                text,
+                SpanStyle(fontSize = fontSize, fontFamily = fontFamily)
+            )
+
+            val textDelegate = MultiParagraphLayoutCache(
+                text = annotatedString,
+                style = TextStyle.Default,
+                fontFamilyResolver = fontFamilyResolver
+            ).also {
+                it.density = this
+            }
+            textDelegate.layoutWithConstraints(Constraints(), layoutDirection)
+            val layoutResult = textDelegate.textLayoutResult
+
+            val selection = layoutResult.getOffsetForPosition(
+                position = Offset((fontSize.toPx() * characterIndex + 1), 0f)
+            )
+
+            assertThat(selection).isEqualTo(characterIndex)
+        }
+    }
+
+    @Test
+    fun hasOverflowShaderFalse() {
+        val text = "Hello"
+        val spanStyle = SpanStyle(fontSize = 20.sp, fontFamily = fontFamily)
+        val annotatedString = AnnotatedString(text, spanStyle)
+        val textDelegate = MultiParagraphLayoutCache(
+            text = annotatedString,
+            style = TextStyle.Default,
+            fontFamilyResolver = fontFamilyResolver
+        ).also {
+            it.density = density
+        }
+
+        textDelegate.layoutWithConstraints(Constraints(), layoutDirection)
+        val layoutResult = textDelegate.textLayoutResult
+
+        assertThat(layoutResult.hasVisualOverflow).isFalse()
+
+        // paint should not throw exception
+        TextPainter.paint(Canvas(android.graphics.Canvas()), layoutResult)
+    }
+
+    @Test
+    fun didOverflowHeight_isTrue_when_maxLines_exceeded() {
+        val text = "HelloHellowHelloHello"
+        val spanStyle = SpanStyle(fontSize = 20.sp, fontFamily = fontFamily)
+        val annotatedString = AnnotatedString(text, spanStyle)
+        val maxLines = 3
+
+        val textDelegate = MultiParagraphLayoutCache(
+            text = annotatedString,
+            style = TextStyle.Default,
+            fontFamilyResolver = fontFamilyResolver,
+            maxLines = maxLines
+        ).also {
+            it.density = density
+        }
+
+        textDelegate.layoutWithConstraints(Constraints.fixed(0, 0), LayoutDirection.Ltr)
+        // Tries to make 5 lines of text, which exceeds the given maxLines(3).
+        val maxWidth = textDelegate.maxIntrinsicWidth(LayoutDirection.Ltr) / 5
+        textDelegate.layoutWithConstraints(
+            Constraints(maxWidth = maxWidth),
+            layoutDirection
+        )
+        val layoutResult = textDelegate.textLayoutResult
+
+        assertThat(layoutResult.didOverflowHeight).isTrue()
+    }
+
+    @Test
+    fun didOverflowHeight_isFalse_when_maxLines_notExceeded() {
+        val text = "HelloHellowHelloHello"
+        val spanStyle = SpanStyle(fontSize = 20.sp, fontFamily = fontFamily)
+        val annotatedString = AnnotatedString(text, spanStyle)
+        val maxLines = 10
+
+        val textDelegate = MultiParagraphLayoutCache(
+            text = annotatedString,
+            style = TextStyle.Default,
+            fontFamilyResolver = fontFamilyResolver,
+            maxLines = maxLines
+        ).also {
+            it.density = density
+        }
+
+        textDelegate.layoutWithConstraints(Constraints.fixed(0, 0), LayoutDirection.Ltr)
+        // Tries to make 5 lines of text, which doesn't exceed the given maxLines(10).
+        val maxWidth = textDelegate.maxIntrinsicWidth(LayoutDirection.Ltr) / 5
+        textDelegate.layoutWithConstraints(
+            Constraints(maxWidth = maxWidth),
+            layoutDirection
+        )
+        val layoutResult = textDelegate.textLayoutResult
+
+        assertThat(layoutResult.didOverflowHeight).isFalse()
+    }
+
+    @Test
+    fun didOverflowHeight_isTrue_when_maxHeight_exceeded() {
+        val text = "HelloHellowHelloHello"
+        val spanStyle = SpanStyle(fontSize = 20.sp, fontFamily = fontFamily)
+        val annotatedString = AnnotatedString(text, spanStyle)
+
+        val textDelegate = MultiParagraphLayoutCache(
+            text = annotatedString,
+            style = TextStyle.Default,
+            fontFamilyResolver = fontFamilyResolver,
+        ).also {
+            it.density = density
+        }
+
+        textDelegate.layoutWithConstraints(
+            Constraints(),
+            layoutDirection
+        )
+
+        val maxIntrinsicsHeight = textDelegate.textLayoutResult.multiParagraph.height
+
+        // Make maxHeight smaller than needed.
+        val maxHeight = floor(maxIntrinsicsHeight / 2).toInt()
+        textDelegate.layoutWithConstraints(
+            Constraints(maxHeight = maxHeight),
+            layoutDirection
+        )
+        val layoutResult = textDelegate.textLayoutResult
+
+        assertThat(layoutResult.didOverflowHeight).isTrue()
+    }
+
+    @Test
+    fun didOverflowHeight_isFalse_when_maxHeight_notExceeded() {
+        val text = "HelloHellowHelloHello"
+        val spanStyle = SpanStyle(fontSize = 20.sp, fontFamily = fontFamily)
+        val annotatedString = AnnotatedString(text, spanStyle)
+
+        val textDelegate = MultiParagraphLayoutCache(
+            text = annotatedString,
+            style = TextStyle.Default,
+            fontFamilyResolver = fontFamilyResolver,
+        ).also {
+            it.density = density
+        }
+
+        textDelegate.layoutWithConstraints(
+            Constraints(),
+            layoutDirection
+        )
+        val maxIntrinsicsHeight = textDelegate.textLayoutResult.multiParagraph.height
+
+        // Make max height larger than the needed.
+        val maxHeight = floor(maxIntrinsicsHeight * 2).toInt()
+        textDelegate.layoutWithConstraints(
+            Constraints(maxHeight = maxHeight),
+            layoutDirection
+        )
+        val layoutResult = textDelegate.textLayoutResult
+
+        assertThat(layoutResult.didOverflowHeight).isFalse()
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/SelectionContainerFocusTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/SelectionContainerFocusTest.kt
index 1552480..0fe45bd 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/SelectionContainerFocusTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/SelectionContainerFocusTest.kt
@@ -26,20 +26,26 @@
 import androidx.compose.foundation.text.BasicText
 import androidx.compose.foundation.text.TEST_FONT_FAMILY
 import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.hapticfeedback.HapticFeedback
 import androidx.compose.ui.hapticfeedback.HapticFeedbackType
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.platform.LocalHapticFeedback
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.platform.LocalTextToolbar
+import androidx.compose.ui.platform.TextToolbar
+import androidx.compose.ui.platform.TextToolbarStatus
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.click
 import androidx.compose.ui.test.hasTestTag
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
 import androidx.compose.ui.test.longClick
+import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.TextStyle
@@ -54,10 +60,10 @@
 import com.nhaarman.mockitokotlin2.mock
 import com.nhaarman.mockitokotlin2.times
 import com.nhaarman.mockitokotlin2.verify
+import java.util.concurrent.CountDownLatch
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import java.util.concurrent.CountDownLatch
 
 @LargeTest
 @RunWith(AndroidJUnit4::class)
@@ -139,6 +145,52 @@
         }
     }
 
+    @Test
+    fun leavingComposition_hidesTextToolbar() {
+        // null -> nothing got called, true -> show, false -> hide
+        var lastShowCalled: Boolean? = null
+        val fakeTextToolbar = FakeTextToolbar(
+            onShowMenu = { _, _, _, _, _ -> lastShowCalled = true },
+            onHideMenu = { lastShowCalled = false }
+        )
+
+        val tag = "SelectionContainer"
+
+        var inComposition by mutableStateOf(true)
+
+        rule.setContent {
+            CompositionLocalProvider(
+                LocalTextToolbar provides fakeTextToolbar
+            ) {
+                if (inComposition) {
+                    SelectionContainer(modifier = Modifier.testTag("SelectionContainer")) {
+                        BasicText(
+                            AnnotatedString(textContent),
+                            Modifier.fillMaxWidth(),
+                            style = TextStyle(fontFamily = fontFamily, fontSize = fontSize),
+                            softWrap = true,
+                            overflow = TextOverflow.Clip,
+                            maxLines = Int.MAX_VALUE,
+                            inlineContent = mapOf(),
+                            onTextLayout = {}
+                        )
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag(tag).performTouchInput { longClick() }
+        rule.runOnIdle {
+            assertThat(lastShowCalled).isTrue()
+        }
+
+        inComposition = false
+
+        rule.runOnIdle {
+            assertThat(lastShowCalled).isFalse()
+        }
+    }
+
     private fun createSelectionContainer(isRtl: Boolean = false) {
         val measureLatch = CountDownLatch(1)
 
@@ -151,9 +203,11 @@
             ) {
                 Column {
                     SelectionContainer(
-                        modifier = Modifier.onGloballyPositioned {
-                            measureLatch.countDown()
-                        }.testTag("selectionContainer1"),
+                        modifier = Modifier
+                            .onGloballyPositioned {
+                                measureLatch.countDown()
+                            }
+                            .testTag("selectionContainer1"),
                         selection = selection1.value,
                         onSelectionChange = {
                             selection1.value = it
@@ -170,14 +224,19 @@
                                 inlineContent = mapOf(),
                                 onTextLayout = {}
                             )
-                            Box(Modifier.size(boxSize, boxSize).testTag("box"))
+                            Box(
+                                Modifier
+                                    .size(boxSize, boxSize)
+                                    .testTag("box"))
                         }
                     }
 
                     SelectionContainer(
-                        modifier = Modifier.onGloballyPositioned {
-                            measureLatch.countDown()
-                        }.testTag("selectionContainer2"),
+                        modifier = Modifier
+                            .onGloballyPositioned {
+                                measureLatch.countDown()
+                            }
+                            .testTag("selectionContainer2"),
                         selection = selection2.value,
                         onSelectionChange = {
                             selection2.value = it
@@ -201,4 +260,44 @@
             view = it.findViewById<ViewGroup>(android.R.id.content)
         }
     }
+}
+
+internal fun FakeTextToolbar(
+    onShowMenu: (
+        rect: Rect,
+        onCopyRequested: (() -> Unit)?,
+        onPasteRequested: (() -> Unit)?,
+        onCutRequested: (() -> Unit)?,
+        onSelectAllRequested: (() -> Unit)?
+    ) -> Unit,
+    onHideMenu: () -> Unit
+): TextToolbar {
+    return object : TextToolbar {
+        private var _status: TextToolbarStatus = TextToolbarStatus.Hidden
+
+        override fun showMenu(
+            rect: Rect,
+            onCopyRequested: (() -> Unit)?,
+            onPasteRequested: (() -> Unit)?,
+            onCutRequested: (() -> Unit)?,
+            onSelectAllRequested: (() -> Unit)?
+        ) {
+            onShowMenu(
+                rect,
+                onCopyRequested,
+                onPasteRequested,
+                onCutRequested,
+                onSelectAllRequested
+            )
+            _status = TextToolbarStatus.Shown
+        }
+
+        override fun hide() {
+            onHideMenu()
+            _status = TextToolbarStatus.Hidden
+        }
+
+        override val status: TextToolbarStatus
+            get() = _status
+    }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2SemanticsTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2SemanticsTest.kt
new file mode 100644
index 0000000..53558f7
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2SemanticsTest.kt
@@ -0,0 +1,305 @@
+package androidx.compose.foundation.text2
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.selection.fetchTextLayoutResult
+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.platform.testTag
+import androidx.compose.ui.semantics.SemanticsActions
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.getOrNull
+import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertTextEquals
+import androidx.compose.ui.test.hasImeAction
+import androidx.compose.ui.test.hasSetTextAction
+import androidx.compose.ui.test.isEnabled
+import androidx.compose.ui.test.isFocused
+import androidx.compose.ui.test.isNotEnabled
+import androidx.compose.ui.test.isNotFocused
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performSemanticsAction
+import androidx.compose.ui.test.performTextInput
+import androidx.compose.ui.test.performTextInputSelection
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.TextFieldValue
+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
+
+@OptIn(ExperimentalFoundationApi::class)
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class BasicTextField2SemanticsTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    private val Tag = "TextField"
+
+    @Test
+    fun defaultSemantics() {
+        rule.setContent {
+            BasicTextField2(
+                modifier = Modifier.testTag(Tag),
+                state = remember { TextFieldState() },
+                decorationBox = {
+                    Column {
+                        BasicText("label")
+                        it()
+                    }
+                }
+            )
+        }
+
+        rule.onNodeWithTag(Tag)
+            .assertEditableTextEquals("")
+            .assertTextEquals("label", includeEditableText = false)
+            .assertHasClickAction()
+            .assert(hasSetTextAction())
+            .assert(hasImeAction(ImeAction.Default))
+            .assert(isNotFocused())
+            .assert(
+                SemanticsMatcher.expectValue(
+                    SemanticsProperties.TextSelectionRange,
+                    TextRange.Zero
+                )
+            )
+            .assert(SemanticsMatcher.keyIsDefined(SemanticsActions.SetText))
+            // TODO(halilibo): enable after selection work is completed.
+            // .assert(SemanticsMatcher.keyIsDefined(SemanticsActions.SetSelection))
+            .assert(SemanticsMatcher.keyIsDefined(SemanticsActions.GetTextLayoutResult))
+
+        val textLayoutResults = mutableListOf<TextLayoutResult>()
+        rule.onNodeWithTag(Tag)
+            .performSemanticsAction(SemanticsActions.GetTextLayoutResult) { it(textLayoutResults) }
+        assert(textLayoutResults.size == 1) { "TextLayoutResult is null" }
+    }
+
+    @Test
+    fun semantics_enabledStatus() {
+        var enabled by mutableStateOf(true)
+        rule.setContent {
+            val state = remember { TextFieldState() }
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag),
+                enabled = enabled
+            )
+        }
+
+        rule.onNodeWithTag(Tag)
+            .assert(isEnabled())
+
+        enabled = false
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(Tag)
+            .assert(isNotEnabled())
+    }
+
+    @Test
+    fun semantics_clickAction() {
+        rule.setContent {
+            val state = remember { TextFieldState() }
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag)
+            )
+        }
+
+        rule.onNodeWithTag(Tag)
+            .assert(isNotFocused())
+            .performSemanticsAction(SemanticsActions.OnClick)
+        rule.onNodeWithTag(Tag)
+            .assert(isFocused())
+    }
+
+    @Test
+    fun semantics_imeOption() {
+        rule.setContent {
+            val state = remember { TextFieldState() }
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag),
+                keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search)
+            )
+        }
+
+        rule.onNodeWithTag(Tag).assert(hasImeAction(ImeAction.Search))
+    }
+
+    @Test
+    fun contentSemanticsAreSet_inTheFirstComposition() {
+        val state = TextFieldState(TextFieldValue("hello"))
+        rule.setContent {
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag)
+            )
+        }
+
+        rule.onNodeWithTag(Tag).assertTextEquals("hello")
+    }
+
+    @Test
+    fun contentSemanticsAreSet_afterRecomposition() {
+        val state = TextFieldState(TextFieldValue("hello"))
+        rule.setContent {
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag)
+            )
+        }
+
+        rule.onNodeWithTag(Tag).assertTextEquals("hello")
+
+        state.editProcessor.reset(TextFieldValue("hello2"))
+
+        rule.onNodeWithTag(Tag).assertTextEquals("hello2")
+    }
+
+    @Test
+    fun selectionSemanticsAreSet_inTheFirstComposition() {
+        val state = TextFieldState(TextFieldValue("hello", selection = TextRange(2)))
+        rule.setContent {
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag)
+            )
+        }
+
+        with(rule.onNodeWithTag(Tag)) {
+            assertTextEquals("hello")
+            assertSelection(TextRange(2))
+        }
+    }
+
+    @Test
+    fun selectionSemanticsAreSet_afterRecomposition() {
+        val state = TextFieldState(TextFieldValue("hello"))
+        rule.setContent {
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag)
+            )
+        }
+
+        with(rule.onNodeWithTag(Tag)) {
+            assertTextEquals("hello")
+            assertSelection(TextRange.Zero)
+        }
+
+        state.editProcessor.reset(TextFieldValue("hello", selection = TextRange(2)))
+
+        with(rule.onNodeWithTag(Tag)) {
+            assertTextEquals("hello")
+            assertSelection(TextRange(2))
+        }
+    }
+
+    @OptIn(ExperimentalTestApi::class)
+    @Test
+    fun inputSelection_changesSelectionState() {
+        val state = TextFieldState(TextFieldValue("hello"))
+        rule.setContent {
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag)
+            )
+        }
+
+        rule.onNodeWithTag(Tag).performTextInputSelection(TextRange(2))
+
+        rule.runOnIdle {
+            assertThat(state.value.selection).isEqualTo(TextRange(2))
+        }
+    }
+
+    @Test
+    fun textLayoutResultSemanticsAreSet_inTheFirstComposition() {
+        val state = TextFieldState(TextFieldValue("hello"))
+        rule.setContent {
+            BasicTextField2(
+                state = state,
+                modifier = Modifier
+                    .testTag(Tag)
+            )
+        }
+
+        rule.onNodeWithTag(Tag).assertTextEquals("hello")
+        assertThat(rule.onNodeWithTag(Tag).fetchTextLayoutResult().layoutInput.text.text)
+            .isEqualTo("hello")
+    }
+
+    @Test
+    fun textLayoutResultSemanticsAreUpdated_afterRecomposition() {
+        val state = TextFieldState()
+        rule.setContent {
+            BasicTextField2(
+                state = state,
+                modifier = Modifier
+                    .testTag(Tag)
+            )
+        }
+
+        rule.onNodeWithTag(Tag).assertTextEquals("")
+        rule.onNodeWithTag(Tag).performTextInput("hello")
+        assertThat(rule.onNodeWithTag(Tag).fetchTextLayoutResult().layoutInput.text.text)
+            .isEqualTo("hello")
+    }
+
+    @Test
+    fun semanticsAreSet_afterStateObjectChanges() {
+        val state1 = TextFieldState(TextFieldValue("hello"))
+        val state2 = TextFieldState(TextFieldValue("world", TextRange(2)))
+        var chosenState by mutableStateOf(true)
+        rule.setContent {
+            BasicTextField2(
+                state = if (chosenState) state1 else state2,
+                modifier = Modifier.testTag(Tag)
+            )
+        }
+
+        with(rule.onNodeWithTag(Tag)) {
+            assertTextEquals("hello")
+            assertSelection(TextRange.Zero)
+        }
+
+        chosenState = false
+
+        with(rule.onNodeWithTag(Tag)) {
+            assertTextEquals("world")
+            assertSelection(TextRange(2))
+        }
+    }
+
+    private fun SemanticsNodeInteraction.assertSelection(expected: TextRange) {
+        val selection = fetchSemanticsNode().config
+            .getOrNull(SemanticsProperties.TextSelectionRange)
+        assertThat(selection).isEqualTo(expected)
+    }
+
+    private fun SemanticsNodeInteraction.assertEditableTextEquals(
+        value: String
+    ): SemanticsNodeInteraction =
+        assert(
+            SemanticsMatcher("${SemanticsProperties.EditableText.name} = '$value'") {
+                it.config.getOrNull(SemanticsProperties.EditableText)?.text.equals(value)
+            }
+        )
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2Test.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2Test.kt
index 58d013b..90ba369 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2Test.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2Test.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalFoundationApi::class)
-
 package androidx.compose.foundation.text2
 
 import androidx.compose.foundation.ExperimentalFoundationApi
@@ -49,10 +47,12 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
+@OptIn(ExperimentalFoundationApi::class)
 @MediumTest
 @RunWith(AndroidJUnit4::class)
 class BasicTextField2Test {
@@ -230,6 +230,7 @@
         }
     }
 
+    @Ignore // b/273412941
     @Test
     fun textField_focus_doesNotShowSoftwareKeyboard_ifDisabled() {
         val state = TextFieldState()
@@ -265,7 +266,7 @@
         rule.setContent {
             BasicTextField2(
                 state = state,
-                enabled = false,
+                enabled = true,
                 modifier = Modifier
                     .fillMaxSize()
                     .testTag(Tag)
@@ -286,7 +287,7 @@
         rule.setContent {
             BasicTextField2(
                 state = state,
-                enabled = false,
+                enabled = true,
                 modifier = Modifier
                     .fillMaxSize()
                     .testTag(Tag)
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/DecorationBoxTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/DecorationBoxTest.kt
new file mode 100644
index 0000000..9a50808
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/DecorationBoxTest.kt
@@ -0,0 +1,183 @@
+/*
+ * 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.text2
+
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertIsFocused
+import androidx.compose.ui.test.click
+import androidx.compose.ui.test.hasParent
+import androidx.compose.ui.test.hasSetTextAction
+import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.isFocused
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.longClick
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalFoundationApi::class)
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class DecorationBoxTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    private val Tag = "BasicTextField2"
+    private val DecorationTag = "DecorationBox"
+
+    @Test
+    fun focusIsAppliedOnDecoratedComposable() {
+        // create a decorated BasicTextField2
+        val state = TextFieldState()
+        rule.setContent {
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag),
+                decorationBox = { innerTextField ->
+                    Box(
+                        modifier = Modifier
+                            .border(BorderStroke(2.dp, SolidColor(Color.Red)))
+                            .padding(16.dp)
+                            .testTag(DecorationTag)
+                    ) {
+                        innerTextField()
+                    }
+                }
+            )
+        }
+
+        // requestFocus on node
+        rule.onNodeWithTag(Tag).performClick()
+
+        // assertThat decoration modifier has a focused parent.
+        rule.onNodeWithTag(DecorationTag, useUnmergedTree = true).assert(hasParent(isFocused()))
+    }
+
+    @Test
+    fun semanticsAreAppliedOnDecoratedComposable() {
+        // create a decorated BasicTextField2
+        val state = TextFieldState(TextFieldValue("hello"))
+        rule.setContent {
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag),
+                decorationBox = { innerTextField ->
+                    Box(
+                        modifier = Modifier
+                            .border(BorderStroke(2.dp, SolidColor(Color.Red)))
+                            .padding(16.dp)
+                            .testTag(DecorationTag)
+                    ) {
+                        innerTextField()
+                    }
+                }
+            )
+        }
+
+        // assertThat decoration modifier has a focused parent.
+        with(rule.onNodeWithTag(DecorationTag, useUnmergedTree = true)) {
+            assert(hasParent(hasText("hello")))
+            assert(hasParent(hasSetTextAction()))
+        }
+    }
+
+    @Test
+    fun clickGestureIsAppliedOnDecoratedComposable() {
+        // create a decorated BasicTextField2
+        val state = TextFieldState(TextFieldValue("hello"))
+        rule.setContent {
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag),
+                decorationBox = { innerTextField ->
+                    Box(
+                        modifier = Modifier
+                            .border(BorderStroke(2.dp, SolidColor(Color.Red)))
+                            .padding(16.dp)
+                            .testTag(DecorationTag)
+                    ) {
+                        innerTextField()
+                    }
+                }
+            )
+        }
+
+        // click on decoration box
+        rule.onNodeWithTag(DecorationTag, useUnmergedTree = true).performTouchInput {
+            // should be on the box not on inner text field since there is a padding
+            click(Offset(1f, 1f))
+        }
+
+        // assertThat textfield has focus
+        rule.onNodeWithTag(Tag).assertIsFocused()
+    }
+
+    @Ignore // TODO(halilibo): enable when pointerInput gestures are enabled
+    @Test
+    fun longClickGestureIsAppliedOnDecoratedComposable() {
+        // create a decorated BasicTextField2
+        val state = TextFieldState(TextFieldValue("hello"))
+        rule.setContent {
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag),
+                decorationBox = { innerTextField ->
+                    Box(
+                        modifier = Modifier
+                            .border(BorderStroke(2.dp, SolidColor(Color.Red)))
+                            .padding(16.dp)
+                            .testTag(DecorationTag)
+                    ) {
+                        innerTextField()
+                    }
+                }
+            )
+        }
+
+        // click on decoration box
+        rule.onNodeWithTag(DecorationTag, useUnmergedTree = true).performTouchInput {
+            // should be on the box not on inner text field since there is a padding
+            longClick(Offset(1f, 1f))
+        }
+
+        // assertThat selection happened
+        rule.runOnIdle {
+            assertThat(state.value.selection).isEqualTo(TextRange(0, 5))
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/HeightInLinesModifierTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/HeightInLinesModifierTest.kt
new file mode 100644
index 0000000..8777e2d1
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/HeightInLinesModifierTest.kt
@@ -0,0 +1,358 @@
+/*
+ * 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(ExperimentalFoundationApi::class)
+
+package androidx.compose.foundation.text2
+
+import android.content.Context
+import android.graphics.Typeface
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.requiredWidth
+import androidx.compose.foundation.text.TEST_FONT
+import androidx.compose.foundation.text.heightInLines
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalFontFamilyResolver
+import androidx.compose.ui.platform.ValueElement
+import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.text.ExperimentalTextApi
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.AndroidFont
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontLoadingStrategy
+import androidx.compose.ui.text.font.FontStyle
+import androidx.compose.ui.text.font.FontVariation
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.font.createFontFamilyResolver
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class HeightInLinesModifierTest {
+
+    private val longText = "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 context = InstrumentationRegistry.getInstrumentation().context
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun minLines_shortInputText() {
+        var subjectLayout: TextLayoutResult? = null
+        var subjectHeight: Int? = null
+        var twoLineHeight: Int? = null
+        val positionedLatch = CountDownLatch(1)
+        val twoLinePositionedLatch = CountDownLatch(1)
+
+        rule.setContent {
+            HeightObservingText(
+                onGlobalHeightPositioned = {
+                    subjectHeight = it
+                    positionedLatch.countDown()
+                },
+                onTextLayoutResult = {
+                    subjectLayout = it
+                },
+                text = "abc",
+                minLines = 2
+            )
+            HeightObservingText(
+                onGlobalHeightPositioned = {
+                    twoLineHeight = it
+                    twoLinePositionedLatch.countDown()
+                },
+                onTextLayoutResult = {},
+                text = "1\n2",
+                minLines = 2
+            )
+        }
+        assertThat(positionedLatch.await(1, TimeUnit.SECONDS)).isTrue()
+        assertThat(twoLinePositionedLatch.await(1, TimeUnit.SECONDS)).isTrue()
+
+        rule.runOnIdle {
+            assertThat(subjectLayout).isNotNull()
+            assertThat(subjectLayout!!.lineCount).isEqualTo(1)
+            assertThat(subjectHeight!!).isEqualTo(twoLineHeight)
+        }
+    }
+
+    @Test
+    fun maxLines_shortInputText() {
+        val (textLayoutResult, height) = setTextFieldWithMaxLines(
+            text = "abc",
+            maxLines = 5
+        )
+
+        rule.runOnIdle {
+            assertThat(textLayoutResult).isNotNull()
+            assertThat(textLayoutResult!!.lineCount).isEqualTo(1)
+            assertThat(textLayoutResult.size.height).isEqualTo(height)
+        }
+    }
+
+    @Test
+    fun maxLines_notApplied_infiniteMaxLines() {
+        val (textLayoutResult, height) =
+            setTextFieldWithMaxLines(longText, Int.MAX_VALUE)
+
+        rule.runOnIdle {
+            assertThat(textLayoutResult).isNotNull()
+            assertThat(textLayoutResult!!.size.height).isEqualTo(height)
+        }
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun minLines_invalidValue() {
+        rule.setContent {
+            Box(
+                modifier = Modifier.heightInLines(textStyle = TextStyle.Default, minLines = 0)
+            )
+        }
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun maxLines_invalidValue() {
+        rule.setContent {
+            Box(
+                modifier = Modifier.heightInLines(textStyle = TextStyle.Default, maxLines = 0)
+            )
+        }
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun minLines_greaterThan_maxLines_invalidValue() {
+        rule.setContent {
+            Box(
+                modifier = Modifier.heightInLines(
+                    textStyle = TextStyle.Default,
+                    minLines = 2,
+                    maxLines = 1
+                )
+            )
+        }
+    }
+
+    @Test
+    fun minLines_longInputText() {
+        val (textLayoutResult, height) = setTextFieldWithMaxLines(
+            text = longText,
+            minLines = 2
+        )
+
+        rule.runOnIdle {
+            assertThat(textLayoutResult).isNotNull()
+            // should be in the 20s, but use this to create invariant for the next assertion
+            assertThat(textLayoutResult!!.lineCount).isGreaterThan(2)
+            assertThat(textLayoutResult.size.height).isEqualTo(height)
+        }
+    }
+
+    @Test
+    fun maxLines_longInputText() {
+        var subjectLayout: TextLayoutResult? = null
+        var subjectHeight: Int? = null
+        var twoLineHeight: Int? = null
+        val positionedLatch = CountDownLatch(1)
+        val twoLinePositionedLatch = CountDownLatch(1)
+
+        rule.setContent {
+            HeightObservingText(
+                onGlobalHeightPositioned = {
+                    subjectHeight = it
+                    positionedLatch.countDown()
+                },
+                onTextLayoutResult = {
+                    subjectLayout = it
+                },
+                text = longText,
+                maxLines = 2
+            )
+            HeightObservingText(
+                onGlobalHeightPositioned = {
+                    twoLineHeight = it
+                    twoLinePositionedLatch.countDown()
+                },
+                onTextLayoutResult = {},
+                text = "1\n2",
+                maxLines = 2
+            )
+        }
+        assertThat(positionedLatch.await(1, TimeUnit.SECONDS)).isTrue()
+        assertThat(twoLinePositionedLatch.await(1, TimeUnit.SECONDS)).isTrue()
+
+        rule.runOnIdle {
+            assertThat(subjectLayout).isNotNull()
+            // should be in the 20s, but use this to create invariant for the next assertion
+            assertThat(subjectLayout!!.lineCount).isGreaterThan(2)
+            assertThat(subjectHeight!!).isEqualTo(twoLineHeight)
+        }
+    }
+
+    @OptIn(ExperimentalTextApi::class, ExperimentalCoroutinesApi::class)
+    @Test
+    fun asyncFontLoad_changesLineHeight() {
+        val testDispatcher = UnconfinedTestDispatcher()
+        val resolver = createFontFamilyResolver(context, testDispatcher)
+
+        val typefaceDeferred = CompletableDeferred<Typeface>()
+        val asyncLoader = object : AndroidFont.TypefaceLoader {
+            override fun loadBlocking(context: Context, font: AndroidFont): Typeface =
+                TODO("Not yet implemented")
+
+            override suspend fun awaitLoad(context: Context, font: AndroidFont): Typeface {
+                return typefaceDeferred.await()
+            }
+        }
+        val fontFamily = FontFamily(
+            object : AndroidFont(FontLoadingStrategy.Async, asyncLoader, FontVariation.Settings()) {
+                override val weight: FontWeight = FontWeight.Normal
+                override val style: FontStyle = FontStyle.Normal
+            },
+            TEST_FONT
+        )
+
+        val heights = mutableListOf<Int>()
+
+        rule.setContent {
+            CompositionLocalProvider(
+                LocalFontFamilyResolver provides resolver,
+                LocalDensity provides Density(1.0f, 1f)
+            ) {
+                HeightObservingText(
+                    onGlobalHeightPositioned = {
+                        heights.add(it)
+                    },
+                    onTextLayoutResult = {},
+                    text = longText,
+                    maxLines = 10,
+                    textStyle = TextStyle.Default.copy(
+                        fontFamily = fontFamily,
+                        fontSize = 80.sp
+                    )
+                )
+            }
+        }
+
+        val before = heights.toList()
+        typefaceDeferred.complete(Typeface.create("cursive", Typeface.BOLD_ITALIC))
+
+        rule.runOnIdle {
+            assertThat(heights.size).isGreaterThan(before.size)
+            assertThat(heights.distinct().size).isGreaterThan(before.distinct().size)
+        }
+    }
+
+    @Test
+    fun testInspectableValue() {
+        isDebugInspectorInfoEnabled = true
+
+        val modifier = Modifier.heightInLines(
+            textStyle = TextStyle.Default,
+            minLines = 5,
+            maxLines = 10
+        ) as InspectableValue
+        assertThat(modifier.nameFallback).isEqualTo("heightInLines")
+        assertThat(modifier.inspectableElements.asIterable()).containsExactly(
+            ValueElement("minLines", 5),
+            ValueElement("maxLines", 10),
+            ValueElement("textStyle", TextStyle.Default)
+        )
+
+        isDebugInspectorInfoEnabled = false
+    }
+
+    private fun setTextFieldWithMaxLines(
+        text: String,
+        minLines: Int = 1,
+        maxLines: Int = Int.MAX_VALUE
+    ): Pair<TextLayoutResult?, Int?> {
+        var textLayoutResult: TextLayoutResult? = null
+        var height: Int? = null
+        val positionedLatch = CountDownLatch(1)
+
+        rule.setContent {
+            HeightObservingText(
+                onGlobalHeightPositioned = {
+                    height = it
+                    positionedLatch.countDown()
+                },
+                onTextLayoutResult = {
+                    textLayoutResult = it
+                },
+                text = text,
+                minLines = minLines,
+                maxLines = maxLines
+            )
+        }
+        assertThat(positionedLatch.await(1, TimeUnit.SECONDS)).isTrue()
+
+        return Pair(textLayoutResult, height)
+    }
+
+    @Composable
+    private fun HeightObservingText(
+        onGlobalHeightPositioned: (Int) -> Unit,
+        onTextLayoutResult: Density.(TextLayoutResult) -> Unit,
+        text: String,
+        minLines: Int = 1,
+        maxLines: Int = Int.MAX_VALUE,
+        textStyle: TextStyle = TextStyle.Default
+    ) {
+        Box(
+            Modifier.onGloballyPositioned {
+                onGlobalHeightPositioned(it.size.height)
+            }
+        ) {
+            BasicTextField2(
+                state = remember { TextFieldState(TextFieldValue(text)) },
+                textStyle = textStyle,
+                minLines = minLines,
+                maxLines = maxLines,
+                modifier = Modifier.requiredWidth(100.dp),
+                onTextLayout = onTextLayoutResult
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldContentSemanticsTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldContentSemanticsTest.kt
deleted file mode 100644
index adcd8d6..0000000
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldContentSemanticsTest.kt
+++ /dev/null
@@ -1,121 +0,0 @@
-@file:OptIn(ExperimentalFoundationApi::class)
-
-package androidx.compose.foundation.text2
-
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.layout.Box
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.semantics.SemanticsProperties
-import androidx.compose.ui.semantics.getOrNull
-import androidx.compose.ui.test.SemanticsNodeInteraction
-import androidx.compose.ui.test.assertTextEquals
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithTag
-import androidx.compose.ui.text.TextRange
-import androidx.compose.ui.text.input.TextFieldValue
-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
-
-@LargeTest
-@RunWith(AndroidJUnit4::class)
-class TextFieldContentSemanticsTest {
-    @get:Rule
-    val rule = createComposeRule()
-
-    private val Tag = "TextField"
-
-    @Test
-    fun contentSemanticsAreSet_inTheFirstComposition() {
-        val state = TextFieldState(TextFieldValue("hello"))
-        rule.setContent {
-            Box(modifier = Modifier.testTag(Tag).then(TextFieldContentSemanticsElement(state)))
-        }
-
-        rule.onNodeWithTag(Tag).assertTextEquals("hello")
-    }
-
-    @Test
-    fun contentSemanticsAreSet_afterRecomposition() {
-        val state = TextFieldState(TextFieldValue("hello"))
-        rule.setContent {
-            Box(modifier = Modifier.testTag(Tag).then(TextFieldContentSemanticsElement(state)))
-        }
-
-        rule.onNodeWithTag(Tag).assertTextEquals("hello")
-
-        state.editProcessor.reset(TextFieldValue("hello2"))
-
-        rule.onNodeWithTag(Tag).assertTextEquals("hello2")
-    }
-
-    @Test
-    fun selectionSemanticsAreSet_inTheFirstComposition() {
-        val state = TextFieldState(TextFieldValue("hello", selection = TextRange(2)))
-        rule.setContent {
-            Box(modifier = Modifier.testTag(Tag).then(TextFieldContentSemanticsElement(state)))
-        }
-
-        with(rule.onNodeWithTag(Tag)) {
-            assertTextEquals("hello")
-            assertSelection(TextRange(2))
-        }
-    }
-
-    @Test
-    fun selectionSemanticsAreSet_afterRecomposition() {
-        val state = TextFieldState(TextFieldValue("hello"))
-        rule.setContent {
-            Box(modifier = Modifier.testTag(Tag).then(TextFieldContentSemanticsElement(state)))
-        }
-
-        with(rule.onNodeWithTag(Tag)) {
-            assertTextEquals("hello")
-            assertSelection(TextRange.Zero)
-        }
-
-        state.editProcessor.reset(TextFieldValue("hello", selection = TextRange(2)))
-
-        with(rule.onNodeWithTag(Tag)) {
-            assertTextEquals("hello")
-            assertSelection(TextRange(2))
-        }
-    }
-
-    @Test
-    fun semanticsAreSet_afterStateObjectChanges() {
-        val state1 = TextFieldState(TextFieldValue("hello"))
-        val state2 = TextFieldState(TextFieldValue("world", TextRange(2)))
-        var chosenState by mutableStateOf(true)
-        rule.setContent {
-            Box(modifier = Modifier.testTag(Tag).then(TextFieldContentSemanticsElement(
-                if (chosenState) state1 else state2
-            )))
-        }
-
-        with(rule.onNodeWithTag(Tag)) {
-            assertTextEquals("hello")
-            assertSelection(TextRange.Zero)
-        }
-
-        chosenState = false
-
-        with(rule.onNodeWithTag(Tag)) {
-            assertTextEquals("world")
-            assertSelection(TextRange(2))
-        }
-    }
-
-    private fun SemanticsNodeInteraction.assertSelection(expected: TextRange) {
-        val selection = fetchSemanticsNode().config
-            .getOrNull(SemanticsProperties.TextSelectionRange)
-        assertThat(selection).isEqualTo(expected)
-    }
-}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldCursorTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldCursorTest.kt
index 48e9db1..121f684 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldCursorTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldCursorTest.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalFoundationApi::class)
-
 package androidx.compose.foundation.text2
 
 import android.os.Build
@@ -23,11 +21,15 @@
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.TEST_FONT_FAMILY
+import androidx.compose.foundation.text.selection.LocalTextSelectionColors
+import androidx.compose.foundation.text.selection.TextSelectionColors
+import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshots.Snapshot
+import androidx.compose.testutils.assertDoesNotContainColor
 import androidx.compose.testutils.assertPixelColor
 import androidx.compose.testutils.assertPixels
 import androidx.compose.testutils.assertShape
@@ -41,12 +43,15 @@
 import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.graphics.SolidColor
 import androidx.compose.ui.graphics.toPixelMap
+import androidx.compose.ui.layout.layout
 import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.assertTextEquals
 import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.hasSetTextAction
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.performClick
-import androidx.compose.ui.test.performTextReplacement
+import androidx.compose.ui.test.performTextInput
+import androidx.compose.ui.test.performTextInputSelection
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.TextStyle
@@ -65,6 +70,7 @@
 import org.junit.Rule
 import org.junit.Test
 
+@OptIn(ExperimentalFoundationApi::class, ExperimentalTestApi::class)
 @LargeTest
 class TextFieldCursorTest {
 
@@ -78,38 +84,41 @@
         it.mainClock.autoAdvance = false
     }
 
-    private val boxPadding = 10.dp
+    private lateinit var state: TextFieldState
+
+    private val boxPadding = 8.dp
+    // Both TextField background and font color should be the same to make sure that only
+    // cursor is visible
+    private val contentColor = Color.White
     private val cursorColor = Color.Red
     private val textStyle = TextStyle(
-        color = Color.White,
-        background = Color.White,
-        fontSize = 10.sp
+        color = contentColor,
+        background = contentColor,
+        fontSize = 10.sp,
+        fontFamily = TEST_FONT_FAMILY
     )
 
-    private val textFieldWidth = 10.dp
-    private val textFieldHeight = 20.dp
-    private val textFieldBgColor = Color.White
     private var isFocused = false
-    private var cursorRect = Rect.Zero
+    private var textLayoutResult: TextLayoutResult? = null
+    private val cursorRect: Rect
+        // assume selection is collapsed
+        get() = textLayoutResult?.getCursorRect(state.value.selection.start) ?: Rect.Zero
 
-    private val bgModifier = Modifier.background(textFieldBgColor)
+    private val backgroundModifier = Modifier.background(contentColor)
     private val focusModifier = Modifier.onFocusChanged { if (it.isFocused) isFocused = true }
-    private val sizeModifier = Modifier.size(textFieldWidth, textFieldHeight)
 
     // default TextFieldModifier
-    private val textFieldModifier = sizeModifier
-        .then(bgModifier)
+    private val textFieldModifier = Modifier
+        .then(backgroundModifier)
         .then(focusModifier)
 
     // default onTextLayout to capture cursor boundaries.
-    private val onTextLayout: Density.(TextLayoutResult) -> Unit = {
-        cursorRect = it.getCursorRect(0)
-    }
+    private val onTextLayout: Density.(TextLayoutResult) -> Unit = { textLayoutResult = it }
 
     @Test
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-    fun textFieldFocused_cursorRendered() = with(rule.density) {
-        val state = TextFieldState()
+    fun textFieldFocused_cursorRendered() {
+        state = TextFieldState()
         rule.setContent {
             Box(Modifier.padding(boxPadding)) {
                 BasicTextField2(
@@ -126,29 +135,26 @@
 
         rule.mainClock.advanceTimeBy(100)
 
-        with(rule.density) {
-            rule.onNode(hasSetTextAction())
-                .captureToImage()
-                .assertCursor(2.dp, this, cursorRect)
-        }
+        rule.onNode(hasSetTextAction())
+            .captureToImage()
+            .assertCursor(2.dp, cursorRect)
     }
 
     @Test
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-    fun textFieldFocused_cursorWithBrush() = with(rule.density) {
-        val state = TextFieldState()
+    fun textFieldFocused_cursorWithBrush() {
+        state = TextFieldState()
         rule.setContent {
             Box(Modifier.padding(boxPadding)) {
                 BasicTextField2(
                     state = state,
                     textStyle = textStyle.copy(fontSize = textStyle.fontSize * 2),
                     modifier = Modifier
-                        .size(textFieldWidth, textFieldHeight * 2)
-                        .then(bgModifier)
+                        .then(backgroundModifier)
                         .then(focusModifier),
                     cursorBrush = Brush.verticalGradient(
-                        // make a brush double/triple color at the beginning and end so we have stable
-                        // colors at the ends.
+                        // make a brush double/triple color at the beginning and end so we have
+                        // stable colors at the ends.
                         // Without triple bottom, the bottom color never hits to the provided color.
                         listOf(
                             Color.Blue,
@@ -179,8 +185,8 @@
 
     @Test
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-    fun cursorBlinkingAnimation() = with(rule.density) {
-        val state = TextFieldState()
+    fun cursorBlinkingAnimation() {
+        state = TextFieldState()
         rule.setContent {
             // The padding helps if the test is run accidentally in landscape. Landscape makes
             // the cursor to be next to the navigation bar which affects the red color to be a bit
@@ -203,7 +209,7 @@
         with(rule.density) {
             rule.onNode(hasSetTextAction())
                 .captureToImage()
-                .assertCursor(2.dp, this, cursorRect)
+                .assertCursor(2.dp, cursorRect)
         }
 
         // cursor invisible during next 500 ms
@@ -213,8 +219,8 @@
             .assertShape(
                 density = rule.density,
                 shape = RectangleShape,
-                shapeColor = Color.White,
-                backgroundColor = Color.White,
+                shapeColor = contentColor,
+                backgroundColor = contentColor,
                 shapeOverlapPixelCount = 0.0f
             )
     }
@@ -223,9 +229,9 @@
     @OptIn(ExperimentalTestApi::class)
     @Test
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-    fun cursorBlinkingAnimation_whenSystemDisablesAnimations() = with(rule.density) {
+    fun cursorBlinkingAnimation_whenSystemDisablesAnimations() {
         motionDurationScale.scaleFactor = 0f
-        val state = TextFieldState()
+        state = TextFieldState()
 
         rule.setContent {
             // The padding helps if the test is run accidentally in landscape. Landscape makes
@@ -249,7 +255,7 @@
         with(rule.density) {
             rule.onNode(hasSetTextAction())
                 .captureToImage()
-                .assertCursor(2.dp, this, cursorRect)
+                .assertCursor(2.dp, cursorRect)
         }
 
         // cursor invisible during next 500 ms
@@ -259,23 +265,23 @@
             .assertShape(
                 density = rule.density,
                 shape = RectangleShape,
-                shapeColor = Color.White,
-                backgroundColor = Color.White,
+                shapeColor = contentColor,
+                backgroundColor = contentColor,
                 shapeOverlapPixelCount = 0.0f
             )
     }
 
     @Test
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-    fun cursorUnsetColor_noCursor() = with(rule.density) {
+    fun cursorUnsetColor_noCursor() {
+        state = TextFieldState(TextFieldValue("hello", selection = TextRange(2)))
         rule.setContent {
             // The padding helps if the test is run accidentally in landscape. Landscape makes
             // the cursor to be next to the navigation bar which affects the red color to be a bit
             // different - possibly anti-aliasing.
             Box(Modifier.padding(boxPadding)) {
-                BasicTextField(
-                    value = "",
-                    onValueChange = {},
+                BasicTextField2(
+                    state = state,
                     textStyle = textStyle,
                     modifier = textFieldModifier,
                     cursorBrush = SolidColor(Color.Unspecified)
@@ -291,8 +297,8 @@
             .assertShape(
                 density = rule.density,
                 shape = RectangleShape,
-                shapeColor = Color.White,
-                backgroundColor = Color.White,
+                shapeColor = contentColor,
+                backgroundColor = contentColor,
                 shapeOverlapPixelCount = 0.0f
             )
 
@@ -303,16 +309,16 @@
             .assertShape(
                 density = rule.density,
                 shape = RectangleShape,
-                shapeColor = Color.White,
-                backgroundColor = Color.White,
+                shapeColor = contentColor,
+                backgroundColor = contentColor,
                 shapeOverlapPixelCount = 0.0f
             )
     }
 
     @Test
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-    fun cursorNotBlinking_whileTyping() = with(rule.density) {
-        val state = TextFieldState(TextFieldValue("test"))
+    fun cursorNotBlinking_whileTyping() {
+        state = TextFieldState(TextFieldValue("test", selection = TextRange(4)))
         rule.setContent {
             // The padding helps if the test is run accidentally in landscape. Landscape makes
             // the cursor to be next to the navigation bar which affects the red color to be a bit
@@ -337,23 +343,20 @@
 
         // change text field value
         rule.onNode(hasSetTextAction())
-            .performTextReplacement("")
+            .performTextInput("s")
 
         // cursor would have been invisible during next 500 ms if cursor blinks while typing.
         // To prevent blinking while typing we restart animation when new symbol is typed.
-        rule.mainClock.advanceTimeBy(400)
-        with(rule.density) {
-            rule.onNode(hasSetTextAction())
-                .captureToImage()
-                .assertCursor(2.dp, this, cursorRect)
-        }
+        rule.mainClock.advanceTimeBy(300)
+        rule.onNode(hasSetTextAction())
+            .captureToImage()
+            .assertCursor(2.dp, cursorRect)
     }
 
     @Test
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-    fun selectionChanges_cursorNotBlinking() = with(rule.density) {
-        rule.mainClock.autoAdvance = false
-        val state = TextFieldState(TextFieldValue("test", selection = TextRange(2)))
+    fun selectionChanges_cursorNotBlinking() {
+        state = TextFieldState(TextFieldValue("test", selection = TextRange(2)))
         rule.setContent {
             // The padding helps if the test is run accidentally in landscape. Landscape makes
             // the cursor to be next to the navigation bar which affects the red color to be a bit
@@ -378,25 +381,22 @@
         // TODO(b/170298051) check here that cursor is visible when we have a way to control
         //  cursor position when sending a text
 
-        rule.runOnIdle {
-            state.editProcessor.reset(state.value.copy(selection = TextRange(0)))
-        }
+        rule.onNode(hasSetTextAction())
+            .performTextInputSelection(TextRange(0))
 
         // necessary for animation to start (shows cursor again)
         rule.mainClock.advanceTimeByFrame()
 
-        with(rule.density) {
-            rule.onNode(hasSetTextAction())
-                .captureToImage()
-                .assertCursor(2.dp, this, cursorRect)
-        }
+        rule.onNode(hasSetTextAction())
+            .captureToImage()
+            .assertCursor(2.dp, cursorRect)
     }
 
     @Test
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
     fun brushChanged_doesntResetTimer() {
         var cursorBrush by mutableStateOf(SolidColor(cursorColor))
-        val state = TextFieldState()
+        state = TextFieldState()
         rule.setContent {
             Box(Modifier.padding(boxPadding)) {
                 BasicTextField2(
@@ -420,21 +420,59 @@
             .assertShape(
                 density = rule.density,
                 shape = RectangleShape,
-                shapeColor = Color.White,
-                backgroundColor = Color.White,
+                shapeColor = contentColor,
+                backgroundColor = contentColor,
                 shapeOverlapPixelCount = 0.0f
             )
     }
 
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    fun selectionNotCollapsed_cursorNotDrawn() {
+        state = TextFieldState(TextFieldValue("test", selection = TextRange(2, 3)))
+        rule.setContent {
+            // The padding helps if the test is run accidentally in landscape. Landscape makes
+            // the cursor to be next to the navigation bar which affects the red color to be a bit
+            // different - possibly anti-aliasing.
+            Box(Modifier.padding(boxPadding)) {
+                // set selection highlight to a known color
+                CompositionLocalProvider(
+                    LocalTextSelectionColors provides TextSelectionColors(Color.Blue, Color.Blue)
+                ) {
+                    BasicTextField2(
+                        state = state,
+                        // make sure that background is not obstructing selection
+                        textStyle = textStyle.copy(
+                            background = Color.Unspecified
+                        ),
+                        modifier = textFieldModifier,
+                        cursorBrush = SolidColor(cursorColor),
+                        onTextLayout = onTextLayout
+                    )
+                }
+            }
+        }
+
+        focusAndWait()
+
+        // cursor should still be visible if there wasn't a selection
+        rule.mainClock.advanceTimeBy(300)
+        rule.mainClock.advanceTimeByFrame()
+
+        rule.onNode(hasSetTextAction())
+            .captureToImage()
+            .assertDoesNotContainColor(cursorColor)
+    }
+
     private fun focusAndWait() {
         rule.onNode(hasSetTextAction()).performClick()
         rule.mainClock.advanceTimeUntil { isFocused }
     }
 
-    private fun ImageBitmap.assertCursor(cursorWidth: Dp, density: Density, cursorRect: Rect) {
+    private fun ImageBitmap.assertCursor(cursorWidth: Dp, cursorRect: Rect) {
         assertThat(cursorRect.height).isNotEqualTo(0f)
         assertThat(cursorRect).isNotEqualTo(Rect.Zero)
-        val cursorWidthPx = (with(density) { cursorWidth.roundToPx() })
+        val cursorWidthPx = (with(rule.density) { cursorWidth.roundToPx() })
 
         // assert cursor width is greater than 2 since we will shrink the check area by 1 on each
         // side
@@ -469,8 +507,44 @@
                 null
             } else {
                 // text field background
-                textFieldBgColor
+                contentColor
             }
         }
     }
+
+    @Test
+    fun textFieldCursor_alwaysReadLatestState_duringDraw() {
+        state = TextFieldState(TextFieldValue("hello world", TextRange(5)))
+        rule.setContent {
+            Box(Modifier.padding(boxPadding)) {
+                BasicTextField2(
+                    state = state,
+                    textStyle = textStyle,
+                    modifier = textFieldModifier.layout { measurable, constraints ->
+                        // change the state during layout so draw can read the new state
+                        val currValue = Snapshot.withoutReadObservation { state.value }
+                        if (currValue.text.isNotEmpty()) {
+                            val newText = currValue.text.dropLast(1)
+                            val newValue = TextFieldValue(newText, TextRange(newText.length))
+                            state.editProcessor.reset(newValue)
+                        }
+
+                        val p = measurable.measure(constraints)
+                        layout(p.width, p.height) {
+                            p.place(0, 0)
+                        }
+                    },
+                    cursorBrush = SolidColor(cursorColor),
+                    onTextLayout = onTextLayout
+                )
+            }
+        }
+
+        focusAndWait()
+
+        rule.waitForIdle()
+
+        rule.onNode(hasSetTextAction()).assertTextEquals("")
+        // this test just needs to finish without crashing. There is no other assertion
+    }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldFocusTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldFocusTest.kt
index 1bc6713..858dd48 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldFocusTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldFocusTest.kt
@@ -1,5 +1,3 @@
-@file:OptIn(ExperimentalFoundationApi::class)
-
 package androidx.compose.foundation.text2
 
 import android.os.SystemClock
@@ -49,6 +47,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
+@OptIn(ExperimentalFoundationApi::class)
 @LargeTest
 @RunWith(AndroidJUnit4::class)
 class TextFieldFocusTest {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldKeyEventTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldKeyEventTest.kt
new file mode 100644
index 0000000..9b6d1b6
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldKeyEventTest.kt
@@ -0,0 +1,551 @@
+/*
+ * 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.text2
+
+import android.view.KeyEvent
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.text.TEST_FONT_FAMILY
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.input.key.nativeKeyCode
+import androidx.compose.ui.platform.LocalClipboardManager
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalTextInputService
+import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.hasSetTextAction
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.performKeyPress
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.text.input.TextInputService
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth
+import com.nhaarman.mockitokotlin2.mock
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalComposeUiApi::class, ExperimentalFoundationApi::class)
+class TextFieldKeyEventTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    private var defaultDensity = Density(1f)
+
+    @Test
+    fun textField_typedEvents() {
+        keysSequenceTest {
+            Key.H.downAndUp()
+            Key.I.downAndUp(KeyEvent.META_SHIFT_ON)
+            expectedText("hI")
+        }
+    }
+
+    @Ignore // re-enable after copy-cut-paste is supported
+    @Test
+    fun textField_copyPaste() {
+        keysSequenceTest(initText = "hello") {
+            Key.A.downAndUp(KeyEvent.META_CTRL_ON)
+            Key.C.downAndUp(KeyEvent.META_CTRL_ON)
+            Key.DirectionRight.downAndUp()
+            Key.Spacebar.downAndUp()
+            Key.V.downAndUp(KeyEvent.META_CTRL_ON)
+            expectedText("hello hello")
+        }
+    }
+
+    @Ignore // re-enable after copy-cut-paste is supported
+    @Test
+    fun textField_directCopyPaste() {
+        keysSequenceTest(initText = "hello") {
+            Key.A.downAndUp(KeyEvent.META_CTRL_ON)
+            Key.Copy.downAndUp()
+            expectedText("hello")
+            Key.DirectionRight.downAndUp()
+            Key.Spacebar.downAndUp()
+            Key.Paste.downAndUp()
+            expectedText("hello hello")
+        }
+    }
+
+    @Ignore // re-enable after copy-cut-paste is supported
+    @Test
+    fun textField_directCutPaste() {
+        keysSequenceTest(initText = "hello") {
+            Key.A.downAndUp(KeyEvent.META_CTRL_ON)
+            Key.Cut.downAndUp()
+            expectedText("")
+            Key.Paste.downAndUp()
+            expectedText("hello")
+        }
+    }
+
+    @Test
+    fun textField_linesNavigation() {
+        keysSequenceTest(initText = "hello\nworld") {
+            Key.DirectionDown.downAndUp()
+            Key.A.downAndUp()
+            Key.DirectionUp.downAndUp()
+            Key.A.downAndUp()
+            expectedText("haello\naworld")
+            Key.DirectionUp.downAndUp()
+            Key.A.downAndUp()
+            expectedText("ahaello\naworld")
+        }
+    }
+
+    @Test
+    fun textField_linesNavigation_cache() {
+        keysSequenceTest(initText = "hello\n\nworld") {
+            Key.DirectionRight.downAndUp()
+            Key.DirectionDown.downAndUp()
+            Key.DirectionDown.downAndUp()
+            Key.Zero.downAndUp()
+            expectedText("hello\n\nw0orld")
+        }
+    }
+
+    @Test
+    fun textField_newLine() {
+        keysSequenceTest(initText = "hello") {
+            Key.Enter.downAndUp()
+            expectedText("\nhello")
+        }
+    }
+
+    @Test
+    fun textField_backspace() {
+        keysSequenceTest(initText = "hello") {
+            Key.DirectionRight.downAndUp()
+            Key.DirectionRight.downAndUp()
+            Key.Backspace.downAndUp()
+            expectedText("hllo")
+        }
+    }
+
+    @Test
+    fun textField_delete() {
+        keysSequenceTest(initText = "hello") {
+            Key.Delete.downAndUp()
+            expectedText("ello")
+        }
+    }
+
+    @Test
+    fun textField_delete_atEnd() {
+        val text = "hello"
+        val state = TextFieldState(
+            TextFieldValue(
+                text,
+                // Place cursor at end.
+                selection = TextRange(text.length)
+            )
+        )
+        keysSequenceTest(state = state) {
+            Key.Delete.downAndUp()
+            expectedText("hello")
+        }
+    }
+
+    @Test
+    fun textField_delete_whenEmpty() {
+        keysSequenceTest(initText = "") {
+            Key.Delete.downAndUp()
+            expectedText("")
+        }
+    }
+
+    @Test
+    fun textField_nextWord() {
+        keysSequenceTest(initText = "hello world") {
+            Key.DirectionRight.downAndUp(KeyEvent.META_CTRL_ON)
+            Key.Zero.downAndUp()
+            expectedText("hello0 world")
+            Key.DirectionRight.downAndUp(KeyEvent.META_CTRL_ON)
+            Key.Zero.downAndUp()
+            expectedText("hello0 world0")
+        }
+    }
+
+    @Test
+    fun textField_nextWord_doubleSpace() {
+        keysSequenceTest(initText = "hello  world") {
+            Key.DirectionRight.downAndUp(KeyEvent.META_CTRL_ON)
+            Key.DirectionRight.downAndUp()
+            Key.DirectionRight.downAndUp(KeyEvent.META_CTRL_ON)
+            Key.Zero.downAndUp()
+            expectedText("hello  world0")
+        }
+    }
+
+    @Test
+    fun textField_prevWord() {
+        keysSequenceTest(initText = "hello world") {
+            Key.DirectionRight.downAndUp(KeyEvent.META_CTRL_ON)
+            Key.DirectionRight.downAndUp(KeyEvent.META_CTRL_ON)
+            Key.DirectionLeft.downAndUp(KeyEvent.META_CTRL_ON)
+            Key.Zero.downAndUp()
+            expectedText("hello 0world")
+        }
+    }
+
+    @Test
+    fun textField_HomeAndEnd() {
+        keysSequenceTest(initText = "hello world") {
+            Key.MoveEnd.downAndUp()
+            Key.Zero.downAndUp()
+            Key.MoveHome.downAndUp()
+            Key.Zero.downAndUp()
+            expectedText("0hello world0")
+        }
+    }
+
+    @Test
+    fun textField_byWordSelection() {
+        keysSequenceTest(initText = "hello  world\nhi") {
+            Key.DirectionRight.downAndUp(KeyEvent.META_SHIFT_ON or KeyEvent.META_CTRL_ON)
+            expectedSelection(TextRange(0, 5))
+            Key.DirectionRight.downAndUp(KeyEvent.META_SHIFT_ON or KeyEvent.META_CTRL_ON)
+            expectedSelection(TextRange(0, 12))
+            Key.DirectionRight.downAndUp(KeyEvent.META_SHIFT_ON or KeyEvent.META_CTRL_ON)
+            expectedSelection(TextRange(0, 15))
+            Key.DirectionLeft.downAndUp(KeyEvent.META_SHIFT_ON or KeyEvent.META_CTRL_ON)
+            expectedSelection(TextRange(0, 13))
+        }
+    }
+
+    @Test
+    fun textField_lineEndStart() {
+        keysSequenceTest(initText = "hello world\nhi") {
+            Key.MoveEnd.downAndUp()
+            Key.Zero.downAndUp()
+            expectedText("hello world0\nhi")
+            Key.MoveEnd.downAndUp()
+            Key.MoveHome.downAndUp()
+            Key.Zero.downAndUp()
+            expectedText("0hello world0\nhi")
+            Key.MoveEnd.downAndUp(KeyEvent.META_SHIFT_ON)
+            expectedSelection(TextRange(1, 16))
+        }
+    }
+
+    @Test
+    fun textField_deleteWords() {
+        keysSequenceTest(initText = "hello world\nhi world") {
+            Key.MoveEnd.downAndUp()
+            Key.Backspace.downAndUp(KeyEvent.META_CTRL_ON)
+            expectedText("hello \nhi world")
+            Key.Delete.downAndUp(KeyEvent.META_CTRL_ON)
+            expectedText("hello  world")
+        }
+    }
+
+    @Test
+    fun textField_deleteToBeginningOfLine() {
+        keysSequenceTest(initText = "hello world\nhi world") {
+            Key.DirectionRight.downAndUp(KeyEvent.META_CTRL_ON)
+            Key.Backspace.downAndUp(KeyEvent.META_ALT_ON)
+            expectedText(" world\nhi world")
+            Key.Backspace.downAndUp(KeyEvent.META_ALT_ON)
+            expectedText(" world\nhi world")
+            repeat(3) { Key.DirectionRight.downAndUp() }
+            Key.Backspace.downAndUp(KeyEvent.META_ALT_ON)
+            expectedText("rld\nhi world")
+            Key.DirectionDown.downAndUp()
+            Key.MoveEnd.downAndUp()
+            Key.Backspace.downAndUp(KeyEvent.META_ALT_ON)
+            expectedText("rld\n")
+            Key.Backspace.downAndUp(KeyEvent.META_ALT_ON)
+            expectedText("rld\n")
+        }
+    }
+
+    @Test
+    fun textField_deleteToEndOfLine() {
+        keysSequenceTest(initText = "hello world\nhi world") {
+            Key.DirectionRight.downAndUp(KeyEvent.META_CTRL_ON)
+            Key.Delete.downAndUp(KeyEvent.META_ALT_ON)
+            expectedText("hello\nhi world")
+            Key.Delete.downAndUp(KeyEvent.META_ALT_ON)
+            expectedText("hello\nhi world")
+            repeat(3) { Key.DirectionRight.downAndUp() }
+            Key.Delete.downAndUp(KeyEvent.META_ALT_ON)
+            expectedText("hello\nhi")
+            Key.MoveHome.downAndUp()
+            Key.Delete.downAndUp(KeyEvent.META_ALT_ON)
+            expectedText("hello\n")
+            Key.Delete.downAndUp(KeyEvent.META_ALT_ON)
+            expectedText("hello\n")
+        }
+    }
+
+    @Test
+    fun textField_paragraphNavigation() {
+        keysSequenceTest(initText = "hello world\nhi") {
+            Key.DirectionDown.downAndUp(KeyEvent.META_CTRL_ON)
+            Key.Zero.downAndUp()
+            expectedText("hello world0\nhi")
+            Key.DirectionDown.downAndUp(KeyEvent.META_CTRL_ON)
+            Key.DirectionUp.downAndUp(KeyEvent.META_CTRL_ON)
+            Key.Zero.downAndUp()
+            expectedText("hello world0\n0hi")
+        }
+    }
+
+    @Ignore // TODO(halilibo): Remove ignore when backing buffer supports reversed selection
+    @Test
+    fun textField_selectionCaret() {
+        keysSequenceTest(initText = "hello world") {
+            Key.DirectionRight.downAndUp(KeyEvent.META_CTRL_ON or KeyEvent.META_SHIFT_ON)
+            expectedSelection(TextRange(0, 5))
+            Key.DirectionRight.downAndUp(KeyEvent.META_SHIFT_ON)
+            expectedSelection(TextRange(0, 6))
+            Key.Backslash.downAndUp(KeyEvent.META_CTRL_ON)
+            expectedSelection(TextRange(6, 6))
+            Key.DirectionLeft.downAndUp(KeyEvent.META_CTRL_ON or KeyEvent.META_SHIFT_ON)
+            expectedSelection(TextRange(6, 0))
+            Key.DirectionRight.downAndUp(KeyEvent.META_SHIFT_ON)
+            expectedSelection(TextRange(1, 6))
+        }
+    }
+
+    @Test
+    fun textField_pageNavigation() {
+        keysSequenceTest(
+            initText = "1\n2\n3\n4\n5",
+            modifier = Modifier.requiredSize(77.dp)
+        ) {
+            // By page down, the cursor should be at the visible top line. In this case the height
+            // constraint is 77dp which covers from 1(30), 2(30) and middle of 3(17). Thus,
+            // by page down, the first line should be 3, and cursor should be the before letter 3,
+            // i.e. index = 4.
+            Key.PageDown.downAndUp()
+            expectedSelection(TextRange(4))
+        }
+    }
+
+    @Test
+    fun textField_tabSingleLine() {
+        keysSequenceTest(initText = "text", singleLine = true) {
+            Key.Tab.downAndUp()
+            expectedText("text") // no change, should try focus change instead
+        }
+    }
+
+    @Test
+    fun textField_tabMultiLine() {
+        keysSequenceTest(initText = "text") {
+            Key.Tab.downAndUp()
+            expectedText("\ttext")
+        }
+    }
+
+    @Test
+    fun textField_shiftTabSingleLine() {
+        keysSequenceTest(initText = "text", singleLine = true) {
+            Key.Tab.downAndUp(metaState = KeyEvent.META_SHIFT_ON)
+            expectedText("text") // no change, should try focus change instead
+        }
+    }
+
+    @Test
+    fun textField_enterSingleLine() {
+        keysSequenceTest(initText = "text", singleLine = true) {
+            Key.Enter.downAndUp()
+            expectedText("text") // no change, should do ime action instead
+        }
+    }
+
+    @Test
+    fun textField_enterMultiLine() {
+        keysSequenceTest(initText = "text") {
+            Key.Enter.downAndUp()
+            expectedText("\ntext")
+        }
+    }
+
+    @Test
+    fun textField_withActiveSelection_tabSingleLine() {
+        keysSequenceTest(initText = "text", singleLine = true) {
+            Key.DirectionRight.downAndUp()
+            Key.DirectionRight.downAndUp(KeyEvent.META_SHIFT_ON)
+            Key.DirectionRight.downAndUp(KeyEvent.META_SHIFT_ON)
+            Key.Tab.downAndUp()
+            expectedText("text") // no change, should try focus change instead
+        }
+    }
+
+    @Test
+    fun textField_withActiveSelection_tabMultiLine() {
+        keysSequenceTest(initText = "text") {
+            Key.DirectionRight.downAndUp()
+            Key.DirectionRight.downAndUp(KeyEvent.META_SHIFT_ON)
+            Key.DirectionRight.downAndUp(KeyEvent.META_SHIFT_ON)
+            Key.Tab.downAndUp()
+            expectedText("t\tt")
+        }
+    }
+
+    @Ignore // TODO(halilibo): Remove ignore when backing buffer supports reversed selection
+    @Test
+    fun textField_selectToLeft() {
+        keysSequenceTest(initText = "hello world hello") {
+            Key.MoveEnd.downAndUp()
+            expectedSelection(TextRange(17))
+            Key.DirectionLeft.downAndUp(KeyEvent.META_SHIFT_ON)
+            Key.DirectionLeft.downAndUp(KeyEvent.META_SHIFT_ON)
+            Key.DirectionLeft.downAndUp(KeyEvent.META_SHIFT_ON)
+            expectedSelection(TextRange(17, 14))
+        }
+    }
+
+    @Test
+    fun textField_withActiveSelection_shiftTabSingleLine() {
+        keysSequenceTest(initText = "text", singleLine = true) {
+            Key.DirectionRight.downAndUp()
+            Key.DirectionRight.downAndUp(KeyEvent.META_SHIFT_ON)
+            Key.DirectionRight.downAndUp(KeyEvent.META_SHIFT_ON)
+            Key.Tab.downAndUp(metaState = KeyEvent.META_SHIFT_ON)
+            expectedText("text") // no change, should try focus change instead
+        }
+    }
+
+    @Test
+    fun textField_withActiveSelection_enterSingleLine() {
+        keysSequenceTest(initText = "text", singleLine = true) {
+            Key.DirectionRight.downAndUp()
+            Key.DirectionRight.downAndUp(KeyEvent.META_SHIFT_ON)
+            Key.DirectionRight.downAndUp(KeyEvent.META_SHIFT_ON)
+            Key.Enter.downAndUp()
+            expectedText("text") // no change, should do ime action instead
+        }
+    }
+
+    @Test
+    fun textField_withActiveSelection_enterMultiLine() {
+        keysSequenceTest(initText = "text") {
+            Key.DirectionRight.downAndUp()
+            Key.DirectionRight.downAndUp(KeyEvent.META_SHIFT_ON)
+            Key.DirectionRight.downAndUp(KeyEvent.META_SHIFT_ON)
+            Key.Enter.downAndUp()
+            expectedText("t\nt")
+        }
+    }
+
+    private inner class SequenceScope(
+        val state: TextFieldState,
+        val nodeGetter: () -> SemanticsNodeInteraction
+    ) {
+        fun Key.downAndUp(metaState: Int = 0) {
+            this.down(metaState)
+            this.up(metaState)
+        }
+
+        fun Key.down(metaState: Int = 0) {
+            nodeGetter().performKeyPress(downEvent(this, metaState))
+        }
+
+        fun Key.up(metaState: Int = 0) {
+            nodeGetter().performKeyPress(upEvent(this, metaState))
+        }
+
+        fun expectedText(text: String) {
+            rule.runOnIdle {
+                Truth.assertThat(state.value.text).isEqualTo(text)
+            }
+        }
+
+        fun expectedSelection(selection: TextRange) {
+            rule.runOnIdle {
+                Truth.assertThat(state.value.selection).isEqualTo(selection)
+            }
+        }
+    }
+
+    private fun keysSequenceTest(
+        initText: String = "",
+        modifier: Modifier = Modifier.fillMaxSize(),
+        singleLine: Boolean = false,
+        sequence: SequenceScope.() -> Unit,
+    ) {
+        val state = TextFieldState(TextFieldValue(initText))
+        keysSequenceTest(
+            state = state,
+            modifier = modifier,
+            singleLine = singleLine,
+            sequence = sequence
+        )
+    }
+
+    private fun keysSequenceTest(
+        state: TextFieldState,
+        modifier: Modifier = Modifier.fillMaxSize(),
+        singleLine: Boolean = false,
+        sequence: SequenceScope.() -> Unit,
+    ) {
+        val inputService = TextInputService(mock())
+        val focusRequester = FocusRequester()
+        rule.setContent {
+            LocalClipboardManager.current.setText(AnnotatedString("InitialTestText"))
+            CompositionLocalProvider(
+                LocalTextInputService provides inputService,
+                LocalDensity provides defaultDensity
+            ) {
+                BasicTextField2(
+                    state = state,
+                    textStyle = TextStyle(
+                        fontFamily = TEST_FONT_FAMILY,
+                        fontSize = 30.sp
+                    ),
+                    modifier = modifier.focusRequester(focusRequester),
+                    maxLines = if (singleLine) 1 else Int.MAX_VALUE,
+                )
+            }
+        }
+
+        rule.runOnIdle { focusRequester.requestFocus() }
+
+        sequence(SequenceScope(state) { rule.onNode(hasSetTextAction()) })
+    }
+}
+
+private fun downEvent(key: Key, metaState: Int = 0): androidx.compose.ui.input.key.KeyEvent {
+    return androidx.compose.ui.input.key.KeyEvent(
+        KeyEvent(0L, 0L, KeyEvent.ACTION_DOWN, key.nativeKeyCode, 0, metaState)
+    )
+}
+
+private fun upEvent(key: Key, metaState: Int = 0): androidx.compose.ui.input.key.KeyEvent {
+    return androidx.compose.ui.input.key.KeyEvent(
+        KeyEvent(0L, 0L, KeyEvent.ACTION_UP, key.nativeKeyCode, 0, metaState)
+    )
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/service/AndroidTextInputAdapterTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/service/AndroidTextInputAdapterTest.kt
index 72bae6d..4dbeebe 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/service/AndroidTextInputAdapterTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/service/AndroidTextInputAdapterTest.kt
@@ -24,7 +24,6 @@
 import androidx.compose.foundation.text2.TextFieldState
 import androidx.compose.ui.platform.LocalPlatformTextInputPluginRegistry
 import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.ImeOptions
@@ -49,7 +48,6 @@
     private lateinit var adapter: AndroidTextInputAdapter
 
     @Before
-    @OptIn(ExperimentalTextApi::class)
     fun setup() {
         rule.setContent {
             val adapterProvider = LocalPlatformTextInputPluginRegistry.current
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/service/ComposeInputMethodManagerTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/service/ComposeInputMethodManagerTest.kt
new file mode 100644
index 0000000..654d713
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/service/ComposeInputMethodManagerTest.kt
@@ -0,0 +1,122 @@
+/*
+ * 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.text2.service
+
+import android.content.Context
+import android.view.View
+import android.view.inputmethod.EditorInfo
+import android.view.inputmethod.InputConnection
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.viewinterop.AndroidView
+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)
+class ComposeInputMethodManagerTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun restartInput_startsNewInputConnection() {
+        var calledCreateInputConnection: EditorInfo? = null
+        var imm: ComposeInputMethodManager? = null
+        var view: View? = null
+        rule.setContent {
+            AndroidView(factory = { context ->
+                TestView(context) { editorInfo ->
+                    calledCreateInputConnection = editorInfo
+                    null
+                }.also {
+                    view = it
+                    imm = ComposeInputMethodManager(it)
+                }
+            })
+        }
+
+        rule.runOnUiThread {
+            view?.requestFocus()
+            imm?.restartInput()
+        }
+
+        rule.runOnIdle {
+            assertThat(calledCreateInputConnection).isNotNull()
+        }
+    }
+
+    @Test
+    fun everyRestartInput_createsNewInputConnection() {
+        var createInputConnectionCalled = 0
+        var imm: ComposeInputMethodManager? = null
+        var view: View? = null
+        rule.setContent {
+            AndroidView(factory = { context ->
+                TestView(context) {
+                    createInputConnectionCalled++
+                    null
+                }.also {
+                    view = it
+                    imm = ComposeInputMethodManager(it)
+                }
+            })
+        }
+
+        rule.runOnUiThread {
+            view?.requestFocus()
+            imm?.restartInput()
+        }
+
+        rule.runOnIdle {
+            // when first time we start input, checkFocus in platform code causes
+            // onCreateInputConnection to be called twice.
+            assertThat(createInputConnectionCalled).isEqualTo(2)
+        }
+
+        rule.runOnUiThread {
+            imm?.restartInput()
+        }
+
+        rule.runOnIdle {
+            assertThat(createInputConnectionCalled).isEqualTo(3)
+        }
+    }
+}
+
+private class TestView(
+    context: Context,
+    val createInputConnection: (EditorInfo?) -> InputConnection? = { null }
+) : View(context) {
+
+    init {
+        isFocusable = true
+        isFocusableInTouchMode = true
+        isEnabled = true
+    }
+
+    override fun onCreateInputConnection(outAttrs: EditorInfo?): InputConnection? {
+        return createInputConnection(outAttrs)
+    }
+
+    override fun isInEditMode(): Boolean {
+        return true
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/service/StatelessInputConnectionTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/service/StatelessInputConnectionTest.kt
index 35a023d..147fcdf 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/service/StatelessInputConnectionTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/service/StatelessInputConnectionTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.text2.service
 
+import android.view.KeyEvent
 import androidx.compose.foundation.text2.input.CommitTextCommand
 import androidx.compose.foundation.text2.input.DeleteSurroundingTextCommand
 import androidx.compose.foundation.text2.input.DeleteSurroundingTextInCodePointsCommand
@@ -51,6 +52,7 @@
     private var value: TextFieldValue = TextFieldValue()
     private var imeOptions: ImeOptions = ImeOptions.Default
     private var onRequestEdits: ((List<EditCommand>) -> Unit)? = null
+    private var onSendKeyEvent: ((KeyEvent) -> Unit)? = null
 
     private val activeSessionProvider: () -> EditableTextInputSession? = { activeSession }
 
@@ -71,6 +73,10 @@
                 onRequestEdits?.invoke(editCommands)
             }
 
+            override fun sendKeyEvent(keyEvent: KeyEvent) {
+                onSendKeyEvent?.invoke(keyEvent)
+            }
+
             override fun dispose() {
                 this@StatelessInputConnectionTest.isOpen = false
             }
@@ -540,4 +546,36 @@
         ic.endBatchEdit()
         assertThat(requestEditsCalled).isEqualTo(0)
     }
+
+    @Test
+    fun sendKeyEvent_whenIMERequests() {
+        val keyEvents = mutableListOf<KeyEvent>()
+        onSendKeyEvent = {
+            keyEvents += it
+        }
+        val keyEvent1 = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0)
+        val keyEvent2 = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_0)
+        ic.sendKeyEvent(keyEvent1)
+        ic.sendKeyEvent(keyEvent2)
+
+        assertThat(keyEvents.size).isEqualTo(2)
+        assertThat(keyEvents.first()).isEqualTo(keyEvent1)
+        assertThat(keyEvents.last()).isEqualTo(keyEvent2)
+    }
+
+    @Test
+    fun sendKeyEvent_noActiveSession() {
+        val keyEvents = mutableListOf<KeyEvent>()
+        onSendKeyEvent = {
+            keyEvents += it
+        }
+        val keyEvent1 = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0)
+        val keyEvent2 = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_0)
+        activeSession = null
+
+        ic.sendKeyEvent(keyEvent1)
+        ic.sendKeyEvent(keyEvent2)
+
+        assertThat(keyEvents.size).isEqualTo(0)
+    }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldTest.kt
index 3d8a7782..ef5913b 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldTest.kt
@@ -14,9 +14,6 @@
 * limitations under the License.
 */
 
-// TODO(b/160821157): Replace FocusState with FocusState2.isFocused
-@file:Suppress("DEPRECATION")
-
 package androidx.compose.foundation.textfield
 
 import android.os.Build
@@ -1373,8 +1370,8 @@
             )
         }
         val textNode = rule.onNodeWithTag(Tag)
-        textNode.performTouchInput { longClick() }
-        textNode.performTextInputSelection(TextRange(0, 4))
+        textNode.performSemanticsAction(SemanticsActions.RequestFocus)
+        textNode.performTextInputSelection(TextRange(0, 5))
         textNode.performKeyPress(
             KeyEvent(
                 NativeKeyEvent(
@@ -1392,14 +1389,18 @@
             )
         )
 
+        rule.waitForIdle()
+        textNode.assertTextEquals("")
+        val selection = textNode.fetchSemanticsNode().config
+            .getOrNull(SemanticsProperties.TextSelectionRange)
+        assertThat(selection).isEqualTo(TextRange(0))
+
         textFieldValue.value = "Hello"
 
         rule.waitForIdle()
-
-        val expected = TextRange(0, 0)
         val actual = textNode.fetchSemanticsNode().config
             .getOrNull(SemanticsProperties.TextSelectionRange)
-        assertThat(actual).isEqualTo(expected)
+        assertThat(actual).isEqualTo(TextRange(0))
     }
 
     @Test
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/SystemGestureExclusion.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/SystemGestureExclusion.kt
index a62fdb5..c798184e 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/SystemGestureExclusion.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/SystemGestureExclusion.kt
@@ -16,19 +16,19 @@
 
 package androidx.compose.foundation
 
+import android.annotation.SuppressLint
 import android.os.Build
 import android.view.View
 import androidx.annotation.RequiresApi
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.collection.mutableVectorOf
-import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.layout.OnGloballyPositionedModifier
 import androidx.compose.ui.layout.boundsInRoot
+import androidx.compose.ui.node.GlobalPositionAwareModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.InspectorInfo
 import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.platform.debugInspectorInfo
 import kotlin.math.roundToInt
@@ -42,11 +42,7 @@
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
         this
     } else {
-        composed(inspectorInfo = debugInspectorInfo {
-            name = "systemGestureExclusion"
-        }) {
-            excludeFromSystemGestureQ(null)
-        }
+        this then excludeFromSystemGestureQ(null)
     }
 
 /**
@@ -63,35 +59,63 @@
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
         this
     } else {
-        composed(inspectorInfo = debugInspectorInfo {
-            name = "systemGestureExclusion"
-            properties["exclusion"] = exclusion
-        }) {
-            excludeFromSystemGestureQ(exclusion)
-        }
+        this then excludeFromSystemGestureQ(exclusion)
     }
 
 @Suppress("NOTHING_TO_INLINE", "ComposableModifierFactory", "ModifierFactoryExtensionFunction")
 @RequiresApi(Build.VERSION_CODES.Q)
-@Composable
 private inline fun excludeFromSystemGestureQ(
     noinline exclusion: ((LayoutCoordinates) -> Rect)?
-): Modifier {
-    val view = LocalView.current
-    val modifier = remember(view, exclusion) { ExcludeFromSystemGestureModifier(view, exclusion) }
-    DisposableEffect(modifier) {
-        onDispose {
-            modifier.removeRect()
+): Modifier = Modifier.composed(
+    debugInspectorInfo {
+        name = "systemGestureExclusion"
+        if (exclusion != null) {
+            properties["exclusion"] = exclusion
         }
     }
-    return modifier
+) {
+    val view = LocalView.current
+    ExcludeFromSystemGestureElement(exclusion, view)
 }
 
 @RequiresApi(Build.VERSION_CODES.Q)
-private class ExcludeFromSystemGestureModifier(
-    val view: View,
-    val exclusion: ((LayoutCoordinates) -> Rect)?
-) : OnGloballyPositionedModifier {
+private class ExcludeFromSystemGestureElement(
+    val exclusion: ((LayoutCoordinates) -> Rect)?,
+    val view: View
+) : ModifierNodeElement<ExcludeFromSystemGestureNode>() {
+    @SuppressLint("NewApi")
+    override fun create(): ExcludeFromSystemGestureNode {
+        return ExcludeFromSystemGestureNode(exclusion, view)
+    }
+
+    override fun update(node: ExcludeFromSystemGestureNode): ExcludeFromSystemGestureNode =
+        node.also {
+            it.exclusion = exclusion
+            it.view = view
+        }
+
+    override fun hashCode(): Int {
+        return exclusion.hashCode() + view.hashCode()
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (other !is ExcludeFromSystemGestureElement) return false
+        return exclusion == other.exclusion && view == other.view
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        name = "systemGestureExclusion"
+        if (exclusion != null) {
+            properties["exclusion"] = exclusion
+        }
+    }
+}
+
+@RequiresApi(Build.VERSION_CODES.Q)
+private class ExcludeFromSystemGestureNode(
+    var exclusion: ((LayoutCoordinates) -> Rect)?,
+    var view: View
+) : Modifier.Node(), GlobalPositionAwareModifierNode {
     var rect: android.graphics.Rect? = null
 
     override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
@@ -104,13 +128,14 @@
                 boundsInRoot.bottom.roundToInt()
             )
         } else {
-            calcBounds(coordinates, exclusion.invoke(coordinates))
+            calcBounds(coordinates, exclusion!!.invoke(coordinates))
         }
         replaceRect(newRect)
     }
 
-    fun removeRect() {
-        replaceRect(null)
+    override fun onDetach() {
+        super.onDetach()
+        replaceRect(null) // On Node detach, reset
     }
 
     fun replaceRect(newRect: android.graphics.Rect?) {
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/BasicTextField2.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/BasicTextField2.kt
index 31dcd3d0..c51dd6e 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/BasicTextField2.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/BasicTextField2.kt
@@ -17,48 +17,33 @@
 package androidx.compose.foundation.text2
 
 import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.clickable
 import androidx.compose.foundation.focusable
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsFocusedAsState
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.text.DefaultMinLines
 import androidx.compose.foundation.text.InternalFoundationTextApi
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.foundation.text.TextDelegate
 import androidx.compose.foundation.text.heightInLines
-import androidx.compose.foundation.text.selection.LocalTextSelectionColors
+import androidx.compose.foundation.text.selection.SimpleLayout
 import androidx.compose.foundation.text.textFieldMinSize
-import androidx.compose.foundation.text2.input.CommitTextCommand
-import androidx.compose.foundation.text2.input.DeleteAllCommand
 import androidx.compose.foundation.text2.service.AndroidTextInputPlugin
-import androidx.compose.foundation.text2.service.TextInputSession
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawBehind
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focus.focusRequester
-import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.SolidColor
-import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
 import androidx.compose.ui.layout.FirstBaseline
 import androidx.compose.ui.layout.LastBaseline
 import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalFontFamilyResolver
 import androidx.compose.ui.platform.LocalPlatformTextInputPluginRegistry
-import androidx.compose.ui.semantics.disabled
-import androidx.compose.ui.semantics.onClick
-import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.semantics.setText
 import androidx.compose.ui.text.TextLayoutResult
-import androidx.compose.ui.text.TextPainter
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
@@ -68,6 +53,7 @@
 /**
  * BasicTextField2 is a new text input Composable under heavy development. Please refrain from
  * using it in production since it has a very unstable API and implementation for the time being.
+ * Many core features like selection, cursor, gestures, etc. may fail or simply not exist.
  *
  * @param state State object that holds the internal state of a [BasicTextField2]
  * @param modifier optional [Modifier] for this text field.
@@ -94,6 +80,12 @@
  * text, baselines and other details. The callback can be used to add additional decoration or
  * functionality to the text. For example, to draw a cursor or selection around the text. [Density]
  * scope is the one that was used while creating the given text layout.
+ * @param decorationBox Composable lambda that allows to add decorations around text field, such
+ * as icon, placeholder, helper messages or similar, and automatically increase the hit target area
+ * of the text field. To allow you to control the placement of the inner text field relative to your
+ * decorations, the text field implementation will pass in a framework-controlled composable
+ * parameter "innerTextField" to the decorationBox lambda you provide. You must call
+ * innerTextField exactly once.
  */
 @ExperimentalFoundationApi
 @OptIn(InternalFoundationTextApi::class)
@@ -109,18 +101,20 @@
     minLines: Int = DefaultMinLines,
     maxLines: Int = Int.MAX_VALUE,
     keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
-    onTextLayout: Density.(TextLayoutResult) -> Unit = {}
+    onTextLayout: Density.(TextLayoutResult) -> Unit = {},
+    decorationBox: @Composable (innerTextField: @Composable () -> Unit) -> Unit =
+        @Composable { innerTextField -> innerTextField() }
 ) {
     // only read from local and create an adapter if this text field is enabled and editable
     val textInputAdapter = LocalPlatformTextInputPluginRegistry.takeIf { enabled && !readOnly }
         ?.current?.rememberAdapter(AndroidTextInputPlugin)
 
-    val focusRequester = remember { FocusRequester() }
-
     val fontFamilyResolver = LocalFontFamilyResolver.current
     val density = LocalDensity.current
-    val selectionBackgroundColor = LocalTextSelectionColors.current.backgroundColor
     val singleLine = minLines == 1 && maxLines == 1
+    // We're using this to communicate focus state to cursor for now.
+    @Suppress("NAME_SHADOWING")
+    val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
 
     val textLayoutState = remember {
         TextLayoutState(
@@ -135,138 +129,69 @@
         )
     }
 
-    var isFocused by remember { mutableStateOf(false) }
-
-    val textInputSessionState = remember { mutableStateOf<TextInputSession?>(null) }
-
-    // Hide the keyboard if made disabled or read-only while focused (b/237308379).
-    if (enabled && !readOnly) {
-        // TODO(b/230536793) This is a workaround since we don't get an explicit focus blur event
-        //  when the text field is removed from the composition entirely.
-        DisposableEffect(state) {
-            if (isFocused) {
-                textInputSessionState.value = textInputAdapter?.startInputSession(
-                    state,
-                    keyboardOptions.toImeOptions(singleLine)
-                )
-            }
-            onDispose {
-                if (isFocused) {
-                    textInputSessionState.value?.dispose()
-                    textInputSessionState.value = null
-                }
-            }
-        }
-    }
-
-    val semanticsModifier = Modifier.semantics {
-        if (!enabled) this.disabled()
-
-        setText { text ->
-            state.editProcessor.update(
-                listOf(
-                    DeleteAllCommand,
-                    CommitTextCommand(text, 1)
-                )
+    val decorationModifiers = modifier
+        .then(
+            // semantics + some focus + input session + touch to focus
+            TextFieldDecoratorModifierElement(
+                textFieldState = state,
+                textLayoutState = textLayoutState,
+                textInputAdapter = textInputAdapter,
+                enabled = enabled,
+                readOnly = readOnly,
+                keyboardOptions = keyboardOptions,
+                singleLine = singleLine
             )
-            true
-        }
-        onClick {
-            // according to the documentation, we still need to provide proper semantics actions
-            // even if the state is 'disabled'
-            if (!isFocused) {
-                focusRequester.requestFocus()
-            }
-            true
-        }
-    }
-
-    val drawModifier = Modifier.drawBehind {
-        textLayoutState.layoutResult?.let { layoutResult ->
-            // draw selection
-            val value = state.value
-            if (!value.selection.collapsed) {
-                val start = value.selection.min
-                val end = value.selection.max
-                if (start != end) {
-                    val selectionPath = layoutResult.getPathForRange(start, end)
-                    drawPath(selectionPath, color = selectionBackgroundColor)
-                }
-            }
-            // draw text
-            drawIntoCanvas { canvas ->
-                TextPainter.paint(canvas, layoutResult)
-            }
-        }
-    }
-
-    val focusModifier = Modifier
-        .focusRequester(focusRequester)
-        .onFocusChanged {
-            if (isFocused == it.isFocused) {
-                return@onFocusChanged
-            }
-            isFocused = it.isFocused
-
-            if (it.isFocused) {
-                textInputSessionState.value = textInputAdapter?.startInputSession(
-                    state,
-                    keyboardOptions.toImeOptions(singleLine)
-                )
-                // TODO(halilibo): bringIntoView
-            } else {
-                state.deselect()
-            }
-        }
+        )
         .focusable(interactionSource = interactionSource, enabled = enabled)
 
-    val cursorModifier = Modifier.cursor(
-        textLayoutState = textLayoutState,
-        isFocused = isFocused,
-        state = state,
-        cursorBrush = cursorBrush,
-        enabled = enabled && !readOnly
-    )
+    Box(decorationModifiers) {
+        decorationBox(innerTextField = {
+            val coreModifiers = Modifier
+                .heightInLines(
+                    textStyle = textStyle,
+                    minLines = minLines,
+                    maxLines = maxLines
+                )
+                .textFieldMinSize(textStyle)
+                .then(TextFieldCoreModifierElement(
+                    isFocused = interactionSource.collectIsFocusedAsState().value,
+                    textLayoutState = textLayoutState,
+                    textFieldState = state,
+                    cursorBrush = cursorBrush,
+                    writeable = enabled && !readOnly
+                ))
+                .onGloballyPositioned {
+                    textLayoutState.proxy?.innerTextFieldCoordinates = it
+                }
 
-    Layout(
-        content = {},
-        modifier = modifier
-            .then(focusModifier)
-            .heightInLines(
-                textStyle = textStyle,
-                minLines = minLines,
-                maxLines = maxLines
-            )
-            .then(drawModifier)
-            .textFieldMinSize(textStyle)
-            .then(cursorModifier)
-            .clickable {
-                focusRequester.requestFocus()
+            // A custom layout that hosts TextLayout, Selection and Cursor Handles.
+            SimpleLayout(coreModifiers) {
+                // Text Layout
+                Layout { _, constraints ->
+                    val result = with(textLayoutState) {
+                        layout(
+                            text = state.value.annotatedString,
+                            textStyle = textStyle,
+                            softWrap = true,
+                            density = density,
+                            fontFamilyResolver = fontFamilyResolver,
+                            constraints = constraints,
+                            onTextLayout = onTextLayout
+                        )
+                    }
+
+                    // TODO: min height
+
+                    layout(
+                        width = result.size.width,
+                        height = result.size.height,
+                        alignmentLines = mapOf(
+                            FirstBaseline to result.firstBaseline.roundToInt(),
+                            LastBaseline to result.lastBaseline.roundToInt()
+                        )
+                    ) {}
+                }
             }
-            .then(semanticsModifier)
-            .then(TextFieldContentSemanticsElement(state))
-    ) { _, constraints ->
-        val result = with(textLayoutState) {
-            layout(
-                text = state.value.annotatedString,
-                textStyle = textStyle,
-                softWrap = true,
-                density = density,
-                fontFamilyResolver = fontFamilyResolver,
-                constraints = constraints,
-                onTextLayout = onTextLayout
-            )
-        }
-
-        // TODO: min height
-
-        layout(
-            width = result.size.width,
-            height = result.size.height,
-            alignmentLines = mapOf(
-                FirstBaseline to result.firstBaseline.roundToInt(),
-                LastBaseline to result.lastBaseline.roundToInt()
-            )
-        ) {}
+        })
     }
 }
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextFieldContentSemantics.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextFieldContentSemantics.kt
deleted file mode 100644
index a0e9a73..0000000
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextFieldContentSemantics.kt
+++ /dev/null
@@ -1,94 +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.text2
-
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.node.ModifierNodeElement
-import androidx.compose.ui.node.SemanticsModifierNode
-import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.semantics.SemanticsConfiguration
-import androidx.compose.ui.semantics.editableText
-import androidx.compose.ui.semantics.textSelectionRange
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.TextRange
-
-@OptIn(ExperimentalFoundationApi::class)
-internal class TextFieldContentSemanticsElement(
-    private val state: TextFieldState
-) : ModifierNodeElement<TextFieldContentSemanticsNode>() {
-    override fun create(): TextFieldContentSemanticsNode = TextFieldContentSemanticsNode(state)
-
-    override fun update(node: TextFieldContentSemanticsNode): TextFieldContentSemanticsNode {
-        node.state = state
-        return node
-    }
-
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        if (other !is TextFieldContentSemanticsElement) return false
-
-        if (state != other.state) return false
-
-        return true
-    }
-
-    override fun hashCode(): Int {
-        return state.hashCode()
-    }
-
-    override fun InspectorInfo.inspectableProperties() {
-        // Show nothing in the inspector.
-    }
-}
-
-@OptIn(ExperimentalFoundationApi::class)
-internal data class TextFieldContentSemanticsNode(
-    var state: TextFieldState
-) : Modifier.Node(), SemanticsModifierNode {
-
-    private var lastText: AnnotatedString? = null
-    private var lastSelection: TextRange? = null
-
-    private var _semanticsConfiguration: SemanticsConfiguration? = null
-
-    private fun generateSemantics(
-        text: AnnotatedString,
-        selection: TextRange
-    ): SemanticsConfiguration {
-        lastText = text
-        lastSelection = selection
-        return SemanticsConfiguration().also {
-            it.editableText = text
-            it.textSelectionRange = selection
-            _semanticsConfiguration = it
-        }
-    }
-
-    override val semanticsConfiguration: SemanticsConfiguration
-        get() {
-            var localSemantics = _semanticsConfiguration
-            val value = state.value
-            if (localSemantics == null ||
-                lastText != value.annotatedString ||
-                lastSelection != value.selection
-            ) {
-                localSemantics = generateSemantics(value.annotatedString, value.selection)
-            }
-            return localSemantics
-        }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextFieldCoreModifier.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextFieldCoreModifier.kt
new file mode 100644
index 0000000..f2e0235
--- /dev/null
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextFieldCoreModifier.kt
@@ -0,0 +1,249 @@
+/*
+ * 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.foundation.text2
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.infiniteRepeatable
+import androidx.compose.animation.core.keyframes
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.text.selection.LocalTextSelectionColors
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.MotionDurationScale
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
+import androidx.compose.ui.graphics.isUnspecified
+import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
+import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.currentValueOf
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextPainter
+import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.unit.dp
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * Modifier element for the core functionality of [BasicTextField2] that is passed as inner
+ * TextField to the decoration box. This is only half the actual modifiers for the field, the other
+ * half are only attached to the decorated text field.
+ *
+ * This modifier mostly handles layout and draw.
+ */
+@OptIn(ExperimentalFoundationApi::class)
+internal data class TextFieldCoreModifierElement(
+    private val isFocused: Boolean,
+    private val textLayoutState: TextLayoutState,
+    private val textFieldState: TextFieldState,
+    private val cursorBrush: Brush,
+    private val writeable: Boolean
+) : ModifierNodeElement<TextFieldCoreModifierNode>() {
+
+    override fun create(): TextFieldCoreModifierNode = TextFieldCoreModifierNode(
+        isFocused = isFocused,
+        textLayoutState = textLayoutState,
+        textFieldState = textFieldState,
+        cursorBrush = cursorBrush,
+        writable = writeable
+    )
+
+    override fun update(node: TextFieldCoreModifierNode): TextFieldCoreModifierNode {
+        node.updateNode(
+            isFocused = isFocused,
+            textLayoutState = textLayoutState,
+            textFieldState = textFieldState,
+            cursorBrush = cursorBrush,
+            writeable = writeable
+        )
+        return node
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        // no inspector info
+    }
+}
+
+/** Modifier node for [TextFieldCoreModifierElement]. */
+@OptIn(ExperimentalFoundationApi::class)
+internal class TextFieldCoreModifierNode(
+    private var isFocused: Boolean,
+    private var textLayoutState: TextLayoutState,
+    private var textFieldState: TextFieldState,
+    private var cursorBrush: Brush,
+    private var writable: Boolean
+) : Modifier.Node(),
+    DrawModifierNode,
+    CompositionLocalConsumerModifierNode {
+
+    /**
+     * Animatable object for cursor's alpha value. Cursor is always drawn, only its alpha gets
+     * animated.
+     */
+    private val cursorAlpha = Animatable(1f)
+
+    /**
+     * Whether to show cursor at all when TextField has focus. This depends on enabled, read only,
+     * and brush at a given time.
+     */
+    private val showCursor: Boolean
+        get() = writable && isFocused && cursorBrush.isSpecified
+
+    /**
+     * Observes the [textFieldState] for any changes to content or selection. If a change happens,
+     * cursor blink animation gets reset.
+     */
+    private var changeObserverJob: Job? = null
+
+    /**
+     * Updates all the related properties and invalidates internal state based on the changes.
+     */
+    fun updateNode(
+        isFocused: Boolean,
+        textLayoutState: TextLayoutState,
+        textFieldState: TextFieldState,
+        cursorBrush: Brush,
+        writeable: Boolean
+    ) {
+        val wasFocused = this.isFocused
+        val previousTextFieldState = this.textFieldState
+
+        this.isFocused = isFocused
+        this.textLayoutState = textLayoutState
+        this.textFieldState = textFieldState
+        this.cursorBrush = cursorBrush
+        this.writable = writeable
+
+        if (!showCursor) {
+            changeObserverJob?.cancel()
+            changeObserverJob = null
+        } else if (!wasFocused || previousTextFieldState != textFieldState) {
+            // this node is writeable, focused and gained that focus just now.
+            // start the state value observation
+            changeObserverJob = coroutineScope.launch {
+                // Animate the cursor even when animations are disabled by the system.
+                withContext(FixedMotionDurationScale) {
+                    snapshotFlow { textFieldState.value }
+                        .collectLatest {
+                            // ensure that the value is always 1f _this_ frame by calling snapTo
+                            cursorAlpha.snapTo(1f)
+                            // then start the cursor blinking on animation clock (500ms on to start)
+                            cursorAlpha.animateTo(0f, cursorAnimationSpec)
+                        }
+                }
+            }
+        }
+    }
+
+    override fun ContentDrawScope.draw() {
+        drawContent()
+        val value = textFieldState.value
+        val textLayoutResult = textLayoutState.layoutResult ?: return
+
+        if (value.selection.collapsed) {
+            drawText(textLayoutResult)
+            drawCursor(value.selection, textLayoutResult)
+        } else {
+            drawSelection(value.selection, textLayoutResult)
+            drawText(textLayoutResult)
+        }
+    }
+
+    /**
+     * Draws the selection highlight.
+     */
+    private fun ContentDrawScope.drawSelection(
+        selection: TextRange,
+        textLayoutResult: TextLayoutResult
+    ) {
+        val start = selection.min
+        val end = selection.max
+        if (start != end) {
+            val selectionBackgroundColor = currentValueOf(LocalTextSelectionColors)
+                .backgroundColor
+            val selectionPath = textLayoutResult.getPathForRange(start, end)
+            drawPath(selectionPath, color = selectionBackgroundColor)
+        }
+    }
+
+    /**
+     * Draws the text content.
+     */
+    private fun ContentDrawScope.drawText(textLayoutResult: TextLayoutResult) {
+        drawIntoCanvas { canvas ->
+            TextPainter.paint(canvas, textLayoutResult)
+        }
+    }
+
+    /**
+     * Draws the cursor indicator. Do not confuse it with cursor handle which is a popup that
+     * carries the cursor movement gestures.
+     */
+    private fun ContentDrawScope.drawCursor(
+        selection: TextRange,
+        textLayoutResult: TextLayoutResult
+    ) {
+        if (!showCursor) return
+
+        val cursorAlphaValue = cursorAlpha.value.coerceIn(0f, 1f)
+        if (cursorAlphaValue == 0f) return
+
+        val cursorRect = textLayoutResult.getCursorRect(selection.start)
+        val cursorWidth = DefaultCursorThickness.toPx()
+        val cursorX = (cursorRect.left + cursorWidth / 2)
+            .coerceAtMost(size.width - cursorWidth / 2)
+
+        drawLine(
+            cursorBrush,
+            Offset(cursorX, cursorRect.top),
+            Offset(cursorX, cursorRect.bottom),
+            alpha = cursorAlphaValue,
+            strokeWidth = cursorWidth
+        )
+    }
+}
+
+private val cursorAnimationSpec: AnimationSpec<Float> = infiniteRepeatable(
+    animation = keyframes {
+        durationMillis = 1000
+        1f at 0
+        1f at 499
+        0f at 500
+        0f at 999
+    }
+)
+
+private val DefaultCursorThickness = 2.dp
+
+/**
+ * If brush has a specified color. It's possible that [SolidColor] contains [Color.Unspecified].
+ */
+private val Brush.isSpecified: Boolean
+    get() = !(this is SolidColor && this.value.isUnspecified)
+
+private object FixedMotionDurationScale : MotionDurationScale {
+    override val scaleFactor: Float
+        get() = 1f
+}
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextFieldCursor.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextFieldCursor.kt
deleted file mode 100644
index 9a14151..0000000
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextFieldCursor.kt
+++ /dev/null
@@ -1,98 +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.foundation.text2
-
-import androidx.compose.animation.core.Animatable
-import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.animation.core.infiniteRepeatable
-import androidx.compose.animation.core.keyframes
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.MotionDurationScale
-import androidx.compose.ui.composed
-import androidx.compose.ui.draw.drawWithContent
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.graphics.Brush
-import androidx.compose.ui.graphics.SolidColor
-import androidx.compose.ui.graphics.isUnspecified
-import androidx.compose.ui.unit.dp
-import kotlinx.coroutines.withContext
-
-@OptIn(ExperimentalFoundationApi::class)
-internal fun Modifier.cursor(
-    textLayoutState: TextLayoutState,
-    isFocused: Boolean,
-    state: TextFieldState,
-    cursorBrush: Brush,
-    enabled: Boolean
-) = if (enabled) composed {
-    val cursorAlpha = remember { Animatable(1f) }
-    val isBrushSpecified = !(cursorBrush is SolidColor && cursorBrush.value.isUnspecified)
-    val value = state.value
-    if (isFocused && value.selection.collapsed && isBrushSpecified) {
-        LaunchedEffect(value.annotatedString, value.selection) {
-            // Animate the cursor even when animations are disabled by the system.
-            withContext(FixedMotionDurationScale) {
-                // ensure that the value is always 1f _this_ frame by calling snapTo
-                cursorAlpha.snapTo(1f)
-                // then start the cursor blinking on animation clock (500ms on to start)
-                cursorAlpha.animateTo(0f, cursorAnimationSpec)
-            }
-        }
-        drawWithContent {
-            this.drawContent()
-            val cursorAlphaValue = cursorAlpha.value.coerceIn(0f, 1f)
-            if (cursorAlphaValue != 0f) {
-                val cursorRect = textLayoutState.layoutResult?.getCursorRect(value.selection.start)
-                    ?: Rect(0f, 0f, 0f, 0f)
-                val cursorWidth = DefaultCursorThickness.toPx()
-                val cursorX = (cursorRect.left + cursorWidth / 2)
-                    .coerceAtMost(size.width - cursorWidth / 2)
-
-                drawLine(
-                    cursorBrush,
-                    Offset(cursorX, cursorRect.top),
-                    Offset(cursorX, cursorRect.bottom),
-                    alpha = cursorAlphaValue,
-                    strokeWidth = cursorWidth
-                )
-            }
-        }
-    } else {
-        Modifier
-    }
-} else this
-
-private val cursorAnimationSpec: AnimationSpec<Float> = infiniteRepeatable(
-    animation = keyframes {
-        durationMillis = 1000
-        1f at 0
-        1f at 499
-        0f at 500
-        0f at 999
-    }
-)
-
-internal val DefaultCursorThickness = 2.dp
-
-private object FixedMotionDurationScale : MotionDurationScale {
-    override val scaleFactor: Float
-        get() = 1f
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextFieldDecoratorModifier.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextFieldDecoratorModifier.kt
new file mode 100644
index 0000000..7d1bd87
--- /dev/null
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextFieldDecoratorModifier.kt
@@ -0,0 +1,335 @@
+/*
+ * 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.text2
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text2.input.CommitTextCommand
+import androidx.compose.foundation.text2.input.DeleteAllCommand
+import androidx.compose.foundation.text2.input.FinishComposingTextCommand
+import androidx.compose.foundation.text2.service.AndroidTextInputAdapter
+import androidx.compose.foundation.text2.service.TextInputSession
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusEventModifierNode
+import androidx.compose.ui.focus.FocusRequesterModifierNode
+import androidx.compose.ui.focus.FocusState
+import androidx.compose.ui.focus.requestFocus
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.key.KeyInputModifierNode
+import androidx.compose.ui.input.pointer.PointerEvent
+import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.changedToDown
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.node.GlobalPositionAwareModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.PointerInputModifierNode
+import androidx.compose.ui.node.SemanticsModifierNode
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.semantics.SemanticsConfiguration
+import androidx.compose.ui.semantics.disabled
+import androidx.compose.ui.semantics.editableText
+import androidx.compose.ui.semantics.getTextLayoutResult
+import androidx.compose.ui.semantics.imeAction
+import androidx.compose.ui.semantics.insertTextAtCursor
+import androidx.compose.ui.semantics.onClick
+import androidx.compose.ui.semantics.setSelection
+import androidx.compose.ui.semantics.setText
+import androidx.compose.ui.semantics.textSelectionRange
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.util.fastAny
+
+/**
+ * Modifier element for most of the functionality of [BasicTextField2] that is attached to the
+ * decoration box. This is only half the actual modifiers for the field, the other half are only
+ * attached to the internal text field.
+ *
+ * This modifier handles input events (both key and pointer), semantics, and focus.
+ */
+@OptIn(ExperimentalFoundationApi::class)
+internal data class TextFieldDecoratorModifierElement(
+    private val textFieldState: TextFieldState,
+    private val textLayoutState: TextLayoutState,
+    private val textInputAdapter: AndroidTextInputAdapter?,
+    private val enabled: Boolean,
+    private val readOnly: Boolean,
+    private val keyboardOptions: KeyboardOptions,
+    private val singleLine: Boolean
+) : ModifierNodeElement<TextFieldDecoratorModifierNode>() {
+    override fun create(): TextFieldDecoratorModifierNode = TextFieldDecoratorModifierNode(
+        textFieldState = textFieldState,
+        textLayoutState = textLayoutState,
+        textInputAdapter = textInputAdapter,
+        enabled = enabled,
+        readOnly = readOnly,
+        keyboardOptions = keyboardOptions,
+        singleLine = singleLine
+    )
+
+    override fun update(node: TextFieldDecoratorModifierNode): TextFieldDecoratorModifierNode {
+        node.updateNode(
+            textFieldState = textFieldState,
+            textLayoutState = textLayoutState,
+            textInputAdapter = textInputAdapter,
+            enabled = enabled,
+            readOnly = readOnly,
+            keyboardOptions = keyboardOptions,
+            singleLine = singleLine
+        )
+        return node
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        // Show nothing in the inspector.
+    }
+}
+
+/** Modifier node for [TextFieldDecoratorModifierElement]. */
+@OptIn(ExperimentalFoundationApi::class)
+internal class TextFieldDecoratorModifierNode(
+    var textFieldState: TextFieldState,
+    var textLayoutState: TextLayoutState,
+    var textInputAdapter: AndroidTextInputAdapter?,
+    var enabled: Boolean,
+    var readOnly: Boolean,
+    var keyboardOptions: KeyboardOptions,
+    var singleLine: Boolean
+) : Modifier.Node(),
+    SemanticsModifierNode,
+    FocusRequesterModifierNode,
+    FocusEventModifierNode,
+    GlobalPositionAwareModifierNode,
+    PointerInputModifierNode,
+    KeyInputModifierNode {
+
+    // semantics properties that require semantics invalidation
+    private var lastText: AnnotatedString? = null
+    private var lastSelection: TextRange? = null
+    private var lastEnabled: Boolean = enabled
+
+    private var isFocused: Boolean = false
+    private var semanticsConfigurationCache: SemanticsConfiguration? = null
+    private var textInputSession: TextInputSession? = null
+
+    /**
+     * Manages key events. These events often are sourced by a hardware keyboard but it's also
+     * possible that IME or some other platform system simulates a KeyEvent.
+     */
+    private val textFieldKeyEventHandler = TextFieldKeyEventHandler()
+
+    /**
+     * Updates all the related properties and invalidates internal state based on the changes.
+     */
+    fun updateNode(
+        textFieldState: TextFieldState,
+        textLayoutState: TextLayoutState,
+        textInputAdapter: AndroidTextInputAdapter?,
+        enabled: Boolean,
+        readOnly: Boolean,
+        keyboardOptions: KeyboardOptions,
+        singleLine: Boolean
+    ) {
+        // Find the diff: current previous and new values before updating current.
+        val previousWriteable = this.enabled && !this.readOnly
+        val writeable = enabled && !readOnly
+        val previousTextFieldState = this.textFieldState
+
+        // Apply the diff.
+        this.textFieldState = textFieldState
+        this.textLayoutState = textLayoutState
+        this.textInputAdapter = textInputAdapter
+        this.enabled = enabled
+        this.readOnly = readOnly
+        this.keyboardOptions = keyboardOptions
+        this.singleLine = singleLine
+
+        // React to diff.
+        // If made writable while focused, or we got a completely new state instance,
+        // start a new input session.
+        if (previousWriteable != writeable || textFieldState != previousTextFieldState) {
+            if (writeable && isFocused) {
+                // The old session will be implicitly disposed.
+                textInputSession = textInputAdapter?.startInputSession(
+                    textFieldState,
+                    keyboardOptions.toImeOptions(singleLine)
+                )
+            } else if (!writeable) {
+                // We were made read-only or disabled, hide the keyboard.
+                disposeInputSession()
+            }
+        }
+    }
+
+    /**
+     * The current semantics for this node. The first time this is read after an update a new
+     * configuration is created by calling [generateSemantics] and then cached.
+     */
+    override val semanticsConfiguration: SemanticsConfiguration
+        get() {
+            var localSemantics = semanticsConfigurationCache
+            val value = textFieldState.value
+            // Cache invalidation is done here instead of only in updateNode because the text or
+            // selection might change without triggering a modifier update.
+            if (localSemantics == null ||
+                lastText != value.annotatedString ||
+                lastSelection != value.selection ||
+                lastEnabled != enabled
+            ) {
+                localSemantics = generateSemantics(value.annotatedString, value.selection)
+            }
+            return localSemantics
+        }
+
+    override fun onFocusEvent(focusState: FocusState) {
+        if (isFocused == focusState.isFocused) {
+            return
+        }
+        isFocused = focusState.isFocused
+
+        if (focusState.isFocused) {
+            textInputSession = textInputAdapter?.startInputSession(
+                textFieldState,
+                keyboardOptions.toImeOptions(singleLine)
+            )
+            // TODO(halilibo): bringIntoView
+        } else {
+            textFieldState.deselect()
+        }
+    }
+
+    override fun onDetach() {
+        if (isFocused) {
+            disposeInputSession()
+        }
+    }
+
+    override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
+        textLayoutState.proxy?.decorationBoxCoordinates = coordinates
+    }
+
+    override fun onPointerEvent(
+        pointerEvent: PointerEvent,
+        pass: PointerEventPass,
+        bounds: IntSize
+    ) {
+        if (pass == PointerEventPass.Main && pointerEvent.changes.fastAny { it.changedToDown() }) {
+            requestFocus()
+        }
+    }
+
+    override fun onCancelPointerInput() {
+        // Nothing to do yet, since onPointerEvent isn't handling any gestures.
+    }
+
+    override fun onPreKeyEvent(event: KeyEvent): Boolean {
+        // TextField does not handle pre key events.
+        return false
+    }
+
+    override fun onKeyEvent(event: KeyEvent): Boolean {
+        return textFieldKeyEventHandler.onKeyEvent(
+            event = event,
+            state = textFieldState,
+            textLayoutState = textLayoutState,
+            editable = enabled && !readOnly,
+            singleLine = singleLine,
+            onSubmit = {
+                // TODO(halilibo): finalize the wiring when KeyboardActions or equivalent is added.
+            }
+        )
+    }
+
+    private fun generateSemantics(
+        text: AnnotatedString,
+        selection: TextRange
+    ): SemanticsConfiguration {
+        lastText = text
+        lastSelection = selection
+        lastEnabled = enabled
+        return SemanticsConfiguration().apply {
+            this.isMergingSemanticsOfDescendants = true
+            getTextLayoutResult {
+                textLayoutState.layoutResult?.let { result -> it.add(result) } ?: false
+            }
+            editableText = text
+            textSelectionRange = selection
+            imeAction = keyboardOptions.imeAction
+            if (!enabled) disabled()
+
+            setText { text ->
+                textFieldState.editProcessor.update(
+                    listOf(
+                        DeleteAllCommand,
+                        CommitTextCommand(text, 1)
+                    )
+                )
+                true
+            }
+            setSelection { start, end, _ ->
+                // BasicTextField2 doesn't have VisualTransformation for the time being and
+                // probably won't have something that uses offsetMapping design. We can safely
+                // skip relativeToOriginalText flag. Assume it's always true.
+
+                if (!enabled) {
+                    false
+                } else if (start == selection.start && end == selection.end) {
+                    false
+                } else if (start.coerceAtMost(end) >= 0 &&
+                    start.coerceAtLeast(end) <= text.length
+                ) {
+                    // reset is required to make sure IME gets the update.
+                    textFieldState.editProcessor.reset(
+                        TextFieldValue(
+                            annotatedString = text,
+                            selection = TextRange(start, end)
+                        )
+                    )
+                    true
+                } else {
+                    false
+                }
+            }
+            insertTextAtCursor { text ->
+                textFieldState.editProcessor.update(
+                    listOf(
+                        // Finish composing text first because when the field is focused the IME
+                        // might set composition.
+                        FinishComposingTextCommand,
+                        CommitTextCommand(text, 1)
+                    )
+                )
+                true
+            }
+            onClick {
+                // according to the documentation, we still need to provide proper semantics actions
+                // even if the state is 'disabled'
+                if (!isFocused) {
+                    requestFocus()
+                }
+                true
+            }
+            semanticsConfigurationCache = this
+        }
+    }
+
+    private fun disposeInputSession() {
+        textInputSession?.dispose()
+        textInputSession = null
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextFieldKeyEventHandler.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextFieldKeyEventHandler.kt
new file mode 100644
index 0000000..629b60a
--- /dev/null
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextFieldKeyEventHandler.kt
@@ -0,0 +1,239 @@
+/*
+ * 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.text2
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.text.DeadKeyCombiner
+import androidx.compose.foundation.text.KeyCommand
+import androidx.compose.foundation.text.appendCodePointX
+import androidx.compose.foundation.text.isTypedEvent
+import androidx.compose.foundation.text.platformDefaultKeyMapping
+import androidx.compose.foundation.text.showCharacterPalette
+import androidx.compose.foundation.text2.TextFieldPreparedSelection.Companion.NoCharacterFound
+import androidx.compose.foundation.text2.input.CommitTextCommand
+import androidx.compose.foundation.text2.input.DeleteSurroundingTextCommand
+import androidx.compose.foundation.text2.input.EditCommand
+import androidx.compose.foundation.text2.input.FinishComposingTextCommand
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.key.KeyEventType
+import androidx.compose.ui.input.key.type
+
+/**
+ * Handles KeyEvents coming to a BasicTextField. This is mostly to support hardware keyboard but
+ * any KeyEvent can also be sent by the IME or other platform systems.
+ */
+@OptIn(ExperimentalFoundationApi::class)
+internal class TextFieldKeyEventHandler {
+    private val preparedSelectionState = TextFieldPreparedSelectionState()
+    private val deadKeyCombiner = DeadKeyCombiner()
+    private val keyMapping = platformDefaultKeyMapping
+
+    fun onKeyEvent(
+        event: KeyEvent,
+        state: TextFieldState,
+        textLayoutState: TextLayoutState,
+        editable: Boolean,
+        singleLine: Boolean,
+        onSubmit: () -> Unit
+    ): Boolean {
+        if (event.type != KeyEventType.KeyDown) {
+            return false
+        }
+        val editCommand = event.toTypedEditCommand()
+        if (editCommand != null) {
+            return if (editable) {
+                editCommand.applyOnto(state)
+                preparedSelectionState.resetCachedX()
+                true
+            } else {
+                false
+            }
+        }
+        val command = keyMapping.map(event)
+        if (command == null || (command.editsText && !editable)) {
+            return false
+        }
+        var consumed = true
+        preparedSelectionContext(state, textLayoutState) {
+            when (command) {
+                // TODO(halilibo): implement after selection is supported.
+                KeyCommand.COPY, // -> selectionManager.copy(false)
+                    // TODO(siyamed): cut & paste will cause a reset input
+                KeyCommand.PASTE, // -> selectionManager.paste()
+                KeyCommand.CUT -> moveCursorRight() // selectionManager.cut()
+                KeyCommand.LEFT_CHAR -> collapseLeftOr { moveCursorLeft() }
+                KeyCommand.RIGHT_CHAR -> collapseRightOr { moveCursorRight() }
+                KeyCommand.LEFT_WORD -> moveCursorLeftByWord()
+                KeyCommand.RIGHT_WORD -> moveCursorRightByWord()
+                KeyCommand.PREV_PARAGRAPH -> moveCursorPrevByParagraph()
+                KeyCommand.NEXT_PARAGRAPH -> moveCursorNextByParagraph()
+                KeyCommand.UP -> moveCursorUpByLine()
+                KeyCommand.DOWN -> moveCursorDownByLine()
+                KeyCommand.PAGE_UP -> moveCursorUpByPage()
+                KeyCommand.PAGE_DOWN -> moveCursorDownByPage()
+                KeyCommand.LINE_START -> moveCursorToLineStart()
+                KeyCommand.LINE_END -> moveCursorToLineEnd()
+                KeyCommand.LINE_LEFT -> moveCursorToLineLeftSide()
+                KeyCommand.LINE_RIGHT -> moveCursorToLineRightSide()
+                KeyCommand.HOME -> moveCursorToHome()
+                KeyCommand.END -> moveCursorToEnd()
+                KeyCommand.DELETE_PREV_CHAR ->
+                    deleteIfSelectedOr {
+                        DeleteSurroundingTextCommand(
+                            selection.end - getPrecedingCharacterIndex(),
+                            0
+                        )
+                    }?.applyOnto(state)
+
+                KeyCommand.DELETE_NEXT_CHAR -> {
+                    // Note that some software keyboards, such as Samsungs, go through this code
+                    // path instead of making calls on the InputConnection directly.
+                    deleteIfSelectedOr {
+                        val nextCharacterIndex = getNextCharacterIndex()
+                        // If there's no next character, it means the cursor is at the end of the
+                        // text, and this should be a no-op. See b/199919707.
+                        if (nextCharacterIndex != NoCharacterFound) {
+                            DeleteSurroundingTextCommand(0, nextCharacterIndex - selection.end)
+                        } else {
+                            null
+                        }
+                    }?.applyOnto(state)
+                }
+
+                KeyCommand.DELETE_PREV_WORD ->
+                    deleteIfSelectedOr {
+                        getPreviousWordOffset()?.let {
+                            DeleteSurroundingTextCommand(selection.end - it, 0)
+                        }
+                    }?.applyOnto(state)
+
+                KeyCommand.DELETE_NEXT_WORD ->
+                    deleteIfSelectedOr {
+                        getNextWordOffset()?.let {
+                            DeleteSurroundingTextCommand(0, it - selection.end)
+                        }
+                    }?.applyOnto(state)
+
+                KeyCommand.DELETE_FROM_LINE_START ->
+                    deleteIfSelectedOr {
+                        getLineStartByOffset()?.let {
+                            DeleteSurroundingTextCommand(selection.end - it, 0)
+                        }
+                    }?.applyOnto(state)
+
+                KeyCommand.DELETE_TO_LINE_END ->
+                    deleteIfSelectedOr {
+                        getLineEndByOffset()?.let {
+                            DeleteSurroundingTextCommand(0, it - selection.end)
+                        }
+                    }?.applyOnto(state)
+
+                KeyCommand.NEW_LINE ->
+                    if (!singleLine) {
+                        CommitTextCommand("\n", 1).applyOnto(state)
+                    } else {
+                        onSubmit()
+                    }
+
+                KeyCommand.TAB ->
+                    if (!singleLine) {
+                        CommitTextCommand("\t", 1).applyOnto(state)
+                    } else {
+                        consumed = false // let propagate to focus system
+                    }
+
+                KeyCommand.SELECT_ALL -> selectAll()
+                KeyCommand.SELECT_LEFT_CHAR -> moveCursorLeft().selectMovement()
+                KeyCommand.SELECT_RIGHT_CHAR -> moveCursorRight().selectMovement()
+                KeyCommand.SELECT_LEFT_WORD -> moveCursorLeftByWord().selectMovement()
+                KeyCommand.SELECT_RIGHT_WORD -> moveCursorRightByWord().selectMovement()
+                KeyCommand.SELECT_PREV_PARAGRAPH -> moveCursorPrevByParagraph().selectMovement()
+                KeyCommand.SELECT_NEXT_PARAGRAPH -> moveCursorNextByParagraph().selectMovement()
+                KeyCommand.SELECT_LINE_START -> moveCursorToLineStart().selectMovement()
+                KeyCommand.SELECT_LINE_END -> moveCursorToLineEnd().selectMovement()
+                KeyCommand.SELECT_LINE_LEFT -> moveCursorToLineLeftSide().selectMovement()
+                KeyCommand.SELECT_LINE_RIGHT -> moveCursorToLineRightSide().selectMovement()
+                KeyCommand.SELECT_UP -> moveCursorUpByLine().selectMovement()
+                KeyCommand.SELECT_DOWN -> moveCursorDownByLine().selectMovement()
+                KeyCommand.SELECT_PAGE_UP -> moveCursorUpByPage().selectMovement()
+                KeyCommand.SELECT_PAGE_DOWN -> moveCursorDownByPage().selectMovement()
+                KeyCommand.SELECT_HOME -> moveCursorToHome().selectMovement()
+                KeyCommand.SELECT_END -> moveCursorToEnd().selectMovement()
+                KeyCommand.DESELECT -> deselect()
+                KeyCommand.UNDO -> {
+                    // undoManager?.makeSnapshot(value)
+                    // undoManager?.undo()?.let { this@TextFieldKeyInput.onValueChange(it) }
+                }
+
+                KeyCommand.REDO -> {
+                    // undoManager?.redo()?.let { this@TextFieldKeyInput.onValueChange(it) }
+                }
+
+                KeyCommand.CHARACTER_PALETTE -> {
+                    showCharacterPalette()
+                }
+            }
+        }
+        // undoManager?.forceNextSnapshot()
+        return consumed
+    }
+
+    private fun KeyEvent.toTypedEditCommand(): CommitTextCommand? {
+        if (!isTypedEvent) {
+            return null
+        }
+
+        val codePoint = deadKeyCombiner.consume(this) ?: return null
+        val text = StringBuilder(2).appendCodePointX(codePoint).toString()
+        return CommitTextCommand(text, 1)
+    }
+
+    private inline fun preparedSelectionContext(
+        state: TextFieldState,
+        textLayoutState: TextLayoutState,
+        block: TextFieldPreparedSelection.() -> Unit
+    ) {
+        val preparedSelection = TextFieldPreparedSelection(
+            state = state,
+            textLayoutState = textLayoutState,
+            textPreparedSelectionState = preparedSelectionState
+        )
+        preparedSelection.block()
+        if (preparedSelection.selection != preparedSelection.initialValue.selection) {
+            // update the editProcessor with the latest selection state.
+            // this has to be a reset because EditCommands do not inform IME.
+            state.editProcessor.reset(
+                preparedSelection.initialValue.copy(selection = preparedSelection.selection)
+            )
+        }
+    }
+
+    /**
+     * Helper function to apply a list of EditCommands in the scope of [TextFieldPreparedSelection]
+     */
+    private fun List<EditCommand>.applyOnto(state: TextFieldState) {
+        state.editProcessor.update(
+            this.toMutableList().apply {
+                add(0, FinishComposingTextCommand)
+            }
+        )
+    }
+
+    private fun EditCommand.applyOnto(state: TextFieldState) {
+        state.editProcessor.update(listOf(FinishComposingTextCommand, this))
+    }
+}
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextFieldState.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextFieldState.kt
index ca62583..6471f0d 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextFieldState.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextFieldState.kt
@@ -45,5 +45,7 @@
 
 @OptIn(ExperimentalFoundationApi::class)
 internal fun TextFieldState.deselect() {
-    editProcessor.reset(value.copy(selection = TextRange.Zero, composition = TextRange.Zero))
+    if (!value.selection.collapsed) {
+        editProcessor.reset(value.copy(selection = TextRange.Zero, composition = TextRange.Zero))
+    }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextLayoutState.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextLayoutState.kt
index c211f83..fa966ee 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextLayoutState.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextLayoutState.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.foundation.text.InternalFoundationTextApi
 import androidx.compose.foundation.text.TextDelegate
+import androidx.compose.foundation.text.TextLayoutResultProxy
 import androidx.compose.foundation.text.updateTextDelegate
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -36,7 +37,7 @@
     /**
      * Set of parameters and an internal cache to compute text layout.
      */
-    var textDelegate: TextDelegate by mutableStateOf(initialTextDelegate)
+    var textDelegate: TextDelegate = initialTextDelegate
         private set
 
     /**
@@ -45,6 +46,11 @@
     var layoutResult: TextLayoutResult? by mutableStateOf(null)
         private set
 
+    /**
+     * A helper class to find positions on text layout relative to wrapping decoration box.
+     */
+    var proxy: TextLayoutResultProxy? = null
+
     fun MeasureScope.layout(
         text: AnnotatedString,
         textStyle: TextStyle,
@@ -76,6 +82,7 @@
                 onTextLayout(it)
             }
             layoutResult = it
+            proxy = TextLayoutResultProxy(it)
         }
     }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextPreparedSelection.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextPreparedSelection.kt
new file mode 100644
index 0000000..754e497
--- /dev/null
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/TextPreparedSelection.kt
@@ -0,0 +1,417 @@
+/*
+ * 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.text2
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.text.TextLayoutResultProxy
+import androidx.compose.foundation.text.findFollowingBreak
+import androidx.compose.foundation.text.findParagraphEnd
+import androidx.compose.foundation.text.findParagraphStart
+import androidx.compose.foundation.text.findPrecedingBreak
+import androidx.compose.foundation.text2.input.CommitTextCommand
+import androidx.compose.foundation.text2.input.EditCommand
+import androidx.compose.foundation.text2.input.SetSelectionCommand
+import androidx.compose.runtime.snapshots.Snapshot
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.style.ResolvedTextDirection
+
+/**
+ * [TextFieldPreparedSelection] provides a scope for many selection-related operations. However,
+ * some vertical cursor operations like moving between lines or page up and down require a cache of
+ * X position in text to remember where to move the cursor in next line.
+ * [TextFieldPreparedSelection] is a disposable scope that cannot hold its own state. This class
+ * helps to pass a cached X value between selection operations in different scopes.
+ */
+internal class TextFieldPreparedSelectionState {
+    /**
+     * it's set at the start of vertical navigation and used as the preferred value to set a new
+     * cursor position.
+     */
+    var cachedX: Float? = null
+
+    /**
+     * Remove and forget the cached X used for vertical navigation.
+     */
+    fun resetCachedX() {
+        cachedX = null
+    }
+}
+
+/**
+ * This utility class implements many selection-related operations on text (including basic
+ * cursor movements and deletions) and combines them, taking into account how the text was
+ * rendered. So, for example, [moveCursorToLineEnd] moves it to the visual line end.
+ *
+ * For many of these operations, it's particularly important to keep the difference between
+ * selection start and selection end. In some systems, they are called "anchor" and "caret"
+ * respectively. For example, for selection from scratch, after [moveCursorLeftByWord]
+ * [moveCursorRight] will move the left side of the selection, but after [moveCursorRightByWord]
+ * the right one.
+ *
+ * To use it in scope of text fields see [TextFieldPreparedSelection]
+ */
+@OptIn(ExperimentalFoundationApi::class)
+internal class TextFieldPreparedSelection(
+    private val state: TextFieldState,
+    private val textLayoutState: TextLayoutState,
+    private val textPreparedSelectionState: TextFieldPreparedSelectionState
+) {
+    /**
+     * Read the value from state without read observation to not accidentally cause recompositions.
+     * Freezing the initial value is necessary to make atomic operations in the scope of this
+     * [TextFieldPreparedSelection]. It is also used to make comparison between the initial state
+     * and the modified state of selection and content.
+     */
+    val initialValue = Snapshot.withoutReadObservation { state.value }
+
+    /**
+     * Current active selection in the context of this [TextFieldPreparedSelection]
+     */
+    var selection = initialValue.selection
+
+    /**
+     * Initial text value.
+     */
+    private val text = initialValue.text
+
+    /**
+     * If there is a non-collapsed selection, delete its contents. Or execute the given [or] block.
+     * Either way this function returns list of [EditCommand]s that should be applied on
+     * [TextFieldState].
+     */
+    fun deleteIfSelectedOr(or: TextFieldPreparedSelection.() -> EditCommand?): List<EditCommand>? {
+        return if (selection.collapsed) {
+            or(this)?.let { editCommand -> listOf(editCommand) }
+        } else {
+            listOf(
+                CommitTextCommand("", 0),
+                SetSelectionCommand(selection.min, selection.min)
+            )
+        }
+    }
+
+    /**
+     * Executes PageUp key
+     */
+    fun moveCursorUpByPage() = applyIfNotEmpty(false) {
+        textLayoutState.proxy?.jumpByPagesOffset(-1)?.let { setCursor(it) }
+    }
+
+    /**
+     * Executes PageDown key
+     */
+    fun moveCursorDownByPage() = applyIfNotEmpty(false) {
+        textLayoutState.proxy?.jumpByPagesOffset(1)?.let { setCursor(it) }
+    }
+
+    /**
+     * Returns a cursor position after jumping back or forth by [pagesAmount] number of pages,
+     * where `page` is the visible amount of space in the text field. Visible rectangle is
+     * calculated by the coordinates of decoration box around the TextField.
+     */
+    private fun TextLayoutResultProxy.jumpByPagesOffset(pagesAmount: Int): Int {
+        val visibleInnerTextFieldRect = innerTextFieldCoordinates?.let { inner ->
+            decorationBoxCoordinates?.localBoundingBoxOf(inner)
+        } ?: Rect.Zero
+        val currentOffset = initialValue.selection.end
+        val currentPos = value.getCursorRect(currentOffset)
+        val x = currentPos.left
+        val y = currentPos.top + visibleInnerTextFieldRect.size.height * pagesAmount
+        return value.getOffsetForPosition(Offset(x, y))
+    }
+
+    /**
+     * Only apply the given [block] if the text is not empty.
+     *
+     * @param resetCachedX Whether to reset the cachedX parameter in [TextFieldPreparedSelectionState].
+     */
+    inline fun applyIfNotEmpty(
+        resetCachedX: Boolean = true,
+        block: TextFieldPreparedSelection.() -> Unit
+    ): TextFieldPreparedSelection {
+        if (resetCachedX) {
+            textPreparedSelectionState.resetCachedX()
+        }
+        if (text.isNotEmpty()) {
+            this.block()
+        }
+        return this
+    }
+
+    /**
+     * Sets a collapsed selection at given [offset].
+     */
+    private fun setCursor(offset: Int) {
+        selection = TextRange(offset, offset)
+    }
+
+    fun selectAll() = applyIfNotEmpty {
+        selection = TextRange(0, text.length)
+    }
+
+    fun deselect() = applyIfNotEmpty {
+        setCursor(selection.end)
+    }
+
+    fun moveCursorLeft() = applyIfNotEmpty {
+        if (isLtr()) {
+            moveCursorPrev()
+        } else {
+            moveCursorNext()
+        }
+    }
+
+    fun moveCursorRight() = applyIfNotEmpty {
+        if (isLtr()) {
+            moveCursorNext()
+        } else {
+            moveCursorPrev()
+        }
+    }
+
+    /**
+     * If there is already a selection, collapse it to the left side. Otherwise, execute [or]
+     */
+    fun collapseLeftOr(or: TextFieldPreparedSelection.() -> Unit) = applyIfNotEmpty {
+        if (selection.collapsed) {
+            or(this)
+        } else {
+            if (isLtr()) {
+                setCursor(selection.min)
+            } else {
+                setCursor(selection.max)
+            }
+        }
+    }
+
+    /**
+     * If there is already a selection, collapse it to the right side. Otherwise, execute [or]
+     */
+    fun collapseRightOr(or: TextFieldPreparedSelection.() -> Unit) = applyIfNotEmpty {
+        if (selection.collapsed) {
+            or(this)
+        } else {
+            if (isLtr()) {
+                setCursor(selection.max)
+            } else {
+                setCursor(selection.min)
+            }
+        }
+    }
+
+    /**
+     * Returns the index of the character break preceding the end of [selection].
+     */
+    fun getPrecedingCharacterIndex() = text.findPrecedingBreak(selection.end)
+
+    /**
+     * Returns the index of the character break following the end of [selection]. Returns
+     * [NoCharacterFound] if there are no more breaks before the end of the string.
+     */
+    fun getNextCharacterIndex() = text.findFollowingBreak(selection.end)
+
+    private fun moveCursorPrev() = applyIfNotEmpty {
+        val prev = getPrecedingCharacterIndex()
+        if (prev != -1) setCursor(prev)
+    }
+
+    private fun moveCursorNext() = applyIfNotEmpty {
+        val next = getNextCharacterIndex()
+        if (next != -1) setCursor(next)
+    }
+
+    fun moveCursorToHome() = applyIfNotEmpty {
+        setCursor(0)
+    }
+
+    fun moveCursorToEnd() = applyIfNotEmpty {
+        setCursor(text.length)
+    }
+
+    fun moveCursorLeftByWord() = applyIfNotEmpty {
+        if (isLtr()) {
+            moveCursorPrevByWord()
+        } else {
+            moveCursorNextByWord()
+        }
+    }
+
+    fun moveCursorRightByWord() = applyIfNotEmpty {
+        if (isLtr()) {
+            moveCursorNextByWord()
+        } else {
+            moveCursorPrevByWord()
+        }
+    }
+
+    fun getNextWordOffset(): Int? = textLayoutState.layoutResult?.getNextWordOffsetForLayout()
+
+    private fun moveCursorNextByWord() = applyIfNotEmpty {
+        getNextWordOffset()?.let { setCursor(it) }
+    }
+
+    fun getPreviousWordOffset(): Int? = textLayoutState.layoutResult?.getPrevWordOffset()
+
+    private fun moveCursorPrevByWord() = applyIfNotEmpty {
+        getPreviousWordOffset()?.let { setCursor(it) }
+    }
+
+    fun moveCursorPrevByParagraph() = applyIfNotEmpty {
+        setCursor(getParagraphStart())
+    }
+
+    fun moveCursorNextByParagraph() = applyIfNotEmpty {
+        setCursor(getParagraphEnd())
+    }
+
+    fun moveCursorUpByLine() = applyIfNotEmpty(false) {
+        textLayoutState.layoutResult?.jumpByLinesOffset(-1)?.let { setCursor(it) }
+    }
+
+    fun moveCursorDownByLine() = applyIfNotEmpty(false) {
+        textLayoutState.layoutResult?.jumpByLinesOffset(1)?.let { setCursor(it) }
+    }
+
+    fun getLineStartByOffset(): Int? = textLayoutState.layoutResult?.getLineStartByOffsetForLayout()
+
+    fun moveCursorToLineStart() = applyIfNotEmpty {
+        getLineStartByOffset()?.let { setCursor(it) }
+    }
+
+    fun getLineEndByOffset(): Int? = textLayoutState.layoutResult?.getLineEndByOffsetForLayout()
+
+    fun moveCursorToLineEnd() = applyIfNotEmpty {
+        getLineEndByOffset()?.let { setCursor(it) }
+    }
+
+    fun moveCursorToLineLeftSide() = applyIfNotEmpty {
+        if (isLtr()) {
+            moveCursorToLineStart()
+        } else {
+            moveCursorToLineEnd()
+        }
+    }
+
+    fun moveCursorToLineRightSide() = applyIfNotEmpty {
+        if (isLtr()) {
+            moveCursorToLineEnd()
+        } else {
+            moveCursorToLineStart()
+        }
+    }
+
+    // it selects a text from the original selection start to a current selection end
+    fun selectMovement() = applyIfNotEmpty(false) {
+        selection = TextRange(initialValue.selection.start, selection.end)
+    }
+
+    private fun isLtr(): Boolean {
+        val direction = textLayoutState.layoutResult?.getParagraphDirection(selection.end)
+        return direction != ResolvedTextDirection.Rtl
+    }
+
+    private tailrec fun TextLayoutResult.getNextWordOffsetForLayout(
+        currentOffset: Int = selection.end
+    ): Int {
+        if (currentOffset >= initialValue.text.length) {
+            return initialValue.text.length
+        }
+        val currentWord = getWordBoundary(charOffset(currentOffset))
+        return if (currentWord.end <= currentOffset) {
+            getNextWordOffsetForLayout(currentOffset + 1)
+        } else {
+            currentWord.end
+        }
+    }
+
+    private tailrec fun TextLayoutResult.getPrevWordOffset(
+        currentOffset: Int = selection.end
+    ): Int {
+        if (currentOffset <= 0) {
+            return 0
+        }
+        val currentWord = getWordBoundary(charOffset(currentOffset))
+        return if (currentWord.start >= currentOffset) {
+            getPrevWordOffset(currentOffset - 1)
+        } else {
+            currentWord.start
+        }
+    }
+
+    private fun TextLayoutResult.getLineStartByOffsetForLayout(
+        currentOffset: Int = selection.min
+    ): Int {
+        val currentLine = getLineForOffset(currentOffset)
+        return getLineStart(currentLine)
+    }
+
+    private fun TextLayoutResult.getLineEndByOffsetForLayout(
+        currentOffset: Int = selection.max
+    ): Int {
+        val currentLine = getLineForOffset(currentOffset)
+        return getLineEnd(currentLine, true)
+    }
+
+    private fun TextLayoutResult.jumpByLinesOffset(linesAmount: Int): Int {
+        val currentOffset = selection.end
+
+        if (textPreparedSelectionState.cachedX == null) {
+            textPreparedSelectionState.cachedX = getCursorRect(currentOffset).left
+        }
+
+        val targetLine = getLineForOffset(currentOffset) + linesAmount
+        when {
+            targetLine < 0 -> {
+                return 0
+            }
+
+            targetLine >= lineCount -> {
+                return text.length
+            }
+        }
+
+        val y = getLineBottom(targetLine) - 1
+        val x = textPreparedSelectionState.cachedX!!.also {
+            if ((isLtr() && it >= getLineRight(targetLine)) ||
+                (!isLtr() && it <= getLineLeft(targetLine))
+            ) {
+                return getLineEnd(targetLine, true)
+            }
+        }
+
+        return getOffsetForPosition(Offset(x, y))
+    }
+
+    private fun charOffset(offset: Int) = offset.coerceAtMost(text.length - 1)
+
+    private fun getParagraphStart() = text.findParagraphStart(selection.min)
+
+    private fun getParagraphEnd() = text.findParagraphEnd(selection.max)
+
+    companion object {
+        /**
+         * Value returned by [getNextCharacterIndex] and [getPrecedingCharacterIndex] when no valid
+         * index could be found, e.g. it would be the end of the string.
+         *
+         * This is equivalent to `BreakIterator.DONE` on JVM/Android.
+         */
+        const val NoCharacterFound = -1
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/service/AndroidTextInputAdapter.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/service/AndroidTextInputAdapter.kt
index d1ef7d2..b7b4437 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/service/AndroidTextInputAdapter.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/service/AndroidTextInputAdapter.kt
@@ -14,20 +14,18 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalFoundationApi::class)
-
 package androidx.compose.foundation.text2.service
 
 import android.os.Looper
 import android.text.InputType
 import android.util.Log
 import android.view.Choreographer
+import android.view.KeyEvent
 import android.view.View
 import android.view.inputmethod.EditorInfo
 import android.view.inputmethod.InputConnection
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.text2.TextFieldState
-import androidx.compose.foundation.text2.input.CommitTextCommand
 import androidx.compose.foundation.text2.input.EditCommand
 import androidx.compose.foundation.text2.input.EditProcessor
 import androidx.compose.runtime.collection.mutableVectorOf
@@ -38,13 +36,13 @@
 import androidx.compose.ui.text.input.PlatformTextInput
 import androidx.compose.ui.text.input.PlatformTextInputAdapter
 import androidx.compose.ui.text.input.TextFieldValue
-import androidx.compose.ui.text.input.TextInputForTests
 import androidx.core.view.inputmethod.EditorInfoCompat
 import java.util.concurrent.Executor
 
 private const val DEBUG = true
 private const val TAG = "BasicTextInputAdapter"
 
+@OptIn(ExperimentalFoundationApi::class)
 internal class AndroidTextInputAdapter constructor(
     view: View,
     private val platformTextInput: PlatformTextInput
@@ -56,17 +54,6 @@
 
     private val textInputCommandExecutor = TextInputCommandExecutor(view, inputMethodManager)
 
-    override val inputForTests: TextInputForTests = object : TextInputForTests {
-        private fun requireSession(): EditableTextInputSession =
-            currentTextInputSession ?: error("There is no active input session. Missing a focus?")
-
-        override fun inputTextForTest(text: String) {
-            requireSession().requestEdits(
-                listOf(CommitTextCommand(text, 1))
-            )
-        }
-    }
-
     override fun createInputConnection(outAttrs: EditorInfo): InputConnection {
         logDebug { "createInputConnection" }
         val value = currentTextInputSession?.value ?: TextFieldValue()
@@ -123,6 +110,10 @@
                 state.editProcessor.update(editCommands)
             }
 
+            override fun sendKeyEvent(keyEvent: KeyEvent) {
+                inputMethodManager.sendKeyEvent(keyEvent)
+            }
+
             override val isOpen: Boolean
                 get() = currentTextInputSession == this
 
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/service/ComposeInputMethodManager.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/service/ComposeInputMethodManager.kt
index 6e107d5..7547ef0 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/service/ComposeInputMethodManager.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/service/ComposeInputMethodManager.kt
@@ -20,8 +20,10 @@
 import android.content.Context
 import android.content.ContextWrapper
 import android.os.Build
+import android.view.KeyEvent
 import android.view.View
 import android.view.Window
+import android.view.inputmethod.BaseInputConnection
 import android.view.inputmethod.ExtractedText
 import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
@@ -54,6 +56,12 @@
         compositionStart: Int,
         compositionEnd: Int
     )
+
+    /**
+     * Sends a [KeyEvent] originated from an InputMethod to the Window. This is a necessary
+     * delegation when the InputConnection itself does not handle the received event.
+     */
+    fun sendKeyEvent(event: KeyEvent)
 }
 
 internal fun ComposeInputMethodManager(view: View): ComposeInputMethodManager {
@@ -67,10 +75,12 @@
             as android.view.inputmethod.InputMethodManager
     }
 
-    private val helper = if (Build.VERSION.SDK_INT < 30) {
-        ImmHelper21(view)
-    } else {
+    private val helper = if (Build.VERSION.SDK_INT >= 30) {
         ImmHelper30(view)
+    } else if (Build.VERSION.SDK_INT >= 24) {
+        ImmHelper24(view)
+    } else {
+        ImmHelper21(view)
     }
 
     override fun restartInput() {
@@ -92,6 +102,10 @@
         imm.updateExtractedText(view, token, extractedText)
     }
 
+    override fun sendKeyEvent(event: KeyEvent) {
+        helper.sendKeyEvent(imm, event)
+    }
+
     override fun updateSelection(
         selectionStart: Int,
         selectionEnd: Int,
@@ -103,12 +117,24 @@
 }
 
 private interface ImmHelper {
+
     fun showSoftInput(imm: android.view.inputmethod.InputMethodManager)
+
     fun hideSoftInput(imm: android.view.inputmethod.InputMethodManager)
+
+    fun sendKeyEvent(imm: android.view.inputmethod.InputMethodManager, event: KeyEvent)
 }
 
 private class ImmHelper21(private val view: View) : ImmHelper {
 
+    /**
+     * Prior to API24, the safest way to delegate IME originated KeyEvents to the window was
+     * through BaseInputConnection.
+     */
+    private val baseInputConnection by lazy(LazyThreadSafetyMode.NONE) {
+        BaseInputConnection(view, false)
+    }
+
     @DoNotInline
     override fun showSoftInput(imm: android.view.inputmethod.InputMethodManager) {
         view.post {
@@ -120,6 +146,32 @@
     override fun hideSoftInput(imm: android.view.inputmethod.InputMethodManager) {
         imm.hideSoftInputFromWindow(view.windowToken, 0)
     }
+
+    @DoNotInline
+    override fun sendKeyEvent(imm: android.view.inputmethod.InputMethodManager, event: KeyEvent) {
+        baseInputConnection.sendKeyEvent(event)
+    }
+}
+
+@RequiresApi(24)
+private class ImmHelper24(private val view: View) : ImmHelper {
+
+    @DoNotInline
+    override fun showSoftInput(imm: android.view.inputmethod.InputMethodManager) {
+        view.post {
+            imm.showSoftInput(view, 0)
+        }
+    }
+
+    @DoNotInline
+    override fun hideSoftInput(imm: android.view.inputmethod.InputMethodManager) {
+        imm.hideSoftInputFromWindow(view.windowToken, 0)
+    }
+
+    @DoNotInline
+    override fun sendKeyEvent(imm: android.view.inputmethod.InputMethodManager, event: KeyEvent) {
+        imm.dispatchKeyEventFromInputMethod(view, event)
+    }
 }
 
 @RequiresApi(30)
@@ -156,6 +208,11 @@
         } ?: immHelper21.hideSoftInput(imm)
     }
 
+    @DoNotInline
+    override fun sendKeyEvent(imm: android.view.inputmethod.InputMethodManager, event: KeyEvent) {
+        imm.dispatchKeyEventFromInputMethod(view, event)
+    }
+
     // TODO(b/221889664) Replace with composition local when available.
     private fun View.findWindow(): Window? =
         (parent as? DialogWindowProvider)?.window
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/service/StatelessInputConnection.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/service/StatelessInputConnection.kt
index 4fcdeab..4703d1e 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/service/StatelessInputConnection.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/service/StatelessInputConnection.kt
@@ -203,8 +203,7 @@
 
     override fun sendKeyEvent(event: KeyEvent): Boolean = ensureActive {
         logDebug("sendKeyEvent($event)")
-        // TODO(halilibo): Delegate BaseInputConnection to adapter
-        // eventCallback.onKeyEvent(event)
+        activeSessionProvider()?.sendKeyEvent(event)
         return true
     }
 
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/service/TextInputSession.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/service/TextInputSession.kt
index 4e29e6b..c2224e8 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/service/TextInputSession.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/service/TextInputSession.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.text2.service
 
+import android.view.KeyEvent
 import android.view.inputmethod.InputConnection
 import androidx.compose.foundation.text2.TextFieldState
 import androidx.compose.foundation.text2.input.EditCommand
@@ -63,6 +64,11 @@
     fun requestEdits(editCommands: List<EditCommand>)
 
     /**
+     * Delegates IME requested KeyEvents.
+     */
+    fun sendKeyEvent(keyEvent: KeyEvent)
+
+    /**
      * IME configuration to use when creating new [InputConnection]s while this session is active.
      */
     val imeOptions: ImeOptions
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
index b249514..0e24104 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
@@ -40,13 +40,16 @@
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.modifier.ModifierLocalConsumer
 import androidx.compose.ui.modifier.ModifierLocalReadScope
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.SemanticsModifierNode
+import androidx.compose.ui.platform.InspectorInfo
 import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.SemanticsConfiguration
 import androidx.compose.ui.semantics.disabled
 import androidx.compose.ui.semantics.onClick
 import androidx.compose.ui.semantics.onLongClick
 import androidx.compose.ui.semantics.role
-import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.center
 import androidx.compose.ui.unit.toOffset
 import kotlinx.coroutines.CoroutineScope
@@ -512,23 +515,6 @@
     onLongClick: (() -> Unit)? = null,
     onClick: () -> Unit
 ): Modifier {
-    fun Modifier.clickSemantics() = this.semantics(mergeDescendants = true) {
-        if (role != null) {
-            this.role = role
-        }
-        // b/156468846:  add long click semantics and double click if needed
-        onClick(
-            action = { onClick(); true },
-            label = onClickLabel
-        )
-        if (onLongClick != null) {
-            onLongClick(action = { onLongClick(); true }, label = onLongClickLabel)
-        }
-        if (!enabled) {
-            disabled()
-        }
-    }
-
     fun Modifier.detectPressAndClickFromKey() = this.onKeyEvent { keyEvent ->
         when {
             enabled && keyEvent.isPress -> {
@@ -555,11 +541,101 @@
             else -> false
         }
     }
-    return this
-        .clickSemantics()
-        .detectPressAndClickFromKey()
-        .indication(interactionSource, indication)
-        .hoverable(enabled = enabled, interactionSource = interactionSource)
-        .focusableInNonTouchMode(enabled = enabled, interactionSource = interactionSource)
-        .then(gestureModifiers)
+    return this then
+        ClickableSemanticsElement(
+            enabled = enabled,
+            role = role,
+            onLongClickLabel = onLongClickLabel,
+            onLongClick = onLongClick,
+            onClickLabel = onClickLabel,
+            onClick = onClick
+        )
+            .detectPressAndClickFromKey()
+            .indication(interactionSource, indication)
+            .hoverable(enabled = enabled, interactionSource = interactionSource)
+            .focusableInNonTouchMode(enabled = enabled, interactionSource = interactionSource)
+            .then(gestureModifiers)
+}
+
+private class ClickableSemanticsElement(
+    private val enabled: Boolean,
+    private val role: Role?,
+    private val onLongClickLabel: String?,
+    private val onLongClick: (() -> Unit)?,
+    private val onClickLabel: String?,
+    private val onClick: () -> Unit
+) : ModifierNodeElement<ClickableSemanticsNode>() {
+    override fun create() = ClickableSemanticsNode(
+        enabled = enabled,
+        role = role,
+        onLongClickLabel = onLongClickLabel,
+        onLongClick = onLongClick,
+        onClickLabel = onClickLabel,
+        onClick = onClick
+    )
+
+    override fun update(node: ClickableSemanticsNode) = node.also {
+        it.enabled = enabled
+        it.role = role
+        it.onLongClickLabel = onLongClickLabel
+        it.onLongClick = onLongClick
+        it.onClickLabel = onClickLabel
+        it.onClick = onClick
+    }
+
+    override fun InspectorInfo.inspectableProperties() = Unit
+
+    override fun hashCode(): Int {
+        var result = enabled.hashCode()
+        result = 31 * result + role.hashCode()
+        result = 31 * result + onLongClickLabel.hashCode()
+        result = 31 * result + onLongClick.hashCode()
+        result = 31 * result + onClickLabel.hashCode()
+        result = 31 * result + onClick.hashCode()
+        return result
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is ClickableSemanticsElement) return false
+
+        if (enabled != other.enabled) return false
+        if (role != other.role) return false
+        if (onLongClickLabel != other.onLongClickLabel) return false
+        if (onLongClick != other.onLongClick) return false
+        if (onClickLabel != other.onClickLabel) return false
+        if (onClick != other.onClick) return false
+
+        return true
+    }
+}
+
+private class ClickableSemanticsNode(
+    var enabled: Boolean,
+    var role: Role?,
+    var onLongClickLabel: String?,
+    var onLongClick: (() -> Unit)?,
+    var onClickLabel: String?,
+    var onClick: () -> Unit,
+) : SemanticsModifierNode, Modifier.Node() {
+    override val semanticsConfiguration
+        get() = SemanticsConfiguration().apply {
+            isMergingSemanticsOfDescendants = true
+            if (this@ClickableSemanticsNode.role != null) {
+                role = this@ClickableSemanticsNode.role!!
+            }
+            onClick(
+                action = { onClick(); true },
+                label = onClickLabel
+            )
+            if (onLongClick != null) {
+                onLongClick(
+                    action = { onLongClick?.invoke(); true },
+                    label = onLongClickLabel
+                )
+            }
+            if (!enabled) {
+                disabled()
+            }
+        }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Hoverable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Hoverable.kt
index 0b6ea3e..4f2ae94 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Hoverable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Hoverable.kt
@@ -18,20 +18,14 @@
 
 import androidx.compose.foundation.interaction.HoverInteraction
 import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
+import androidx.compose.ui.input.pointer.PointerEvent
+import androidx.compose.ui.input.pointer.PointerEventPass
 import androidx.compose.ui.input.pointer.PointerEventType
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.platform.debugInspectorInfo
-import kotlinx.coroutines.currentCoroutineContext
-import kotlinx.coroutines.isActive
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.PointerInputModifierNode
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.unit.IntSize
 import kotlinx.coroutines.launch
 
 /**
@@ -46,15 +40,67 @@
 fun Modifier.hoverable(
     interactionSource: MutableInteractionSource,
     enabled: Boolean = true
-): Modifier = composed(
-    inspectorInfo = debugInspectorInfo {
+) = this then if (enabled) HoverableElement(interactionSource) else Modifier
+
+private class HoverableElement(
+    private val interactionSource: MutableInteractionSource
+) : ModifierNodeElement<HoverableNode>() {
+    override fun create() = HoverableNode(interactionSource)
+
+    override fun update(node: HoverableNode) = node.apply {
+        updateInteractionSource(interactionSource)
+    }
+
+    override fun hashCode(): Int {
+        return 31 * interactionSource.hashCode()
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is HoverableElement) return false
+        if (other.interactionSource != interactionSource) return false
+        return true
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
         name = "hoverable"
         properties["interactionSource"] = interactionSource
-        properties["enabled"] = enabled
     }
-) {
-    val scope = rememberCoroutineScope()
-    var hoverInteraction by remember { mutableStateOf<HoverInteraction.Enter?>(null) }
+}
+
+private class HoverableNode(
+    private var interactionSource: MutableInteractionSource
+) : PointerInputModifierNode, Modifier.Node() {
+    private var hoverInteraction: HoverInteraction.Enter? = null
+
+    fun updateInteractionSource(interactionSource: MutableInteractionSource) {
+        if (this.interactionSource != interactionSource) {
+            tryEmitExit()
+            // b/273699888 TODO: Define behavior if there is an ongoing hover
+            this.interactionSource = interactionSource
+        }
+    }
+
+    override fun onPointerEvent(
+        pointerEvent: PointerEvent,
+        pass: PointerEventPass,
+        bounds: IntSize
+    ) {
+        if (pass == PointerEventPass.Main) {
+            when (pointerEvent.type) {
+                PointerEventType.Enter -> coroutineScope.launch { emitEnter() }
+                PointerEventType.Exit -> coroutineScope.launch { emitExit() }
+            }
+        }
+    }
+
+    override fun onCancelPointerInput() {
+        tryEmitExit()
+    }
+
+    override fun onDetach() {
+        tryEmitExit()
+    }
 
     suspend fun emitEnter() {
         if (hoverInteraction == null) {
@@ -79,37 +125,4 @@
             hoverInteraction = null
         }
     }
-
-    DisposableEffect(interactionSource) {
-        onDispose { tryEmitExit() }
-    }
-    LaunchedEffect(enabled) {
-        if (!enabled) {
-            emitExit()
-        }
-    }
-
-    if (enabled) {
-        Modifier
-// TODO(b/202505231):
-//  because we only react to input events, and not on layout changes, we can have a situation when
-//  Composable is under the cursor, but not hovered. To fix that, we have two ways:
-//  a. Trigger Enter/Exit on any layout change, inside Owner
-//  b. Manually react on layout changes via Modifier.onGloballyPosition, and check something like
-//  LocalPointerPosition.current
-            .pointerInput(interactionSource) {
-                val currentContext = currentCoroutineContext()
-                awaitPointerEventScope {
-                    while (currentContext.isActive) {
-                        val event = awaitPointerEvent()
-                        when (event.type) {
-                            PointerEventType.Enter -> scope.launch { emitEnter() }
-                            PointerEventType.Exit -> scope.launch { emitExit() }
-                        }
-                    }
-                }
-            }
-    } else {
-        Modifier
-    }
 }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyLayoutSemanticState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyLayoutSemanticState.kt
new file mode 100644
index 0000000..5709700
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyLayoutSemanticState.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy
+
+import androidx.compose.foundation.gestures.animateScrollBy
+import androidx.compose.foundation.lazy.layout.LazyLayoutSemanticState
+import androidx.compose.ui.semantics.CollectionInfo
+
+internal fun LazyLayoutSemanticState(
+    state: LazyListState,
+    isVertical: Boolean
+): LazyLayoutSemanticState = object : LazyLayoutSemanticState {
+
+    override val currentPosition: Float
+        get() = state.firstVisibleItemIndex + state.firstVisibleItemScrollOffset / 100_000f
+    override val canScrollForward: Boolean
+        get() = state.canScrollForward
+
+    override suspend fun animateScrollBy(delta: Float) {
+        state.animateScrollBy(delta)
+    }
+
+    override suspend fun scrollToItem(index: Int) {
+        state.scrollToItem(index)
+    }
+
+    override fun collectionInfo(): CollectionInfo =
+        if (isVertical) {
+            CollectionInfo(rowCount = -1, columnCount = 1)
+        } else {
+            CollectionInfo(rowCount = 1, columnCount = -1)
+        }
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemProvider.kt
index 664ac94..31cbd85 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemProvider.kt
@@ -92,9 +92,9 @@
  * We use the idea of sliding window as an optimization, so user can scroll up to this number of
  * items until we have to regenerate the key to index map.
  */
-private const val NearestItemsSlidingWindowSize = 30
+internal const val NearestItemsSlidingWindowSize = 30
 
 /**
  * The minimum amount of items near the current first visible item we want to have mapping for.
  */
-private const val NearestItemsExtraItemCount = 100
\ No newline at end of file
+internal const val NearestItemsExtraItemCount = 100
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutSemanticState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutSemanticState.kt
deleted file mode 100644
index c4e0403..0000000
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutSemanticState.kt
+++ /dev/null
@@ -1,47 +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.lazy.layout
-
-import androidx.compose.foundation.gestures.animateScrollBy
-import androidx.compose.foundation.lazy.LazyListState
-import androidx.compose.ui.semantics.CollectionInfo
-
-internal fun LazyLayoutSemanticState(
-    state: LazyListState,
-    isVertical: Boolean
-): LazyLayoutSemanticState = object : LazyLayoutSemanticState {
-
-    override val currentPosition: Float
-        get() = state.firstVisibleItemIndex + state.firstVisibleItemScrollOffset / 100_000f
-    override val canScrollForward: Boolean
-        get() = state.canScrollForward
-
-    override suspend fun animateScrollBy(delta: Float) {
-        state.animateScrollBy(delta)
-    }
-
-    override suspend fun scrollToItem(index: Int) {
-        state.scrollToItem(index)
-    }
-
-    override fun collectionInfo(): CollectionInfo =
-        if (isVertical) {
-            CollectionInfo(rowCount = -1, columnCount = 1)
-        } else {
-            CollectionInfo(rowCount = 1, columnCount = -1)
-        }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt
index 0697a35..d9a9270 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt
@@ -42,8 +42,8 @@
     state: LazyStaggeredGridState,
     /** The layout orientation of the grid */
     orientation: Orientation,
-    /** Prefix sums of cross axis sizes of slots per line, e.g. the columns for vertical grid. */
-    slotSizesSums: Density.(Constraints) -> IntArray,
+    /** Cross axis positions and sizes of slots per line, e.g. the columns for vertical grid. */
+    slots: Density.(Constraints) -> LazyStaggeredGridSlots,
     /** Modifier to be applied for the inner layout */
     modifier: Modifier = Modifier,
     /** The inner padding to be added for the whole content (not for each individual item) */
@@ -72,7 +72,7 @@
         orientation,
         mainAxisSpacing,
         crossAxisSpacing,
-        slotSizesSums
+        slots
     )
     val semanticState = rememberLazyStaggeredGridSemanticState(state, reverseLayout)
 
@@ -119,4 +119,10 @@
     if (itemProvider.itemCount > 0) {
         state.updateScrollPositionIfTheFirstItemWasMoved(itemProvider)
     }
-}
\ No newline at end of file
+}
+
+/** Slot configuration of staggered grid **/
+internal class LazyStaggeredGridSlots(
+    val positions: IntArray,
+    val sizes: IntArray
+)
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridCells.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridCells.kt
index fc01154..a783690 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridCells.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridCells.kt
@@ -46,7 +46,7 @@
      * @param spacing cross axis spacing, e.g. horizontal spacing for [LazyVerticalStaggeredGrid].
      * The spacing is passed from the corresponding [Arrangement] param of the lazy grid.
      */
-    fun Density.calculateCrossAxisCellSizes(availableSize: Int, spacing: Int): List<Int>
+    fun Density.calculateCrossAxisCellSizes(availableSize: Int, spacing: Int): IntArray
 
     /**
      * Defines a grid with fixed number of rows or columns.
@@ -62,7 +62,7 @@
         override fun Density.calculateCrossAxisCellSizes(
             availableSize: Int,
             spacing: Int
-        ): List<Int> {
+        ): IntArray {
             return calculateCellsCrossAxisSizeImpl(availableSize, count, spacing)
         }
 
@@ -92,7 +92,7 @@
         override fun Density.calculateCrossAxisCellSizes(
             availableSize: Int,
             spacing: Int
-        ): List<Int> {
+        ): IntArray {
             val count = maxOf((availableSize + spacing) / (minSize.roundToPx() + spacing), 1)
             return calculateCellsCrossAxisSizeImpl(availableSize, count, spacing)
         }
@@ -105,17 +105,51 @@
             return other is Adaptive && minSize == other.minSize
         }
     }
+
+    /**
+     * Defines a grid with as many rows or columns as possible on the condition that
+     * every cell takes exactly [size] space. The remaining space will be arranged through
+     * [LazyStaggeredGrid] arrangements on corresponding axis. If [size] is larger than
+     * container size, the cell will be size to match the container.
+     *
+     * For example, for the vertical [LazyVerticalStaggeredGrid] FixedSize(20.dp) would mean that
+     * there will be as many columns as possible and every column will be exactly 20.dp.
+     * If the screen is 88.dp wide tne there will be 4 columns 20.dp each with remaining 8.dp
+     * distributed through [Arrangement.Horizontal].
+     */
+    class FixedSize(private val size: Dp) : StaggeredGridCells {
+        override fun Density.calculateCrossAxisCellSizes(
+            availableSize: Int,
+            spacing: Int
+        ): IntArray {
+            val cellSize = size.roundToPx()
+            return if (cellSize + spacing < availableSize) {
+                val cellCount = availableSize / (cellSize + spacing)
+                IntArray(cellCount) { cellSize }
+            } else {
+                IntArray(1) { availableSize }
+            }
+        }
+
+        override fun hashCode(): Int {
+            return size.hashCode()
+        }
+
+        override fun equals(other: Any?): Boolean {
+            return other is FixedSize && size == other.size
+        }
+    }
 }
 
 private fun calculateCellsCrossAxisSizeImpl(
     gridSize: Int,
     slotCount: Int,
     spacing: Int
-): List<Int> {
+): IntArray {
     val gridSizeWithoutSpacing = gridSize - spacing * (slotCount - 1)
     val slotSize = gridSizeWithoutSpacing / slotCount
     val remainingPixels = gridSizeWithoutSpacing % slotCount
-    return List(slotCount) {
+    return IntArray(slotCount) {
         slotSize + if (it < remainingPixels) 1 else 0
     }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridDsl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridDsl.kt
index 6251101..e099946 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridDsl.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridDsl.kt
@@ -78,24 +78,24 @@
         crossAxisSpacing = horizontalArrangement.spacing,
         flingBehavior = flingBehavior,
         userScrollEnabled = userScrollEnabled,
-        slotSizesSums = rememberColumnWidthSums(columns, horizontalArrangement, contentPadding),
+        slots = rememberColumnSlots(columns, horizontalArrangement, contentPadding),
         content = content
     )
 }
 
-/** calculates prefix sums for columns used in staggered grid measure */
+/** calculates sizes for columns used in staggered grid measure */
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
-private fun rememberColumnWidthSums(
+private fun rememberColumnSlots(
     columns: StaggeredGridCells,
     horizontalArrangement: Arrangement.Horizontal,
     contentPadding: PaddingValues
-) = remember<Density.(Constraints) -> IntArray>(
+) = remember<Density.(Constraints) -> LazyStaggeredGridSlots>(
     columns,
     horizontalArrangement,
     contentPadding,
 ) {
-    { constraints ->
+    LazyStaggeredGridSlotCache { constraints ->
         require(constraints.maxWidth != Constraints.Infinity) {
             "LazyVerticalStaggeredGrid's width should be bound by parent."
         }
@@ -107,12 +107,13 @@
             calculateCrossAxisCellSizes(
                 gridWidth,
                 horizontalArrangement.spacing.roundToPx()
-            ).run {
-                val result = IntArray(size) { this[it] }
-                for (i in 1 until size) {
-                    result[i] += result[i - 1]
+            ).let { sizes ->
+                val positions = IntArray(sizes.size)
+                with(horizontalArrangement) {
+                    // Arrange with Ltr here, as placement will reverse positions if needed
+                    arrange(gridWidth, sizes, LayoutDirection.Ltr, positions)
                 }
-                result
+                LazyStaggeredGridSlots(positions, sizes)
             }
         }
     }
@@ -163,24 +164,24 @@
         crossAxisSpacing = verticalArrangement.spacing,
         flingBehavior = flingBehavior,
         userScrollEnabled = userScrollEnabled,
-        slotSizesSums = rememberRowHeightSums(rows, verticalArrangement, contentPadding),
+        slots = rememberRowSlots(rows, verticalArrangement, contentPadding),
         content = content
     )
 }
 
-/** calculates prefix sums for rows used in staggered grid measure */
+/** calculates sizes for rows used in staggered grid measure */
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
-private fun rememberRowHeightSums(
+private fun rememberRowSlots(
     rows: StaggeredGridCells,
     verticalArrangement: Arrangement.Vertical,
     contentPadding: PaddingValues
-) = remember<Density.(Constraints) -> IntArray>(
+) = remember<Density.(Constraints) -> LazyStaggeredGridSlots>(
     rows,
     verticalArrangement,
     contentPadding,
 ) {
-    { constraints ->
+    LazyStaggeredGridSlotCache { constraints ->
         require(constraints.maxHeight != Constraints.Infinity) {
             "LazyHorizontalStaggeredGrid's height should be bound by parent."
         }
@@ -191,12 +192,39 @@
             calculateCrossAxisCellSizes(
                 gridHeight,
                 verticalArrangement.spacing.roundToPx()
-            ).run {
-                val result = IntArray(size) { this[it] }
-                for (i in 1 until size) {
-                    result[i] += result[i - 1]
+            ).let { sizes ->
+                val positions = IntArray(sizes.size)
+                with(verticalArrangement) {
+                    arrange(gridHeight, sizes, positions)
                 }
-                result
+                LazyStaggeredGridSlots(positions, sizes)
+            }
+        }
+    }
+}
+
+/** measurement cache to avoid recalculating row/column sizes on each scroll. */
+private class LazyStaggeredGridSlotCache(
+    private val calculation: Density.(Constraints) -> LazyStaggeredGridSlots
+) : (Density, Constraints) -> LazyStaggeredGridSlots {
+    private var cachedConstraints = Constraints()
+    private var cachedDensity: Float = 0f
+    private var cachedSizes: LazyStaggeredGridSlots? = null
+
+    override fun invoke(density: Density, constraints: Constraints): LazyStaggeredGridSlots {
+        with(density) {
+            if (
+                cachedSizes != null &&
+                cachedConstraints == constraints &&
+                cachedDensity == this.density
+            ) {
+                return cachedSizes!!
+            }
+
+            cachedConstraints = constraints
+            cachedDensity = this.density
+            return calculation(constraints).also {
+                cachedSizes = it
             }
         }
     }
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 a17de5a..5b9f69a 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
@@ -78,21 +78,20 @@
 internal fun LazyLayoutMeasureScope.measureStaggeredGrid(
     state: LazyStaggeredGridState,
     itemProvider: LazyStaggeredGridItemProvider,
-    resolvedSlotSums: IntArray,
+    resolvedSlots: LazyStaggeredGridSlots,
     constraints: Constraints,
     isVertical: Boolean,
     reverseLayout: Boolean,
     contentOffset: IntOffset,
     mainAxisAvailableSize: Int,
     mainAxisSpacing: Int,
-    crossAxisSpacing: Int,
     beforeContentPadding: Int,
     afterContentPadding: Int,
 ): LazyStaggeredGridMeasureResult {
     val context = LazyStaggeredGridMeasureContext(
         state = state,
         itemProvider = itemProvider,
-        resolvedSlotSums = resolvedSlotSums,
+        resolvedSlots = resolvedSlots,
         constraints = constraints,
         isVertical = isVertical,
         contentOffset = contentOffset,
@@ -101,7 +100,6 @@
         afterContentPadding = afterContentPadding,
         reverseLayout = reverseLayout,
         mainAxisSpacing = mainAxisSpacing,
-        crossAxisSpacing = crossAxisSpacing,
         measureScope = this,
     )
 
@@ -113,13 +111,13 @@
         val firstVisibleOffsets = state.scrollPosition.offsets
 
         initialItemIndices =
-            if (firstVisibleIndices.size == resolvedSlotSums.size) {
+            if (firstVisibleIndices.size == context.laneCount) {
                 firstVisibleIndices
             } else {
                 // Grid got resized (or we are in a initial state)
                 // Adjust indices accordingly
                 context.laneInfo.reset()
-                IntArray(resolvedSlotSums.size).apply {
+                IntArray(context.laneCount).apply {
                     // Try to adjust indices in case grid got resized
                     for (lane in indices) {
                         this[lane] = if (
@@ -139,12 +137,12 @@
                 }
             }
         initialItemOffsets =
-            if (firstVisibleOffsets.size == resolvedSlotSums.size) {
+            if (firstVisibleOffsets.size == context.laneCount) {
                 firstVisibleOffsets
             } else {
                 // Grid got resized (or we are in a initial state)
                 // Adjust offsets accordingly
-                IntArray(resolvedSlotSums.size).apply {
+                IntArray(context.laneCount).apply {
                     // Adjust offsets to match previously set ones
                     for (lane in indices) {
                         this[lane] = if (lane < firstVisibleOffsets.size) {
@@ -169,7 +167,7 @@
 private class LazyStaggeredGridMeasureContext(
     val state: LazyStaggeredGridState,
     val itemProvider: LazyStaggeredGridItemProvider,
-    val resolvedSlotSums: IntArray,
+    val resolvedSlots: LazyStaggeredGridSlots,
     val constraints: Constraints,
     val isVertical: Boolean,
     val measureScope: LazyLayoutMeasureScope,
@@ -179,14 +177,12 @@
     val afterContentPadding: Int,
     val reverseLayout: Boolean,
     val mainAxisSpacing: Int,
-    val crossAxisSpacing: Int,
 ) {
     val measuredItemProvider = LazyStaggeredGridMeasureProvider(
         isVertical = isVertical,
         itemProvider = itemProvider,
         measureScope = measureScope,
-        resolvedSlotSums = resolvedSlotSums,
-        crossAxisSpacing = crossAxisSpacing
+        resolvedSlots = resolvedSlots,
     ) { index, lane, span, key, placeables ->
         LazyStaggeredGridMeasuredItem(
             index = index,
@@ -201,7 +197,7 @@
 
     val laneInfo = state.laneInfo
 
-    val laneCount = resolvedSlotSums.size
+    val laneCount = resolvedSlots.sizes.size
 
     fun LazyStaggeredGridItemProvider.isFullSpan(itemIndex: Int): Boolean =
         spanProvider.isFullSpan(itemIndex)
@@ -772,7 +768,6 @@
         val canScrollForward = currentItemOffsets.any { it > mainAxisAvailableSize } ||
             currentItemIndices.all { it < itemCount - 1 }
 
-        @Suppress("UNCHECKED_CAST")
         return LazyStaggeredGridMeasureResult(
             firstVisibleItemIndices = firstItemIndices,
             firstVisibleItemScrollOffsets = firstItemOffsets,
@@ -826,12 +821,7 @@
 
         val spanRange = SpanRange(item.lane, item.span)
         val mainAxisOffset = itemScrollOffsets.maxInRange(spanRange)
-        val crossAxisOffset =
-            if (laneIndex == 0) {
-                0
-            } else {
-                resolvedSlotSums[laneIndex - 1] + crossAxisSpacing * laneIndex
-            }
+        val crossAxisOffset = resolvedSlots.positions[laneIndex]
 
         if (item.placeables.isEmpty()) {
             // nothing to place, ignore spacings
@@ -973,14 +963,20 @@
     private val isVertical: Boolean,
     private val itemProvider: LazyLayoutItemProvider,
     private val measureScope: LazyLayoutMeasureScope,
-    private val resolvedSlotSums: IntArray,
-    private val crossAxisSpacing: Int,
+    private val resolvedSlots: LazyStaggeredGridSlots,
     private val measuredItemFactory: MeasuredItemFactory,
 ) {
     private fun childConstraints(slot: Int, span: Int): Constraints {
-        val previousSum = if (slot == 0) 0 else resolvedSlotSums[slot - 1]
-        val crossAxisSize =
-            resolvedSlotSums[slot + span - 1] - previousSum + crossAxisSpacing * (span - 1)
+        // resolved slots contain [offset, size] pair per each slot.
+        val crossAxisSize = if (span == 1) {
+            resolvedSlots.sizes[slot]
+        } else {
+            val start = resolvedSlots.positions[slot]
+            val endSlot = slot + span - 1
+            val end = resolvedSlots.positions[endSlot] + resolvedSlots.sizes[endSlot]
+            end - start
+        }
+
         return if (isVertical) {
             Constraints.fixedWidth(crossAxisSize)
         } else {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt
index 4fa39a1..61607e27 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt
@@ -43,7 +43,7 @@
     orientation: Orientation,
     mainAxisSpacing: Dp,
     crossAxisSpacing: Dp,
-    slotSizesSums: Density.(Constraints) -> IntArray
+    slots: Density.(Constraints) -> LazyStaggeredGridSlots
 ): LazyLayoutMeasureScope.(Constraints) -> LazyStaggeredGridMeasureResult = remember(
     state,
     itemProvider,
@@ -52,18 +52,18 @@
     orientation,
     mainAxisSpacing,
     crossAxisSpacing,
-    slotSizesSums
+    slots
 ) {
     { constraints ->
         checkScrollableContainerConstraints(
             constraints,
             orientation
         )
-        val resolvedSlotSums = slotSizesSums(this, constraints)
+        val resolvedSlots = slots(this, constraints)
         val isVertical = orientation == Orientation.Vertical
 
         // setup information for prefetch
-        state.laneWidthsPrefixSum = resolvedSlotSums
+        state.slots = resolvedSlots
         state.isVertical = isVertical
         state.spanProvider = itemProvider.spanProvider
 
@@ -99,13 +99,12 @@
         measureStaggeredGrid(
             state = state,
             itemProvider = itemProvider,
-            resolvedSlotSums = resolvedSlotSums,
+            resolvedSlots = resolvedSlots,
             constraints = constraints.copy(
                 minWidth = constraints.constrainWidth(horizontalPadding),
                 minHeight = constraints.constrainHeight(verticalPadding)
             ),
             mainAxisSpacing = mainAxisSpacing.roundToPx(),
-            crossAxisSpacing = crossAxisSpacing.roundToPx(),
             contentOffset = contentOffset,
             mainAxisAvailableSize = mainAxisAvailableSize,
             isVertical = isVertical,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
index 9bada77..51bb252 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
@@ -186,7 +186,7 @@
 
     /** transient information from measure required for prefetching **/
     internal var isVertical = false
-    internal var laneWidthsPrefixSum: IntArray = IntArray(0)
+    internal var slots: LazyStaggeredGridSlots? = null
     internal var spanProvider: LazyStaggeredGridSpanProvider? = null
     /** prefetch state **/
     private var prefetchBaseIndex: Int = -1
@@ -194,7 +194,7 @@
 
     /** state required for implementing [animateScrollScope] **/
     internal var density: Density = Density(1f, 1f)
-    internal val laneCount get() = laneWidthsPrefixSum.size
+    internal val laneCount get() = slots?.sizes?.size ?: 0
 
     /**
      * [InteractionSource] that will be used to dispatch drag events when this
@@ -345,7 +345,7 @@
 
             val prefetchHandlesUsed = mutableSetOf<Int>()
             var targetIndex = prefetchIndex
-            for (lane in laneWidthsPrefixSum.indices) {
+            for (lane in 0 until laneCount) {
                 val previousIndex = targetIndex
 
                 // find the next item for each line and prefetch if it is valid
@@ -370,8 +370,17 @@
                 val slot = if (isFullSpan) 0 else lane
                 val span = if (isFullSpan) laneCount else 1
 
-                val crossAxisSize = laneWidthsPrefixSum[slot + span - 1] -
-                    if (slot == 0) 0 else laneWidthsPrefixSum[slot - 1]
+                val slots = slots
+                val crossAxisSize = when {
+                    slots == null -> 0
+                    span == 1 -> slots.sizes[slot]
+                    else -> {
+                        val start = slots.positions[slot]
+                        val endSlot = slot + span - 1
+                        val end = slots.positions[endSlot] + slots.sizes[endSlot]
+                        end - start
+                    }
+                }
 
                 val constraints = if (isVertical) {
                     Constraints.fixedWidth(crossAxisSize)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
new file mode 100644
index 0000000..ce3dd25
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.pager
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.clipScrollableContainer
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.ScrollableDefaults
+import androidx.compose.foundation.gestures.scrollable
+import androidx.compose.foundation.gestures.snapping.SnapFlingBehavior
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.lazy.LazyListBeyondBoundsInfo
+import androidx.compose.foundation.lazy.NearestItemsExtraItemCount
+import androidx.compose.foundation.lazy.NearestItemsSlidingWindowSize
+import androidx.compose.foundation.lazy.layout.IntervalList
+import androidx.compose.foundation.lazy.layout.LazyLayout
+import androidx.compose.foundation.lazy.layout.LazyLayoutIntervalContent
+import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
+import androidx.compose.foundation.lazy.layout.LazyLayoutKeyIndexMap
+import androidx.compose.foundation.lazy.layout.MutableIntervalList
+import androidx.compose.foundation.lazy.layout.NearestRangeKeyIndexMapState
+import androidx.compose.foundation.lazy.layout.PinnableItem
+import androidx.compose.foundation.lazy.layout.lazyLayoutSemantics
+import androidx.compose.foundation.overscroll
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+
+@ExperimentalFoundationApi
+@Composable
+internal fun Pager(
+    /** Modifier to be applied for the inner layout */
+    modifier: Modifier,
+    /** The amount of Pages that will be present in this Pager **/
+    pageCount: Int,
+    /** State controlling the scroll position */
+    state: PagerState,
+    /** The inner padding to be added for the whole content(not for each individual page) */
+    contentPadding: PaddingValues,
+    /** reverse the direction of scrolling and layout */
+    reverseLayout: Boolean,
+    /** The layout orientation of the Pager */
+    orientation: Orientation,
+    /** fling behavior to be used for flinging */
+    flingBehavior: SnapFlingBehavior,
+    /** Whether scrolling via the user gestures is allowed. */
+    userScrollEnabled: Boolean,
+    /** Number of pages to layout before and after the visible pages */
+    beyondBoundsPageCount: Int = 0,
+    /** Space between pages **/
+    pageSpacing: Dp = 0.dp,
+    /** Allows to change how to calculate the Page size **/
+    pageSize: PageSize,
+    /** A [NestedScrollConnection] that dictates how this [Pager] behaves with nested lists.  **/
+    pageNestedScrollConnection: NestedScrollConnection,
+    /** a stable and unique key representing the Page **/
+    key: ((index: Int) -> Any)?,
+    /** The alignment to align pages horizontally. Required when isVertical is true */
+    horizontalAlignment: Alignment.Horizontal,
+    /** The alignment to align pages vertically. Required when isVertical is false */
+    verticalAlignment: Alignment.Vertical,
+    /** The content of the list */
+    pageContent: @Composable (page: Int) -> Unit
+) {
+    require(beyondBoundsPageCount >= 0) {
+        "beyondBoundsPageCount should be greater than or equal to 0, " +
+            "you selected $beyondBoundsPageCount"
+    }
+
+    val overscrollEffect = ScrollableDefaults.overscrollEffect()
+
+    val pagerItemProvider = rememberPagerItemProvider(
+        state = state,
+        pageContent = pageContent,
+        key = key,
+        pageCount = pageCount
+    )
+
+    val beyondBoundsInfo = remember { LazyListBeyondBoundsInfo() }
+
+    val measurePolicy = rememberPagerMeasurePolicy(
+        state = state,
+        contentPadding = contentPadding,
+        reverseLayout = reverseLayout,
+        orientation = orientation,
+        beyondBoundsPageCount = beyondBoundsPageCount,
+        pageSpacing = pageSpacing,
+        pageSize = pageSize,
+        horizontalAlignment = horizontalAlignment,
+        verticalAlignment = verticalAlignment,
+        itemProvider = pagerItemProvider,
+        pageCount = pageCount,
+        beyondBoundsInfo = beyondBoundsInfo
+    )
+
+    val pagerFlingBehavior = remember(flingBehavior, state) {
+        PagerWrapperFlingBehavior(flingBehavior, state)
+    }
+
+    val pagerSemantics = if (userScrollEnabled) {
+        Modifier.pagerSemantics(state, orientation == Orientation.Vertical)
+    } else {
+        Modifier
+    }
+
+    val semanticState = rememberPagerSemanticState(
+        state,
+        pagerItemProvider,
+        reverseLayout,
+        orientation == Orientation.Vertical
+    )
+
+    LazyLayout(
+        modifier = modifier
+            .then(state.remeasurementModifier)
+            .then(state.awaitLayoutModifier)
+            .then(pagerSemantics)
+            .lazyLayoutSemantics(
+                itemProvider = pagerItemProvider,
+                state = semanticState,
+                orientation = orientation,
+                userScrollEnabled = userScrollEnabled,
+                reverseScrolling = reverseLayout
+            )
+            .clipScrollableContainer(orientation)
+            .pagerBeyondBoundsModifier(state, beyondBoundsInfo, reverseLayout, orientation)
+            .overscroll(overscrollEffect)
+            .scrollable(
+                orientation = orientation,
+                reverseDirection = ScrollableDefaults.reverseDirection(
+                    LocalLayoutDirection.current,
+                    orientation,
+                    reverseLayout
+                ),
+                interactionSource = state.internalInteractionSource,
+                flingBehavior = pagerFlingBehavior,
+                state = state,
+                overscrollEffect = overscrollEffect,
+                enabled = userScrollEnabled
+            )
+            .nestedScroll(pageNestedScrollConnection),
+        measurePolicy = measurePolicy,
+        prefetchState = state.prefetchState,
+        itemProvider = pagerItemProvider
+    )
+}
+
+@ExperimentalFoundationApi
+internal class PagerLazyLayoutItemProvider(
+    val state: PagerState,
+    latestContent: () -> (@Composable (page: Int) -> Unit),
+    key: ((index: Int) -> Any)?,
+    pageCount: Int
+) : LazyLayoutItemProvider {
+    private val pagerContent =
+        PagerLayoutIntervalContent(latestContent(), key = key, pageCount = pageCount)
+    private val keyToIndexMap: LazyLayoutKeyIndexMap by NearestRangeKeyIndexMapState(
+        firstVisibleItemIndex = { state.firstVisiblePage },
+        slidingWindowSize = { NearestItemsSlidingWindowSize },
+        extraItemCount = { NearestItemsExtraItemCount },
+        content = { pagerContent }
+    )
+    override val itemCount: Int
+        get() = pagerContent.itemCount
+
+    @Composable
+    override fun Item(index: Int) {
+        pagerContent.PinnableItem(index, state.pinnedPages) { localIndex ->
+            item(localIndex)
+        }
+    }
+
+    override fun getKey(index: Int): Any = pagerContent.getKey(index)
+
+    override fun getIndex(key: Any): Int = keyToIndexMap[key]
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+private class PagerLayoutIntervalContent(
+    val pageContent: @Composable (page: Int) -> Unit,
+    val key: ((index: Int) -> Any)?,
+    val pageCount: Int
+) : LazyLayoutIntervalContent<PagerIntervalContent>() {
+    override val intervals: IntervalList<PagerIntervalContent>
+        get() = MutableIntervalList<PagerIntervalContent>().apply {
+            addInterval(pageCount, PagerIntervalContent(key = key, item = pageContent))
+        }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+internal class PagerIntervalContent(
+    override val key: ((page: Int) -> Any)?,
+    val item: @Composable (page: Int) -> Unit
+) : LazyLayoutIntervalContent.Interval
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+private fun rememberPagerItemProvider(
+    state: PagerState,
+    pageContent: @Composable (page: Int) -> Unit,
+    key: ((index: Int) -> Any)?,
+    pageCount: Int
+): PagerLazyLayoutItemProvider {
+    val latestContent = rememberUpdatedState(pageContent)
+    return remember(state, latestContent, key, pageCount) {
+        PagerLazyLayoutItemProvider(
+            state = state,
+            latestContent = { latestContent.value },
+            key = key,
+            pageCount = pageCount
+        )
+    }
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutSemanticState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutSemanticState.kt
new file mode 100644
index 0000000..ab6795b
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutSemanticState.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.pager
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.gestures.animateScrollBy
+import androidx.compose.foundation.lazy.layout.LazyLayoutSemanticState
+import androidx.compose.ui.semantics.CollectionInfo
+
+@OptIn(ExperimentalFoundationApi::class)
+internal fun LazyLayoutSemanticState(
+    state: PagerState,
+    isVertical: Boolean
+): LazyLayoutSemanticState = object : LazyLayoutSemanticState {
+
+    override val currentPosition: Float
+        get() = state.firstVisiblePage + state.firstVisiblePageOffset / 100_000f
+    override val canScrollForward: Boolean
+        get() = state.canScrollForward
+
+    override suspend fun animateScrollBy(delta: Float) {
+        state.animateScrollBy(delta)
+    }
+
+    override suspend fun scrollToItem(index: Int) {
+        state.scrollToPage(index)
+    }
+
+    override fun collectionInfo(): CollectionInfo =
+        if (isVertical) {
+            CollectionInfo(rowCount = -1, columnCount = 1)
+        } else {
+            CollectionInfo(rowCount = 1, columnCount = -1)
+        }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/MeasuredPage.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/MeasuredPage.kt
new file mode 100644
index 0000000..8543acd
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/MeasuredPage.kt
@@ -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.compose.foundation.pager
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.util.fastForEach
+
+internal class MeasuredPage(
+    val index: Int,
+    val size: Int,
+    val placeables: List<Placeable>,
+    val visualOffset: IntOffset,
+    val key: Any,
+    val orientation: Orientation,
+    val horizontalAlignment: Alignment.Horizontal?,
+    val verticalAlignment: Alignment.Vertical?,
+    val layoutDirection: LayoutDirection,
+    val reverseLayout: Boolean,
+    val beforeContentPadding: Int,
+    val afterContentPadding: Int,
+) {
+
+    val crossAxisSize: Int
+
+    init {
+        var maxCrossAxis = 0
+        placeables.fastForEach {
+            maxCrossAxis = maxOf(
+                maxCrossAxis,
+                if (orientation != Orientation.Vertical) it.height else it.width
+            )
+        }
+        crossAxisSize = maxCrossAxis
+    }
+
+    fun position(
+        offset: Int,
+        layoutWidth: Int,
+        layoutHeight: Int
+    ): PositionedPage {
+        val wrappers = mutableListOf<PagerPlaceableWrapper>()
+        val mainAxisLayoutSize =
+            if (orientation == Orientation.Vertical) layoutHeight else layoutWidth
+        var mainAxisOffset = if (reverseLayout) {
+            mainAxisLayoutSize - offset - size
+        } else {
+            offset
+        }
+        var index = if (reverseLayout) placeables.lastIndex else 0
+        while (if (reverseLayout) index >= 0 else index < placeables.size) {
+            val it = placeables[index]
+            val addIndex = if (reverseLayout) 0 else wrappers.size
+            val placeableOffset = if (orientation == Orientation.Vertical) {
+                val x = requireNotNull(horizontalAlignment)
+                    .align(it.width, layoutWidth, layoutDirection)
+                IntOffset(x, mainAxisOffset)
+            } else {
+                val y = requireNotNull(verticalAlignment).align(it.height, layoutHeight)
+                IntOffset(mainAxisOffset, y)
+            }
+            mainAxisOffset += if (orientation == Orientation.Vertical) it.height else it.width
+            wrappers.add(
+                addIndex,
+                PagerPlaceableWrapper(placeableOffset, it, placeables[index].parentData)
+            )
+            if (reverseLayout) index-- else index++
+        }
+        return PositionedPage(
+            offset = offset,
+            index = this.index,
+            key = key,
+            orientation = orientation,
+            wrappers = wrappers,
+            visualOffset = visualOffset,
+        )
+    }
+}
+
+internal class PagerPlaceableWrapper(
+    val offset: IntOffset,
+    val placeable: Placeable,
+    val parentData: Any?
+)
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PageInfo.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PageInfo.kt
new file mode 100644
index 0000000..e37fb46
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PageInfo.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.pager
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+
+@ExperimentalFoundationApi
+internal interface PageInfo {
+    val index: Int
+    val offset: Int
+}
\ No newline at end of file
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 537d000..0c55052 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
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -30,30 +30,17 @@
 import androidx.compose.foundation.gestures.ScrollScope
 import androidx.compose.foundation.gestures.snapping.SnapFlingBehavior
 import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
-import androidx.compose.foundation.gestures.snapping.calculateDistanceToDesiredSnapPosition
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.BoxWithConstraints
 import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.lazy.LazyList
-import androidx.compose.foundation.lazy.LazyListLayoutInfo
-import androidx.compose.foundation.lazy.LazyListState
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
 import androidx.compose.ui.input.nestedscroll.NestedScrollSource
-import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.semantics.pageDown
 import androidx.compose.ui.semantics.pageLeft
 import androidx.compose.ui.semantics.pageRight
@@ -61,19 +48,14 @@
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.fastFirstOrNull
 import androidx.compose.ui.util.fastForEach
-import androidx.compose.ui.util.fastSumBy
 import kotlin.math.absoluteValue
 import kotlin.math.ceil
 import kotlin.math.floor
-import kotlin.math.roundToInt
 import kotlin.math.sign
-import kotlinx.coroutines.flow.drop
-import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.launch
 
 /**
@@ -85,7 +67,7 @@
  * If you need snapping with pages of different size, you can use a [SnapFlingBehavior] with a
  * [SnapLayoutInfoProvider] adapted to a LazyList.
  * @see androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider for the implementation
- * of a [SnapLayoutInfoProvider] that uses [LazyListState].
+ * of a [SnapLayoutInfoProvider] that uses [androidx.compose.foundation.lazy.LazyListState].
  *
  * Please refer to the sample to learn how to use this API.
  * @sample androidx.compose.foundation.samples.SimpleHorizontalPagerSample
@@ -110,9 +92,9 @@
  * is allowed. You can still scroll programmatically using [PagerState.scroll] even when it is
  * disabled.
  * @param reverseLayout reverse the direction of scrolling and layout.
- * @param key a stable and unique key representing the item. 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 key a stable and unique key representing the page. When you specify the key the scroll
+ * position will be maintained based on the key, which means if you add/remove pages before the
+ * current visible page the page with the given key will be kept as the first visible one.
  * @param pageNestedScrollConnection A [NestedScrollConnection] that dictates how this [Pager]
  * behaves with nested lists. The default behavior will see [Pager] to consume all nested deltas.
  * @param pageContent This Pager's page Composable.
@@ -139,20 +121,21 @@
 ) {
     Pager(
         modifier = modifier,
-        state = state,
         pageCount = pageCount,
-        pageSpacing = pageSpacing,
-        userScrollEnabled = userScrollEnabled,
-        orientation = Orientation.Horizontal,
-        verticalAlignment = verticalAlignment,
-        reverseLayout = reverseLayout,
+        state = state,
         contentPadding = contentPadding,
-        beyondBoundsPageCount = beyondBoundsPageCount,
-        pageSize = pageSize,
+        reverseLayout = reverseLayout,
+        orientation = Orientation.Horizontal,
         flingBehavior = flingBehavior,
-        key = key,
+        userScrollEnabled = userScrollEnabled,
+        pageSize = pageSize,
+        beyondBoundsPageCount = beyondBoundsPageCount,
+        pageSpacing = pageSpacing,
+        pageContent = pageContent,
         pageNestedScrollConnection = pageNestedScrollConnection,
-        pageContent = pageContent
+        verticalAlignment = verticalAlignment,
+        horizontalAlignment = Alignment.CenterHorizontally,
+        key = key
     )
 }
 
@@ -165,7 +148,7 @@
  * If you need snapping with pages of different size, you can use a [SnapFlingBehavior] with a
  * [SnapLayoutInfoProvider] adapted to a LazyList.
  * @see androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider for the implementation
- * of a [SnapLayoutInfoProvider] that uses [LazyListState].
+ * of a [SnapLayoutInfoProvider] that uses [androidx.compose.foundation.lazy.LazyListState].
  *
  * Please refer to the sample to learn how to use this API.
  * @sample androidx.compose.foundation.samples.SimpleVerticalPagerSample
@@ -190,9 +173,9 @@
  * is allowed. You can still scroll programmatically using [PagerState.scroll] even when it is
  * disabled.
  * @param reverseLayout reverse the direction of scrolling and layout.
- * @param key a stable and unique key representing the item. 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 key a stable and unique key representing the page. When you specify the key the scroll
+ * position will be maintained based on the key, which means if you add/remove pages before the
+ * current visible page the page with the given key will be kept as the first visible one.
  * @param pageNestedScrollConnection A [NestedScrollConnection] that dictates how this [Pager] behaves
  * with nested lists. The default behavior will see [Pager] to consume all nested deltas.
  * @param pageContent This Pager's page Composable.
@@ -219,175 +202,24 @@
 ) {
     Pager(
         modifier = modifier,
-        state = state,
         pageCount = pageCount,
-        pageSpacing = pageSpacing,
-        horizontalAlignment = horizontalAlignment,
-        userScrollEnabled = userScrollEnabled,
-        orientation = Orientation.Vertical,
-        reverseLayout = reverseLayout,
+        state = state,
         contentPadding = contentPadding,
-        beyondBoundsPageCount = beyondBoundsPageCount,
-        pageSize = pageSize,
+        reverseLayout = reverseLayout,
+        orientation = Orientation.Vertical,
         flingBehavior = flingBehavior,
-        key = key,
+        userScrollEnabled = userScrollEnabled,
+        pageSize = pageSize,
+        beyondBoundsPageCount = beyondBoundsPageCount,
+        pageSpacing = pageSpacing,
+        pageContent = pageContent,
         pageNestedScrollConnection = pageNestedScrollConnection,
-        pageContent = pageContent
+        verticalAlignment = Alignment.CenterVertically,
+        horizontalAlignment = horizontalAlignment,
+        key = key
     )
 }
 
-@OptIn(ExperimentalFoundationApi::class)
-@Composable
-internal fun Pager(
-    modifier: Modifier,
-    state: PagerState,
-    pageCount: Int,
-    pageSize: PageSize,
-    pageSpacing: Dp,
-    orientation: Orientation,
-    beyondBoundsPageCount: Int,
-    verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
-    horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
-    contentPadding: PaddingValues,
-    flingBehavior: SnapFlingBehavior,
-    userScrollEnabled: Boolean,
-    reverseLayout: Boolean,
-    key: ((index: Int) -> Any)?,
-    pageNestedScrollConnection: NestedScrollConnection,
-    pageContent: @Composable (page: Int) -> Unit
-) {
-    require(beyondBoundsPageCount >= 0) {
-        "beyondBoundsPageCount should be greater than or equal to 0, " +
-            "you selected $beyondBoundsPageCount"
-    }
-
-    val isVertical = orientation == Orientation.Vertical
-    val density = LocalDensity.current
-    val layoutDirection = LocalLayoutDirection.current
-    val calculatedContentPaddings = remember(contentPadding, orientation, layoutDirection) {
-        calculateContentPaddings(
-            contentPadding,
-            orientation,
-            layoutDirection
-        )
-    }
-
-    val pagerFlingBehavior = remember(flingBehavior, state) {
-        PagerWrapperFlingBehavior(flingBehavior, state)
-    }
-
-    LaunchedEffect(density, state, pageSpacing) {
-        with(density) { state.pageSpacing = pageSpacing.roundToPx() }
-    }
-
-    LaunchedEffect(state) {
-        snapshotFlow { state.isScrollInProgress }
-            .filter { !it }
-            .drop(1) // Initial scroll is false
-            .collect { state.updateOnScrollStopped() }
-    }
-
-    val pagerSemantics = if (userScrollEnabled) {
-        Modifier.pagerSemantics(state, isVertical)
-    } else {
-        Modifier
-    }
-
-    BoxWithConstraints(modifier = modifier.then(pagerSemantics)) {
-        val mainAxisSize = if (isVertical) constraints.maxHeight else constraints.maxWidth
-        // Calculates how pages are shown across the main axis
-        val pageAvailableSize = remember(
-            density,
-            mainAxisSize,
-            pageSpacing,
-            calculatedContentPaddings
-        ) {
-            with(density) {
-                val pageSpacingPx = pageSpacing.roundToPx()
-                val contentPaddingPx = calculatedContentPaddings.roundToPx()
-                with(pageSize) {
-                    density.calculateMainAxisPageSize(
-                        mainAxisSize - contentPaddingPx,
-                        pageSpacingPx
-                    )
-                }.toDp()
-            }
-        }
-
-        val horizontalAlignmentForSpacedArrangement =
-            if (!reverseLayout) Alignment.Start else Alignment.End
-        val verticalAlignmentForSpacedArrangement =
-            if (!reverseLayout) Alignment.Top else Alignment.Bottom
-
-        val lazyListState = remember(state) {
-            val initialPageOffset =
-                with(density) { pageAvailableSize.roundToPx() } * state.initialPageOffsetFraction
-            LazyListState(state.initialPage, initialPageOffset.roundToInt()).also {
-                state.loadNewState(it)
-            }
-        }
-
-        LazyList(
-            modifier = Modifier,
-            state = lazyListState,
-            contentPadding = contentPadding,
-            flingBehavior = pagerFlingBehavior,
-            horizontalAlignment = horizontalAlignment,
-            horizontalArrangement = Arrangement.spacedBy(
-                pageSpacing,
-                horizontalAlignmentForSpacedArrangement
-            ),
-            verticalArrangement = Arrangement.spacedBy(
-                pageSpacing,
-                verticalAlignmentForSpacedArrangement
-            ),
-            verticalAlignment = verticalAlignment,
-            isVertical = isVertical,
-            reverseLayout = reverseLayout,
-            userScrollEnabled = userScrollEnabled,
-            beyondBoundsItemCount = beyondBoundsPageCount
-        ) {
-
-            items(pageCount, key = key) {
-                val pageMainAxisSizeModifier = if (isVertical) {
-                    Modifier.height(pageAvailableSize)
-                } else {
-                    Modifier.width(pageAvailableSize)
-                }
-                Box(
-                    modifier = Modifier
-                        .then(pageMainAxisSizeModifier)
-                        .nestedScroll(pageNestedScrollConnection),
-                    contentAlignment = Alignment.Center
-                ) {
-                    pageContent(it)
-                }
-            }
-        }
-    }
-}
-
-private fun calculateContentPaddings(
-    contentPadding: PaddingValues,
-    orientation: Orientation,
-    layoutDirection: LayoutDirection
-): Dp {
-
-    val startPadding = if (orientation == Orientation.Vertical) {
-        contentPadding.calculateTopPadding()
-    } else {
-        contentPadding.calculateLeftPadding(layoutDirection)
-    }
-
-    val endPadding = if (orientation == Orientation.Vertical) {
-        contentPadding.calculateBottomPadding()
-    } else {
-        contentPadding.calculateRightPadding(layoutDirection)
-    }
-
-    return startPadding + endPadding
-}
-
 /**
  * This is used to determine how Pages are laid out in [Pager]. By changing the size of the pages
  * one can change how many pages are shown.
@@ -480,7 +312,11 @@
             density
         ) {
             val snapLayoutInfoProvider =
-                SnapLayoutInfoProvider(state, pagerSnapDistance, highVelocityAnimationSpec)
+                SnapLayoutInfoProvider(
+                    state,
+                    pagerSnapDistance,
+                    highVelocityAnimationSpec
+                )
             SnapFlingBehavior(
                 snapLayoutInfoProvider = snapLayoutInfoProvider,
                 lowVelocityAnimationSpec = lowVelocityAnimationSpec,
@@ -587,26 +423,26 @@
     decayAnimationSpec: DecayAnimationSpec<Float>
 ): SnapLayoutInfoProvider {
     return object : SnapLayoutInfoProvider {
-        val layoutInfo: LazyListLayoutInfo
+        val layoutInfo: PagerLayoutInfo
             get() = pagerState.layoutInfo
 
         override fun Density.calculateSnappingOffsetBounds(): ClosedFloatingPointRange<Float> {
             var lowerBoundOffset = Float.NEGATIVE_INFINITY
             var upperBoundOffset = Float.POSITIVE_INFINITY
 
-            layoutInfo.visibleItemsInfo.fastForEach { item ->
+            layoutInfo.visiblePagesInfo.fastForEach { page ->
                 val offset = calculateDistanceToDesiredSnapPosition(
                     layoutInfo,
-                    item,
+                    page,
                     SnapAlignmentStartToStart
                 )
 
-                // Find item that is closest to the snap position, but before it
+                // Find page that is closest to the snap position, but before it
                 if (offset <= 0 && offset > lowerBoundOffset) {
                     lowerBoundOffset = offset
                 }
 
-                // Find item that is closest to the snap position, but after it
+                // Find page that is closest to the snap position, but after it
                 if (offset >= 0 && offset < upperBoundOffset) {
                     upperBoundOffset = offset
                 }
@@ -615,24 +451,18 @@
             return lowerBoundOffset.rangeTo(upperBoundOffset)
         }
 
-        override fun Density.calculateSnapStepSize(): Float = with(layoutInfo) {
-            if (visibleItemsInfo.isNotEmpty()) {
-                visibleItemsInfo.fastSumBy { it.size } / visibleItemsInfo.size.toFloat()
-            } else {
-                0f
-            }
-        }
+        override fun Density.calculateSnapStepSize(): Float = layoutInfo.pageSize.toFloat()
 
         override fun Density.calculateApproachOffset(initialVelocity: Float): Float {
             val effectivePageSizePx = pagerState.pageSize + pagerState.pageSpacing
             val animationOffsetPx =
                 decayAnimationSpec.calculateTargetValue(0f, initialVelocity)
-            val startPage = pagerState.firstVisiblePage?.let {
+            val startPage = pagerState.firstVisiblePageInfo?.let {
                 if (initialVelocity < 0) it.index + 1 else it.index
             } ?: pagerState.currentPage
 
             val scrollOffset =
-                layoutInfo.visibleItemsInfo.fastFirstOrNull { it.index == startPage }?.offset ?: 0
+                layoutInfo.visiblePagesInfo.fastFirstOrNull { it.index == startPage }?.offset ?: 0
 
             debugLog {
                 "Initial Offset=$scrollOffset " +
@@ -681,7 +511,7 @@
 }
 
 @OptIn(ExperimentalFoundationApi::class)
-private class PagerWrapperFlingBehavior(
+internal class PagerWrapperFlingBehavior(
     val originalFlingBehavior: SnapFlingBehavior,
     val pagerState: PagerState
 ) : FlingBehavior {
@@ -736,7 +566,7 @@
 @OptIn(ExperimentalFoundationApi::class)
 @Suppress("ComposableModifierFactory")
 @Composable
-private fun Modifier.pagerSemantics(state: PagerState, isVertical: Boolean): Modifier {
+internal fun Modifier.pagerSemantics(state: PagerState, isVertical: Boolean): Modifier {
     val scope = rememberCoroutineScope()
     fun performForwardPaging(): Boolean {
         return if (state.canScrollForward) {
@@ -771,11 +601,31 @@
     })
 }
 
+@OptIn(ExperimentalFoundationApi::class)
+internal fun Density.calculateDistanceToDesiredSnapPosition(
+    layoutInfo: PagerLayoutInfo,
+    page: PageInfo,
+    positionInLayout: Density.(layoutSize: Float, itemSize: Float) -> Float
+): Float {
+    val containerSize =
+        with(layoutInfo) { mainAxisViewportSize - beforeContentPadding - afterContentPadding }
+
+    val desiredDistance =
+        positionInLayout(containerSize.toFloat(), layoutInfo.pageSize.toFloat())
+
+    val itemCurrentPosition = page.offset
+    return itemCurrentPosition - desiredDistance
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+private val PagerLayoutInfo.mainAxisViewportSize: Int
+    get() = if (orientation == Orientation.Vertical) viewportSize.height else viewportSize.width
+
+private const val LowVelocityAnimationDefaultDuration = 500
+
 private const val DEBUG = false
 private inline fun debugLog(generateMsg: () -> String) {
     if (DEBUG) {
         println("Pager: ${generateMsg()}")
     }
 }
-
-private const val LowVelocityAnimationDefaultDuration = 500
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerBeyondBoundsModifier.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerBeyondBoundsModifier.kt
new file mode 100644
index 0000000..bc26545
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerBeyondBoundsModifier.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.compose.foundation.pager
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.lazy.LazyListBeyondBoundsInfo
+import androidx.compose.foundation.lazy.layout.BeyondBoundsState
+import androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsModifierLocal
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalLayoutDirection
+
+/**
+ * This modifier is used to measure and place additional pages when the Pager receives a
+ * request to layout pages beyond the visible bounds.
+ */
+@OptIn(ExperimentalFoundationApi::class)
+@Suppress("ComposableModifierFactory")
+@Composable
+internal fun Modifier.pagerBeyondBoundsModifier(
+    state: PagerState,
+    beyondBoundsInfo: LazyListBeyondBoundsInfo,
+    reverseLayout: Boolean,
+    orientation: Orientation
+): Modifier {
+    val layoutDirection = LocalLayoutDirection.current
+    return this then remember(
+        state,
+        beyondBoundsInfo,
+        reverseLayout,
+        layoutDirection,
+        orientation
+    ) {
+        LazyLayoutBeyondBoundsModifierLocal(
+            PagerBeyondBoundsState(state),
+            beyondBoundsInfo,
+            reverseLayout,
+            layoutDirection,
+            orientation
+        )
+    }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+internal class PagerBeyondBoundsState(val state: PagerState) : BeyondBoundsState {
+    override fun remeasure() {
+        state.remeasurement?.forceRemeasure()
+    }
+
+    override val itemCount: Int
+        get() = state.layoutInfo.pagesCount
+    override val hasVisibleItems: Boolean
+        get() = state.layoutInfo.visiblePagesInfo.isNotEmpty()
+    override val firstVisibleIndex: Int
+        get() = state.firstVisiblePage
+    override val lastVisibleIndex: Int
+        get() = state.layoutInfo.visiblePagesInfo.last().index
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerLayoutInfo.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerLayoutInfo.kt
new file mode 100644
index 0000000..10a25d2
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerLayoutInfo.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.compose.foundation.pager
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.unit.IntSize
+
+@ExperimentalFoundationApi
+internal interface PagerLayoutInfo {
+    val visiblePagesInfo: List<PageInfo>
+    val closestPageToSnapPosition: PageInfo?
+    val pagesCount: Int
+    val pageSize: Int
+    val pageSpacing: Int
+    val viewportStartOffset: Int
+    val viewportEndOffset: Int
+    val beforeContentPadding: Int
+    val afterContentPadding: Int
+    val viewportSize: IntSize
+    val orientation: Orientation
+    val reverseLayout: Boolean
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
new file mode 100644
index 0000000..78aa13a
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
@@ -0,0 +1,645 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.pager
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.fastFilter
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy
+import androidx.compose.foundation.lazy.LazyListBeyondBoundsInfo
+import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
+import androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.constrainHeight
+import androidx.compose.ui.unit.constrainWidth
+import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastMaxBy
+import kotlin.math.abs
+import kotlin.math.min
+import kotlin.math.roundToInt
+import kotlin.math.sign
+
+@OptIn(ExperimentalFoundationApi::class)
+internal fun LazyLayoutMeasureScope.measurePager(
+    pageCount: Int,
+    pagerItemProvider: PagerLazyLayoutItemProvider,
+    mainAxisAvailableSize: Int,
+    beforeContentPadding: Int,
+    afterContentPadding: Int,
+    spaceBetweenPages: Int,
+    firstVisiblePage: Int,
+    firstVisiblePageOffset: Int,
+    scrollToBeConsumed: Float,
+    constraints: Constraints,
+    orientation: Orientation,
+    verticalAlignment: Alignment.Vertical?,
+    horizontalAlignment: Alignment.Horizontal?,
+    reverseLayout: Boolean,
+    visualPageOffset: IntOffset,
+    pageAvailableSize: Int,
+    beyondBoundsPageCount: Int,
+    beyondBoundsInfo: LazyListBeyondBoundsInfo,
+    pinnedPages: LazyLayoutPinnedItemList,
+    layout: (Int, Int, Placeable.PlacementScope.() -> Unit) -> MeasureResult
+): PagerMeasureResult {
+    require(beforeContentPadding >= 0)
+    require(afterContentPadding >= 0)
+
+    val pageSizeWithSpacing = (pageAvailableSize + spaceBetweenPages).coerceAtLeast(0)
+
+    debugLog { "Remeasuring..." }
+
+    return if (pageCount <= 0) {
+        PagerMeasureResult(
+            visiblePagesInfo = emptyList(),
+            pagesCount = 0,
+            pageSize = pageAvailableSize,
+            pageSpacing = spaceBetweenPages,
+            afterContentPadding = afterContentPadding,
+            orientation = orientation,
+            viewportStartOffset = -beforeContentPadding,
+            viewportEndOffset = mainAxisAvailableSize + afterContentPadding,
+            measureResult = layout(constraints.minWidth, constraints.minHeight) {},
+            consumedScroll = 0f,
+            closestPageToSnapPosition = null,
+            firstVisiblePage = null,
+            firstVisiblePageOffset = 0,
+            reverseLayout = false,
+            canScrollForward = false
+        )
+    } else {
+
+        val childConstraints = Constraints(
+            maxWidth = if (orientation == Orientation.Vertical) {
+                constraints.maxWidth
+            } else {
+                pageAvailableSize
+            },
+            maxHeight = if (orientation != Orientation.Vertical) {
+                constraints.maxHeight
+            } else {
+                pageAvailableSize
+            }
+        )
+
+        var currentFirstPage = firstVisiblePage
+        var currentFirstPageScrollOffset = firstVisiblePageOffset
+        if (currentFirstPage >= pageCount) {
+            // the data set has been updated and now we have less pages that we were
+            // scrolled to before
+            currentFirstPage = pageCount - 1
+            currentFirstPageScrollOffset = 0
+        }
+
+        // represents the real amount of scroll we applied as a result of this measure pass.
+        var scrollDelta = scrollToBeConsumed.roundToInt()
+
+        // applying the whole requested scroll offset. we will figure out if we can't consume
+        // all of it later
+        currentFirstPageScrollOffset -= scrollDelta
+
+        // if the current scroll offset is less than minimally possible
+        if (currentFirstPage == 0 && currentFirstPageScrollOffset < 0) {
+            scrollDelta += currentFirstPageScrollOffset
+            currentFirstPageScrollOffset = 0
+        }
+
+        // this will contain all the measured pages representing the visible pages
+        val visiblePages = mutableListOf<MeasuredPage>()
+
+        // define min and max offsets
+        val minOffset = -beforeContentPadding + if (spaceBetweenPages < 0) spaceBetweenPages else 0
+        val maxOffset = mainAxisAvailableSize
+
+        // include the start padding so we compose pages in the padding area and neutralise page
+        // spacing (if the spacing is negative this will make sure the previous page is composed)
+        // before starting scrolling forward we will remove it back
+        currentFirstPageScrollOffset += minOffset
+
+        // max of cross axis sizes of all visible pages
+        var maxCrossAxis = 0
+
+        // we had scrolled backward or we compose pages in the start padding area, which means
+        // pages before current firstPageScrollOffset should be visible. compose them and update
+        // firstPageScrollOffset
+        while (currentFirstPageScrollOffset < 0 && currentFirstPage > 0) {
+            val previous = currentFirstPage - 1
+            val measuredPage = getAndMeasure(
+                index = previous,
+                childConstraints = childConstraints,
+                pagerItemProvider = pagerItemProvider,
+                visualPageOffset = visualPageOffset,
+                orientation = orientation,
+                horizontalAlignment = horizontalAlignment,
+                verticalAlignment = verticalAlignment,
+                afterContentPadding = afterContentPadding,
+                beforeContentPadding = beforeContentPadding,
+                layoutDirection = layoutDirection,
+                reverseLayout = reverseLayout,
+                pageAvailableSize = pageAvailableSize
+            )
+            visiblePages.add(0, measuredPage)
+            maxCrossAxis = maxOf(maxCrossAxis, measuredPage.crossAxisSize)
+            currentFirstPageScrollOffset += pageSizeWithSpacing
+            currentFirstPage = previous
+        }
+
+        // if we were scrolled backward, but there were not enough pages before. this means
+        // not the whole scroll was consumed
+        if (currentFirstPageScrollOffset < minOffset) {
+            scrollDelta += currentFirstPageScrollOffset
+            currentFirstPageScrollOffset = minOffset
+        }
+
+        // neutralize previously added padding as we stopped filling the before content padding
+        currentFirstPageScrollOffset -= minOffset
+
+        var index = currentFirstPage
+        val maxMainAxis = (maxOffset + afterContentPadding).coerceAtLeast(0)
+        var currentMainAxisOffset = -currentFirstPageScrollOffset
+
+        // first we need to skip pages we already composed while composing backward
+        visiblePages.fastForEach {
+            index++
+            currentMainAxisOffset += pageSizeWithSpacing
+        }
+
+        // then composing visible pages forward until we fill the whole viewport.
+        // we want to have at least one page in visiblePages even if in fact all the pages are
+        // offscreen, this can happen if the content padding is larger than the available size.
+        while (index < pageCount &&
+            (currentMainAxisOffset < maxMainAxis ||
+                currentMainAxisOffset <= 0 || // filling beforeContentPadding area
+                visiblePages.isEmpty())
+        ) {
+            val measuredPage = getAndMeasure(
+                index = index,
+                childConstraints = childConstraints,
+                pagerItemProvider = pagerItemProvider,
+                visualPageOffset = visualPageOffset,
+                orientation = orientation,
+                horizontalAlignment = horizontalAlignment,
+                verticalAlignment = verticalAlignment,
+                afterContentPadding = afterContentPadding,
+                beforeContentPadding = beforeContentPadding,
+                layoutDirection = layoutDirection,
+                reverseLayout = reverseLayout,
+                pageAvailableSize = pageAvailableSize
+            )
+            currentMainAxisOffset += pageSizeWithSpacing
+
+            if (currentMainAxisOffset <= minOffset && index != pageCount - 1) {
+                // this page is offscreen and will not be placed. advance firstVisiblePage
+                currentFirstPage = index + 1
+                currentFirstPageScrollOffset -= pageSizeWithSpacing
+            } else {
+                maxCrossAxis = maxOf(maxCrossAxis, measuredPage.crossAxisSize)
+                visiblePages.add(measuredPage)
+            }
+
+            index++
+        }
+
+        // we didn't fill the whole viewport with pages starting from firstVisiblePage.
+        // lets try to scroll back if we have enough pages before firstVisiblePage.
+        if (currentMainAxisOffset < maxOffset) {
+            val toScrollBack = maxOffset - currentMainAxisOffset
+            currentFirstPageScrollOffset -= toScrollBack
+            currentMainAxisOffset += toScrollBack
+            while (currentFirstPageScrollOffset < beforeContentPadding &&
+                currentFirstPage > 0
+            ) {
+                val previousIndex = currentFirstPage - 1
+                val measuredPage = getAndMeasure(
+                    index = previousIndex,
+                    childConstraints = childConstraints,
+                    pagerItemProvider = pagerItemProvider,
+                    visualPageOffset = visualPageOffset,
+                    orientation = orientation,
+                    horizontalAlignment = horizontalAlignment,
+                    verticalAlignment = verticalAlignment,
+                    afterContentPadding = afterContentPadding,
+                    beforeContentPadding = beforeContentPadding,
+                    layoutDirection = layoutDirection,
+                    reverseLayout = reverseLayout,
+                    pageAvailableSize = pageAvailableSize
+                )
+                visiblePages.add(0, measuredPage)
+                maxCrossAxis = maxOf(maxCrossAxis, measuredPage.crossAxisSize)
+                currentFirstPageScrollOffset += pageSizeWithSpacing
+                currentFirstPage = previousIndex
+            }
+            scrollDelta += toScrollBack
+            if (currentFirstPageScrollOffset < 0) {
+                scrollDelta += currentFirstPageScrollOffset
+                currentMainAxisOffset += currentFirstPageScrollOffset
+                currentFirstPageScrollOffset = 0
+            }
+        }
+
+        // report the amount of pixels we consumed. scrollDelta can be smaller than
+        // scrollToBeConsumed if there were not enough pages to fill the offered space or it
+        // can be larger if pages were resized, or if, for example, we were previously
+        // displaying the page 15, but now we have only 10 pages in total in the data set.
+        val consumedScroll = if (scrollToBeConsumed.roundToInt().sign == scrollDelta.sign &&
+            abs(scrollToBeConsumed.roundToInt()) >= abs(scrollDelta)
+        ) {
+            scrollDelta.toFloat()
+        } else {
+            scrollToBeConsumed
+        }
+
+        // the initial offset for pages from visiblePages list
+        require(currentFirstPageScrollOffset >= 0)
+        val visiblePagesScrollOffset = -currentFirstPageScrollOffset
+        var firstPage = visiblePages.first()
+
+        // even if we compose pages to fill before content padding we should ignore pages fully
+        // located there for the state's scroll position calculation (first page + first offset)
+        if (beforeContentPadding > 0 || spaceBetweenPages < 0) {
+            for (i in visiblePages.indices) {
+                val size = pageSizeWithSpacing
+                if (currentFirstPageScrollOffset != 0 && size <= currentFirstPageScrollOffset &&
+                    i != visiblePages.lastIndex
+                ) {
+                    currentFirstPageScrollOffset -= size
+                    firstPage = visiblePages[i + 1]
+                } else {
+                    break
+                }
+            }
+        }
+
+        // Compose extra pages before
+        val extraPagesBefore = createPagesBeforeList(
+            beyondBoundsInfo = beyondBoundsInfo,
+            currentFirstPage = currentFirstPage,
+            pagesCount = pageCount,
+            beyondBoundsPageCount = beyondBoundsPageCount,
+            pinnedPages = pinnedPages
+        ) {
+            getAndMeasure(
+                index = it,
+                childConstraints = childConstraints,
+                pagerItemProvider = pagerItemProvider,
+                visualPageOffset = visualPageOffset,
+                orientation = orientation,
+                horizontalAlignment = horizontalAlignment,
+                verticalAlignment = verticalAlignment,
+                afterContentPadding = afterContentPadding,
+                beforeContentPadding = beforeContentPadding,
+                layoutDirection = layoutDirection,
+                reverseLayout = reverseLayout,
+                pageAvailableSize = pageAvailableSize
+            )
+        }
+
+        // Update maxCrossAxis with extra pages
+        extraPagesBefore.fastForEach {
+            maxCrossAxis = maxOf(maxCrossAxis, it.crossAxisSize)
+        }
+
+        // Compose pages after last page
+        val extraPagesAfter = createPagesAfterList(
+            beyondBoundsInfo = beyondBoundsInfo,
+            visiblePages = visiblePages,
+            pagesCount = pageCount,
+            beyondBoundsPageCount = beyondBoundsPageCount,
+            pinnedPages = pinnedPages
+        ) {
+            getAndMeasure(
+                index = it,
+                childConstraints = childConstraints,
+                pagerItemProvider = pagerItemProvider,
+                visualPageOffset = visualPageOffset,
+                orientation = orientation,
+                horizontalAlignment = horizontalAlignment,
+                verticalAlignment = verticalAlignment,
+                afterContentPadding = afterContentPadding,
+                beforeContentPadding = beforeContentPadding,
+                layoutDirection = layoutDirection,
+                reverseLayout = reverseLayout,
+                pageAvailableSize = pageAvailableSize
+            )
+        }
+
+        // Update maxCrossAxis with extra pages
+        extraPagesAfter.fastForEach {
+            maxCrossAxis = maxOf(maxCrossAxis, it.crossAxisSize)
+        }
+
+        val noExtraPages = firstPage == visiblePages.first() &&
+            extraPagesBefore.isEmpty() &&
+            extraPagesAfter.isEmpty()
+
+        val layoutWidth = constraints
+            .constrainWidth(
+                if (orientation == Orientation.Vertical)
+                    maxCrossAxis
+                else
+                    currentMainAxisOffset
+            )
+        val layoutHeight = constraints
+            .constrainHeight(
+                if (orientation == Orientation.Vertical)
+                    currentMainAxisOffset
+                else
+                    maxCrossAxis
+            )
+
+        val positionedPages = calculatePagesOffsets(
+            pages = visiblePages,
+            extraPagesBefore = extraPagesBefore,
+            extraPagesAfter = extraPagesAfter,
+            layoutWidth = layoutWidth,
+            layoutHeight = layoutHeight,
+            finalMainAxisOffset = currentMainAxisOffset,
+            maxOffset = maxOffset,
+            pagesScrollOffset = visiblePagesScrollOffset,
+            orientation = orientation,
+            reverseLayout = reverseLayout,
+            density = this,
+            pageAvailableSize = pageAvailableSize,
+            spaceBetweenPages = spaceBetweenPages
+        )
+
+        val visiblePagesInfo = if (noExtraPages) positionedPages else positionedPages.fastFilter {
+            (it.index >= visiblePages.first().index && it.index <= visiblePages.last().index)
+        }
+        val viewPortSize = if (orientation == Orientation.Vertical) layoutHeight else layoutWidth
+
+        val closestPageToSnapPosition = visiblePagesInfo.fastMaxBy {
+            -abs(
+                calculateDistanceToDesiredSnapPosition(
+                    viewPortSize,
+                    beforeContentPadding,
+                    afterContentPadding,
+                    pageAvailableSize,
+                    it,
+                    SnapAlignmentStartToStart
+                )
+            )
+        }
+
+        return PagerMeasureResult(
+            firstVisiblePage = firstPage,
+            firstVisiblePageOffset = currentFirstPageScrollOffset,
+            closestPageToSnapPosition = closestPageToSnapPosition,
+            consumedScroll = consumedScroll,
+            measureResult = layout(layoutWidth, layoutHeight) {
+                positionedPages.fastForEach {
+                    it.place(this)
+                }
+            },
+            viewportStartOffset = -beforeContentPadding,
+            viewportEndOffset = maxOffset + afterContentPadding,
+            visiblePagesInfo = visiblePagesInfo,
+            pagesCount = pageCount,
+            reverseLayout = reverseLayout,
+            orientation = orientation,
+            pageSize = pageAvailableSize,
+            pageSpacing = spaceBetweenPages,
+            afterContentPadding = afterContentPadding,
+            canScrollForward = index < pageCount || currentMainAxisOffset > maxOffset
+        )
+    }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+private fun Density.calculateDistanceToDesiredSnapPosition(
+    axisViewPortSize: Int,
+    beforeContentPadding: Int,
+    afterContentPadding: Int,
+    pageSize: Int,
+    page: PageInfo,
+    positionInLayout: Density.(layoutSize: Float, itemSize: Float) -> Float
+): Float {
+    val containerSize = axisViewPortSize - beforeContentPadding - afterContentPadding
+
+    val desiredDistance =
+        positionInLayout(containerSize.toFloat(), pageSize.toFloat())
+
+    val itemCurrentPosition = page.offset
+    return itemCurrentPosition - desiredDistance
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+private fun createPagesAfterList(
+    beyondBoundsInfo: LazyListBeyondBoundsInfo,
+    visiblePages: MutableList<MeasuredPage>,
+    pagesCount: Int,
+    beyondBoundsPageCount: Int,
+    pinnedPages: LazyLayoutPinnedItemList,
+    getAndMeasure: (Int) -> MeasuredPage
+): List<MeasuredPage> {
+    fun LazyListBeyondBoundsInfo.endIndex() = min(end, pagesCount - 1)
+
+    var list: MutableList<MeasuredPage>? = null
+
+    var end = visiblePages.last().index
+
+    fun addPage(index: Int) {
+        if (list == null) list = mutableListOf()
+        requireNotNull(list).add(getAndMeasure(index))
+    }
+
+    if (beyondBoundsInfo.hasIntervals()) {
+        end = maxOf(beyondBoundsInfo.endIndex(), end)
+    }
+
+    end = minOf(end + beyondBoundsPageCount, pagesCount - 1)
+
+    for (i in visiblePages.last().index + 1..end) {
+        addPage(i)
+    }
+
+    pinnedPages.fastForEach { page ->
+        if (page.index in (end + 1) until pagesCount) {
+            addPage(page.index)
+        }
+    }
+
+    return list ?: emptyList()
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+private fun createPagesBeforeList(
+    beyondBoundsInfo: LazyListBeyondBoundsInfo,
+    currentFirstPage: Int,
+    pagesCount: Int,
+    beyondBoundsPageCount: Int,
+    pinnedPages: LazyLayoutPinnedItemList,
+    getAndMeasure: (Int) -> MeasuredPage
+): List<MeasuredPage> {
+    fun LazyListBeyondBoundsInfo.startIndex() = min(start, pagesCount - 1)
+
+    var list: MutableList<MeasuredPage>? = null
+
+    var start = currentFirstPage
+
+    fun addPage(index: Int) {
+        if (list == null) list = mutableListOf()
+        requireNotNull(list).add(
+            getAndMeasure(index)
+        )
+    }
+
+    if (beyondBoundsInfo.hasIntervals()) {
+        start = minOf(beyondBoundsInfo.startIndex(), start)
+    }
+
+    start = maxOf(0, start - beyondBoundsPageCount)
+
+    for (i in currentFirstPage - 1 downTo start) {
+        addPage(i)
+    }
+
+    pinnedPages.fastForEach { page ->
+        if (page.index < start) {
+            addPage(page.index)
+        }
+    }
+
+    return list ?: emptyList()
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+private fun LazyLayoutMeasureScope.getAndMeasure(
+    index: Int,
+    childConstraints: Constraints,
+    pagerItemProvider: PagerLazyLayoutItemProvider,
+    visualPageOffset: IntOffset,
+    orientation: Orientation,
+    horizontalAlignment: Alignment.Horizontal?,
+    verticalAlignment: Alignment.Vertical?,
+    afterContentPadding: Int,
+    beforeContentPadding: Int,
+    layoutDirection: LayoutDirection,
+    reverseLayout: Boolean,
+    pageAvailableSize: Int
+): MeasuredPage {
+    val key = pagerItemProvider.getKey(index)
+    val placeable = measure(index, childConstraints)
+
+    return MeasuredPage(
+        index = index,
+        placeables = placeable,
+        visualOffset = visualPageOffset,
+        horizontalAlignment = horizontalAlignment,
+        verticalAlignment = verticalAlignment,
+        afterContentPadding = afterContentPadding,
+        beforeContentPadding = beforeContentPadding,
+        layoutDirection = layoutDirection,
+        reverseLayout = reverseLayout,
+        size = pageAvailableSize,
+        orientation = orientation,
+        key = key
+    )
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+private fun LazyLayoutMeasureScope.calculatePagesOffsets(
+    pages: List<MeasuredPage>,
+    extraPagesBefore: List<MeasuredPage>,
+    extraPagesAfter: List<MeasuredPage>,
+    layoutWidth: Int,
+    layoutHeight: Int,
+    finalMainAxisOffset: Int,
+    maxOffset: Int,
+    pagesScrollOffset: Int,
+    orientation: Orientation,
+    reverseLayout: Boolean,
+    density: Density,
+    spaceBetweenPages: Int,
+    pageAvailableSize: Int
+): MutableList<PositionedPage> {
+    val pageSizeWithSpacing = (pageAvailableSize + spaceBetweenPages)
+    val mainAxisLayoutSize = if (orientation == Orientation.Vertical) layoutHeight else layoutWidth
+    val hasSpareSpace = finalMainAxisOffset < minOf(mainAxisLayoutSize, maxOffset)
+    if (hasSpareSpace) {
+        check(pagesScrollOffset == 0)
+    }
+    val positionedPages =
+        ArrayList<PositionedPage>(pages.size + extraPagesBefore.size + extraPagesAfter.size)
+
+    if (hasSpareSpace) {
+        require(extraPagesBefore.isEmpty() && extraPagesAfter.isEmpty())
+
+        val pagesCount = pages.size
+        fun Int.reverseAware() =
+            if (!reverseLayout) this else pagesCount - this - 1
+
+        val sizes = IntArray(pagesCount) { pageAvailableSize }
+        val offsets = IntArray(pagesCount) { 0 }
+
+        val arrangement = spacedBy(pageAvailableSize.toDp())
+        if (orientation == Orientation.Vertical) {
+            with(arrangement) { density.arrange(mainAxisLayoutSize, sizes, offsets) }
+        } else {
+            with(arrangement) {
+                // Enforces Ltr layout direction as it is mirrored with placeRelative later.
+                density.arrange(mainAxisLayoutSize, sizes, LayoutDirection.Ltr, offsets)
+            }
+        }
+
+        val reverseAwareOffsetIndices =
+            if (!reverseLayout) offsets.indices else offsets.indices.reversed()
+        for (index in reverseAwareOffsetIndices) {
+            val absoluteOffset = offsets[index]
+            // when reverseLayout == true, offsets are stored in the reversed order to pages
+            val page = pages[index.reverseAware()]
+            val relativeOffset = if (reverseLayout) {
+                // inverse offset to align with scroll direction for positioning
+                mainAxisLayoutSize - absoluteOffset - page.size
+            } else {
+                absoluteOffset
+            }
+            positionedPages.add(page.position(relativeOffset, layoutWidth, layoutHeight))
+        }
+    } else {
+        var currentMainAxis = pagesScrollOffset
+        extraPagesBefore.fastForEach {
+            currentMainAxis -= pageSizeWithSpacing
+            positionedPages.add(it.position(currentMainAxis, layoutWidth, layoutHeight))
+        }
+
+        currentMainAxis = pagesScrollOffset
+        pages.fastForEach {
+            positionedPages.add(it.position(currentMainAxis, layoutWidth, layoutHeight))
+            currentMainAxis += pageSizeWithSpacing
+        }
+
+        extraPagesAfter.fastForEach {
+            positionedPages.add(it.position(currentMainAxis, layoutWidth, layoutHeight))
+            currentMainAxis += pageSizeWithSpacing
+        }
+    }
+    return positionedPages
+}
+
+private const val DEBUG = false
+private inline fun debugLog(generateMsg: () -> String) {
+    if (DEBUG) {
+        println("PagerMeasure: ${generateMsg()}")
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt
new file mode 100644
index 0000000..f535064
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.pager
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.checkScrollableContainerConstraints
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.calculateEndPadding
+import androidx.compose.foundation.layout.calculateStartPadding
+import androidx.compose.foundation.lazy.LazyListBeyondBoundsInfo
+import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
+import androidx.compose.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.unit.Constraints
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.constrainHeight
+import androidx.compose.ui.unit.constrainWidth
+import androidx.compose.ui.unit.offset
+import kotlin.math.roundToInt
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+internal fun rememberPagerMeasurePolicy(
+    itemProvider: PagerLazyLayoutItemProvider,
+    state: PagerState,
+    contentPadding: PaddingValues,
+    reverseLayout: Boolean,
+    orientation: Orientation,
+    beyondBoundsPageCount: Int,
+    pageSpacing: Dp,
+    pageSize: PageSize,
+    horizontalAlignment: Alignment.Horizontal?,
+    verticalAlignment: Alignment.Vertical?,
+    pageCount: Int,
+    beyondBoundsInfo: LazyListBeyondBoundsInfo
+) = remember<LazyLayoutMeasureScope.(Constraints) -> MeasureResult>(
+    contentPadding,
+    pageSpacing,
+    pageSize,
+    state,
+    contentPadding,
+    reverseLayout,
+    orientation,
+    horizontalAlignment,
+    verticalAlignment,
+    pageCount,
+    beyondBoundsInfo
+) {
+    { containerConstraints ->
+        val isVertical = orientation == Orientation.Vertical
+        checkScrollableContainerConstraints(
+            containerConstraints,
+            if (isVertical) Orientation.Vertical else Orientation.Horizontal
+        )
+
+        // resolve content paddings
+        val startPadding =
+            if (isVertical) {
+                contentPadding.calculateLeftPadding(layoutDirection).roundToPx()
+            } else {
+                // in horizontal configuration, padding is reversed by placeRelative
+                contentPadding.calculateStartPadding(layoutDirection).roundToPx()
+            }
+
+        val endPadding =
+            if (isVertical) {
+                contentPadding.calculateRightPadding(layoutDirection).roundToPx()
+            } else {
+                // in horizontal configuration, padding is reversed by placeRelative
+                contentPadding.calculateEndPadding(layoutDirection).roundToPx()
+            }
+        val topPadding = contentPadding.calculateTopPadding().roundToPx()
+        val bottomPadding = contentPadding.calculateBottomPadding().roundToPx()
+        val totalVerticalPadding = topPadding + bottomPadding
+        val totalHorizontalPadding = startPadding + endPadding
+        val totalMainAxisPadding = if (isVertical) totalVerticalPadding else totalHorizontalPadding
+        val beforeContentPadding = when {
+            isVertical && !reverseLayout -> topPadding
+            isVertical && reverseLayout -> bottomPadding
+            !isVertical && !reverseLayout -> startPadding
+            else -> endPadding // !isVertical && reverseLayout
+        }
+        val afterContentPadding = totalMainAxisPadding - beforeContentPadding
+        val contentConstraints =
+            containerConstraints.offset(-totalHorizontalPadding, -totalVerticalPadding)
+
+        state.density = this
+
+        val spaceBetweenPages = pageSpacing.roundToPx()
+
+        // can be negative if the content padding is larger than the max size from constraints
+        val mainAxisAvailableSize = if (isVertical) {
+            containerConstraints.maxHeight - totalVerticalPadding
+        } else {
+            containerConstraints.maxWidth - totalHorizontalPadding
+        }
+        val visualItemOffset = if (!reverseLayout || mainAxisAvailableSize > 0) {
+            IntOffset(startPadding, topPadding)
+        } else {
+            // When layout is reversed and paddings together take >100% of the available space,
+            // layout size is coerced to 0 when positioning. To take that space into account,
+            // we offset start padding by negative space between paddings.
+            IntOffset(
+                if (isVertical) startPadding else startPadding + mainAxisAvailableSize,
+                if (isVertical) topPadding + mainAxisAvailableSize else topPadding
+            )
+        }
+
+        val pageAvailableSize =
+            with(pageSize) { calculateMainAxisPageSize(mainAxisAvailableSize, spaceBetweenPages) }
+
+        state.premeasureConstraints = Constraints(
+            maxWidth = if (orientation == Orientation.Vertical) {
+                contentConstraints.maxWidth
+            } else {
+                pageAvailableSize
+            },
+            maxHeight = if (orientation != Orientation.Vertical) {
+                contentConstraints.maxHeight
+            } else {
+                pageAvailableSize
+            }
+        )
+
+        val firstVisiblePage: Int
+        val firstVisiblePageOffset: Int
+        Snapshot.withoutReadObservation {
+            firstVisiblePage = state.firstVisiblePage
+            firstVisiblePageOffset = if (state.layoutInfo == EmptyLayoutInfo) {
+                (state.initialPageOffsetFraction * pageAvailableSize).roundToInt()
+            } else {
+                state.firstVisiblePageOffset
+            }
+        }
+
+        measurePager(
+            beforeContentPadding = beforeContentPadding,
+            afterContentPadding = afterContentPadding,
+            constraints = contentConstraints,
+            pageCount = pageCount,
+            spaceBetweenPages = spaceBetweenPages,
+            mainAxisAvailableSize = mainAxisAvailableSize,
+            visualPageOffset = visualItemOffset,
+            pageAvailableSize = pageAvailableSize,
+            beyondBoundsPageCount = beyondBoundsPageCount,
+            orientation = orientation,
+            firstVisiblePage = firstVisiblePage,
+            firstVisiblePageOffset = firstVisiblePageOffset,
+            horizontalAlignment = horizontalAlignment,
+            verticalAlignment = verticalAlignment,
+            pagerItemProvider = itemProvider,
+            reverseLayout = reverseLayout,
+            scrollToBeConsumed = state.scrollToBeConsumed,
+            beyondBoundsInfo = beyondBoundsInfo,
+            pinnedPages = state.pinnedPages,
+            layout = { width, height, placement ->
+                layout(
+                    containerConstraints.constrainWidth(width + totalHorizontalPadding),
+                    containerConstraints.constrainHeight(height + totalVerticalPadding),
+                    emptyMap(),
+                    placement
+                )
+            }
+        ).also {
+            state.applyMeasureResult(it)
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasureResult.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasureResult.kt
new file mode 100644
index 0000000..3a2d241
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasureResult.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.pager
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.unit.IntSize
+
+@OptIn(ExperimentalFoundationApi::class)
+internal class PagerMeasureResult(
+    override val visiblePagesInfo: List<PageInfo>,
+    override val pagesCount: Int,
+    override val pageSize: Int,
+    override val pageSpacing: Int,
+    override val afterContentPadding: Int,
+    override val orientation: Orientation,
+    override val viewportStartOffset: Int,
+    override val viewportEndOffset: Int,
+    override val reverseLayout: Boolean,
+    val consumedScroll: Float,
+    val firstVisiblePage: MeasuredPage?,
+    override val closestPageToSnapPosition: PageInfo?,
+    val firstVisiblePageOffset: Int,
+    val canScrollForward: Boolean,
+    measureResult: MeasureResult,
+) : PagerLayoutInfo, MeasureResult by measureResult {
+    override val viewportSize: IntSize
+        get() = IntSize(width, height)
+    override val beforeContentPadding: Int get() = -viewportStartOffset
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerScrollPosition.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerScrollPosition.kt
new file mode 100644
index 0000000..cf2cb6b
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerScrollPosition.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.compose.foundation.pager
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshots.Snapshot
+
+/**
+ * Contains the current scroll position represented by the first visible page  and the first
+ * visible page scroll offset.
+ */
+@OptIn(ExperimentalFoundationApi::class)
+internal class PagerScrollPosition(
+    initialPage: Int = 0,
+    initialScrollOffset: Int = 0
+) {
+    var firstVisiblePage by mutableStateOf(initialPage)
+    var currentPage by mutableStateOf(initialPage)
+
+    var scrollOffset by mutableStateOf(initialScrollOffset)
+        private set
+
+    private var hadFirstNotEmptyLayout = false
+
+    /** The last know key of the page at [firstVisiblePage] position. */
+    private var lastKnownFirstPageKey: Any? = null
+
+    /**
+     * Updates the current scroll position based on the results of the last measurement.
+     */
+    fun updateFromMeasureResult(measureResult: PagerMeasureResult) {
+        lastKnownFirstPageKey = measureResult.firstVisiblePage?.key
+        // we ignore the index and offset from measureResult until we get at least one
+        // measurement with real pages. otherwise the initial index and scroll passed to the
+        // state would be lost and overridden with zeros.
+        if (hadFirstNotEmptyLayout || measureResult.pagesCount > 0) {
+            hadFirstNotEmptyLayout = true
+            val scrollOffset = measureResult.firstVisiblePageOffset
+            check(scrollOffset >= 0f) { "scrollOffset should be non-negative ($scrollOffset)" }
+
+            Snapshot.withoutReadObservation {
+                update(
+                    measureResult.firstVisiblePage?.index ?: 0,
+                    scrollOffset
+                )
+                measureResult.closestPageToSnapPosition?.index?.let {
+                    if (it != this.currentPage) {
+                        this.currentPage = it
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Updates the scroll position - the passed values will be used as a start position for
+     * composing the pages during the next measure pass and will be updated by the real
+     * position calculated during the measurement. This means that there is no guarantee that
+     * exactly this index and offset will be applied as it is possible that:
+     * a) there will be no page at this index in reality
+     * b) page at this index will be smaller than the asked scrollOffset, which means we would
+     * switch to the next page
+     * c) there will be not enough pages to fill the viewport after the requested index, so we
+     * would have to compose few elements before the asked index, changing the first visible page.
+     */
+    fun requestPosition(index: Int, scrollOffset: Int) {
+        update(index, scrollOffset)
+        // clear the stored key as we have a direct request to scroll to [index] position and the
+        // next [checkIfFirstVisibleItemWasMoved] shouldn't override this.
+        lastKnownFirstPageKey = null
+    }
+
+    private fun update(index: Int, scrollOffset: Int) {
+        require(index >= 0f) { "Index should be non-negative ($index)" }
+        if (index != this.firstVisiblePage) {
+            this.firstVisiblePage = index
+        }
+        if (scrollOffset != this.scrollOffset) {
+            this.scrollOffset = scrollOffset
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerSemantics.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerSemantics.kt
new file mode 100644
index 0000000..82d10fd
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerSemantics.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.pager
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
+import androidx.compose.foundation.lazy.layout.LazyLayoutSemanticState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+internal fun rememberPagerSemanticState(
+    state: PagerState,
+    itemProvider: LazyLayoutItemProvider,
+    reverseScrolling: Boolean,
+    isVertical: Boolean
+): LazyLayoutSemanticState {
+    return remember(state, itemProvider, reverseScrolling, isVertical) {
+        LazyLayoutSemanticState(state, isVertical)
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
index f6d7127..485e4e2 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
@@ -1,18 +1,18 @@
 /*
-* 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.
-*/
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 package androidx.compose.foundation.pager
 
@@ -21,15 +21,15 @@
 import androidx.compose.animation.core.spring
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.MutatePriority
+import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.ScrollScope
 import androidx.compose.foundation.gestures.ScrollableState
 import androidx.compose.foundation.gestures.animateScrollBy
-import androidx.compose.foundation.gestures.snapping.calculateDistanceToDesiredSnapPosition
-import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.InteractionSource
-import androidx.compose.foundation.lazy.LazyListItemInfo
-import androidx.compose.foundation.lazy.LazyListLayoutInfo
-import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.lazy.AwaitFirstLayoutModifier
+import androidx.compose.foundation.lazy.layout.LazyLayoutPinnedItemList
+import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.derivedStateOf
@@ -39,17 +39,17 @@
 import androidx.compose.runtime.saveable.listSaver
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
+import androidx.compose.runtime.structuralEqualityPolicy
+import androidx.compose.ui.layout.Remeasurement
+import androidx.compose.ui.layout.RemeasurementModifier
+import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.util.fastMaxBy
-import kotlin.coroutines.Continuation
-import kotlin.coroutines.resume
-import kotlin.coroutines.suspendCoroutine
+import androidx.compose.ui.util.fastFirstOrNull
 import kotlin.math.abs
 import kotlin.math.roundToInt
 import kotlin.math.sign
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.emptyFlow
 
 /**
  * Creates and remember a [PagerState] to be used with a [Pager]
@@ -95,23 +95,63 @@
 
     internal var snapRemainingScrollOffset by mutableStateOf(0f)
 
-    private var lazyListState by mutableStateOf<LazyListState?>(null)
+    private val scrollPosition = PagerScrollPosition(initialPage, 0)
 
-    internal var pageSpacing by mutableStateOf(0)
+    internal val firstVisiblePage: Int get() = scrollPosition.firstVisiblePage
 
-    private val awaitLazyListStateSet = AwaitLazyListStateSet()
+    internal val firstVisiblePageOffset: Int get() = scrollPosition.scrollOffset
+
+    internal var scrollToBeConsumed = 0f
+        private set
+
+    /**
+     * The ScrollableController instance. We keep it as we need to call stopAnimation on it once
+     * we reached the end of the list.
+     */
+    private val scrollableState = ScrollableState { -performScroll(-it) }
+
+    /**
+     * Only used for testing to confirm that we're not making too many measure passes
+     */
+    internal var numMeasurePasses: Int = 0
+        private set
+
+    /**
+     * Only used for testing to disable prefetching when needed to test the main logic.
+     */
+    internal var prefetchingEnabled: Boolean = true
+
+    /**
+     * The index scheduled to be prefetched (or the last prefetched index if the prefetch is done).
+     */
+    private var indexToPrefetch = -1
+
+    /**
+     * The handle associated with the current index from [indexToPrefetch].
+     */
+    private var currentPrefetchHandle: LazyLayoutPrefetchState.PrefetchHandle? = null
+
+    /**
+     * Keeps the scrolling direction during the previous calculation in order to be able to
+     * detect the scrolling direction change.
+     */
+    private var wasScrollingForward = false
+
+    /** Backing state for PagerLayoutInfo **/
+    private var pagerLayoutInfoState = mutableStateOf(EmptyLayoutInfo)
+
+    internal val layoutInfo: PagerLayoutInfo get() = pagerLayoutInfoState.value
+
+    internal val pageSpacing: Int
+        get() = pagerLayoutInfoState.value.pageSpacing
 
     internal val pageSize: Int
-        get() = visiblePages.firstOrNull()?.size ?: 0
+        get() = pagerLayoutInfoState.value.pageSize
 
-    private val density: Density
-        get() = lazyListState?.density ?: UnitDensity
+    internal var density: Density by mutableStateOf(UnitDensity)
 
-    internal val layoutInfo: LazyListLayoutInfo
-        get() = lazyListState?.layoutInfo ?: EmptyLayoutInfo
-
-    private val visiblePages: List<LazyListItemInfo>
-        get() = layoutInfo.visibleItemsInfo
+    private val visiblePages: List<PageInfo>
+        get() = pagerLayoutInfoState.value.visiblePagesInfo
 
     private val pageAvailableSpace: Int
         get() = pageSize + pageSpacing
@@ -127,30 +167,19 @@
         }
 
     internal val pageCount: Int
-        get() = layoutInfo.totalItemsCount
+        get() = pagerLayoutInfoState.value.pagesCount
 
-    private val closestPageToSnappedPosition: LazyListItemInfo?
-        get() = visiblePages.fastMaxBy {
-            -abs(
-                density.calculateDistanceToDesiredSnapPosition(
-                    layoutInfo,
-                    it,
-                    SnapAlignmentStartToStart
-                )
-            )
-        }
-
-    internal val firstVisiblePage: LazyListItemInfo?
+    internal val firstVisiblePageInfo: PageInfo?
         get() = visiblePages.lastOrNull {
             density.calculateDistanceToDesiredSnapPosition(
-                layoutInfo,
+                pagerLayoutInfoState.value,
                 it,
                 SnapAlignmentStartToStart
             ) <= 0
         }
 
     private val distanceToSnapPosition: Float
-        get() = closestPageToSnappedPosition?.let {
+        get() = layoutInfo.closestPageToSnapPosition?.let {
             density.calculateDistanceToDesiredSnapPosition(
                 layoutInfo,
                 it,
@@ -158,13 +187,15 @@
             )
         } ?: 0f
 
+    internal val internalInteractionSource: MutableInteractionSource = MutableInteractionSource()
+
     /**
      * [InteractionSource] that will be used to dispatch drag events when this
      * list is being dragged. If you want to know whether the fling (or animated scroll) is in
      * progress, use [isScrollInProgress].
      */
     val interactionSource: InteractionSource
-        get() = lazyListState?.interactionSource ?: EmptyInteractionSources
+        get() = internalInteractionSource
 
     /**
      * The page that sits closest to the snapped position. This is an observable value and will
@@ -174,7 +205,7 @@
      * @sample androidx.compose.foundation.samples.ObservingStateChangesInPagerStateSample
      *
      */
-    val currentPage: Int by derivedStateOf { closestPageToSnappedPosition?.index ?: initialPage }
+    val currentPage: Int get() = scrollPosition.currentPage
 
     private var animationTargetPage by mutableStateOf(-1)
 
@@ -188,8 +219,12 @@
      * Please refer to the sample to learn how to use this API.
      * @sample androidx.compose.foundation.samples.ObservingStateChangesInPagerStateSample
      */
-    val settledPage: Int by derivedStateOf {
-        if (pageCount == 0) 0 else settledPageState.coerceInPageRange()
+    val settledPage by derivedStateOf(structuralEqualityPolicy()) {
+        if (isScrollInProgress) {
+            settledPageState
+        } else {
+            currentPage
+        }
     }
 
     /**
@@ -201,7 +236,7 @@
      * Please refer to the sample to learn how to use this API.
      * @sample androidx.compose.foundation.samples.ObservingStateChangesInPagerStateSample
      */
-    val targetPage: Int by derivedStateOf {
+    val targetPage: Int by derivedStateOf(structuralEqualityPolicy()) {
         val finalPage = if (!isScrollInProgress) {
             currentPage
         } else if (animationTargetPage != -1) {
@@ -235,8 +270,9 @@
      * Please refer to the sample to learn how to use this API.
      * @sample androidx.compose.foundation.samples.ObservingStateChangesInPagerStateSample
      */
-    val currentPageOffsetFraction: Float by derivedStateOf {
-        val currentPagePositionOffset = closestPageToSnappedPosition?.offset ?: 0
+    val currentPageOffsetFraction: Float by derivedStateOf(structuralEqualityPolicy()) {
+        val currentPagePositionOffset =
+            layoutInfo.visiblePagesInfo.fastFirstOrNull { it.index == currentPage }?.offset ?: 0
         val pageUsedSpace = pageAvailableSpace.toFloat()
         if (pageUsedSpace == 0f) {
             // Default to 0 when there's no info about the page size yet.
@@ -248,6 +284,40 @@
         }
     }
 
+    internal val prefetchState = LazyLayoutPrefetchState()
+
+    /**
+     * Provides a modifier which allows to delay some interactions (e.g. scroll)
+     * until layout is ready.
+     */
+    internal val awaitLayoutModifier = AwaitFirstLayoutModifier()
+
+    /**
+     * The [Remeasurement] object associated with our layout. It allows us to remeasure
+     * synchronously during scroll.
+     */
+    internal var remeasurement: Remeasurement? by mutableStateOf(null)
+        private set
+
+    /**
+     * The modifier which provides [remeasurement].
+     */
+    internal val remeasurementModifier = object : RemeasurementModifier {
+        override fun onRemeasurementAvailable(remeasurement: Remeasurement) {
+            this@PagerState.remeasurement = remeasurement
+        }
+    }
+
+    /**
+     * Constraints passed to the prefetcher for premeasuring the prefetched items.
+     */
+    internal var premeasureConstraints by mutableStateOf(Constraints())
+
+    /**
+     * Stores currently pinned pages which are always composed, used by for beyond bound pages.
+     */
+    internal val pinnedPages = LazyLayoutPinnedItemList()
+
     /**
      * Scroll (jump immediately) to a given [page].
      *
@@ -258,15 +328,18 @@
      * @param pageOffsetFraction A fraction of the page size that indicates the offset the
      * destination page will be offset from its snapped position.
      */
-    suspend fun scrollToPage(page: Int, pageOffsetFraction: Float = 0f) {
+    suspend fun scrollToPage(page: Int, pageOffsetFraction: Float = 0f) = scroll {
         debugLog { "Scroll from page=$currentPage to page=$page" }
         awaitScrollDependencies()
         require(pageOffsetFraction in -0.5..0.5) {
             "pageOffsetFraction $pageOffsetFraction is not within the range -0.5 to 0.5"
         }
         val targetPage = page.coerceInPageRange()
-        val pageOffsetToCorrectPosition = (pageAvailableSpace * pageOffsetFraction).roundToInt()
-        requireNotNull(lazyListState).scrollToItem(targetPage, pageOffsetToCorrectPosition)
+        scrollPosition.requestPosition(
+            targetPage,
+            (pageAvailableSpace * pageOffsetFraction).roundToInt()
+        )
+        remeasurement?.forceRemeasure()
     }
 
     /**
@@ -313,8 +386,8 @@
                 "animateScrollToPage with pre-jump to position=$preJumpPosition"
             }
 
-            // Pre-jump to 1 viewport away from destination item, if possible
-            requireNotNull(lazyListState).scrollToItem(preJumpPosition)
+            // Pre-jump to 1 viewport away from destination page, if possible
+            scrollToPage(preJumpPosition)
             currentPosition = preJumpPosition
         }
 
@@ -326,34 +399,50 @@
         val displacement = targetOffset - currentOffset + pageOffsetToSnappedPosition
 
         debugLog { "animateScrollToPage $displacement pixels" }
-        requireNotNull(lazyListState).animateScrollBy(displacement, animationSpec)
+        animateScrollBy(displacement, animationSpec)
         animationTargetPage = -1
     }
 
     private suspend fun awaitScrollDependencies() {
-        awaitLazyListStateSet.waitFinalLazyListSetting()
-        requireNotNull(lazyListState).awaitLayoutModifier.waitForFirstLayout()
+        awaitLayoutModifier.waitForFirstLayout()
     }
 
     override suspend fun scroll(
         scrollPriority: MutatePriority,
         block: suspend ScrollScope.() -> Unit
     ) {
-        lazyListState?.scroll(scrollPriority, block)
+        awaitScrollDependencies()
+        scrollableState.scroll(scrollPriority, block)
     }
 
     override fun dispatchRawDelta(delta: Float): Float {
-        return lazyListState?.dispatchRawDelta(delta) ?: 0f
+        return scrollableState.dispatchRawDelta(delta)
     }
 
     override val isScrollInProgress: Boolean
-        get() = lazyListState?.isScrollInProgress ?: false
+        get() = scrollableState.isScrollInProgress
 
-    override val canScrollForward: Boolean
-        get() = lazyListState?.canScrollForward ?: true
+    override var canScrollForward: Boolean by mutableStateOf(false)
+        private set
+    override var canScrollBackward: Boolean by mutableStateOf(false)
+        private set
 
-    override val canScrollBackward: Boolean
-        get() = lazyListState?.canScrollBackward ?: true
+    /**
+     *  Updates the state with the new calculated scroll position and consumed scroll.
+     */
+    internal fun applyMeasureResult(result: PagerMeasureResult) {
+        scrollPosition.updateFromMeasureResult(result)
+        scrollToBeConsumed -= result.consumedScroll
+        pagerLayoutInfoState.value = result
+        canScrollForward = result.canScrollForward
+        canScrollBackward = (result.firstVisiblePage?.index ?: 0) != 0 ||
+            result.firstVisiblePageOffset != 0
+        numMeasurePasses++
+        cancelPrefetchIfVisibleItemsChanged(result)
+        if (!isScrollInProgress) {
+            settledPageState = currentPage
+        }
+    }
 
     private fun Int.coerceInPageRange() = if (pageCount > 0) {
         coerceIn(0, pageCount - 1)
@@ -361,13 +450,84 @@
         0
     }
 
-    internal fun updateOnScrollStopped() {
-        settledPageState = currentPage
+    private fun performScroll(distance: Float): Float {
+        if (distance < 0 && !canScrollForward || distance > 0 && !canScrollBackward) {
+            return 0f
+        }
+        check(abs(scrollToBeConsumed) <= 0.5f) {
+            "entered drag with non-zero pending scroll: $scrollToBeConsumed"
+        }
+        scrollToBeConsumed += distance
+
+        // scrollToBeConsumed will be consumed synchronously during the forceRemeasure invocation
+        // inside measuring we do scrollToBeConsumed.roundToInt() so there will be no scroll if
+        // we have less than 0.5 pixels
+        if (abs(scrollToBeConsumed) > 0.5f) {
+            val preScrollToBeConsumed = scrollToBeConsumed
+            remeasurement?.forceRemeasure()
+            if (prefetchingEnabled) {
+                notifyPrefetch(preScrollToBeConsumed - scrollToBeConsumed)
+            }
+        }
+
+        // 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
+        }
     }
 
-    internal fun loadNewState(newState: LazyListState) {
-        lazyListState = newState
-        awaitLazyListStateSet.onStateLoaded()
+    private fun notifyPrefetch(delta: Float) {
+        if (!prefetchingEnabled) {
+            return
+        }
+        val info = layoutInfo
+        if (info.visiblePagesInfo.isNotEmpty()) {
+            val scrollingForward = delta < 0
+            val indexToPrefetch = if (scrollingForward) {
+                info.visiblePagesInfo.last().index + 1
+            } else {
+                info.visiblePagesInfo.first().index - 1
+            }
+            if (indexToPrefetch != this.indexToPrefetch &&
+                indexToPrefetch in 0 until info.pagesCount
+            ) {
+                if (wasScrollingForward != scrollingForward) {
+                    // the scrolling direction has been changed which means the last prefetched
+                    // is not going to be reached anytime soon so it is safer to dispose it.
+                    // if this item is already visible it is safe to call the method anyway
+                    // as it will be no-op
+                    currentPrefetchHandle?.cancel()
+                }
+                this.wasScrollingForward = scrollingForward
+                this.indexToPrefetch = indexToPrefetch
+                currentPrefetchHandle = prefetchState.schedulePrefetch(
+                    indexToPrefetch, premeasureConstraints
+                )
+            }
+        }
+    }
+
+    private fun cancelPrefetchIfVisibleItemsChanged(info: PagerLayoutInfo) {
+        if (indexToPrefetch != -1 && info.visiblePagesInfo.isNotEmpty()) {
+            val expectedPrefetchIndex = if (wasScrollingForward) {
+                info.visiblePagesInfo.last().index + 1
+            } else {
+                info.visiblePagesInfo.first().index - 1
+            }
+            if (indexToPrefetch != expectedPrefetchIndex) {
+                indexToPrefetch = -1
+                currentPrefetchHandle?.cancel()
+                currentPrefetchHandle = null
+            }
+        }
     }
 
     companion object {
@@ -403,38 +563,23 @@
 
 private const val MinPageOffset = -0.5f
 private const val MaxPageOffset = 0.5f
-internal val SnapAlignmentStartToStart: Density.(layoutSize: Float, itemSize: Float) -> Float =
-    { _, _ -> 0f }
 internal val DefaultPositionThreshold = 56.dp
 private const val MaxPagesForAnimateScroll = 3
 
-private class AwaitLazyListStateSet {
-    private var continuation: Continuation<Unit>? = null
-    private var stateWasLoaded = false
-
-    suspend fun waitFinalLazyListSetting() {
-        if (!stateWasLoaded) {
-            val previousContinuation = continuation
-            suspendCoroutine<Unit> { continuation = it }
-            previousContinuation?.resume(Unit)
-        }
-    }
-
-    fun onStateLoaded() {
-        if (!stateWasLoaded) {
-            stateWasLoaded = true
-            continuation?.resume(Unit)
-            continuation = null
-        }
-    }
-}
-
-private val EmptyLayoutInfo = object : LazyListLayoutInfo {
-    override val visibleItemsInfo: List<LazyListItemInfo> = emptyList()
+@OptIn(ExperimentalFoundationApi::class)
+internal val EmptyLayoutInfo = object : PagerLayoutInfo {
+    override val visiblePagesInfo: List<PageInfo> = emptyList()
+    override val closestPageToSnapPosition: PageInfo? = null
+    override val pagesCount: Int = 0
+    override val pageSize: Int = 0
+    override val pageSpacing: Int = 0
+    override val beforeContentPadding: Int = 0
+    override val afterContentPadding: Int = 0
+    override val viewportSize: IntSize = IntSize.Zero
+    override val orientation: Orientation = Orientation.Horizontal
     override val viewportStartOffset: Int = 0
     override val viewportEndOffset: Int = 0
-    override val totalItemsCount: Int = 0
-    override val mainAxisItemSpacing: Int = 0
+    override val reverseLayout: Boolean = false
 }
 
 private val UnitDensity = object : Density {
@@ -442,10 +587,8 @@
     override val fontScale: Float = 1f
 }
 
-private val EmptyInteractionSources = object : InteractionSource {
-    override val interactions: Flow<Interaction>
-        get() = emptyFlow()
-}
+internal val SnapAlignmentStartToStart: Density.(layoutSize: Float, itemSize: Float) -> Float =
+    { _, _ -> 0f }
 
 private const val DEBUG = false
 private inline fun debugLog(generateMsg: () -> String) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PositionedPage.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PositionedPage.kt
new file mode 100644
index 0000000..57ce77f
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PositionedPage.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.pager
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.unit.IntOffset
+
+@OptIn(ExperimentalFoundationApi::class)
+internal class PositionedPage(
+    override val index: Int,
+    override val offset: Int,
+    val key: Any,
+    val orientation: Orientation,
+    val wrappers: MutableList<PagerPlaceableWrapper>,
+    val visualOffset: IntOffset
+) : PageInfo {
+    fun place(scope: Placeable.PlacementScope) = with(scope) {
+        repeat(wrappers.size) { index ->
+            val placeable = wrappers[index].placeable
+            val offset = wrappers[index].offset
+            if (orientation == Orientation.Vertical) {
+                placeable.placeWithLayer(offset + visualOffset)
+            } else {
+                placeable.placeRelativeWithLayer(offset + visualOffset)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/AnnotatedStringResolveInlineContent.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/AnnotatedStringResolveInlineContent.kt
new file mode 100644
index 0000000..71c3b75
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/AnnotatedStringResolveInlineContent.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.text
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.Placeholder
+import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastMap
+
+/**
+ * Attempts to match AnnotatedString placeholders with passed [InlineTextContent]
+ *
+ * Matches will produce a entry in both returned lists.
+ *
+ * Non-matches will be ignored silently.
+ */
+internal fun AnnotatedString.resolveInlineContent(
+    inlineContent: Map<String, InlineTextContent>?
+): Pair<List<PlaceholderRange>, List<InlineContentRange>> {
+    if (inlineContent.isNullOrEmpty()) {
+        return EmptyInlineContent
+    }
+    val inlineContentAnnotations = getStringAnnotations(INLINE_CONTENT_TAG, 0, text.length)
+
+    val placeholders = mutableListOf<AnnotatedString.Range<Placeholder>>()
+    val inlineComposables = mutableListOf<AnnotatedString.Range<@Composable (String) -> Unit>>()
+    inlineContentAnnotations.fastForEach { annotation ->
+        inlineContent[annotation.item]?.let { inlineTextContent ->
+            placeholders.add(
+                AnnotatedString.Range(
+                    inlineTextContent.placeholder,
+                    annotation.start,
+                    annotation.end
+                )
+            )
+            inlineComposables.add(
+                AnnotatedString.Range(
+                    inlineTextContent.children,
+                    annotation.start,
+                    annotation.end
+                )
+            )
+        }
+    }
+    return Pair(placeholders, inlineComposables)
+}
+
+internal fun AnnotatedString.hasInlineContent(): Boolean =
+    hasStringAnnotations(INLINE_CONTENT_TAG, 0, text.length)
+
+@Composable
+internal fun InlineChildren(
+    text: AnnotatedString,
+    inlineContents: List<InlineContentRange>
+) {
+    inlineContents.fastForEach { (content, start, end) ->
+        Layout(
+            content = { content(text.subSequence(start, end).text) }
+        ) { children, constrains ->
+            val placeables = children.fastMap { it.measure(constrains) }
+            layout(width = constrains.maxWidth, height = constrains.maxHeight) {
+                placeables.fastForEach { it.placeRelative(0, 0) }
+            }
+        }
+    }
+}
+
+private val EmptyInlineContent: Pair<List<PlaceholderRange>, List<InlineContentRange>> =
+    Pair(emptyList(), emptyList())
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicText.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicText.kt
index 64c39235..070b437 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicText.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicText.kt
@@ -22,9 +22,12 @@
 import androidx.compose.foundation.text.selection.hasSelection
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.currentComposer
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.Saver
 import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.platform.LocalDensity
@@ -62,12 +65,26 @@
     text: String,
     modifier: Modifier = Modifier,
     style: TextStyle = TextStyle.Default,
-    onTextLayout: (TextLayoutResult) -> Unit = {},
+    onTextLayout: ((TextLayoutResult) -> Unit)? = null,
     overflow: TextOverflow = TextOverflow.Clip,
     softWrap: Boolean = true,
     maxLines: Int = Int.MAX_VALUE,
     minLines: Int = 1
 ) {
+    @Suppress("DEPRECATION")
+    if (NewTextRendering1_5) {
+        TextUsingModifier(
+            text = text,
+            modifier = modifier,
+            style = style,
+            onTextLayout = onTextLayout,
+            overflow = overflow,
+            softWrap = softWrap,
+            maxLines = maxLines,
+            minLines = minLines
+        )
+        return
+    }
     // NOTE(text-perf-review): consider precomputing layout here by pushing text to a channel...
     // something like:
     // remember(text) { precomputeTextLayout(text) }
@@ -133,7 +150,7 @@
             )
         )
     }
-    state.onTextLayout = onTextLayout
+    state.onTextLayout = onTextLayout ?: {}
     controller.update(selectionRegistrar)
     if (selectionRegistrar != null) {
         state.selectionBackgroundColor = LocalTextSelectionColors.current.backgroundColor
@@ -177,13 +194,28 @@
     text: AnnotatedString,
     modifier: Modifier = Modifier,
     style: TextStyle = TextStyle.Default,
-    onTextLayout: (TextLayoutResult) -> Unit = {},
+    onTextLayout: ((TextLayoutResult) -> Unit)? = null,
     overflow: TextOverflow = TextOverflow.Clip,
     softWrap: Boolean = true,
     maxLines: Int = Int.MAX_VALUE,
     minLines: Int = 1,
     inlineContent: Map<String, InlineTextContent> = mapOf()
 ) {
+    @Suppress("DEPRECATION")
+    if (NewTextRendering1_5) {
+        TextUsingModifier(
+            text = text,
+            modifier = modifier,
+            style = style,
+            onTextLayout = onTextLayout,
+            overflow = overflow,
+            softWrap = softWrap,
+            maxLines = maxLines,
+            minLines = minLines,
+            inlineContent = inlineContent
+        )
+        return
+    }
     // Unlike text field for which validation happens inside the 'heightInLines' modifier, in text
     // 'maxLines' are not handled by the modifier but instead passed to the StaticLayout, therefore
     // we perform validation here
@@ -250,7 +282,7 @@
             )
         )
     }
-    state.onTextLayout = onTextLayout
+    state.onTextLayout = onTextLayout ?: {}
     state.selectionBackgroundColor = selectionBackgroundColor
 
     controller.update(selectionRegistrar)
@@ -274,7 +306,7 @@
     text: String,
     modifier: Modifier = Modifier,
     style: TextStyle = TextStyle.Default,
-    onTextLayout: (TextLayoutResult) -> Unit = {},
+    onTextLayout: ((TextLayoutResult) -> Unit)? = null,
     overflow: TextOverflow = TextOverflow.Clip,
     softWrap: Boolean = true,
     maxLines: Int = Int.MAX_VALUE
@@ -297,7 +329,7 @@
     text: AnnotatedString,
     modifier: Modifier = Modifier,
     style: TextStyle = TextStyle.Default,
-    onTextLayout: (TextLayoutResult) -> Unit = {},
+    onTextLayout: ((TextLayoutResult) -> Unit)? = null,
     overflow: TextOverflow = TextOverflow.Clip,
     softWrap: Boolean = true,
     maxLines: Int = Int.MAX_VALUE,
@@ -317,6 +349,20 @@
 }
 
 /**
+ * Optionally use legacy text rendering stack from 1.4.
+ *
+ * This flag will be removed by 1.5 beta01. If you find any issues with the new stack, flip this
+ * flag to false to confirm they are newly introduced then file a bug.
+ */
+@Deprecated(
+    message = "This flag will be removed by 1.5 beta1 and should only be used for debugging " +
+        "text related issues in the new 1.5 text stack.",
+    replaceWith = ReplaceWith(""),
+    level = DeprecationLevel.WARNING
+)
+var NewTextRendering1_5: Boolean by mutableStateOf(true)
+
+/**
  * A custom saver that won't save if no selection is active.
  */
 private fun selectionIdSaver(selectionRegistrar: SelectionRegistrar?) = Saver<Long, Long>(
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
index cd39f68..5e1150a 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreText.kt
@@ -45,7 +45,6 @@
 import androidx.compose.ui.layout.IntrinsicMeasurable
 import androidx.compose.ui.layout.IntrinsicMeasureScope
 import androidx.compose.ui.layout.LastBaseline
-import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasurePolicy
@@ -66,29 +65,11 @@
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.util.fastForEach
-import androidx.compose.ui.util.fastMap
 import kotlin.math.floor
 import kotlin.math.roundToInt
 
-private typealias PlaceholderRange = AnnotatedString.Range<Placeholder>
-private typealias InlineContentRange = AnnotatedString.Range<@Composable (String) -> Unit>
-
-@Composable
-internal fun InlineChildren(
-    text: AnnotatedString,
-    inlineContents: List<InlineContentRange>
-) {
-    inlineContents.fastForEach { (content, start, end) ->
-        Layout(
-            content = { content(text.subSequence(start, end).text) }
-        ) { children, constrains ->
-            val placeables = children.fastMap { it.measure(constrains) }
-            layout(width = constrains.maxWidth, height = constrains.maxHeight) {
-                placeables.fastForEach { it.placeRelative(0, 0) }
-            }
-        }
-    }
-}
+internal typealias PlaceholderRange = AnnotatedString.Range<Placeholder>
+internal typealias InlineContentRange = AnnotatedString.Range<@Composable (String) -> Unit>
 
 // NOTE(text-perf-review): consider merging this with TextDelegate?
 @OptIn(InternalFoundationTextApi::class)
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 e5427cd..49de4bc 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
@@ -82,11 +82,12 @@
 import androidx.compose.ui.semantics.editableText
 import androidx.compose.ui.semantics.getTextLayoutResult
 import androidx.compose.ui.semantics.imeAction
-import androidx.compose.ui.semantics.performImeAction
+import androidx.compose.ui.semantics.insertTextAtCursor
 import androidx.compose.ui.semantics.onClick
 import androidx.compose.ui.semantics.onLongClick
 import androidx.compose.ui.semantics.password
 import androidx.compose.ui.semantics.pasteText
+import androidx.compose.ui.semantics.performImeAction
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.semantics.setSelection
 import androidx.compose.ui.semantics.setText
@@ -99,6 +100,7 @@
 import androidx.compose.ui.text.input.CommitTextCommand
 import androidx.compose.ui.text.input.DeleteAllCommand
 import androidx.compose.ui.text.input.EditProcessor
+import androidx.compose.ui.text.input.FinishComposingTextCommand
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.ImeOptions
 import androidx.compose.ui.text.input.OffsetMapping
@@ -438,6 +440,28 @@
             }
             true
         }
+        insertTextAtCursor { 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(
+                    // 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)
+                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
             // selection in original text. In non-traversal mode selection comes from the Talkback
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextUsingModifier.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextUsingModifier.kt
new file mode 100644
index 0000000..a282a7d
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextUsingModifier.kt
@@ -0,0 +1,281 @@
+/*
+ * 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.text
+
+import androidx.compose.foundation.fastMapIndexedNotNull
+import androidx.compose.foundation.text.modifiers.SelectableTextAnnotatedStringElement
+import androidx.compose.foundation.text.modifiers.SelectionController
+import androidx.compose.foundation.text.modifiers.TextAnnotatedStringElement
+import androidx.compose.foundation.text.modifiers.TextStringSimpleElement
+import androidx.compose.foundation.text.selection.LocalSelectionRegistrar
+import androidx.compose.foundation.text.selection.LocalTextSelectionColors
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.graphics.graphicsLayer
+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.platform.LocalFontFamilyResolver
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.Placeholder
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.util.fastForEach
+import kotlin.math.floor
+import kotlin.math.roundToInt
+
+/**
+ * Implementation of [BasicText] using the new Modifier system.
+ *
+ * All features are the same.
+ */
+@Composable
+internal fun TextUsingModifier(
+    text: String,
+    modifier: Modifier = Modifier,
+    style: TextStyle = TextStyle.Default,
+    onTextLayout: ((TextLayoutResult) -> Unit)? = null,
+    overflow: TextOverflow = TextOverflow.Clip,
+    softWrap: Boolean = true,
+    maxLines: Int = Int.MAX_VALUE,
+    minLines: Int = 1,
+) {
+    validateMinMaxLines(minLines, maxLines)
+    val selectionRegistrar = LocalSelectionRegistrar.current
+    val selectionController = if (selectionRegistrar != null) {
+        val backgroundSelectionColor = LocalTextSelectionColors.current.backgroundColor
+        remember(selectionRegistrar, backgroundSelectionColor) {
+            SelectionController(
+                selectionRegistrar,
+                backgroundSelectionColor
+            )
+        }
+    } else {
+        null
+    }
+    val finalModifier = if (selectionController != null || onTextLayout != null) {
+        modifier
+            // TODO(b/274781644): Remove this graphicsLayer
+            .graphicsLayer()
+            .textModifier(
+                AnnotatedString(text),
+                style = style,
+                onTextLayout = onTextLayout,
+                overflow = overflow,
+                softWrap = softWrap,
+                maxLines = maxLines,
+                minLines = minLines,
+                fontFamilyResolver = LocalFontFamilyResolver.current,
+                placeholders = null,
+                onPlaceholderLayout = null,
+                selectionController = selectionController
+            )
+    } else {
+        modifier
+            // TODO(b/274781644): Remove this graphicsLayer
+            .graphicsLayer() then TextStringSimpleElement(
+            text,
+            style,
+            LocalFontFamilyResolver.current,
+            overflow,
+            softWrap,
+            maxLines,
+            minLines
+        )
+    }
+    Layout(finalModifier, EmptyMeasurePolicy)
+}
+
+/**
+ * Implementation of [BasicText] using the new Modifier system.
+ *
+ * All features are the same.
+ */
+@Composable
+internal fun TextUsingModifier(
+    text: AnnotatedString,
+    modifier: Modifier = Modifier,
+    style: TextStyle = TextStyle.Default,
+    onTextLayout: ((TextLayoutResult) -> Unit)? = null,
+    overflow: TextOverflow = TextOverflow.Clip,
+    softWrap: Boolean = true,
+    maxLines: Int = Int.MAX_VALUE,
+    minLines: Int = 1,
+    inlineContent: Map<String, InlineTextContent>? = null,
+) {
+    validateMinMaxLines(minLines, maxLines)
+    val selectionRegistrar = LocalSelectionRegistrar.current
+    val selectionController = if (selectionRegistrar != null) {
+        val backgroundSelectionColor = LocalTextSelectionColors.current.backgroundColor
+        remember(selectionRegistrar, backgroundSelectionColor) {
+            SelectionController(
+                selectionRegistrar,
+                backgroundSelectionColor
+            )
+        }
+    } else {
+        null
+    }
+
+    if (!text.hasInlineContent()) {
+        // this is the same as text: String, use all the early exits
+        Layout(
+            modifier = modifier
+                // TODO(b/274781644): Remove this graphicsLayer
+                .graphicsLayer()
+                .textModifier(
+                text = text,
+                style = style,
+                onTextLayout = onTextLayout,
+                overflow = overflow,
+                softWrap = softWrap,
+                maxLines = maxLines,
+                minLines = minLines,
+                fontFamilyResolver = LocalFontFamilyResolver.current,
+                placeholders = null,
+                onPlaceholderLayout = null,
+                selectionController = selectionController
+            ),
+            EmptyMeasurePolicy
+        )
+    } else {
+        // do the inline content allocs
+        val (placeholders, inlineComposables) = text.resolveInlineContent(inlineContent)
+        val measuredPlaceholderPositions = remember {
+            mutableStateOf<List<Rect?>?>(null)
+        }
+        Layout(
+            content = { InlineChildren(text, inlineComposables) },
+            modifier = modifier
+                // TODO(b/274781644): Remove this graphicsLayer
+                .graphicsLayer()
+                .textModifier(
+                text = text,
+                style = style,
+                onTextLayout = onTextLayout,
+                overflow = overflow,
+                softWrap = softWrap,
+                maxLines = maxLines,
+                minLines = minLines,
+                fontFamilyResolver = LocalFontFamilyResolver.current,
+                placeholders = placeholders,
+                onPlaceholderLayout = { measuredPlaceholderPositions.value = it },
+                selectionController = selectionController
+            ),
+            measurePolicy = TextMeasurePolicy { measuredPlaceholderPositions.value }
+        )
+    }
+}
+
+private object EmptyMeasurePolicy : MeasurePolicy {
+    private val placementBlock: Placeable.PlacementScope.() -> Unit = {}
+    override fun MeasureScope.measure(
+        measurables: List<Measurable>,
+        constraints: Constraints
+    ): MeasureResult {
+        return layout(constraints.maxWidth, constraints.maxHeight, placementBlock = placementBlock)
+    }
+}
+
+private class TextMeasurePolicy(
+    private val placements: () -> List<Rect?>?
+) : MeasurePolicy {
+    override fun MeasureScope.measure(
+        measurables: List<Measurable>,
+        constraints: Constraints
+    ): MeasureResult {
+        val toPlace = placements()?.fastMapIndexedNotNull { index, rect ->
+            // PlaceholderRect will be null if it's ellipsized. In that case, the corresponding
+            // inline children won't be measured or placed.
+            rect?.let {
+                Pair(
+                    measurables[index].measure(
+                        Constraints(
+                            maxWidth = floor(it.width).toInt(),
+                            maxHeight = floor(it.height).toInt()
+                        )
+                    ),
+                    IntOffset(it.left.roundToInt(), it.top.roundToInt())
+                )
+            }
+        }
+        return layout(
+            constraints.maxWidth,
+            constraints.maxHeight,
+        ) {
+            toPlace?.fastForEach { (placeable, position) ->
+                placeable.place(position)
+            }
+        }
+    }
+}
+
+private fun Modifier.textModifier(
+    text: AnnotatedString,
+    style: TextStyle,
+    onTextLayout: ((TextLayoutResult) -> Unit)?,
+    overflow: TextOverflow,
+    softWrap: Boolean,
+    maxLines: Int,
+    minLines: Int,
+    fontFamilyResolver: FontFamily.Resolver,
+    placeholders: List<AnnotatedString.Range<Placeholder>>?,
+    onPlaceholderLayout: ((List<Rect?>) -> Unit)?,
+    selectionController: SelectionController?
+): Modifier {
+    if (selectionController == null) {
+        val staticTextModifier = TextAnnotatedStringElement(
+            text,
+            style,
+            fontFamilyResolver,
+            onTextLayout,
+            overflow,
+            softWrap,
+            maxLines,
+            minLines,
+            placeholders,
+            onPlaceholderLayout,
+            null
+        )
+        return this then Modifier /* selection position */ then staticTextModifier
+    } else {
+        val selectableTextModifier = SelectableTextAnnotatedStringElement(
+            text,
+            style,
+            fontFamilyResolver,
+            onTextLayout,
+            overflow,
+            softWrap,
+            maxLines,
+            minLines,
+            placeholders,
+            onPlaceholderLayout,
+            selectionController
+        )
+        return this then selectionController.modifier then selectableTextModifier
+    }
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/LayoutUtils.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/LayoutUtils.kt
new file mode 100644
index 0000000..bb111a3
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/LayoutUtils.kt
@@ -0,0 +1,127 @@
+/*
+ * 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.modifiers
+
+import androidx.compose.foundation.text.ceilToIntPx
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Constraints
+
+/**
+ * Find the constraints to pass to Paragraph based on all the parameters.
+ */
+internal fun finalConstraints(
+    constraints: Constraints,
+    softWrap: Boolean,
+    overflow: TextOverflow,
+    maxIntrinsicWidth: Float
+): Constraints = Constraints(
+        maxWidth = finalMaxWidth(constraints, softWrap, overflow, maxIntrinsicWidth),
+        maxHeight = constraints.maxHeight
+    )
+
+/**
+ * Find the final max width a Paragraph would use based on all parameters.
+ */
+internal fun finalMaxWidth(
+    constraints: Constraints,
+    softWrap: Boolean,
+    overflow: TextOverflow,
+    maxIntrinsicWidth: Float
+): Int {
+    val widthMatters = softWrap || overflow == TextOverflow.Ellipsis
+    val maxWidth = if (widthMatters && constraints.hasBoundedWidth) {
+        constraints.maxWidth
+    } else {
+        Constraints.Infinity
+    }
+
+    // if minWidth == maxWidth the width is fixed.
+    //    therefore we can pass that value to our paragraph and use it
+    // if minWidth != maxWidth there is a range
+    //    then we should check if the max intrinsic width is in this range to decide the
+    //    width to be passed to Paragraph
+    //        if max intrinsic width is between minWidth and maxWidth
+    //           we can use it to layout
+    //        else if max intrinsic width is greater than maxWidth, we can only use maxWidth
+    //        else if max intrinsic width is less than minWidth, we should use minWidth
+    return if (constraints.minWidth == maxWidth) {
+        maxWidth
+    } else {
+        maxIntrinsicWidth.ceilToIntPx().coerceIn(constraints.minWidth, maxWidth)
+    }
+}
+
+/**
+ * Find the maxLines to pass to text layout based on all parameters
+ */
+internal fun finalMaxLines(softWrap: Boolean, overflow: TextOverflow, maxLinesIn: Int): Int {
+    // This is a fallback behavior because native text layout doesn't support multiple
+    // ellipsis in one text layout.
+    // When softWrap is turned off and overflow is ellipsis, it's expected that each line
+    // that exceeds maxWidth will be ellipsized.
+    // For example,
+    // input text:
+    //     "AAAA\nAAAA"
+    // maxWidth:
+    //     3 * fontSize that only allow 3 characters to be displayed each line.
+    // expected output:
+    //     AA…
+    //     AA…
+    // Here we assume there won't be any '\n' character when softWrap is false. And make
+    // maxLines 1 to implement the similar behavior.
+    val overwriteMaxLines = !softWrap && overflow == TextOverflow.Ellipsis
+    return if (overwriteMaxLines) 1 else maxLinesIn.coerceAtLeast(1)
+}
+
+/**
+ * Assuming we're laying out the same text in two different constraints, see if breaks could change
+ *
+ * If text or other text-layout attributes change, this method will not return accurate results.
+ */
+internal fun canChangeBreaks(
+    canWrap: Boolean,
+    newConstraints: Constraints,
+    oldConstraints: Constraints,
+    maxIntrinsicWidth: Float,
+    softWrap: Boolean,
+    overflow: TextOverflow,
+): Boolean {
+    // no breaks
+    if (!canWrap) return false
+    // we can assume maxIntrinsicWidth is the same, or other invalidate would have happened
+    // earlier (resetting para, etc)
+    val prevMaxWidth = finalMaxWidth(oldConstraints, softWrap, overflow, maxIntrinsicWidth)
+    val newMaxWidth = finalMaxWidth(newConstraints, softWrap, overflow, maxIntrinsicWidth)
+
+    if (prevMaxWidth != newMaxWidth) {
+        if (prevMaxWidth >= maxIntrinsicWidth && newMaxWidth >= maxIntrinsicWidth) {
+            // nothing can change, layout >= text width.
+            return false
+        }
+
+        return if (prevMaxWidth < newMaxWidth) {
+            // we're growing, so return if we could have broken last time
+            prevMaxWidth < maxIntrinsicWidth
+        } else {
+            // we're shrinking, so return if we could have broken this time
+            newMaxWidth < maxIntrinsicWidth
+        }
+    }
+
+    // widths haven't changed, layouts will be same
+    return false
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/MinLinesConstrainer.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/MinLinesConstrainer.kt
new file mode 100644
index 0000000..02d1f32
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/MinLinesConstrainer.kt
@@ -0,0 +1,140 @@
+/*
+ * 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.modifiers
+
+import androidx.compose.ui.text.Paragraph
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.resolveDefaults
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
+import kotlin.math.roundToInt
+
+/**
+ * Coerce min and max lines into actual constraints.
+ *
+ * Results are cached with the assumption that there is typically N=1 style being coerced at once.
+ */
+internal class MinLinesConstrainer private constructor(
+    val layoutDirection: LayoutDirection,
+    val inputTextStyle: TextStyle,
+    val density: Density,
+    val fontFamilyResolver: FontFamily.Resolver
+) {
+    private val resolvedStyle = resolveDefaults(inputTextStyle, layoutDirection)
+    private var lineHeightCache: Float = Float.NaN
+    private var oneLineHeightCache: Float = Float.NaN
+
+    companion object {
+        // LRU cache of one since this tends to be used for similar styles
+        // ... it may be useful to increase this cache if requested by some dev use case
+        private var last: MinLinesConstrainer? = null
+
+        /**
+         * Returns a coercer (possibly cached) with these parameters
+         */
+        fun from(
+            minMaxUtil: MinLinesConstrainer?,
+            layoutDirection: LayoutDirection,
+            paramStyle: TextStyle,
+            density: Density,
+            fontFamilyResolver: FontFamily.Resolver
+        ): MinLinesConstrainer {
+            minMaxUtil?.let {
+                if (layoutDirection == it.layoutDirection &&
+                    paramStyle == it.inputTextStyle &&
+                    density.density == it.density.density &&
+                    fontFamilyResolver === it.fontFamilyResolver) {
+                    return it
+                }
+            }
+            last?.let {
+                if (layoutDirection == it.layoutDirection &&
+                    paramStyle == it.inputTextStyle &&
+                    density.density == it.density.density &&
+                    fontFamilyResolver === it.fontFamilyResolver) {
+                    return it
+                }
+            }
+            return MinLinesConstrainer(
+                layoutDirection,
+                resolveDefaults(paramStyle, layoutDirection),
+                density,
+                fontFamilyResolver
+            ).also {
+                last = it
+            }
+        }
+    }
+
+    /**
+     * Coerce inConstraints to have min and max lines applied.
+     *
+     * On first invocation this will cause (2) Paragraph measurements.
+     */
+    internal fun coerceMinLines(
+        inConstraints: Constraints,
+        minLines: Int
+    ): Constraints {
+        var oneLineHeight = oneLineHeightCache
+        var lineHeight = lineHeightCache
+        if (oneLineHeight.isNaN() || lineHeight.isNaN()) {
+            oneLineHeight = Paragraph(
+                text = EmptyTextReplacement,
+                style = resolvedStyle,
+                constraints = Constraints(),
+                density = density,
+                fontFamilyResolver = fontFamilyResolver,
+                maxLines = 1,
+                ellipsis = false
+            ).height
+
+            val twoLineHeight = Paragraph(
+                text = TwoLineTextReplacement,
+                style = resolvedStyle,
+                constraints = Constraints(),
+                density = density,
+                fontFamilyResolver = fontFamilyResolver,
+                maxLines = 2,
+                ellipsis = false
+            ).height
+
+            lineHeight = twoLineHeight - oneLineHeight
+            oneLineHeightCache = oneLineHeight
+            lineHeightCache = lineHeight
+        }
+        val minHeight = if (minLines != 1) {
+            (oneLineHeight + (lineHeight * (minLines - 1)))
+                .roundToInt()
+                .coerceAtLeast(0)
+                .coerceAtMost(inConstraints.maxHeight)
+        } else {
+            inConstraints.minHeight
+        }
+        return Constraints(
+            minHeight = minHeight,
+            maxHeight = inConstraints.maxHeight,
+            minWidth = inConstraints.minWidth,
+            maxWidth = inConstraints.maxWidth,
+        )
+    }
+}
+
+private const val DefaultWidthCharCount = 10 // min width for TextField is 10 chars long
+private val EmptyTextReplacement = "H".repeat(DefaultWidthCharCount) // just a reference character.
+private val TwoLineTextReplacement = EmptyTextReplacement + "\n" + EmptyTextReplacement
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/ModifierUtils.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/ModifierUtils.kt
new file mode 100644
index 0000000..050ff6d
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/ModifierUtils.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.text.modifiers
+
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Constraints
+
+internal fun Constraints.maxWidthForTextLayout(
+    softWrap: Boolean,
+    overflow: TextOverflow
+): Int {
+    val widthMatters = softWrap || overflow == TextOverflow.Ellipsis
+    val maxWidth = if (widthMatters && hasBoundedWidth) {
+        maxWidth
+    } else {
+        Constraints.Infinity
+    }
+    return maxWidth
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/MultiParagraphLayoutCache.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/MultiParagraphLayoutCache.kt
new file mode 100644
index 0000000..b2d6ffe
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/MultiParagraphLayoutCache.kt
@@ -0,0 +1,357 @@
+/*
+ * 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.modifiers
+
+import androidx.compose.foundation.text.DefaultMinLines
+import androidx.compose.foundation.text.ceilToIntPx
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.MultiParagraph
+import androidx.compose.ui.text.MultiParagraphIntrinsics
+import androidx.compose.ui.text.Placeholder
+import androidx.compose.ui.text.TextLayoutInput
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.resolveDefaults
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.constrain
+
+/**
+ * Performs text layout using [MultiParagraph].
+ *
+ * Results are cached whenever possible, for example when only constraints change in a way that
+ * cannot reflow text.
+ *
+ * All measurements are cached.
+ */
+internal class MultiParagraphLayoutCache(
+    private var text: AnnotatedString,
+    private var style: TextStyle,
+    private var fontFamilyResolver: FontFamily.Resolver,
+    private var overflow: TextOverflow = TextOverflow.Clip,
+    private var softWrap: Boolean = true,
+    private var maxLines: Int = Int.MAX_VALUE,
+    private var minLines: Int = DefaultMinLines,
+    private var placeholders: List<AnnotatedString.Range<Placeholder>>? = null,
+) {
+    /**
+     * Convert min max lines into actual constraints
+     */
+    private var mMinLinesConstrainer: MinLinesConstrainer? = null
+
+    /**
+     * Density that text layout is performed in
+     */
+    internal var density: Density? = null
+        set(value) {
+            val localField = field
+            if (localField == null) {
+                field = value
+                return
+            }
+
+            if (value == null) {
+                field = value
+                markDirty()
+                return
+            }
+
+            if (localField.density != value.density || localField.fontScale != value.fontScale) {
+                field = value
+                // none of our results are correct if density changed
+                markDirty()
+            }
+        }
+
+    /**
+     * [MultiParagraphIntrinsics] will be initialized lazily
+     */
+    private var paragraphIntrinsics: MultiParagraphIntrinsics? = null
+
+    /**
+     * [LayoutDirection] used to compute [MultiParagraphIntrinsics]
+     */
+    private var intrinsicsLayoutDirection: LayoutDirection? = null
+
+    /**
+     * Cached value of final [TextLayoutResult]
+     */
+    private var layoutCache: TextLayoutResult? = null
+
+    /**
+     * Input width for the last call to [intrinsicHeight]
+     */
+    private var cachedIntrinsicHeightInputWidth: Int = -1
+
+    /**
+     * Output height for last call to [intrinsicHeight] at [cachedIntrinsicHeightInputWidth]
+     */
+    private var cachedIntrinsicHeight: Int = -1
+
+    /**
+     * The last computed TextLayoutResult, or throws if not initialized.
+     */
+    val textLayoutResult: TextLayoutResult
+        get() = layoutCache
+            ?: throw IllegalStateException("You must call layoutWithConstraints first")
+
+    /**
+     * The last computed TextLayoutResult, or null if not initialized.
+     */
+    val layoutOrNull: TextLayoutResult?
+        get() = layoutCache
+
+    /**
+     * Update layout constraints for this text
+     *
+     * @return true if constraints caused a text layout invalidation
+     */
+    fun layoutWithConstraints(
+        constraints: Constraints,
+        layoutDirection: LayoutDirection
+    ): Boolean {
+        val finalConstraints = if (minLines > 1) {
+            val localMin = MinLinesConstrainer.from(
+                mMinLinesConstrainer,
+                layoutDirection,
+                style,
+                density!!,
+                fontFamilyResolver
+            ).also {
+                mMinLinesConstrainer = it
+            }
+            localMin.coerceMinLines(
+                inConstraints = constraints,
+                minLines = minLines
+            )
+        } else {
+            constraints
+        }
+        if (!layoutCache.newLayoutWillBeDifferent(finalConstraints, layoutDirection)) {
+            if (finalConstraints == layoutCache!!.layoutInput.constraints) return false
+            // we need to regen the input, constraints aren't the same
+            layoutCache = textLayoutResult(
+                layoutDirection = layoutDirection,
+                finalConstraints = finalConstraints,
+                multiParagraph = layoutCache!!.multiParagraph
+            )
+            return true
+        }
+        val multiParagraph = layoutText(finalConstraints, layoutDirection)
+
+        layoutCache = textLayoutResult(layoutDirection, finalConstraints, multiParagraph)
+        return true
+    }
+
+    private fun textLayoutResult(
+        layoutDirection: LayoutDirection,
+        finalConstraints: Constraints,
+        multiParagraph: MultiParagraph
+    ): TextLayoutResult {
+        return TextLayoutResult(
+            TextLayoutInput(
+                text,
+                style,
+                placeholders.orEmpty(),
+                maxLines,
+                softWrap,
+                overflow,
+                density!!,
+                layoutDirection,
+                fontFamilyResolver,
+                finalConstraints
+            ),
+            multiParagraph,
+            finalConstraints.constrain(
+                IntSize(
+                    multiParagraph.width.ceilToIntPx(),
+                    multiParagraph.height.ceilToIntPx()
+                )
+            )
+        )
+    }
+
+    /**
+     * The natural height of text at [width] in [layoutDirection]
+     */
+    fun intrinsicHeight(width: Int, layoutDirection: LayoutDirection): Int {
+        val localWidth = cachedIntrinsicHeightInputWidth
+        val localHeght = cachedIntrinsicHeight
+        if (width == localWidth && localWidth != -1) return localHeght
+        val result = layoutText(
+            Constraints(0, width, 0, Constraints.Infinity),
+            layoutDirection
+        ).height.ceilToIntPx()
+
+        cachedIntrinsicHeightInputWidth = width
+        cachedIntrinsicHeight = result
+        return result
+    }
+
+    /**
+     * Call when any parameters change, invalidation is a result of calling this method.
+     */
+    fun update(
+        text: AnnotatedString,
+        style: TextStyle,
+        fontFamilyResolver: FontFamily.Resolver,
+        overflow: TextOverflow,
+        softWrap: Boolean,
+        maxLines: Int,
+        minLines: Int,
+        placeholders: List<AnnotatedString.Range<Placeholder>>?
+    ) {
+        this.text = text
+        this.style = style
+        this.fontFamilyResolver = fontFamilyResolver
+        this.overflow = overflow
+        this.softWrap = softWrap
+        this.maxLines = maxLines
+        this.minLines = minLines
+        this.placeholders = placeholders
+        markDirty()
+    }
+
+    /**
+     * Minimum information required to compute [MultiParagraphIntrinsics].
+     *
+     * After calling paragraphIntrinsics is cached.
+     */
+    private fun setLayoutDirection(layoutDirection: LayoutDirection): MultiParagraphIntrinsics {
+        val localIntrinsics = paragraphIntrinsics
+        val intrinsics = if (
+            localIntrinsics == null ||
+            layoutDirection != intrinsicsLayoutDirection ||
+            localIntrinsics.hasStaleResolvedFonts
+        ) {
+            intrinsicsLayoutDirection = layoutDirection
+            MultiParagraphIntrinsics(
+                annotatedString = text,
+                style = resolveDefaults(style, layoutDirection),
+                density = density!!,
+                fontFamilyResolver = fontFamilyResolver,
+                placeholders = placeholders.orEmpty()
+            )
+        } else {
+            localIntrinsics
+        }
+
+        paragraphIntrinsics = intrinsics
+        return intrinsics
+    }
+
+    /**
+     * Computes the visual position of the glyphs for painting the text.
+     *
+     * The text will layout with a width that's as close to its max intrinsic width as possible
+     * while still being greater than or equal to `minWidth` and less than or equal to `maxWidth`.
+     */
+    private fun layoutText(
+        constraints: Constraints,
+        layoutDirection: LayoutDirection
+    ): MultiParagraph {
+        val localParagraphIntrinsics = setLayoutDirection(layoutDirection)
+
+        return MultiParagraph(
+            intrinsics = localParagraphIntrinsics,
+            constraints = finalConstraints(
+                constraints,
+                softWrap,
+                overflow,
+                localParagraphIntrinsics.maxIntrinsicWidth
+            ),
+            // This is a fallback behavior for ellipsis. Native
+            maxLines = finalMaxLines(softWrap, overflow, maxLines),
+            ellipsis = overflow == TextOverflow.Ellipsis
+        )
+    }
+
+    /**
+     * Attempt to compute if the new layout will be the same for the given constraints and
+     * layoutDirection.
+     */
+    private fun TextLayoutResult?.newLayoutWillBeDifferent(
+        constraints: Constraints,
+        layoutDirection: LayoutDirection
+    ): Boolean {
+        // no layout yet
+        if (this == null) return true
+
+        // async typeface changes
+        if (this.multiParagraph.intrinsics.hasStaleResolvedFonts) return true
+
+        // layout direction changed
+        if (layoutDirection != layoutInput.layoutDirection) return true
+
+        // if we were passed identical constraints just skip more work
+        if (constraints == layoutInput.constraints) return false
+
+        // see if width would produce the same wraps
+        if (canChangeBreaks(
+                canWrap = softWrap && maxLines > 1,
+                newConstraints = constraints,
+                oldConstraints = layoutInput.constraints,
+                maxIntrinsicWidth = this.multiParagraph.intrinsics.maxIntrinsicWidth,
+                softWrap = softWrap,
+                overflow = overflow
+            )) return true
+
+        // if we get here width won't change, height may be clipped
+        if (constraints.maxHeight < multiParagraph.height) {
+            // vertical clip changes
+            return true
+        }
+
+        // breaks can't change, height can't change
+        return false
+    }
+
+    /**
+     * Compute the maxWidth for text layout from [Constraints]
+     *
+     * Falls back to [paragraphIntrinsics.maxIntrinsicWidth] when not exact constraints.
+     */
+    private fun maxWidth(constraints: Constraints): Int = finalMaxWidth(
+            constraints,
+            softWrap,
+            overflow,
+            paragraphIntrinsics!!.maxIntrinsicWidth
+        )
+
+    private fun markDirty() {
+        paragraphIntrinsics = null
+        layoutCache = null
+    }
+
+    /**
+     * The width at which increasing the width of the text no longer decreases the height.
+     */
+    fun maxIntrinsicWidth(layoutDirection: LayoutDirection): Int {
+        return setLayoutDirection(layoutDirection).maxIntrinsicWidth.ceilToIntPx()
+    }
+
+    /**
+     * The width for text if all soft wrap opportunities were taken.
+     */
+    fun minIntrinsicWidth(layoutDirection: LayoutDirection): Int {
+        return setLayoutDirection(layoutDirection).minIntrinsicWidth.ceilToIntPx()
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/ParagraphLayoutCache.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/ParagraphLayoutCache.kt
new file mode 100644
index 0000000..988d0c7
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/ParagraphLayoutCache.kt
@@ -0,0 +1,392 @@
+/*
+ * 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.modifiers
+
+import androidx.compose.foundation.text.DefaultMinLines
+import androidx.compose.foundation.text.ceilToIntPx
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.MultiParagraph
+import androidx.compose.ui.text.MultiParagraphIntrinsics
+import androidx.compose.ui.text.Paragraph
+import androidx.compose.ui.text.ParagraphIntrinsics
+import androidx.compose.ui.text.TextLayoutInput
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.resolveDefaults
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.constrain
+
+/**
+ * Performs text layout using [Paragraph].
+ *
+ * Results are cached whenever possible, for example when only constraints change in a way that
+ * cannot reflow text.
+ *
+ * All measurements are cached.
+ */
+internal class ParagraphLayoutCache(
+    private var text: String,
+    private var style: TextStyle,
+    private var fontFamilyResolver: FontFamily.Resolver,
+    private var overflow: TextOverflow = TextOverflow.Clip,
+    private var softWrap: Boolean = true,
+    private var maxLines: Int = Int.MAX_VALUE,
+    private var minLines: Int = DefaultMinLines,
+) {
+    /**
+     * Density that text layout is performed in
+     */
+    internal var density: Density? = null
+        set(value) {
+            val localField = field
+            if (localField == null) {
+                field = value
+                return
+            }
+
+            if (value == null) {
+                field = value
+                markDirty()
+                return
+            }
+
+            if (localField.density != value.density || localField.fontScale != value.fontScale) {
+                field = value
+                // none of our results are correct if density changed
+                markDirty()
+            }
+        }
+
+    /**
+     * Read to set up a snapshot observer observe changes to fonts.
+     */
+    internal val observeFontChanges: Unit
+        get() {
+            paragraphIntrinsics?.hasStaleResolvedFonts
+        }
+
+    /**
+     * The last computed paragraph
+     */
+    internal var paragraph: Paragraph? = null
+
+    /**
+     * The text did overflow
+     */
+    internal var didOverflow: Boolean = false
+
+    /**
+     * The last computed layout size (as would have been reported in TextLayoutResult)
+     */
+    internal var layoutSize: IntSize = IntSize(0, 0)
+
+    /**
+     * Convert min max lines into actual constraints
+     */
+    private var mMinLinesConstrainer: MinLinesConstrainer? = null
+
+    /**
+     * [ParagraphIntrinsics] will be initialized lazily
+     */
+    private var paragraphIntrinsics: ParagraphIntrinsics? = null
+
+    /**
+     * [LayoutDirection] used to compute [ParagraphIntrinsics]
+     */
+    private var intrinsicsLayoutDirection: LayoutDirection? = null
+
+    /**
+     * Constraints passed to last layout.
+     */
+    private var prevConstraints: Constraints = Constraints.fixed(0, 0)
+
+    /**
+     * Input width for the last call to [intrinsicHeight]
+     */
+    private var cachedIntrinsicHeightInputWidth: Int = -1
+
+    /**
+     * Output height for last call to [intrinsicHeight] at [cachedIntrinsicHeightInputWidth]
+     */
+    private var cachedIntrinsicHeight: Int = -1
+
+    /**
+     * Update layout constraints for this text
+     *
+     * @return true if constraints caused a text layout invalidation
+     */
+    fun layoutWithConstraints(
+        constraints: Constraints,
+        layoutDirection: LayoutDirection
+    ): Boolean {
+        val finalConstraints = if (minLines > 1) {
+            val localMin = MinLinesConstrainer.from(
+                mMinLinesConstrainer,
+                layoutDirection,
+                style,
+                density!!,
+                fontFamilyResolver
+            ).also {
+                mMinLinesConstrainer = it
+            }
+            localMin.coerceMinLines(
+                inConstraints = constraints,
+                minLines = minLines
+            )
+        } else {
+            constraints
+        }
+        if (!newLayoutWillBeDifferent(finalConstraints, layoutDirection)) {
+            if (finalConstraints != prevConstraints) {
+                // ensure size and overflow is still accurate
+                val localParagraph = paragraph!!
+                val localSize = finalConstraints.constrain(
+                    IntSize(
+                        localParagraph.width.ceilToIntPx(),
+                        localParagraph.height.ceilToIntPx()
+                    )
+                )
+                layoutSize = localSize
+                didOverflow = overflow != TextOverflow.Visible &&
+                    (localSize.width < localParagraph.width ||
+                        localSize.height < localParagraph.height)
+            }
+            return false
+        }
+        paragraph = layoutText(finalConstraints, layoutDirection).also {
+            prevConstraints = finalConstraints
+            val localSize = finalConstraints.constrain(
+                IntSize(
+                    it.width.ceilToIntPx(),
+                    it.height.ceilToIntPx()
+                )
+            )
+            layoutSize = localSize
+            didOverflow = overflow != TextOverflow.Visible &&
+                (localSize.width < it.width || localSize.height < it.height)
+        }
+        return true
+    }
+
+    /**
+     * The natural height of text at [width] in [layoutDirection]
+     */
+    fun intrinsicHeight(width: Int, layoutDirection: LayoutDirection): Int {
+        val localWidth = cachedIntrinsicHeightInputWidth
+        val localHeght = cachedIntrinsicHeight
+        if (width == localWidth && localWidth != -1) return localHeght
+        val result = layoutText(
+            Constraints(0, width, 0, Constraints.Infinity),
+            layoutDirection
+        ).height.ceilToIntPx()
+
+        cachedIntrinsicHeightInputWidth = width
+        cachedIntrinsicHeight = result
+        return result
+    }
+
+    /**
+     * Call when any parameters change, invalidation is a result of calling this method.
+     */
+    fun update(
+        text: String,
+        style: TextStyle,
+        fontFamilyResolver: FontFamily.Resolver,
+        overflow: TextOverflow,
+        softWrap: Boolean,
+        maxLines: Int,
+        minLines: Int
+    ) {
+        this.text = text
+        this.style = style
+        this.fontFamilyResolver = fontFamilyResolver
+        this.overflow = overflow
+        this.softWrap = softWrap
+        this.maxLines = maxLines
+        this.minLines = minLines
+        markDirty()
+    }
+
+    /**
+     * Minimum information required to compute [MultiParagraphIntrinsics].
+     *
+     * After calling paragraphIntrinsics is cached.
+     */
+    private fun setLayoutDirection(layoutDirection: LayoutDirection): ParagraphIntrinsics {
+        val localIntrinsics = paragraphIntrinsics
+        val intrinsics = if (
+            localIntrinsics == null ||
+            layoutDirection != intrinsicsLayoutDirection ||
+            localIntrinsics.hasStaleResolvedFonts
+        ) {
+            intrinsicsLayoutDirection = layoutDirection
+            ParagraphIntrinsics(
+                text = text,
+                style = resolveDefaults(style, layoutDirection),
+                density = density!!,
+                fontFamilyResolver = fontFamilyResolver
+            )
+        } else {
+            localIntrinsics
+        }
+        paragraphIntrinsics = intrinsics
+        return intrinsics
+    }
+
+    /**
+     * Computes the visual position of the glyphs for painting the text.
+     *
+     * The text will layout with a width that's as close to its max intrinsic width as possible
+     * while still being greater than or equal to `minWidth` and less than or equal to `maxWidth`.
+     */
+    private fun layoutText(
+        constraints: Constraints,
+        layoutDirection: LayoutDirection
+    ): Paragraph {
+        val localParagraphIntrinsics = setLayoutDirection(layoutDirection)
+
+        return Paragraph(
+            paragraphIntrinsics = localParagraphIntrinsics,
+            constraints = finalConstraints(
+                constraints,
+                softWrap,
+                overflow,
+                localParagraphIntrinsics.maxIntrinsicWidth
+            ),
+            // This is a fallback behavior for ellipsis. Native
+            maxLines = finalMaxLines(softWrap, overflow, maxLines),
+            ellipsis = overflow == TextOverflow.Ellipsis
+        )
+    }
+
+    /**
+     * Attempt to compute if the new layout will be the same for the given constraints and
+     * layoutDirection.
+     */
+    private fun newLayoutWillBeDifferent(
+        constraints: Constraints,
+        layoutDirection: LayoutDirection
+    ): Boolean {
+        // paragarph and paragraphIntrinsics are from previous run
+        val localParagraph = paragraph ?: return true
+        val localParagraphIntrinsics = paragraphIntrinsics ?: return true
+        // no layout yet
+
+        // async typeface changes
+        if (localParagraphIntrinsics.hasStaleResolvedFonts) return true
+
+        // layout direction changed
+        if (layoutDirection != intrinsicsLayoutDirection) return true
+
+        // if we were passed identical constraints just skip more work
+        if (constraints == prevConstraints) return false
+
+        // see if width would produce the same wraps
+        if (canChangeBreaks(
+                canWrap = softWrap && maxLines > 1,
+                newConstraints = constraints,
+                oldConstraints = prevConstraints,
+                maxIntrinsicWidth = localParagraphIntrinsics.maxIntrinsicWidth,
+                softWrap = softWrap,
+                overflow = overflow
+            )) return true
+
+        // if we get here width won't change, height may be clipped
+        if (constraints.maxHeight < localParagraph.height) {
+            // vertical clip changes
+            return true
+        }
+
+        // breaks can't change, height can't change
+        return false
+    }
+
+    private fun markDirty() {
+        paragraph = null
+        paragraphIntrinsics = null
+        intrinsicsLayoutDirection = null
+        cachedIntrinsicHeightInputWidth = -1
+        cachedIntrinsicHeight = -1
+        prevConstraints = Constraints.fixed(0, 0)
+        layoutSize = IntSize(0, 0)
+        didOverflow = false
+    }
+
+    /**
+     * Compute a [TextLayoutResult] for the current Layout values.
+     *
+     * This does an entire Text layout to produce the result, it is slow.
+     *
+     * Exposed for semantics GetTextLayoutResult
+     */
+    fun slowCreateTextLayoutResultOrNull(): TextLayoutResult? {
+        // make sure we're in a valid place
+        val localLayoutDirection = intrinsicsLayoutDirection ?: return null
+        val localDensity = density ?: return null
+        val annotatedString = AnnotatedString(text)
+        paragraph ?: return null
+        paragraphIntrinsics ?: return null
+
+        // and redo layout with MultiParagraph
+        return TextLayoutResult(
+            TextLayoutInput(
+                annotatedString,
+                style,
+                emptyList(),
+                maxLines,
+                softWrap,
+                overflow,
+                localDensity,
+                localLayoutDirection,
+                fontFamilyResolver,
+                prevConstraints
+            ),
+            MultiParagraph(
+                MultiParagraphIntrinsics(
+                    annotatedString = annotatedString,
+                    style = style,
+                    placeholders = emptyList(),
+                    density = localDensity,
+                    fontFamilyResolver = fontFamilyResolver
+                ),
+                prevConstraints,
+                maxLines,
+                overflow == TextOverflow.Ellipsis
+            ),
+            layoutSize
+        )
+    }
+
+    /**
+     * The width for text if all soft wrap opportunities were taken.
+     */
+    fun minIntrinsicWidth(layoutDirection: LayoutDirection): Int {
+        return setLayoutDirection(layoutDirection).minIntrinsicWidth.ceilToIntPx()
+    }
+
+    /**
+     * The width at which increasing the width of the text no lonfger decreases the height.
+     */
+    fun maxIntrinsicWidth(layoutDirection: LayoutDirection): Int {
+        return setLayoutDirection(layoutDirection).maxIntrinsicWidth.ceilToIntPx()
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringElement.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringElement.kt
new file mode 100644
index 0000000..b2293d4
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringElement.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.text.modifiers
+
+import androidx.compose.foundation.text.DefaultMinLines
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.Placeholder
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.style.TextOverflow
+
+/**
+ * Element for any text that is in a selection container.
+ */
+internal data class SelectableTextAnnotatedStringElement(
+    private val text: AnnotatedString,
+    private val style: TextStyle,
+    private val fontFamilyResolver: FontFamily.Resolver,
+    private val onTextLayout: ((TextLayoutResult) -> Unit)? = null,
+    private val overflow: TextOverflow = TextOverflow.Clip,
+    private val softWrap: Boolean = true,
+    private val maxLines: Int = Int.MAX_VALUE,
+    private val minLines: Int = DefaultMinLines,
+    private val placeholders: List<AnnotatedString.Range<Placeholder>>? = null,
+    private val onPlaceholderLayout: ((List<Rect?>) -> Unit)? = null,
+    private val selectionController: SelectionController? = null
+) : ModifierNodeElement<SelectableTextAnnotatedStringNode>() {
+
+    override fun create(): SelectableTextAnnotatedStringNode = SelectableTextAnnotatedStringNode(
+        text,
+        style,
+        fontFamilyResolver,
+        onTextLayout,
+        overflow,
+        softWrap,
+        maxLines,
+        minLines,
+        placeholders,
+        onPlaceholderLayout,
+        selectionController
+    )
+
+    override fun update(
+        node: SelectableTextAnnotatedStringNode
+    ): SelectableTextAnnotatedStringNode {
+        node.update(
+            text = text,
+            style = style,
+            placeholders = placeholders,
+            minLines = minLines,
+            maxLines = maxLines,
+            softWrap = softWrap,
+            fontFamilyResolver = fontFamilyResolver,
+            overflow = overflow,
+            onTextLayout = onTextLayout,
+            onPlaceholderLayout = onPlaceholderLayout,
+            selectionController = selectionController
+        )
+        return node
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+
+        if (other !is SelectableTextAnnotatedStringElement) return false
+
+        // these three are most likely to actually change
+        if (text != other.text) return false
+        if (style != other.style) return false
+        if (placeholders != other.placeholders) return false
+
+        // these are equally unlikely to change
+        if (fontFamilyResolver != other.fontFamilyResolver) return false
+        if (onTextLayout != other.onTextLayout) return false
+        if (overflow != other.overflow) return false
+        if (softWrap != other.softWrap) return false
+        if (maxLines != other.maxLines) return false
+        if (minLines != other.minLines) return false
+
+        // these never change, but check anyway for correctness
+        if (onPlaceholderLayout != other.onPlaceholderLayout) return false
+        if (selectionController != other.selectionController) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = text.hashCode()
+        result = 31 * result + style.hashCode()
+        result = 31 * result + fontFamilyResolver.hashCode()
+        result = 31 * result + (onTextLayout?.hashCode() ?: 0)
+        result = 31 * result + overflow.hashCode()
+        result = 31 * result + softWrap.hashCode()
+        result = 31 * result + maxLines
+        result = 31 * result + minLines
+        result = 31 * result + (placeholders?.hashCode() ?: 0)
+        result = 31 * result + (onPlaceholderLayout?.hashCode() ?: 0)
+        result = 31 * result + (selectionController?.hashCode() ?: 0)
+        return result
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        // Show nothing in the inspector.
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringNode.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringNode.kt
new file mode 100644
index 0000000..9680f3b
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectableTextAnnotatedStringNode.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.compose.foundation.text.modifiers
+
+import androidx.compose.foundation.text.DefaultMinLines
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+import androidx.compose.ui.layout.IntrinsicMeasurable
+import androidx.compose.ui.layout.IntrinsicMeasureScope
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.GlobalPositionAwareModifierNode
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.SemanticsModifierNode
+import androidx.compose.ui.node.invalidateMeasurements
+import androidx.compose.ui.semantics.SemanticsConfiguration
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.Placeholder
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Constraints
+
+/**
+ * Node for any text that is in a selection container.
+ *
+ * This adds [GlobalPositionAwareModifierNode].
+ */
+internal class SelectableTextAnnotatedStringNode(
+    text: AnnotatedString,
+    style: TextStyle,
+    fontFamilyResolver: FontFamily.Resolver,
+    onTextLayout: ((TextLayoutResult) -> Unit)? = null,
+    overflow: TextOverflow = TextOverflow.Clip,
+    softWrap: Boolean = true,
+    maxLines: Int = Int.MAX_VALUE,
+    minLines: Int = DefaultMinLines,
+    placeholders: List<AnnotatedString.Range<Placeholder>>? = null,
+    onPlaceholderLayout: ((List<Rect?>) -> Unit)? = null,
+    private val selectionController: SelectionController? = null
+) : DelegatingNode(), LayoutModifierNode, DrawModifierNode, GlobalPositionAwareModifierNode,
+    SemanticsModifierNode {
+
+    private val delegate = delegated {
+        TextAnnotatedStringNode(
+            text = text,
+            style = style,
+            fontFamilyResolver = fontFamilyResolver,
+            onTextLayout = onTextLayout,
+            overflow = overflow,
+            softWrap = softWrap,
+            maxLines = maxLines,
+            minLines = minLines,
+            placeholders = placeholders,
+            onPlaceholderLayout = onPlaceholderLayout,
+            selectionController = selectionController
+        )
+    }
+
+    init {
+        requireNotNull(selectionController) {
+            "Do not use SelectionCapableStaticTextModifier unless selectionController != null"
+        }
+    }
+
+    override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
+        selectionController?.updateGlobalPosition(coordinates)
+    }
+
+    override fun ContentDrawScope.draw() = delegate.drawNonExtension(this)
+
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints
+    ): MeasureResult = delegate.measureNonExtension(this, measurable, constraints)
+
+    override val semanticsConfiguration: SemanticsConfiguration
+        get() = delegate.semanticsConfiguration
+
+    override fun IntrinsicMeasureScope.minIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int
+    ): Int = delegate.minIntrinsicWidthNonExtension(this, measurable, height)
+
+    override fun IntrinsicMeasureScope.minIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int
+    ): Int = delegate.minIntrinsicHeightNonExtension(this, measurable, width)
+
+    override fun IntrinsicMeasureScope.maxIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int
+    ): Int = delegate.maxIntrinsicWidthNonExtension(this, measurable, height)
+
+    override fun IntrinsicMeasureScope.maxIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int
+    ): Int = delegate.maxIntrinsicHeightNonExtension(this, measurable, width)
+
+    fun update(
+        text: AnnotatedString,
+        style: TextStyle,
+        placeholders: List<AnnotatedString.Range<Placeholder>>?,
+        minLines: Int,
+        maxLines: Int,
+        softWrap: Boolean,
+        fontFamilyResolver: FontFamily.Resolver,
+        overflow: TextOverflow,
+        onTextLayout: ((TextLayoutResult) -> Unit)?,
+        onPlaceholderLayout: ((List<Rect?>) -> Unit)?,
+        selectionController: SelectionController?
+    ) {
+        delegate.doInvalidations(
+            textChanged = delegate.updateText(
+                text = text
+            ),
+            layoutChanged = delegate.updateLayoutRelatedArgs(
+                style = style,
+                placeholders = placeholders,
+                minLines = minLines,
+                maxLines = maxLines,
+                softWrap = softWrap,
+                fontFamilyResolver = fontFamilyResolver,
+                overflow = overflow
+            ),
+            callbacksChanged = delegate.updateCallbacks(
+                onTextLayout = onTextLayout,
+                onPlaceholderLayout = onPlaceholderLayout,
+                selectionController = selectionController
+            )
+        )
+        // we always relayout when we're selectable
+        invalidateMeasurements()
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectionController.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectionController.kt
new file mode 100644
index 0000000..7a9c5c2
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/SelectionController.kt
@@ -0,0 +1,359 @@
+/*
+ * 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.modifiers
+
+import androidx.compose.foundation.text.TextDragObserver
+import androidx.compose.foundation.text.detectDragGesturesAfterLongPressWithObserver
+import androidx.compose.foundation.text.isInTouchMode
+import androidx.compose.foundation.text.selection.MouseSelectionObserver
+import androidx.compose.foundation.text.selection.MultiWidgetSelectionDelegate
+import androidx.compose.foundation.text.selection.Selectable
+import androidx.compose.foundation.text.selection.SelectionAdjustment
+import androidx.compose.foundation.text.selection.SelectionRegistrar
+import androidx.compose.foundation.text.selection.hasSelection
+import androidx.compose.foundation.text.selection.mouseSelectionDetector
+import androidx.compose.foundation.text.textPointerHoverIcon
+import androidx.compose.foundation.text.textPointerIcon
+import androidx.compose.runtime.RememberObserver
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.graphics.drawscope.clipRect
+import androidx.compose.ui.input.pointer.pointerHoverIcon
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.style.TextOverflow
+
+internal open class StaticTextSelectionParams(
+    val layoutCoordinates: LayoutCoordinates?,
+    val textLayoutResult: TextLayoutResult?
+) {
+    companion object {
+        val Empty = StaticTextSelectionParams(null, null)
+    }
+
+    open fun getPathForRange(start: Int, end: Int): Path? {
+        return textLayoutResult?.getPathForRange(start, end)
+    }
+
+    open val shouldClip: Boolean
+        get() = textLayoutResult?.layoutInput?.overflow == TextOverflow.Visible
+
+    // if this copy shows up in traces, this class may become mutable
+    fun copy(
+        layoutCoordinates: LayoutCoordinates? = this.layoutCoordinates,
+        textLayoutResult: TextLayoutResult? = this.textLayoutResult
+    ): StaticTextSelectionParams {
+        return StaticTextSelectionParams(
+            layoutCoordinates,
+            textLayoutResult
+        )
+    }
+}
+
+/**
+ * Holder for selection modifiers while we wait for pointerInput to be ported to new modifiers.
+ */
+// This is _basically_ a Modifier.Node but moved into remember because we need to do pointerInput
+internal class SelectionController(
+    private val selectionRegistrar: SelectionRegistrar,
+    private val backgroundSelectionColor: Color,
+    // TODO: Move these into Modifer.element eventually
+    private var params: StaticTextSelectionParams = StaticTextSelectionParams.Empty
+) : RememberObserver {
+    private var selectable: Selectable? = null
+    private val selectableId = selectionRegistrar.nextSelectableId()
+
+    val modifier: Modifier = selectionRegistrar.makeSelectionModifier(
+        selectableId = selectableId,
+        layoutCoordinates = { params.layoutCoordinates },
+        textLayoutResult = { params.textLayoutResult },
+        isInTouchMode = isInTouchMode
+    ).textPointerHoverIcon(selectionRegistrar)
+
+    override fun onRemembered() {
+        selectable = selectionRegistrar.subscribe(
+            MultiWidgetSelectionDelegate(
+                selectableId = selectableId,
+                coordinatesCallback = { params.layoutCoordinates },
+                layoutResultCallback = { params.textLayoutResult }
+            )
+        )
+    }
+
+    override fun onForgotten() {
+        val localSelectable = selectable
+        if (localSelectable != null) {
+            selectionRegistrar.unsubscribe(localSelectable)
+            selectable = null
+        }
+    }
+
+    override fun onAbandoned() {
+        val localSelectable = selectable
+        if (localSelectable != null) {
+            selectionRegistrar.unsubscribe(localSelectable)
+            selectable = null
+        }
+    }
+
+    fun updateTextLayout(textLayoutResult: TextLayoutResult) {
+        params = params.copy(textLayoutResult = textLayoutResult)
+    }
+
+    fun updateGlobalPosition(coordinates: LayoutCoordinates) {
+        params = params.copy(layoutCoordinates = coordinates)
+    }
+
+    fun draw(drawScope: DrawScope) {
+        val selection = selectionRegistrar.subselections[selectableId] ?: return
+
+        val start = if (!selection.handlesCrossed) {
+            selection.start.offset
+        } else {
+            selection.end.offset
+        }
+        val end = if (!selection.handlesCrossed) {
+            selection.end.offset
+        } else {
+            selection.start.offset
+        }
+
+        if (start == end) return
+
+        val lastOffset = selectable?.getLastVisibleOffset() ?: 0
+        val clippedStart = start.coerceAtMost(lastOffset)
+        val clippedEnd = end.coerceAtMost(lastOffset)
+
+        val selectionPath = params.getPathForRange(clippedStart, clippedEnd) ?: return
+
+        with(drawScope) {
+            if (params.shouldClip) {
+                clipRect {
+                    drawPath(selectionPath, backgroundSelectionColor)
+                }
+            } else {
+                drawPath(selectionPath, backgroundSelectionColor)
+            }
+        }
+    }
+}
+
+// this is not chained, but is a standalone factory
+@Suppress("ModifierFactoryExtensionFunction")
+private fun SelectionRegistrar.makeSelectionModifier(
+    selectableId: Long,
+    layoutCoordinates: () -> LayoutCoordinates?,
+    textLayoutResult: () -> TextLayoutResult?,
+    isInTouchMode: Boolean
+): Modifier {
+    return if (isInTouchMode) {
+        val longPressDragObserver = object : TextDragObserver {
+            /**
+             * The beginning position of the drag gesture. Every time a new drag gesture starts, it wil be
+             * recalculated.
+             */
+            var lastPosition = Offset.Zero
+
+            /**
+             * The total distance being dragged of the drag gesture. Every time a new drag gesture starts,
+             * it will be zeroed out.
+             */
+            var dragTotalDistance = Offset.Zero
+
+            override fun onDown(point: Offset) {
+                // Not supported for long-press-drag.
+            }
+
+            override fun onUp() {
+                // Nothing to do.
+            }
+
+            override fun onStart(startPoint: Offset) {
+                layoutCoordinates()?.let {
+                    if (!it.isAttached) return
+
+                    if (textLayoutResult().outOfBoundary(startPoint, startPoint)) {
+                        notifySelectionUpdateSelectAll(
+                            selectableId = selectableId
+                        )
+                    } else {
+                        notifySelectionUpdateStart(
+                            layoutCoordinates = it,
+                            startPosition = startPoint,
+                            adjustment = SelectionAdjustment.Word
+                        )
+                    }
+
+                    lastPosition = startPoint
+                }
+                // selection never started
+                if (!hasSelection(selectableId)) return
+                // Zero out the total distance that being dragged.
+                dragTotalDistance = Offset.Zero
+            }
+
+            override fun onDrag(delta: Offset) {
+                layoutCoordinates()?.let {
+                    if (!it.isAttached) return
+                    // selection never started, did not consume any drag
+                    if (!hasSelection(selectableId)) return
+
+                    dragTotalDistance += delta
+                    val newPosition = lastPosition + dragTotalDistance
+
+                    if (!textLayoutResult().outOfBoundary(lastPosition, newPosition)) {
+                        // Notice that only the end position needs to be updated here.
+                        // Start position is left unchanged. This is typically important when
+                        // long-press is using SelectionAdjustment.WORD or
+                        // SelectionAdjustment.PARAGRAPH that updates the start handle position from
+                        // the dragBeginPosition.
+                        val consumed = notifySelectionUpdate(
+                            layoutCoordinates = it,
+                            previousPosition = lastPosition,
+                            newPosition = newPosition,
+                            isStartHandle = false,
+                            adjustment = SelectionAdjustment.CharacterWithWordAccelerate
+                        )
+                        if (consumed) {
+                            lastPosition = newPosition
+                            dragTotalDistance = Offset.Zero
+                        }
+                    }
+                }
+            }
+
+            override fun onStop() {
+                if (hasSelection(selectableId)) {
+                    notifySelectionUpdateEnd()
+                }
+            }
+
+            override fun onCancel() {
+                if (hasSelection(selectableId)) {
+                    notifySelectionUpdateEnd()
+                }
+            }
+        }
+        Modifier.pointerInput(longPressDragObserver) {
+            detectDragGesturesAfterLongPressWithObserver(
+                longPressDragObserver
+            )
+        }
+    } else {
+        val mouseSelectionObserver = object : MouseSelectionObserver {
+            var lastPosition = Offset.Zero
+
+            override fun onExtend(downPosition: Offset): Boolean {
+                layoutCoordinates()?.let { layoutCoordinates ->
+                    if (!layoutCoordinates.isAttached) return false
+                    val consumed = notifySelectionUpdate(
+                        layoutCoordinates = layoutCoordinates,
+                        newPosition = downPosition,
+                        previousPosition = lastPosition,
+                        isStartHandle = false,
+                        adjustment = SelectionAdjustment.None
+                    )
+                    if (consumed) {
+                        lastPosition = downPosition
+                    }
+                    return hasSelection(selectableId)
+                }
+                return false
+            }
+
+            override fun onExtendDrag(dragPosition: Offset): Boolean {
+                layoutCoordinates()?.let { layoutCoordinates ->
+                    if (!layoutCoordinates.isAttached) return false
+                    if (!hasSelection(selectableId)) return false
+
+                    val consumed = notifySelectionUpdate(
+                        layoutCoordinates = layoutCoordinates,
+                        newPosition = dragPosition,
+                        previousPosition = lastPosition,
+                        isStartHandle = false,
+                        adjustment = SelectionAdjustment.None
+                    )
+
+                    if (consumed) {
+                        lastPosition = dragPosition
+                    }
+                }
+                return true
+            }
+
+            override fun onStart(
+                downPosition: Offset,
+                adjustment: SelectionAdjustment
+            ): Boolean {
+                layoutCoordinates()?.let {
+                    if (!it.isAttached) return false
+
+                    notifySelectionUpdateStart(
+                        layoutCoordinates = it,
+                        startPosition = downPosition,
+                        adjustment = adjustment
+                    )
+
+                    lastPosition = downPosition
+                    return hasSelection(selectableId)
+                }
+
+                return false
+            }
+
+            override fun onDrag(
+                dragPosition: Offset,
+                adjustment: SelectionAdjustment
+            ): Boolean {
+                layoutCoordinates()?.let {
+                    if (!it.isAttached) return false
+                    if (!hasSelection(selectableId)) return false
+
+                    val consumed = notifySelectionUpdate(
+                        layoutCoordinates = it,
+                        previousPosition = lastPosition,
+                        newPosition = dragPosition,
+                        isStartHandle = false,
+                        adjustment = adjustment
+                    )
+                    if (consumed) {
+                        lastPosition = dragPosition
+                    }
+                }
+                return true
+            }
+        }
+        Modifier.pointerInput(mouseSelectionObserver) {
+            mouseSelectionDetector(mouseSelectionObserver)
+        }.pointerHoverIcon(textPointerIcon)
+    }
+}
+
+private fun TextLayoutResult?.outOfBoundary(start: Offset, end: Offset): Boolean {
+    this ?: return false
+
+    val lastOffset = layoutInput.text.text.length
+    val rawStartOffset = getOffsetForPosition(start)
+    val rawEndOffset = getOffsetForPosition(end)
+
+    return rawStartOffset >= lastOffset - 1 && rawEndOffset >= lastOffset - 1 ||
+        rawStartOffset < 0 && rawEndOffset < 0
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringElement.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringElement.kt
new file mode 100644
index 0000000..377e096
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringElement.kt
@@ -0,0 +1,129 @@
+/*
+ * 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.modifiers
+
+import androidx.compose.foundation.text.DefaultMinLines
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.Placeholder
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.style.TextOverflow
+
+/**
+ * Modifier element for any Text with [AnnotatedString] or [onTextLayout] parameters
+ *
+ * This is slower than [TextAnnotatedStringElement]
+ */
+internal class TextAnnotatedStringElement(
+    private val text: AnnotatedString,
+    private val style: TextStyle,
+    private val fontFamilyResolver: FontFamily.Resolver,
+    private val onTextLayout: ((TextLayoutResult) -> Unit)? = null,
+    private val overflow: TextOverflow = TextOverflow.Clip,
+    private val softWrap: Boolean = true,
+    private val maxLines: Int = Int.MAX_VALUE,
+    private val minLines: Int = DefaultMinLines,
+    private val placeholders: List<AnnotatedString.Range<Placeholder>>? = null,
+    private val onPlaceholderLayout: ((List<Rect?>) -> Unit)? = null,
+    private val selectionController: SelectionController? = null
+) : ModifierNodeElement<TextAnnotatedStringNode>() {
+
+    override fun create(): TextAnnotatedStringNode = TextAnnotatedStringNode(
+        text,
+        style,
+        fontFamilyResolver,
+        onTextLayout,
+        overflow,
+        softWrap,
+        maxLines,
+        minLines,
+        placeholders,
+        onPlaceholderLayout,
+        selectionController
+    )
+
+    override fun update(node: TextAnnotatedStringNode): TextAnnotatedStringNode {
+        node.doInvalidations(
+            textChanged = node.updateText(
+                text = text
+            ),
+            layoutChanged = node.updateLayoutRelatedArgs(
+                style = style,
+                placeholders = placeholders,
+                minLines = minLines,
+                maxLines = maxLines,
+                softWrap = softWrap,
+                fontFamilyResolver = fontFamilyResolver,
+                overflow = overflow
+            ),
+            callbacksChanged = node.updateCallbacks(
+                onTextLayout = onTextLayout,
+                onPlaceholderLayout = onPlaceholderLayout,
+                selectionController = selectionController
+            )
+        )
+        return node
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+
+        if (other !is TextAnnotatedStringElement) return false
+
+        // these three are most likely to actually change
+        if (text != other.text) return false
+        if (style != other.style) return false
+        if (placeholders != other.placeholders) return false
+
+        // these are equally unlikely to change
+        if (fontFamilyResolver != other.fontFamilyResolver) return false
+        if (onTextLayout != other.onTextLayout) return false
+        if (overflow != other.overflow) return false
+        if (softWrap != other.softWrap) return false
+        if (maxLines != other.maxLines) return false
+        if (minLines != other.minLines) return false
+
+        // these never change, but check anyway for correctness
+        if (onPlaceholderLayout != other.onPlaceholderLayout) return false
+        if (selectionController != other.selectionController) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = text.hashCode()
+        result = 31 * result + style.hashCode()
+        result = 31 * result + fontFamilyResolver.hashCode()
+        result = 31 * result + (onTextLayout?.hashCode() ?: 0)
+        result = 31 * result + overflow.hashCode()
+        result = 31 * result + softWrap.hashCode()
+        result = 31 * result + maxLines
+        result = 31 * result + minLines
+        result = 31 * result + (placeholders?.hashCode() ?: 0)
+        result = 31 * result + (onPlaceholderLayout?.hashCode() ?: 0)
+        result = 31 * result + (selectionController?.hashCode() ?: 0)
+        return result
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        // Show nothing in the inspector.
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt
new file mode 100644
index 0000000..f948a2f
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextAnnotatedStringNode.kt
@@ -0,0 +1,360 @@
+/*
+ * 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.modifiers
+
+import androidx.compose.foundation.text.DefaultMinLines
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
+import androidx.compose.ui.layout.AlignmentLine
+import androidx.compose.ui.layout.FirstBaseline
+import androidx.compose.ui.layout.IntrinsicMeasurable
+import androidx.compose.ui.layout.IntrinsicMeasureScope
+import androidx.compose.ui.layout.LastBaseline
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.SemanticsModifierNode
+import androidx.compose.ui.node.invalidateDraw
+import androidx.compose.ui.node.invalidateLayer
+import androidx.compose.ui.node.invalidateMeasurements
+import androidx.compose.ui.node.invalidateSemantics
+import androidx.compose.ui.semantics.SemanticsConfiguration
+import androidx.compose.ui.semantics.getTextLayoutResult
+import androidx.compose.ui.semantics.text
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.Placeholder
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextPainter
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import kotlin.math.roundToInt
+
+/**
+ * Node that implements Text for [AnnotatedString] or [onTextLayout] parameters.
+ */
+internal class TextAnnotatedStringNode(
+    private var text: AnnotatedString,
+    private var style: TextStyle,
+    private var fontFamilyResolver: FontFamily.Resolver,
+    private var onTextLayout: ((TextLayoutResult) -> Unit)? = null,
+    private var overflow: TextOverflow = TextOverflow.Clip,
+    private var softWrap: Boolean = true,
+    private var maxLines: Int = Int.MAX_VALUE,
+    private var minLines: Int = DefaultMinLines,
+    private var placeholders: List<AnnotatedString.Range<Placeholder>>? = null,
+    private var onPlaceholderLayout: ((List<Rect?>) -> Unit)? = null,
+    private var selectionController: SelectionController? = null
+) : Modifier.Node(), LayoutModifierNode, DrawModifierNode, SemanticsModifierNode {
+    private var baselineCache: Map<AlignmentLine, Int>? = null
+
+    private var _layoutCache: MultiParagraphLayoutCache? = null
+    private val layoutCache: MultiParagraphLayoutCache
+        get() {
+            if (_layoutCache == null) {
+                _layoutCache = MultiParagraphLayoutCache(
+                    text,
+                    style,
+                    fontFamilyResolver,
+                    overflow,
+                    softWrap,
+                    maxLines,
+                    minLines,
+                    placeholders
+                )
+            }
+            return _layoutCache!!
+        }
+
+    private fun getLayoutCache(density: Density): MultiParagraphLayoutCache {
+        return layoutCache.also { it.density = density }
+    }
+
+    /**
+     * Element has text parameters to update
+     */
+    fun updateText(text: AnnotatedString): Boolean {
+        if (this.text == text) return false
+        this.text = text
+        return true
+    }
+
+    /**
+     * Element has layout parameters to update
+     */
+    fun updateLayoutRelatedArgs(
+        style: TextStyle,
+        placeholders: List<AnnotatedString.Range<Placeholder>>?,
+        minLines: Int,
+        maxLines: Int,
+        softWrap: Boolean,
+        fontFamilyResolver: FontFamily.Resolver,
+        overflow: TextOverflow
+    ): Boolean {
+        var changed = false
+        if (this.style != style) {
+            this.style = style
+            changed = true
+        }
+        if (this.placeholders != placeholders) {
+            this.placeholders = placeholders
+            changed = true
+        }
+
+        if (this.minLines != minLines) {
+            this.minLines = minLines
+            changed = true
+        }
+
+        if (this.maxLines != maxLines) {
+            this.maxLines = maxLines
+            changed = true
+        }
+
+        if (this.softWrap != softWrap) {
+            this.softWrap = softWrap
+            changed = true
+        }
+
+        if (this.fontFamilyResolver != fontFamilyResolver) {
+            this.fontFamilyResolver = fontFamilyResolver
+            changed = true
+        }
+
+        if (this.overflow != overflow) {
+            this.overflow = overflow
+            changed = true
+        }
+
+        return changed
+    }
+
+    /**
+     * Element has callback parameters to update
+     */
+    fun updateCallbacks(
+        onTextLayout: ((TextLayoutResult) -> Unit)?,
+        onPlaceholderLayout: ((List<Rect?>) -> Unit)?,
+        selectionController: SelectionController?
+    ): Boolean {
+        var changed = false
+
+        if (this.onTextLayout != onTextLayout) {
+            this.onTextLayout = onTextLayout
+            changed = true
+        }
+
+        if (this.onPlaceholderLayout != onPlaceholderLayout) {
+            this.onPlaceholderLayout = onPlaceholderLayout
+            changed = true
+        }
+
+        if (this.selectionController != selectionController) {
+            this.selectionController = selectionController
+            changed = true
+        }
+        return changed
+    }
+
+    /**
+     * Do appropriate invalidate calls based on the results of update above.
+     */
+    fun doInvalidations(
+        textChanged: Boolean,
+        layoutChanged: Boolean,
+        callbacksChanged: Boolean
+    ) {
+        if (textChanged) {
+            _semanticsConfiguration = null
+            invalidateSemantics()
+        }
+
+        if (textChanged || layoutChanged || callbacksChanged) {
+            layoutCache.update(
+                text = text,
+                style = style,
+                fontFamilyResolver = fontFamilyResolver,
+                overflow = overflow,
+                softWrap = softWrap,
+                maxLines = maxLines,
+                minLines = minLines,
+                placeholders = placeholders
+            )
+            invalidateMeasurements()
+            invalidateDraw()
+        }
+    }
+
+    private var _semanticsConfiguration: SemanticsConfiguration? = null
+
+    private var semanticsTextLayoutResult: ((MutableList<TextLayoutResult>) -> Boolean)? = null
+
+    private fun generateSemantics(text: AnnotatedString): SemanticsConfiguration {
+        var localSemanticsTextLayoutResult = semanticsTextLayoutResult
+        if (localSemanticsTextLayoutResult == null) {
+            localSemanticsTextLayoutResult = { textLayoutResult ->
+                val layout = layoutCache.layoutOrNull?.also {
+                    textLayoutResult.add(it)
+                }
+                layout != null
+            }
+            semanticsTextLayoutResult = localSemanticsTextLayoutResult
+        }
+        return SemanticsConfiguration().also {
+            it.isMergingSemanticsOfDescendants = false
+            it.isClearingSemantics = false
+            it.text = text
+            it.getTextLayoutResult(action = localSemanticsTextLayoutResult)
+        }
+    }
+
+    override val semanticsConfiguration: SemanticsConfiguration
+        get() {
+            var localSemantics = _semanticsConfiguration
+            if (localSemantics == null) {
+                localSemantics = generateSemantics(text)
+                _semanticsConfiguration = localSemantics
+            }
+            return localSemantics
+        }
+
+    fun measureNonExtension(
+        measureScope: MeasureScope,
+        measurable: Measurable,
+        constraints: Constraints
+    ): MeasureResult {
+        return measureScope.measure(measurable, constraints)
+    }
+
+    /**
+     * Text layout is performed here.
+     */
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints
+    ): MeasureResult {
+        val layoutCache = getLayoutCache(this)
+
+        val didChangeLayout = layoutCache.layoutWithConstraints(constraints, layoutDirection)
+        val textLayoutResult = layoutCache.textLayoutResult
+
+        // ensure measure restarts when hasStaleResolvedFonts by reading in measure
+        textLayoutResult.multiParagraph.intrinsics.hasStaleResolvedFonts
+
+        if (didChangeLayout) {
+            invalidateLayer()
+            onTextLayout?.invoke(textLayoutResult)
+            selectionController?.updateTextLayout(textLayoutResult)
+            baselineCache = mapOf(
+                FirstBaseline to textLayoutResult.firstBaseline.roundToInt(),
+                LastBaseline to textLayoutResult.lastBaseline.roundToInt()
+            )
+        }
+
+        // first share the placeholders
+        onPlaceholderLayout?.invoke(textLayoutResult.placeholderRects)
+
+        // then allow children to measure _inside_ our final box, with the above placeholders
+        val placeable = measurable.measure(
+            Constraints.fixed(
+                textLayoutResult.size.width,
+                textLayoutResult.size.height
+            )
+        )
+
+        return layout(
+            textLayoutResult.size.width,
+            textLayoutResult.size.height,
+            baselineCache!!
+        ) {
+            // this is basically a graphicsLayer
+            placeable.place(0, 0)
+        }
+    }
+
+    fun minIntrinsicWidthNonExtension(
+        intrinsicMeasureScope: IntrinsicMeasureScope,
+        measurable: IntrinsicMeasurable,
+        height: Int
+    ): Int {
+        return intrinsicMeasureScope.minIntrinsicWidth(measurable, height)
+    }
+
+    override fun IntrinsicMeasureScope.minIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int
+    ): Int {
+        return getLayoutCache(this).minIntrinsicWidth(layoutDirection)
+    }
+
+    fun minIntrinsicHeightNonExtension(
+        intrinsicMeasureScope: IntrinsicMeasureScope,
+        measurable: IntrinsicMeasurable,
+        width: Int
+    ): Int {
+        return intrinsicMeasureScope.minIntrinsicHeight(measurable, width)
+    }
+
+    override fun IntrinsicMeasureScope.minIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int
+    ): Int = getLayoutCache(this).intrinsicHeight(width, layoutDirection)
+
+    fun maxIntrinsicWidthNonExtension(
+        intrinsicMeasureScope: IntrinsicMeasureScope,
+        measurable: IntrinsicMeasurable,
+        height: Int
+    ): Int = intrinsicMeasureScope.maxIntrinsicWidth(measurable, height)
+
+    override fun IntrinsicMeasureScope.maxIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int
+    ): Int = getLayoutCache(this).maxIntrinsicWidth(layoutDirection)
+
+    fun maxIntrinsicHeightNonExtension(
+        intrinsicMeasureScope: IntrinsicMeasureScope,
+        measurable: IntrinsicMeasurable,
+        width: Int
+    ): Int = intrinsicMeasureScope.maxIntrinsicHeight(measurable, width)
+
+    override fun IntrinsicMeasureScope.maxIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int
+    ): Int = getLayoutCache(this).intrinsicHeight(width, layoutDirection)
+
+    fun drawNonExtension(
+        contentDrawScope: ContentDrawScope
+    ) {
+        return contentDrawScope.draw()
+    }
+
+    override fun ContentDrawScope.draw() {
+        selectionController?.draw(this)
+        drawIntoCanvas { canvas ->
+            TextPainter.paint(canvas, requireNotNull(layoutCache.textLayoutResult))
+        }
+        if (!placeholders.isNullOrEmpty()) {
+            drawContent()
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleElement.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleElement.kt
new file mode 100644
index 0000000..63be1b7
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleElement.kt
@@ -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.compose.foundation.text.modifiers
+
+import androidx.compose.foundation.text.DefaultMinLines
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.style.TextOverflow
+
+/**
+ * Modifier element for any Text with [AnnotatedString] or [onTextLayout] parameters
+ *
+ * This is slower than [TextAnnotatedStringElement]
+ */
+internal class TextStringSimpleElement(
+    private val text: String,
+    private val style: TextStyle,
+    private val fontFamilyResolver: FontFamily.Resolver,
+    private val overflow: TextOverflow = TextOverflow.Clip,
+    private val softWrap: Boolean = true,
+    private val maxLines: Int = Int.MAX_VALUE,
+    private val minLines: Int = DefaultMinLines,
+) : ModifierNodeElement<TextStringSimpleNode>() {
+
+    override fun create(): TextStringSimpleNode = TextStringSimpleNode(
+        text,
+        style,
+        fontFamilyResolver,
+        overflow,
+        softWrap,
+        maxLines,
+        minLines
+    )
+
+    override fun update(node: TextStringSimpleNode): TextStringSimpleNode {
+        node.doInvalidations(
+            textChanged = node.updateText(
+                text = text
+            ),
+            layoutChanged = node.updateLayoutRelatedArgs(
+                style = style,
+                minLines = minLines,
+                maxLines = maxLines,
+                softWrap = softWrap,
+                fontFamilyResolver = fontFamilyResolver,
+                overflow = overflow
+            )
+        )
+        return node
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+
+        if (other !is TextStringSimpleElement) return false
+
+        // these three are most likely to actually change
+        if (text != other.text) return false
+        if (style != other.style) return false
+
+        // these are equally unlikely to change
+        if (fontFamilyResolver != other.fontFamilyResolver) return false
+        if (overflow != other.overflow) return false
+        if (softWrap != other.softWrap) return false
+        if (maxLines != other.maxLines) return false
+        if (minLines != other.minLines) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = text.hashCode()
+        result = 31 * result + style.hashCode()
+        result = 31 * result + fontFamilyResolver.hashCode()
+        result = 31 * result + overflow.hashCode()
+        result = 31 * result + softWrap.hashCode()
+        result = 31 * result + maxLines
+        result = 31 * result + minLines
+        return result
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        // Show nothing in the inspector.
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNode.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNode.kt
new file mode 100644
index 0000000..93c1391
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/TextStringSimpleNode.kt
@@ -0,0 +1,320 @@
+/*
+ * 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.modifiers
+
+import androidx.compose.foundation.text.DefaultMinLines
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Shadow
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+import androidx.compose.ui.graphics.drawscope.Fill
+import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
+import androidx.compose.ui.layout.AlignmentLine
+import androidx.compose.ui.layout.FirstBaseline
+import androidx.compose.ui.layout.IntrinsicMeasurable
+import androidx.compose.ui.layout.IntrinsicMeasureScope
+import androidx.compose.ui.layout.LastBaseline
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.SemanticsModifierNode
+import androidx.compose.ui.node.invalidateLayer
+import androidx.compose.ui.node.invalidateMeasurements
+import androidx.compose.ui.node.invalidateSemantics
+import androidx.compose.ui.semantics.SemanticsConfiguration
+import androidx.compose.ui.semantics.getTextLayoutResult
+import androidx.compose.ui.semantics.text
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.style.TextDecoration
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import kotlin.math.roundToInt
+
+/**
+ * Node that implements Text for [String].
+ *
+ * It has reduced functionality, and as a result gains in performance.
+ *
+ * Note that this Node never calculates [TextLayoutResult] unless needed by semantics.
+ */
+internal class TextStringSimpleNode(
+    private var text: String,
+    private var style: TextStyle,
+    private var fontFamilyResolver: FontFamily.Resolver,
+    private var overflow: TextOverflow = TextOverflow.Clip,
+    private var softWrap: Boolean = true,
+    private var maxLines: Int = Int.MAX_VALUE,
+    private var minLines: Int = DefaultMinLines
+) : Modifier.Node(), LayoutModifierNode, DrawModifierNode, SemanticsModifierNode {
+    private var baselineCache: Map<AlignmentLine, Int>? = null
+
+    private var _layoutCache: ParagraphLayoutCache? = null
+    private val layoutCache: ParagraphLayoutCache
+        get() {
+            if (_layoutCache == null) {
+                _layoutCache = ParagraphLayoutCache(
+                    text,
+                    style,
+                    fontFamilyResolver,
+                    overflow,
+                    softWrap,
+                    maxLines,
+                    minLines,
+                )
+            }
+            return _layoutCache!!
+        }
+
+    private fun getLayoutCache(density: Density): ParagraphLayoutCache {
+        return layoutCache.also { it.density = density }
+    }
+
+    /**
+     * Element has text params to update
+     */
+    fun updateText(text: String): Boolean {
+        if (this.text == text) return false
+        this.text = text
+        return true
+    }
+
+    /**
+     * Element has layout related params to update
+     */
+    fun updateLayoutRelatedArgs(
+        style: TextStyle,
+        minLines: Int,
+        maxLines: Int,
+        softWrap: Boolean,
+        fontFamilyResolver: FontFamily.Resolver,
+        overflow: TextOverflow
+    ): Boolean {
+        var changed = false
+        if (this.style != style) {
+            this.style = style
+            changed = true
+        }
+
+        if (this.minLines != minLines) {
+            this.minLines = minLines
+            changed = true
+        }
+
+        if (this.maxLines != maxLines) {
+            this.maxLines = maxLines
+            changed = true
+        }
+
+        if (this.softWrap != softWrap) {
+            this.softWrap = softWrap
+            changed = true
+        }
+
+        if (this.fontFamilyResolver != fontFamilyResolver) {
+            this.fontFamilyResolver = fontFamilyResolver
+            changed = true
+        }
+
+        if (this.overflow != overflow) {
+            this.overflow = overflow
+            changed = true
+        }
+
+        return changed
+    }
+
+    /**
+     * request invalidate based on the results of [updateText] and [updateLayoutRelatedArgs]
+     */
+    fun doInvalidations(
+        textChanged: Boolean,
+        layoutChanged: Boolean
+    ) {
+        if (textChanged) {
+            _semanticsConfiguration = null
+            invalidateSemantics()
+        }
+
+        if (textChanged || layoutChanged) {
+            layoutCache.update(
+                text = text,
+                style = style,
+                fontFamilyResolver = fontFamilyResolver,
+                overflow = overflow,
+                softWrap = softWrap,
+                maxLines = maxLines,
+                minLines = minLines
+            )
+            invalidateMeasurements()
+            invalidateLayer()
+        }
+    }
+
+    private var _semanticsConfiguration: SemanticsConfiguration? = null
+
+    private var semanticsTextLayoutResult: ((MutableList<TextLayoutResult>) -> Boolean)? = null
+
+    private fun generateSemantics(text: String): SemanticsConfiguration {
+        var localSemanticsTextLayoutResult = semanticsTextLayoutResult
+        if (localSemanticsTextLayoutResult == null) {
+            localSemanticsTextLayoutResult = { textLayoutResult ->
+                val layout = layoutCache.slowCreateTextLayoutResultOrNull()?.also {
+                    textLayoutResult.add(it)
+                }
+                layout != null
+                false
+            }
+            semanticsTextLayoutResult = localSemanticsTextLayoutResult
+        }
+        return SemanticsConfiguration().also {
+            it.isMergingSemanticsOfDescendants = false
+            it.isClearingSemantics = false
+            it.text = AnnotatedString(text)
+            it.getTextLayoutResult(action = localSemanticsTextLayoutResult)
+        }
+    }
+
+    override val semanticsConfiguration: SemanticsConfiguration
+        get() {
+            var localSemantics = _semanticsConfiguration
+            if (localSemantics == null) {
+                localSemantics = generateSemantics(text)
+                _semanticsConfiguration = localSemantics
+            }
+            return localSemantics
+        }
+
+    /**
+     * Text layout happens here
+     */
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints
+    ): MeasureResult {
+        val layoutCache = getLayoutCache(this)
+
+        val didChangeLayout = layoutCache.layoutWithConstraints(constraints, layoutDirection)
+        // ensure measure restarts when hasStaleResolvedFonts by reading in measure
+        layoutCache.observeFontChanges
+        val paragraph = layoutCache.paragraph!!
+        val layoutSize = layoutCache.layoutSize
+
+        if (didChangeLayout) {
+            invalidateLayer()
+            baselineCache = mapOf(
+                FirstBaseline to paragraph.firstBaseline.roundToInt(),
+                LastBaseline to paragraph.lastBaseline.roundToInt()
+            )
+        }
+
+        // then allow children to measure _inside_ our final box, with the above placeholders
+        val placeable = measurable.measure(
+            Constraints.fixed(
+                layoutSize.width,
+                layoutSize.height
+            )
+        )
+
+        return layout(
+            layoutSize.width,
+            layoutSize.height,
+            baselineCache!!
+        ) {
+            // this is basically a graphicsLayer
+            placeable.place(0, 0)
+        }
+    }
+
+    override fun IntrinsicMeasureScope.minIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int
+    ): Int {
+        return getLayoutCache(this).minIntrinsicWidth(layoutDirection)
+    }
+
+    override fun IntrinsicMeasureScope.minIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int
+    ): Int = getLayoutCache(this).intrinsicHeight(width, layoutDirection)
+
+    override fun IntrinsicMeasureScope.maxIntrinsicWidth(
+        measurable: IntrinsicMeasurable,
+        height: Int
+    ): Int = getLayoutCache(this).maxIntrinsicWidth(layoutDirection)
+
+    override fun IntrinsicMeasureScope.maxIntrinsicHeight(
+        measurable: IntrinsicMeasurable,
+        width: Int
+    ): Int = getLayoutCache(this).intrinsicHeight(width, layoutDirection)
+
+    /**
+     * Optimized Text draw.
+     */
+    @OptIn(ExperimentalTextApi::class)
+    override fun ContentDrawScope.draw() {
+        val localParagraph = requireNotNull(layoutCache.paragraph)
+        drawIntoCanvas { canvas ->
+            val willClip = layoutCache.didOverflow
+            if (willClip) {
+                val width = layoutCache.layoutSize.width.toFloat()
+                val height = layoutCache.layoutSize.height.toFloat()
+                val bounds = Rect(Offset.Zero, Size(width, height))
+                canvas.save()
+                canvas.clipRect(bounds)
+            }
+            try {
+                val textDecoration = style.textDecoration ?: TextDecoration.None
+                val shadow = style.shadow ?: Shadow.None
+                val drawStyle = style.drawStyle ?: Fill
+                val brush = style.brush
+                if (brush != null) {
+                    val alpha = style.alpha
+                    localParagraph.paint(
+                        canvas = canvas,
+                        brush = brush,
+                        alpha = alpha,
+                        shadow = shadow,
+                        drawStyle = drawStyle,
+                        textDecoration = textDecoration
+                    )
+                } else {
+                    val color = style.color
+                    localParagraph.paint(
+                        canvas = canvas,
+                        color = color,
+                        shadow = shadow,
+                        textDecoration = textDecoration,
+                        drawStyle = drawStyle
+                    )
+                }
+            } finally {
+                if (willClip) {
+                    canvas.restore()
+                }
+            }
+        }
+    }
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionContainer.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionContainer.kt
index 0d4733d..e2e8484 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionContainer.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionContainer.kt
@@ -137,7 +137,8 @@
 
     DisposableEffect(manager) {
         onDispose {
-            manager.hideSelectionToolbar()
+            manager.onRelease()
+            manager.hasFocus = false
         }
     }
 }
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/SuspendingGestureTestUtil.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/SuspendingGestureTestUtil.kt
deleted file mode 100644
index 4d1347a..0000000
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/SuspendingGestureTestUtil.kt
+++ /dev/null
@@ -1,359 +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.foundation.gestures
-
-import androidx.compose.runtime.Applier
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Composer
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.ControlledComposition
-import androidx.compose.runtime.InternalComposeApi
-import androidx.compose.runtime.MonotonicFrameClock
-import androidx.compose.runtime.Recomposer
-import androidx.compose.runtime.currentComposer
-import androidx.compose.runtime.withRunningRecomposer
-import androidx.compose.testutils.TestViewConfiguration
-import androidx.compose.ui.Modifier
-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.PointerId
-import androidx.compose.ui.input.pointer.PointerInputChange
-import androidx.compose.ui.input.pointer.PointerInputFilter
-import androidx.compose.ui.input.pointer.PointerInputScope
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.materialize
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalViewConfiguration
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.DpSize
-import androidx.compose.ui.unit.IntSize
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.test.runTest
-import kotlinx.coroutines.withContext
-import kotlinx.coroutines.yield
-
-/**
- * Manages suspending pointer input for a single gesture detector, passed in
- * [gestureDetector]. The [width] and [height] of the LayoutNode may
- * be provided.
- */
-internal class SuspendingGestureTestUtil(
-    val width: Int = 10,
-    val height: Int = 10,
-    private val gestureDetector: suspend PointerInputScope.() -> Unit,
-) {
-    private var nextPointerId = 0L
-    private val activePointers = mutableMapOf<PointerId, PointerInputChange>()
-    private var pointerInputFilter: PointerInputFilter? = null
-    private var lastTime = 0L
-    private var isExecuting = false
-
-    /**
-     * Executes the block in composition, creating a gesture detector from
-     * [gestureDetector]. The [down], [moveTo], and [up] can then be
-     * called within [block].
-     *
-     * This is not reentrant.
-     */
-    @OptIn(ExperimentalCoroutinesApi::class)
-    fun executeInComposition(block: suspend SuspendingGestureTestUtil.() -> Unit) {
-        check(!isExecuting) { "executeInComposition is not reentrant" }
-        try {
-            isExecuting = true
-            runTest {
-                val frameClock = TestFrameClock()
-
-                withContext(frameClock) {
-                    composeGesture(block)
-                }
-            }
-        } finally {
-            isExecuting = false
-            pointerInputFilter = null
-            lastTime = 0
-            activePointers.clear()
-        }
-    }
-
-    private suspend fun composeGesture(block: suspend SuspendingGestureTestUtil.() -> Unit) {
-        withRunningRecomposer { recomposer ->
-            compose(recomposer) {
-                CompositionLocalProvider(
-                    LocalDensity provides Density(1f),
-                    LocalViewConfiguration provides TestViewConfiguration(
-                        minimumTouchTargetSize = DpSize.Zero
-                    )
-                ) {
-                    pointerInputFilter = currentComposer
-                        .materialize(Modifier.pointerInput(Unit, gestureDetector)) as
-                        PointerInputFilter
-                }
-            }
-            yield()
-            block()
-            // Pointer input effects will loop indefinitely; fully cancel them.
-            recomposer.cancel()
-        }
-    }
-
-    /**
-     * Creates a new pointer being down at [timeDiffMillis] from the previous event. The position
-     * [x], [y] is used for the touch point. The [PointerInputChange] may be mutated
-     * prior to invoking the change on all passes in [initial], if provided. All other "down"
-     * pointers will also be included in the change event.
-     */
-    suspend fun down(
-        x: Float,
-        y: Float,
-        timeDiffMillis: Long = 10,
-        main: PointerInputChange.() -> Unit = {},
-        final: PointerInputChange.() -> Unit = {},
-        initial: PointerInputChange.() -> Unit = {}
-    ): PointerInputChange {
-        lastTime += timeDiffMillis
-        val change = PointerInputChange(
-            id = PointerId(nextPointerId++),
-            uptimeMillis = lastTime,
-            position = Offset(x, y),
-            pressed = true,
-            previousUptimeMillis = lastTime,
-            previousPosition = Offset(x, y),
-            previousPressed = false,
-            isInitiallyConsumed = false
-        )
-        activePointers[change.id] = change
-        invokeOverAllPasses(change, initial, main, final)
-        return change
-    }
-
-    /**
-     * Creates a new pointer being down at [timeDiffMillis] from the previous event. The position
-     * [offset] is used for the touch point. The [PointerInputChange] may be mutated
-     * prior to invoking the change on all passes in [initial], if provided. All other "down"
-     * pointers will also be included in the change event.
-     */
-    suspend fun down(
-        offset: Offset = Offset.Zero,
-        timeDiffMillis: Long = 10,
-        main: PointerInputChange.() -> Unit = {},
-        final: PointerInputChange.() -> Unit = {},
-        initial: PointerInputChange.() -> Unit = {}
-    ): PointerInputChange {
-        return down(offset.x, offset.y, timeDiffMillis, main, final, initial)
-    }
-
-    /**
-     * Raises the pointer. [initial] will be called on the [PointerInputChange] prior to the
-     * event being invoked on all passes. After [up], the event will no longer participate
-     * in other events. [timeDiffMillis] indicates the time from the previous event that
-     * the [up] takes place.
-     */
-    suspend fun PointerInputChange.up(
-        timeDiffMillis: Long = 10,
-        main: PointerInputChange.() -> Unit = {},
-        final: PointerInputChange.() -> Unit = {},
-        initial: PointerInputChange.() -> Unit = {}
-    ): PointerInputChange {
-        lastTime += timeDiffMillis
-        val change = PointerInputChange(
-            id = id,
-            previousUptimeMillis = uptimeMillis,
-            previousPressed = pressed,
-            previousPosition = position,
-            uptimeMillis = lastTime,
-            pressed = false,
-            position = position,
-            isInitiallyConsumed = false
-        )
-        activePointers[change.id] = change
-        invokeOverAllPasses(change, initial, main, final)
-        activePointers.remove(change.id)
-        return change
-    }
-
-    /**
-     * Moves an existing [down] pointer to a new position at [timeDiffMillis] from the most recent
-     * event. [initial] will be called on the [PointerInputChange] prior to invoking the event
-     * on all passes.
-     */
-    suspend fun PointerInputChange.moveTo(
-        x: Float,
-        y: Float,
-        timeDiffMillis: Long = 10,
-        main: PointerInputChange.() -> Unit = {},
-        final: PointerInputChange.() -> Unit = {},
-        initial: PointerInputChange.() -> Unit = {}
-    ): PointerInputChange {
-        lastTime += timeDiffMillis
-        val change = PointerInputChange(
-            id = id,
-            previousUptimeMillis = uptimeMillis,
-            previousPosition = position,
-            previousPressed = pressed,
-            uptimeMillis = lastTime,
-            position = Offset(x, y),
-            pressed = true,
-            isInitiallyConsumed = false
-        )
-        initial(change)
-        activePointers[change.id] = change
-        invokeOverAllPasses(change, initial, main, final)
-        return change
-    }
-
-    /**
-     * Moves an existing [down] pointer to a new position at [timeDiffMillis] from the most recent
-     * event. [initial] will be called on the [PointerInputChange] prior to invoking the event
-     * on all passes.
-     */
-    suspend fun PointerInputChange.moveTo(
-        offset: Offset,
-        timeDiffMillis: Long = 10,
-        main: PointerInputChange.() -> Unit = {},
-        final: PointerInputChange.() -> Unit = {},
-        initial: PointerInputChange.() -> Unit = {}
-    ): PointerInputChange = moveTo(offset.x, offset.y, timeDiffMillis, main, final, initial)
-
-    /**
-     * Moves an existing [down] pointer to a new position at [timeDiffMillis] from the most recent
-     * event. [initial] will be called on the [PointerInputChange] prior to invoking the event
-     * on all passes.
-     */
-    suspend fun PointerInputChange.moveBy(
-        offset: Offset,
-        timeDiffMillis: Long = 10,
-        main: PointerInputChange.() -> Unit = {},
-        final: PointerInputChange.() -> Unit = {},
-        initial: PointerInputChange.() -> Unit = {}
-    ): PointerInputChange = moveTo(
-        position.x + offset.x,
-        position.y + offset.y,
-        timeDiffMillis,
-        main,
-        final,
-        initial
-    )
-
-    /**
-     * Removes all pointers from the active pointers. This can simulate a faulty pointer stream
-     * for robustness testing.
-     */
-    fun clearPointerStream() {
-        activePointers.clear()
-    }
-
-    /**
-     * Updates all changes so that all events are at the current time.
-     */
-    private fun updateCurrentTime() {
-        val currentTime = lastTime
-        activePointers.entries.forEach { entry ->
-            val change = entry.value
-            if (change.uptimeMillis != currentTime) {
-                entry.setValue(
-                    PointerInputChange(
-                        id = change.id,
-                        previousUptimeMillis = change.uptimeMillis,
-                        previousPressed = change.pressed,
-                        previousPosition = change.position,
-                        uptimeMillis = currentTime,
-                        pressed = change.pressed,
-                        position = change.position,
-                        isInitiallyConsumed = false
-                    )
-                )
-            }
-        }
-    }
-
-    /**
-     * Invokes events for all passes.
-     */
-    private suspend fun invokeOverAllPasses(
-        change: PointerInputChange,
-        initial: PointerInputChange.() -> Unit,
-        main: PointerInputChange.() -> Unit,
-        final: PointerInputChange.() -> Unit
-    ) {
-        updateCurrentTime()
-        val event = PointerEvent(activePointers.values.toList())
-        val size = IntSize(width, height)
-
-        change.initial()
-        pointerInputFilter?.onPointerEvent(event, PointerEventPass.Initial, size)
-        yield()
-        change.main()
-        pointerInputFilter?.onPointerEvent(event, PointerEventPass.Main, size)
-        yield()
-        change.final()
-        pointerInputFilter?.onPointerEvent(event, PointerEventPass.Final, size)
-        yield()
-    }
-
-    @OptIn(InternalComposeApi::class)
-    private fun compose(
-        recomposer: Recomposer,
-        block: @Composable () -> Unit
-    ) {
-        ControlledComposition(
-            EmptyApplier(),
-            recomposer
-        ).apply {
-            composeContent {
-                @Suppress("UNCHECKED_CAST")
-                val fn = block as (Composer, Int) -> Unit
-                fn(currentComposer, 0)
-            }
-            applyChanges()
-            verifyConsistent()
-        }
-    }
-
-    internal class TestFrameClock : MonotonicFrameClock {
-
-        private val frameCh = Channel<Long>()
-
-        @Suppress("unused")
-        suspend fun frame(frameTimeNanos: Long) {
-            frameCh.send(frameTimeNanos)
-        }
-
-        override suspend fun <R> withFrameNanos(onFrame: (Long) -> R): R =
-            onFrame(frameCh.receive())
-    }
-
-    class EmptyApplier : Applier<Unit> {
-        override val current: Unit = Unit
-        override fun down(node: Unit) {}
-        override fun up() {}
-        override fun insertTopDown(index: Int, instance: Unit) {
-            error("Unexpected")
-        }
-        override fun insertBottomUp(index: Int, instance: Unit) {
-            error("Unexpected")
-        }
-        override fun remove(index: Int, count: Int) {
-            error("Unexpected")
-        }
-        override fun move(from: Int, to: Int, count: Int) {
-            error("Unexpected")
-        }
-        override fun clear() {}
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/TapGestureDetectorTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/TapGestureDetectorTest.kt
deleted file mode 100644
index 6d1613b..0000000
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/TapGestureDetectorTest.kt
+++ /dev/null
@@ -1,649 +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.foundation.gestures
-
-import kotlinx.coroutines.delay
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@RunWith(JUnit4::class)
-class TapGestureDetectorTest {
-    private var pressed = false
-    private var released = false
-    private var canceled = false
-    private var tapped = false
-    private var doubleTapped = false
-    private var longPressed = false
-
-    /** The time before a long press gesture attempts to win. */
-    private val LongPressTimeoutMillis: Long = 500L
-
-    /**
-     * The maximum time from the start of the first tap to the start of the second
-     * tap in a double-tap gesture.
-     */
-// TODO(shepshapard): In Android, this is actually the time from the first's up event
-// to the second's down event, according to the ViewConfiguration docs.
-    private val DoubleTapTimeoutMillis: Long = 300L
-
-    private val util = SuspendingGestureTestUtil {
-        detectTapGestures(
-            onPress = {
-                pressed = true
-                if (tryAwaitRelease()) {
-                    released = true
-                } else {
-                    canceled = true
-                }
-            },
-            onTap = {
-                tapped = true
-            }
-        )
-    }
-
-    private val utilWithShortcut = SuspendingGestureTestUtil {
-        detectTapAndPress(
-            onPress = {
-                pressed = true
-                if (tryAwaitRelease()) {
-                    released = true
-                } else {
-                    canceled = true
-                }
-            },
-            onTap = {
-                tapped = true
-            }
-        )
-    }
-
-    private val allGestures = SuspendingGestureTestUtil {
-        detectTapGestures(
-            onPress = {
-                pressed = true
-                try {
-                    awaitRelease()
-                    released = true
-                } catch (_: GestureCancellationException) {
-                    canceled = true
-                }
-            },
-            onTap = { tapped = true },
-            onLongPress = { longPressed = true },
-            onDoubleTap = { doubleTapped = true }
-        )
-    }
-
-    @Before
-    fun setup() {
-        pressed = false
-        released = false
-        canceled = false
-        tapped = false
-        doubleTapped = false
-        longPressed = false
-    }
-
-    /**
-     * Clicking in the region should result in the callback being invoked.
-     */
-    @Test
-    fun normalTap() = util.executeInComposition {
-        val down = down(5f, 5f)
-        assertTrue(down.isConsumed)
-        assertTrue(down.isConsumed)
-
-        assertTrue(pressed)
-        assertFalse(tapped)
-        assertFalse(released)
-
-        val up = down.up(50)
-        assertTrue(up.isConsumed)
-        assertTrue(up.isConsumed)
-
-        assertTrue(tapped)
-        assertTrue(released)
-        assertFalse(canceled)
-    }
-
-    /**
-     * Clicking in the region should result in the callback being invoked.
-     */
-    @Test
-    fun normalTap_withShortcut() = utilWithShortcut.executeInComposition {
-        val down = down(5f, 5f)
-        assertTrue(down.isConsumed)
-
-        assertTrue(pressed)
-        assertFalse(tapped)
-        assertFalse(released)
-
-        val up = down.up(50)
-        assertTrue(up.isConsumed)
-
-        assertTrue(tapped)
-        assertTrue(released)
-        assertFalse(canceled)
-    }
-
-    /**
-     * Clicking in the region should result in the callback being invoked.
-     */
-    @Test
-    fun normalTapWithAllGestures() = allGestures.executeInComposition {
-        val down = down(5f, 5f)
-        assertTrue(down.isConsumed)
-
-        assertTrue(pressed)
-
-        val up = down.up(50)
-        assertTrue(up.isConsumed)
-
-        assertTrue(released)
-
-        // we have to wait for the double-tap timeout before we receive an event
-
-        assertFalse(tapped)
-        assertFalse(doubleTapped)
-
-        delay(DoubleTapTimeoutMillis + 10)
-
-        assertTrue(tapped)
-        assertFalse(doubleTapped)
-    }
-
-    /**
-     * Clicking in the region should result in the callback being invoked.
-     */
-    @Test
-    fun normalDoubleTap() = allGestures.executeInComposition {
-        val up = down(5f, 5f)
-            .up()
-        assertTrue(up.isConsumed)
-
-        assertTrue(pressed)
-        assertTrue(released)
-        assertFalse(tapped)
-        assertFalse(doubleTapped)
-
-        pressed = false
-        released = false
-
-        val up2 = down(5f, 5f, 50)
-            .up()
-        assertTrue(up2.isConsumed)
-
-        assertFalse(tapped)
-        assertTrue(doubleTapped)
-        assertTrue(pressed)
-        assertTrue(released)
-    }
-
-    /**
-     * Long press in the region should result in the callback being invoked.
-     */
-    @Test
-    fun normalLongPress() = allGestures.executeInComposition {
-        val down = down(5f, 5f)
-        assertTrue(down.isConsumed)
-
-        assertTrue(pressed)
-        delay(LongPressTimeoutMillis + 10)
-
-        assertTrue(longPressed)
-
-        val up = down.up(500)
-        assertTrue(up.isConsumed)
-
-        assertFalse(tapped)
-        assertFalse(doubleTapped)
-        assertTrue(released)
-        assertFalse(canceled)
-    }
-
-    /**
-     * Pressing in the region, sliding out and then lifting should result in
-     * the callback not being invoked
-     */
-    @Test
-    fun tapMiss() = util.executeInComposition {
-        val up = down(5f, 5f)
-            .moveTo(15f, 15f)
-            .up()
-
-        assertTrue(pressed)
-        assertTrue(canceled)
-        assertFalse(released)
-        assertFalse(tapped)
-        assertFalse(up.isConsumed)
-        assertFalse(up.isConsumed)
-    }
-
-    /**
-     * Pressing in the region, sliding out and then lifting should result in
-     * the callback not being invoked
-     */
-    @Test
-    fun tapMiss_withShortcut() = utilWithShortcut.executeInComposition {
-        val up = down(5f, 5f)
-            .moveTo(15f, 15f)
-            .up()
-
-        assertTrue(pressed)
-        assertTrue(canceled)
-        assertFalse(released)
-        assertFalse(tapped)
-        assertFalse(up.isConsumed)
-    }
-
-    /**
-     * Pressing in the region, sliding out and then lifting should result in
-     * the callback not being invoked
-     */
-    @Test
-    fun longPressMiss() = allGestures.executeInComposition {
-        val pointer = down(5f, 5f)
-            .moveTo(15f, 15f)
-
-        delay(DoubleTapTimeoutMillis + 10)
-        val up = pointer.up()
-        assertFalse(up.isConsumed)
-
-        assertTrue(pressed)
-        assertFalse(released)
-        assertTrue(canceled)
-        assertFalse(tapped)
-        assertFalse(longPressed)
-        assertFalse(doubleTapped)
-    }
-
-    /**
-     * Pressing in the region, sliding out and then lifting should result in
-     * the callback not being invoked for double-tap
-     */
-    @Test
-    fun doubleTapMiss() = allGestures.executeInComposition {
-        val up1 = down(5f, 5f).up()
-        assertTrue(up1.isConsumed)
-
-        assertTrue(pressed)
-        assertTrue(released)
-        assertFalse(canceled)
-
-        pressed = false
-        released = false
-
-        val up2 = down(5f, 5f, 50)
-            .moveTo(15f, 15f)
-            .up()
-
-        assertFalse(up2.isConsumed)
-
-        assertTrue(pressed)
-        assertFalse(released)
-        assertTrue(canceled)
-        assertTrue(tapped)
-        assertFalse(longPressed)
-        assertFalse(doubleTapped)
-    }
-
-    /**
-     * Pressing in the region, sliding out, then back in, then lifting
-     * should result the gesture being canceled.
-     */
-    @Test
-    fun tapOutAndIn() = util.executeInComposition {
-        val up = down(5f, 5f)
-            .moveTo(15f, 15f)
-            .moveTo(6f, 6f)
-            .up()
-
-        assertFalse(tapped)
-        assertFalse(up.isConsumed)
-        assertTrue(pressed)
-        assertFalse(released)
-        assertTrue(canceled)
-    }
-
-    /**
-     * Pressing in the region, sliding out, then back in, then lifting
-     * should result the gesture being canceled.
-     */
-    @Test
-    fun tapOutAndIn_withShortcut() = utilWithShortcut.executeInComposition {
-        val up = down(5f, 5f)
-            .moveTo(15f, 15f)
-            .moveTo(6f, 6f)
-            .up()
-
-        assertFalse(tapped)
-        assertFalse(up.isConsumed)
-        assertTrue(pressed)
-        assertFalse(released)
-        assertTrue(canceled)
-    }
-
-    /**
-     * After a first tap, a second tap should also be detected.
-     */
-    @Test
-    fun secondTap() = util.executeInComposition {
-        down(5f, 5f)
-            .up()
-
-        assertTrue(pressed)
-        assertTrue(released)
-        assertFalse(canceled)
-
-        tapped = false
-        pressed = false
-        released = false
-
-        val up2 = down(4f, 4f)
-            .up()
-        assertTrue(tapped)
-        assertTrue(up2.isConsumed)
-        assertTrue(pressed)
-        assertTrue(released)
-        assertFalse(canceled)
-    }
-
-    /**
-     * After a first tap, a second tap should also be detected.
-     */
-    @Test
-    fun secondTap_withShortcut() = utilWithShortcut.executeInComposition {
-        down(5f, 5f)
-            .up()
-
-        assertTrue(pressed)
-        assertTrue(released)
-        assertFalse(canceled)
-
-        tapped = false
-        pressed = false
-        released = false
-
-        val up2 = down(4f, 4f)
-            .up()
-        assertTrue(tapped)
-        assertTrue(up2.isConsumed)
-        assertTrue(pressed)
-        assertTrue(released)
-        assertFalse(canceled)
-    }
-
-    /**
-     * Clicking in the region with the up already consumed should result in the callback not
-     * being invoked.
-     */
-    @Test
-    fun consumedUpTap() = util.executeInComposition {
-        val down = down(5f, 5f)
-
-        assertFalse(tapped)
-        assertTrue(pressed)
-
-        down.up {
-            if (pressed != previousPressed) consume()
-        }
-
-        assertFalse(tapped)
-        assertFalse(released)
-        assertTrue(canceled)
-    }
-
-    /**
-     * Clicking in the region with the up already consumed should result in the callback not
-     * being invoked.
-     */
-    @Test
-    fun consumedUpTap_withShortcut() = utilWithShortcut.executeInComposition {
-        val down = down(5f, 5f)
-
-        assertFalse(tapped)
-        assertTrue(pressed)
-
-        down.up {
-            if (pressed != previousPressed) consume()
-        }
-
-        assertFalse(tapped)
-        assertFalse(released)
-        assertTrue(canceled)
-    }
-
-    /**
-     * Clicking in the region with the motion consumed should result in the callback not
-     * being invoked.
-     */
-    @Test
-    fun consumedMotionTap() = util.executeInComposition {
-        down(5f, 5f)
-            .moveTo(6f, 2f) {
-                consume()
-            }
-            .up(50)
-
-        assertFalse(tapped)
-        assertTrue(pressed)
-        assertFalse(released)
-        assertTrue(canceled)
-    }
-
-    /**
-     * Clicking in the region with the motion consumed should result in the callback not
-     * being invoked.
-     */
-    @Test
-    fun consumedMotionTap_withShortcut() = utilWithShortcut.executeInComposition {
-        down(5f, 5f)
-            .moveTo(6f, 2f) {
-                consume()
-            }
-            .up(50)
-
-        assertFalse(tapped)
-        assertTrue(pressed)
-        assertFalse(released)
-        assertTrue(canceled)
-    }
-
-    @Test
-    fun consumedChange_MotionTap() = util.executeInComposition {
-        down(5f, 5f)
-            .moveTo(6f, 2f) {
-                consume()
-            }
-            .up(50)
-
-        assertFalse(tapped)
-        assertTrue(pressed)
-        assertFalse(released)
-        assertTrue(canceled)
-    }
-
-    /**
-     * Clicking in the region with the up already consumed should result in the callback not
-     * being invoked.
-     */
-    @Test
-    fun consumedChange_upTap() = util.executeInComposition {
-        val down = down(5f, 5f)
-
-        assertFalse(tapped)
-        assertTrue(pressed)
-
-        down.up {
-            consume()
-        }
-
-        assertFalse(tapped)
-        assertFalse(released)
-        assertTrue(canceled)
-    }
-
-    /**
-     * Ensure that two-finger taps work.
-     */
-    @Test
-    fun twoFingerTap() = util.executeInComposition {
-        val down = down(1f, 1f)
-        assertTrue(down.isConsumed)
-
-        assertTrue(pressed)
-        pressed = false
-
-        val down2 = down(9f, 5f)
-        assertFalse(down2.isConsumed)
-        assertFalse(down2.isConsumed)
-
-        assertFalse(pressed)
-
-        val up = down.up()
-        assertFalse(up.isConsumed)
-        assertFalse(up.isConsumed)
-        assertFalse(tapped)
-        assertFalse(released)
-
-        val up2 = down2.up()
-        assertTrue(up2.isConsumed)
-        assertTrue(up2.isConsumed)
-
-        assertTrue(tapped)
-        assertTrue(released)
-        assertFalse(canceled)
-    }
-
-    /**
-     * Ensure that two-finger taps work.
-     */
-    @Test
-    fun twoFingerTap_withShortcut() = utilWithShortcut.executeInComposition {
-        val down = down(1f, 1f)
-        assertTrue(down.isConsumed)
-
-        assertTrue(pressed)
-        pressed = false
-
-        val down2 = down(9f, 5f)
-        assertFalse(down2.isConsumed)
-
-        assertFalse(pressed)
-
-        val up = down.up()
-        assertFalse(up.isConsumed)
-        assertFalse(tapped)
-        assertFalse(released)
-
-        val up2 = down2.up()
-        assertTrue(up2.isConsumed)
-
-        assertTrue(tapped)
-        assertTrue(released)
-        assertFalse(canceled)
-    }
-
-    /**
-     * A position change consumption on any finger should cause tap to cancel.
-     */
-    @Test
-    fun twoFingerTapCancel() = util.executeInComposition {
-        val down = down(1f, 1f)
-
-        assertTrue(pressed)
-
-        val down2 = down(9f, 5f)
-
-        val up = down.moveTo(5f, 5f) {
-            consume()
-        }.up()
-        assertFalse(up.isConsumed)
-
-        assertFalse(tapped)
-        assertTrue(canceled)
-
-        val up2 = down2.up(50)
-        assertFalse(up2.isConsumed)
-
-        assertFalse(tapped)
-        assertFalse(released)
-    }
-
-    /**
-     * A position change consumption on any finger should cause tap to cancel.
-     */
-    @Test
-    fun twoFingerTapCancel_withShortcut() = utilWithShortcut.executeInComposition {
-        val down = down(1f, 1f)
-
-        assertTrue(pressed)
-
-        val down2 = down(9f, 5f)
-
-        val up = down.moveTo(5f, 5f) {
-            consume()
-        }.up()
-        assertFalse(up.isConsumed)
-
-        assertFalse(tapped)
-        assertTrue(canceled)
-
-        val up2 = down2.up(50)
-        assertFalse(up2.isConsumed)
-
-        assertFalse(tapped)
-        assertFalse(released)
-    }
-
-    /**
-     * Detect the second tap as long press.
-     */
-    @Test
-    fun secondTapLongPress() = allGestures.executeInComposition {
-        down(5f, 5f).up()
-
-        assertTrue(pressed)
-        assertTrue(released)
-        assertFalse(canceled)
-        assertFalse(tapped)
-        assertFalse(doubleTapped)
-        assertFalse(longPressed)
-
-        pressed = false
-        released = false
-
-        val secondDown = down(5f, 5f, 50)
-
-        assertTrue(pressed)
-
-        delay(LongPressTimeoutMillis + 10)
-
-        assertTrue(tapped)
-        assertTrue(longPressed)
-        assertFalse(released)
-        assertFalse(canceled)
-
-        secondDown.up(500)
-        assertTrue(released)
-    }
-}
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/TransformGestureDetectorTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/TransformGestureDetectorTest.kt
deleted file mode 100644
index 77c4a91..0000000
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/TransformGestureDetectorTest.kt
+++ /dev/null
@@ -1,352 +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.foundation.gestures
-
-import androidx.compose.ui.geometry.Offset
-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.junit.runner.RunWith
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-class TransformGestureDetectorTest(val panZoomLock: Boolean) {
-    companion object {
-        @JvmStatic
-        @Parameterized.Parameters
-        fun parameters() = arrayOf(false, true)
-    }
-
-    private var centroid = Offset.Zero
-    private var panned = false
-    private var panAmount = Offset.Zero
-    private var rotated = false
-    private var rotateAmount = 0f
-    private var zoomed = false
-    private var zoomAmount = 1f
-
-    private val util = SuspendingGestureTestUtil {
-        detectTransformGestures(panZoomLock = panZoomLock) { c, pan, gestureZoom, gestureAngle ->
-            centroid = c
-            if (gestureAngle != 0f) {
-                rotated = true
-                rotateAmount += gestureAngle
-            }
-            if (gestureZoom != 1f) {
-                zoomed = true
-                zoomAmount *= gestureZoom
-            }
-            if (pan != Offset.Zero) {
-                panned = true
-                panAmount += pan
-            }
-        }
-    }
-
-    @Before
-    fun setup() {
-        panned = false
-        panAmount = Offset.Zero
-        rotated = false
-        rotateAmount = 0f
-        zoomed = false
-        zoomAmount = 1f
-    }
-
-    /**
-     * Single finger pan.
-     */
-    @Test
-    fun singleFingerPan() = util.executeInComposition {
-        val down = down(5f, 5f)
-        assertFalse(down.isConsumed)
-
-        assertFalse(panned)
-
-        val move1 = down.moveBy(Offset(12.7f, 12.7f))
-        assertFalse(move1.isConsumed)
-
-        assertFalse(panned)
-
-        val move2 = move1.moveBy(Offset(0.1f, 0.1f))
-        assertTrue(move2.isConsumed)
-
-        assertEquals(17.7f, centroid.x, 0.1f)
-        assertEquals(17.7f, centroid.y, 0.1f)
-        assertTrue(panned)
-        assertFalse(zoomed)
-        assertFalse(rotated)
-
-        assertTrue(panAmount.getDistance() < 1f)
-
-        panAmount = Offset.Zero
-        val move3 = move2.moveBy(Offset(1f, 0f))
-        assertTrue(move3.isConsumed)
-
-        assertEquals(Offset(1f, 0f), panAmount)
-
-        move3.up().also { assertFalse(it.isConsumed) }
-
-        assertFalse(rotated)
-        assertFalse(zoomed)
-    }
-
-    /**
-     * Multi-finger pan
-     */
-    @Test
-    fun multiFingerPanZoom() = util.executeInComposition {
-        val downA = down(5f, 5f)
-        assertFalse(downA.isConsumed)
-
-        val downB = down(25f, 25f)
-        assertFalse(downB.isConsumed)
-
-        assertFalse(panned)
-
-        val moveA1 = downA.moveBy(Offset(12.8f, 12.8f))
-        assertFalse(moveA1.isConsumed)
-
-        val moveB1 = downB.moveBy(Offset(12.8f, 12.8f))
-        // Now we've averaged enough movement
-        assertTrue(moveB1.isConsumed)
-
-        assertEquals((5f + 25f + 12.8f) / 2f, centroid.x, 0.1f)
-        assertEquals((5f + 25f + 12.8f) / 2f, centroid.y, 0.1f)
-        assertTrue(panned)
-        assertTrue(zoomed)
-        assertFalse(rotated)
-
-        assertEquals(6.4f, panAmount.x, 0.1f)
-        assertEquals(6.4f, panAmount.y, 0.1f)
-
-        moveA1.up()
-        moveB1.up()
-    }
-
-    /**
-     * 2-pointer zoom
-     */
-    @Test
-    fun zoom2Pointer() = util.executeInComposition {
-        val downA = down(5f, 5f)
-        assertFalse(downA.isConsumed)
-
-        val downB = down(25f, 5f)
-        assertFalse(downB.isConsumed)
-
-        val moveB1 = downB.moveBy(Offset(35.95f, 0f))
-        assertFalse(moveB1.isConsumed)
-
-        val moveB2 = moveB1.moveBy(Offset(0.1f, 0f))
-        assertTrue(moveB2.isConsumed)
-
-        assertTrue(panned)
-        assertTrue(zoomed)
-        assertFalse(rotated)
-
-        // both should be small movements
-        assertTrue(panAmount.getDistance() < 1f)
-        assertTrue(zoomAmount in 1f..1.1f)
-
-        zoomAmount = 1f
-        panAmount = Offset.Zero
-
-        val moveA1 = downA.moveBy(Offset(-1f, 0f))
-        assertTrue(moveA1.isConsumed)
-
-        val moveB3 = moveB2.moveBy(Offset(1f, 0f))
-        assertTrue(moveB3.isConsumed)
-
-        assertEquals(0f, panAmount.x, 0.01f)
-        assertEquals(0f, panAmount.y, 0.01f)
-
-        assertEquals(48f / 46f, zoomAmount, 0.01f)
-
-        moveA1.up()
-        moveB3.up()
-    }
-
-    /**
-     * 4-pointer zoom
-     */
-    @Test
-    fun zoom4Pointer() = util.executeInComposition {
-        val downA = down(0f, 50f)
-        // just get past the touch slop
-        val slop1 = downA.moveBy(Offset(-1000f, 0f))
-        val slop2 = slop1.moveBy(Offset(1000f, 0f))
-
-        panned = false
-        panAmount = Offset.Zero
-
-        val downB = down(100f, 50f)
-        val downC = down(50f, 0f)
-        val downD = down(50f, 100f)
-
-        val moveA = slop2.moveBy(Offset(-50f, 0f))
-        val moveB = downB.moveBy(Offset(50f, 0f))
-
-        assertTrue(zoomed)
-        assertTrue(panned)
-
-        assertEquals(0f, panAmount.x, 0.1f)
-        assertEquals(0f, panAmount.y, 0.1f)
-        assertEquals(1.5f, zoomAmount, 0.1f)
-
-        val moveC = downC.moveBy(Offset(0f, -50f))
-        val moveD = downD.moveBy(Offset(0f, 50f))
-
-        assertEquals(0f, panAmount.x, 0.1f)
-        assertEquals(0f, panAmount.y, 0.1f)
-        assertEquals(2f, zoomAmount, 0.1f)
-
-        moveA.up()
-        moveB.up()
-        moveC.up()
-        moveD.up()
-    }
-
-    /**
-     * 2 pointer rotation.
-     */
-    @Test
-    fun rotation2Pointer() = util.executeInComposition {
-        val downA = down(0f, 50f)
-        val downB = down(100f, 50f)
-        val moveA = downA.moveBy(Offset(50f, -50f))
-        val moveB = downB.moveBy(Offset(-50f, 50f))
-
-        // assume some of the above was touch slop
-        assertTrue(rotated)
-        rotateAmount = 0f
-        rotated = false
-        zoomAmount = 1f
-        panAmount = Offset.Zero
-
-        // now do the real rotation:
-        val moveA2 = moveA.moveBy(Offset(-50f, 50f))
-        val moveB2 = moveB.moveBy(Offset(50f, -50f))
-
-        moveA2.up()
-        moveB2.up()
-
-        assertTrue(rotated)
-        assertEquals(-90f, rotateAmount, 0.01f)
-        assertEquals(0f, panAmount.x, 0.1f)
-        assertEquals(0f, panAmount.y, 0.1f)
-        assertEquals(1f, zoomAmount, 0.1f)
-    }
-
-    /**
-     * 2 pointer rotation, with early panning.
-     */
-    @Test
-    fun rotation2PointerLock() = util.executeInComposition {
-        val downA = down(0f, 50f)
-        // just get past the touch slop with panning
-        val slop1 = downA.moveBy(Offset(-1000f, 0f))
-        val slop2 = slop1.moveBy(Offset(1000f, 0f))
-
-        val downB = down(100f, 50f)
-
-        // now do the rotation:
-        val moveA2 = slop2.moveBy(Offset(50f, -50f))
-        val moveB2 = downB.moveBy(Offset(-50f, 50f))
-
-        moveA2.up()
-        moveB2.up()
-
-        if (panZoomLock) {
-            assertFalse(rotated)
-        } else {
-            assertTrue(rotated)
-            assertEquals(90f, rotateAmount, 0.01f)
-        }
-        assertEquals(0f, panAmount.x, 0.1f)
-        assertEquals(0f, panAmount.y, 0.1f)
-        assertEquals(1f, zoomAmount, 0.1f)
-    }
-
-    /**
-     * Adding or removing a pointer won't change the current values
-     */
-    @Test
-    fun noChangeOnPointerDownUp() = util.executeInComposition {
-        val downA = down(0f, 50f)
-        val downB = down(100f, 50f)
-        val moveA = downA.moveBy(Offset(50f, -50f))
-        val moveB = downB.moveBy(Offset(-50f, 50f))
-
-        // now we've gotten past the touch slop
-        rotated = false
-        panned = false
-        zoomed = false
-
-        val downC = down(0f, 50f)
-
-        assertFalse(rotated)
-        assertFalse(panned)
-        assertFalse(zoomed)
-
-        val downD = down(100f, 50f)
-        assertFalse(rotated)
-        assertFalse(panned)
-        assertFalse(zoomed)
-
-        moveA.up()
-        moveB.up()
-        downC.up()
-        downD.up()
-
-        assertFalse(rotated)
-        assertFalse(panned)
-        assertFalse(zoomed)
-    }
-
-    /**
-     * Consuming position during touch slop will cancel the current gesture.
-     */
-    @Test
-    fun touchSlopCancel() = util.executeInComposition {
-        down(5f, 5f)
-            .moveBy(Offset(50f, 0f)) { consume() }
-            .up()
-
-        assertFalse(panned)
-        assertFalse(zoomed)
-        assertFalse(rotated)
-    }
-
-    /**
-     * Consuming position after touch slop will cancel the current gesture.
-     */
-    @Test
-    fun afterTouchSlopCancel() = util.executeInComposition {
-        down(5f, 5f)
-            .moveBy(Offset(50f, 0f))
-            .moveBy(Offset(50f, 0f)) { consume() }
-            .up()
-
-        assertTrue(panned)
-        assertFalse(zoomed)
-        assertFalse(rotated)
-        assertEquals(50f, panAmount.x, 0.1f)
-    }
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/PointerMoveDetectorTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/PointerMoveDetectorTest.kt
deleted file mode 100644
index aa03db7..0000000
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/PointerMoveDetectorTest.kt
+++ /dev/null
@@ -1,122 +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.text
-
-import androidx.compose.foundation.gestures.SuspendingGestureTestUtil
-import androidx.compose.ui.geometry.Offset
-import com.google.common.truth.Correspondence
-import com.google.common.truth.IterableSubject
-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 PointerMoveDetectorTest {
-    @Test
-    fun whenSimpleMovement_allMovesAreReported() {
-        val actualMoves = mutableListOf<Offset>()
-        SuspendingGestureTestUtil {
-            detectMoves { actualMoves.add(it) }
-        }.executeInComposition {
-            down(5f, 5f)
-                .moveTo(4f, 4f)
-                .moveTo(3f, 3f)
-                .moveTo(2f, 2f)
-                .moveTo(1f, 1f)
-                .up()
-
-            assertThat(actualMoves).hasEqualOffsets(
-                listOf(
-                    Offset(4f, 4f),
-                    Offset(3f, 3f),
-                    Offset(2f, 2f),
-                    Offset(1f, 1f),
-                )
-            )
-        }
-    }
-
-    @Test
-    fun whenMultiplePointers_onlyUseFirst() {
-        val actualMoves = mutableListOf<Offset>()
-        SuspendingGestureTestUtil {
-            detectMoves { actualMoves.add(it) }
-        }.executeInComposition {
-            var m1 = down(5f, 5f)
-            var m2 = down(6f, 6f)
-            m1 = m1.moveTo(4f, 4f)
-            m2 = m2.moveTo(7f, 7f)
-            m1 = m1.moveTo(3f, 3f)
-            m2 = m2.moveTo(8f, 8f)
-            m1 = m1.moveTo(2f, 2f)
-            m2 = m2.moveTo(9f, 9f)
-            m1.moveTo(1f, 1f)
-            m2.moveTo(10f, 10f)
-            m1.up()
-            m2.up()
-
-            assertThat(actualMoves).hasEqualOffsets(
-                listOf(
-                    Offset(4f, 4f),
-                    Offset(3f, 3f),
-                    Offset(2f, 2f),
-                    Offset(1f, 1f),
-                )
-            )
-        }
-    }
-
-    @Test
-    fun whenMultiplePointers_thenFirstReleases_handOffToNextPointer() {
-        val actualMoves = mutableListOf<Offset>()
-        SuspendingGestureTestUtil {
-            detectMoves { actualMoves.add(it) }
-        }.executeInComposition {
-            var m1 = down(5f, 5f) // ignored because not a move
-            m1 = m1.moveTo(4f, 4f) // used
-            m1 = m1.moveTo(3f, 3f) // used
-            var m2 = down(4f, 4f) // ignored because still tracking m1
-            m1 = m1.moveTo(2f, 2f) // used
-            m2 = m2.moveTo(3f, 3f) // ignored because still tracking m1
-            m1.up() // ignored because not a move
-            m2.moveTo(2f, 2f) // ignored because equal to the previous used move
-            m2.moveTo(1f, 1f) // used
-            m2.up() // ignored because not a move
-
-            assertThat(actualMoves).hasEqualOffsets(
-                listOf(
-                    Offset(4f, 4f),
-                    Offset(3f, 3f),
-                    Offset(2f, 2f),
-                    Offset(1f, 1f),
-                )
-            )
-        }
-    }
-
-    private fun IterableSubject.hasEqualOffsets(expectedMoves: List<Offset>) {
-        comparingElementsUsing(offsetCorrespondence)
-            .containsExactly(*expectedMoves.toTypedArray())
-            .inOrder()
-    }
-
-    private val offsetCorrespondence: Correspondence<Offset, Offset> = Correspondence.from(
-        { o1, o2 -> o1!!.x == o2!!.x && o1.y == o2.y },
-        "has the offset of",
-    )
-}
diff --git a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoApp.kt b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoApp.kt
index 140b281..d47a856 100644
--- a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoApp.kt
+++ b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoApp.kt
@@ -31,6 +31,7 @@
 import androidx.compose.foundation.layout.wrapContentHeight
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.text.NewTextRendering1_5
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.integration.demos.common.ActivityDemo
 import androidx.compose.integration.demos.common.ComposableDemo
@@ -51,6 +52,7 @@
 import androidx.compose.material3.Scaffold
 import androidx.compose.material3.Surface
 import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
 import androidx.compose.material3.TopAppBar
 import androidx.compose.material3.TopAppBarDefaults
 import androidx.compose.material3.TopAppBarScrollBehavior
@@ -252,6 +254,7 @@
             actions = {
                 AppBarIcons.Filter(onClick = onStartFiltering)
                 AppBarIcons.Settings(onClick = launchSettings)
+                AppBarIcons.NewTextToggler()
             }
         )
     }
@@ -282,6 +285,24 @@
             Icon(Icons.Filled.Settings, null)
         }
     }
+
+    @Suppress("DEPRECATION")
+    @Composable
+    fun NewTextToggler() {
+        val isNewText = NewTextRendering1_5
+        val onClick = {
+            NewTextRendering1_5 = !NewTextRendering1_5
+        }
+        if (isNewText) {
+            TextButton(onClick = onClick) {
+                Text("New\nText!")
+            }
+        } else {
+            TextButton(onClick = onClick) {
+                Text("Old\nText")
+            }
+        }
+    }
 }
 
 @Composable
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml b/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
index e228203..8d6b34e 100644
--- a/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
+++ b/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
@@ -34,10 +34,10 @@
         <activity
             android:name=".TrivialStartupActivity"
             android:exported="true">
-<!--            <intent-filter>-->
-<!--                <action android:name="android.intent.action.MAIN" />-->
-<!--                <category android:name="android.intent.category.LAUNCHER" />-->
-<!--            </intent-filter>-->
+            <!--            <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.TRIVIAL_STARTUP_ACTIVITY" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -54,10 +54,10 @@
         <activity
             android:name=".IoSettingsActivity"
             android:exported="true">
-<!--            <intent-filter>-->
-<!--                <action android:name="android.intent.action.MAIN" />-->
-<!--                <category android:name="android.intent.category.LAUNCHER" />-->
-<!--            </intent-filter>-->
+            <!--            <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.IO_SETTINGS_ACTIVITY" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -123,6 +123,26 @@
         </activity>
 
         <activity
+            android:name=".RecyclerViewAsCarouselActivity"
+            android:exported="true"
+            android:theme="@style/Theme.AppCompat">
+            <intent-filter>
+                <action android:name="androidx.compose.integration.macrobenchmark.target.RecyclerViewAsCarouselActivity" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name=".PagerAsCarouselActivity"
+            android:exported="true"
+            android:theme="@style/Theme.AppCompat">
+            <intent-filter>
+                <action android:name="androidx.compose.integration.macrobenchmark.target.PagerAsCarouselActivity" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <activity
             android:name=".PagerActivity"
             android:exported="true"
             android:theme="@style/Theme.AppCompat">
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/PagerAsCarouselActivity.kt b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/PagerAsCarouselActivity.kt
new file mode 100644
index 0000000..50fc66b
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/PagerAsCarouselActivity.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.integration.macrobenchmark.target
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.ExperimentalFoundationApi
+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.foundation.layout.height
+import androidx.compose.foundation.pager.HorizontalPager
+import androidx.compose.foundation.pager.PageSize
+import androidx.compose.foundation.pager.rememberPagerState
+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.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.dp
+
+class PagerAsCarouselActivity : ComponentActivity() {
+    @OptIn(ExperimentalFoundationApi::class)
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        val itemCount = intent.getIntExtra(ExtraItemCount, 3000)
+
+        setContent {
+            val pagerState = rememberPagerState()
+            Box(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .background(Color.White),
+                contentAlignment = Alignment.Center
+            ) {
+                HorizontalPager(
+                    modifier = Modifier
+                        .semantics { contentDescription = "Carousel" }
+                        .background(Color.White),
+                    state = pagerState,
+                    pageCount = itemCount,
+                    pageSize = PageSize.Fixed(200.dp)
+                ) {
+                    PagerItem(it)
+                }
+            }
+        }
+
+        launchIdlenessTracking()
+    }
+
+    companion object {
+        const val ExtraItemCount = "ITEM_COUNT"
+    }
+}
+
+@Composable
+private fun PagerItem(index: Int) {
+    Box(
+        modifier = Modifier
+            .fillMaxWidth()
+            .height(200.dp)
+            .background(Color.Black)
+    ) {
+        Text(text = index.toString(), color = Color.White)
+    }
+}
\ No newline at end of file
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/RecyclerViewAsCarouselActivity.kt b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/RecyclerViewAsCarouselActivity.kt
new file mode 100644
index 0000000..3766e35
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/RecyclerViewAsCarouselActivity.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.integration.macrobenchmark.target
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.PagerSnapHelper
+import androidx.recyclerview.widget.RecyclerView
+
+class RecyclerViewAsCarouselActivity : AppCompatActivity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_view_carousel)
+        val pager = findViewById<RecyclerView>(R.id.carousel)
+        pager.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
+        val itemCount = intent.getIntExtra(ExtraItemCount, 3000)
+        val adapter = RecyclerViewAdapter(itemCount)
+        val scroller = PagerSnapHelper()
+        scroller.attachToRecyclerView(pager)
+        pager.adapter = adapter
+        launchIdlenessTracking()
+    }
+
+    companion object {
+        const val ExtraItemCount = "ITEM_COUNT"
+    }
+}
+
+private class RecyclerViewAdapter(val items: Int) :
+    RecyclerView.Adapter<RecyclerViewAsPagerViewHolder>() {
+    override fun onCreateViewHolder(
+        parent: ViewGroup,
+        viewType: Int
+    ): RecyclerViewAsPagerViewHolder {
+        val view = LayoutInflater
+            .from(parent.context)
+            .inflate(R.layout.recycler_view_as_carousel_item, parent, false)
+
+        return RecyclerViewAsPagerViewHolder(view)
+    }
+
+    override fun onBindViewHolder(holder: RecyclerViewAsPagerViewHolder, position: Int) {
+        holder.bind(position.toString())
+    }
+
+    override fun getItemCount(): Int = items
+}
+
+private class RecyclerViewAsPagerViewHolder(val itemView: View) :
+    RecyclerView.ViewHolder(itemView) {
+    fun bind(item: String) {
+        itemView.findViewById<TextView>(R.id.view_carousel_item).text = item
+    }
+}
\ No newline at end of file
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/res/layout/activity_view_carousel.xml b/compose/integration-tests/macrobenchmark-target/src/main/res/layout/activity_view_carousel.xml
new file mode 100644
index 0000000..61e862c
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark-target/src/main/res/layout/activity_view_carousel.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 20233 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:background="#fff"
+    android:layout_gravity="center"
+    android:gravity="center"
+    android:layout_width="match_parent"
+    android:layout_height="400dp">
+
+    <androidx.viewpager2.widget.ViewPager2
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/carousel"
+        android:layout_gravity="center"
+        android:gravity="center"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+</FrameLayout>
\ No newline at end of file
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/res/layout/recycler_view_as_carousel_item.xml b/compose/integration-tests/macrobenchmark-target/src/main/res/layout/recycler_view_as_carousel_item.xml
new file mode 100644
index 0000000..03eb6bd
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark-target/src/main/res/layout/recycler_view_as_carousel_item.xml
@@ -0,0 +1,27 @@
+<?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.
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="200dp"
+    android:background="#000000"
+    android:layout_height="200dp"
+    android:layout_gravity="center">
+
+    <TextView
+        android:id="@+id/view_carousel_item"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+</FrameLayout>
\ No newline at end of file
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/PagerAsCarouselBenchmark.kt b/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/PagerAsCarouselBenchmark.kt
new file mode 100644
index 0000000..f28d5ac
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/PagerAsCarouselBenchmark.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.integration.macrobenchmark
+
+import android.content.Intent
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.FrameTimingMetric
+import androidx.benchmark.macro.junit4.MacrobenchmarkRule
+import androidx.test.filters.LargeTest
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Direction
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
+import androidx.testutils.createCompilationParams
+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 PagerAsCarouselBenchmark(
+    private val compilationMode: CompilationMode
+) {
+    @get:Rule
+    val benchmarkRule = MacrobenchmarkRule()
+
+    private lateinit var device: UiDevice
+
+    @Before
+    fun setUp() {
+        val instrumentation = InstrumentationRegistry.getInstrumentation()
+        device = UiDevice.getInstance(instrumentation)
+    }
+
+    @Test
+    fun scroll() {
+        val carousel = device.findObject(By.desc(ContentDescription))
+        benchmarkRule.performRepeatedScroll(PackageName, compilationMode, Action, carousel) {
+            device.wait(Until.findObject(By.desc(ComposeIdle)), 3000)
+        }
+    }
+
+    companion object {
+        private const val PackageName = "androidx.compose.integration.macrobenchmark.target"
+        private const val Action =
+            "androidx.compose.integration.macrobenchmark.target.PagerAsCarouselActivity"
+        private const val ContentDescription = "Carousel"
+        private const val ComposeIdle = "COMPOSE-IDLE"
+
+        @Parameterized.Parameters(name = "compilation={0}")
+        @JvmStatic
+        fun parameters() = createCompilationParams()
+    }
+}
+
+internal fun MacrobenchmarkRule.performRepeatedScroll(
+    packageName: String,
+    compilationMode: CompilationMode,
+    intentAction: String,
+    targetComponent: UiObject2,
+    repeatCount: Int = 10,
+    onSwipeFinished: () -> Unit
+) {
+    measureRepeated(
+        packageName = packageName,
+        metrics = listOf(FrameTimingMetric()),
+        compilationMode = compilationMode,
+        iterations = 10,
+        setupBlock = {
+            val intent = Intent()
+            intent.action = intentAction
+            startActivityAndWait(intent)
+        }
+    ) {
+        // Setting a gesture margin is important otherwise gesture nav is triggered.
+        targetComponent.setGestureMargin(device.displayWidth / 5)
+        for (i in 1..repeatCount) {
+            targetComponent.swipe(Direction.LEFT, 1.0f)
+            onSwipeFinished()
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/RecyclerViewAsCarouselBenchmark.kt b/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/RecyclerViewAsCarouselBenchmark.kt
new file mode 100644
index 0000000..ac953f6
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/RecyclerViewAsCarouselBenchmark.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.integration.macrobenchmark
+
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.junit4.MacrobenchmarkRule
+import androidx.test.filters.LargeTest
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.testutils.createCompilationParams
+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 RecyclerViewAsCarouselBenchmark(
+    private val compilationMode: CompilationMode
+) {
+    @get:Rule
+    val benchmarkRule = MacrobenchmarkRule()
+    private lateinit var device: UiDevice
+
+    @Before
+    fun setUp() {
+        val instrumentation = InstrumentationRegistry.getInstrumentation()
+        device = UiDevice.getInstance(instrumentation)
+    }
+
+    @Test
+    fun scroll() {
+        val carousel = device.findObject(
+            By.res(
+                PackageName,
+                ResourceId
+            )
+        )
+        benchmarkRule.performRepeatedScroll(PackageName, compilationMode, Action, carousel) {
+            device.waitForIdle()
+        }
+    }
+
+    companion object {
+        private const val PackageName = "androidx.compose.integration.macrobenchmark.target"
+        private const val Action =
+            "androidx.compose.integration.macrobenchmark.target.RecyclerViewAsCarouselActivity"
+        private const val ResourceId = "carousel"
+
+        @Parameterized.Parameters(name = "compilation={0}")
+        @JvmStatic
+        fun parameters() = createCompilationParams()
+    }
+}
\ No newline at end of file
diff --git a/compose/integration-tests/material-catalog/src/main/AndroidManifest.xml b/compose/integration-tests/material-catalog/src/main/AndroidManifest.xml
index eb40d79..9b0c25a 100644
--- a/compose/integration-tests/material-catalog/src/main/AndroidManifest.xml
+++ b/compose/integration-tests/material-catalog/src/main/AndroidManifest.xml
@@ -21,6 +21,7 @@
     <application
         android:label="@string/compose_material_catalog"
         android:icon="@mipmap/ic_launcher"
+        android:supportsRtl="true"
         android:theme="@style/Theme.Catalog">
         <activity android:name=".CatalogActivity" android:exported="true">
             <intent-filter>
diff --git a/compose/material/OWNERS b/compose/material/OWNERS
index f6b40b2..8c2dd0a 100644
--- a/compose/material/OWNERS
+++ b/compose/material/OWNERS
@@ -7,3 +7,4 @@
 lpf@google.com
 soboleva@google.com
 sgibly@google.com
+jossiwolf@google.com
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 82ec5ec..ccbd6ff 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
@@ -33,6 +33,7 @@
 import androidx.compose.material.samples.ButtonWithIconSample
 import androidx.compose.material.samples.CardSample
 import androidx.compose.material.samples.CheckboxSample
+import androidx.compose.material.samples.ChipGroupReflowSample
 import androidx.compose.material.samples.ChipGroupSingleLineSample
 import androidx.compose.material.samples.ChipSample
 import androidx.compose.material.samples.CircularProgressIndicatorSample
@@ -300,6 +301,13 @@
         sourceUrl = ChipsExampleSourceUrl
     ) {
         ChipGroupSingleLineSample()
+    },
+    Example(
+        name = ::ChipGroupReflowSample.name,
+        description = ChipsExampleDescription,
+        sourceUrl = ChipsExampleSourceUrl
+    ) {
+        ChipGroupReflowSample()
     }
 )
 
diff --git a/compose/material/material/samples/build.gradle b/compose/material/material/samples/build.gradle
index eda78e0..0b5f612 100644
--- a/compose/material/material/samples/build.gradle
+++ b/compose/material/material/samples/build.gradle
@@ -32,7 +32,7 @@
 
     implementation("androidx.compose.animation:animation:1.2.1")
     implementation("androidx.compose.foundation:foundation:1.2.1")
-    implementation("androidx.compose.foundation:foundation-layout:1.2.1")
+    implementation("androidx.compose.foundation:foundation-layout:1.4.0-beta02")
     implementation(project(":compose:material:material"))
     implementation("androidx.compose.runtime:runtime:1.2.1")
     implementation("androidx.compose.ui:ui:1.2.1")
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ChipSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ChipSamples.kt
index c72e08d..14a45d5 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ChipSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ChipSamples.kt
@@ -18,10 +18,15 @@
 
 import androidx.annotation.Sampled
 import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ExperimentalLayoutApi
+import androidx.compose.foundation.layout.FlowRow
 import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.layout.wrapContentHeight
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.material.Chip
 import androidx.compose.material.ChipDefaults
@@ -143,7 +148,7 @@
         Row(modifier = Modifier.horizontalScroll(rememberScrollState())) {
             repeat(9) { index ->
                 Chip(
-                    modifier = Modifier.padding(horizontal = 8.dp),
+                    modifier = Modifier.padding(horizontal = 4.dp),
                     onClick = { /* do something*/ }) {
                     Text("Chip $index")
                 }
@@ -151,3 +156,26 @@
         }
     }
 }
+
+@Sampled
+@OptIn(ExperimentalMaterialApi::class, ExperimentalLayoutApi::class)
+@Composable
+fun ChipGroupReflowSample() {
+    Column() {
+        FlowRow(
+            Modifier
+                .fillMaxWidth(1f)
+                .wrapContentHeight(align = Alignment.Top),
+            verticalAlignment = Alignment.CenterVertically,
+            horizontalArrangement = Arrangement.Start,
+        ) {
+            repeat(10) { index ->
+                Chip(
+                    modifier = Modifier.padding(horizontal = 4.dp),
+                    onClick = { /* do something*/ }) {
+                    Text("Chip $index")
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomSheetScaffoldTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomSheetScaffoldTest.kt
index 75ab9f3..72632ca 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomSheetScaffoldTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomSheetScaffoldTest.kt
@@ -488,4 +488,21 @@
             Truth.assertThat(innerPadding.calculateBottomPadding()).isEqualTo(peekHeight)
         }
     }
+
+    /*
+     * This is a validity check for b/235588730. We can not verify actual placement behavior in this
+     * test as it would require child composables.
+     */
+    @Test
+    fun bottomSheetScaffold_emptySlots_doesNotCrash() {
+        rule.setMaterialContent {
+            BottomSheetScaffold(
+                sheetContent = { },
+                topBar = { },
+                snackbarHost = { },
+                floatingActionButton = { },
+                content = { }
+            )
+        }
+    }
 }
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/pullrefresh/PullRefreshIndicatorTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/pullrefresh/PullRefreshIndicatorTest.kt
index 98399ac..9055b1d 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/pullrefresh/PullRefreshIndicatorTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/pullrefresh/PullRefreshIndicatorTest.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.material.pullrefresh
 
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.lazy.LazyColumn
@@ -26,6 +28,9 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.layout.onSizeChanged
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertIsNotDisplayed
@@ -33,8 +38,10 @@
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onChild
 import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performClick
 import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.test.swipeDown
+import androidx.compose.ui.unit.IntSize
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
@@ -162,6 +169,50 @@
         assertThat(indicatorNode.getUnclippedBoundsInRoot()).isEqualTo(restingBounds)
     }
 
+    // Regression test for b/271777421
+    @Test
+    fun indicatorDoesNotCapturePointerEvents() {
+        var indicatorSize: IntSize? = null
+        lateinit var state: PullRefreshState
+        var downEvent: PointerInputChange? = null
+
+        rule.setContent {
+            state = rememberPullRefreshState(false, {})
+
+            Box {
+                Box(Modifier.fillMaxSize().pointerInput(Unit) {
+                    awaitEachGesture {
+                        downEvent = awaitFirstDown()
+                    }
+                })
+                PullRefreshIndicator(
+                    refreshing = false,
+                    state = state,
+                    modifier = Modifier.onSizeChanged {
+                        // The indicator starts as offset by its negative height in the y direction,
+                        // so work out its height so we can place it inside its normal layout
+                        // bounds
+                        indicatorSize = it
+                    }.testTag(IndicatorTag)
+                )
+            }
+        }
+
+        rule.runOnIdle {
+            // Pull by twice the indicator height (since pull delta is halved) - this will make the
+            // indicator fully visible in its layout bounds, so when we performClick() the indicator
+            // will be visibly inside those coordinates.
+            state.onPull(indicatorSize!!.height.toFloat() * 2)
+        }
+
+        rule.onNodeWithTag(IndicatorTag).performClick()
+        rule.runOnIdle {
+            // The indicator should not have blocked its sibling (placed first, so below) from
+            // seeing touch events.
+            assertThat(downEvent).isNotNull()
+        }
+    }
+
     private val pullRefreshNode get() = rule.onNodeWithTag(PullRefreshTag)
     private val indicatorNode get() = rule.onNodeWithTag(IndicatorTag).onChild()
 }
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 7ffe514..2f3f657 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
@@ -46,6 +46,8 @@
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastMaxBy
 import kotlin.math.roundToInt
 import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.launch
@@ -489,24 +491,28 @@
         val layoutWidth = constraints.maxWidth
         val layoutHeight = constraints.maxHeight
         val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
-        val sheetPlaceable = subcompose(BottomSheetScaffoldLayoutSlot.Sheet) {
+
+        val sheetPlaceables = subcompose(BottomSheetScaffoldLayoutSlot.Sheet) {
             bottomSheet(layoutHeight)
-        }[0].measure(looseConstraints)
+        }.map { it.measure(looseConstraints) }
         val sheetOffsetY = sheetOffset().roundToInt()
-        val topBarPlaceable = topBar?.let {
-            subcompose(BottomSheetScaffoldLayoutSlot.TopBar) { topBar() }[0]
-                .measure(looseConstraints)
+
+        val topBarPlaceables = topBar?.let {
+            subcompose(BottomSheetScaffoldLayoutSlot.TopBar, topBar)
+                .map { it.measure(looseConstraints) }
         }
-        val topBarHeight = topBarPlaceable?.height ?: 0
+        val topBarHeight = topBarPlaceables?.fastMaxBy { it.height }?.height ?: 0
+
         val bodyConstraints = looseConstraints.copy(maxHeight = layoutHeight - topBarHeight)
-        val bodyPlaceable = subcompose(BottomSheetScaffoldLayoutSlot.Body) {
+        val bodyPlaceables = subcompose(BottomSheetScaffoldLayoutSlot.Body) {
             body(PaddingValues(bottom = sheetPeekHeight))
-        }[0].measure(bodyConstraints)
+        }.map { it.measure(bodyConstraints) }
+
         val fabPlaceable = floatingActionButton?.let { fab ->
-            subcompose(BottomSheetScaffoldLayoutSlot.Fab, fab)[0].measure(looseConstraints)
+            subcompose(BottomSheetScaffoldLayoutSlot.Fab, fab).map { it.measure(looseConstraints) }
         }
-        val fabWidth = fabPlaceable?.width ?: 0
-        val fabHeight = fabPlaceable?.height ?: 0
+        val fabWidth = fabPlaceable?.fastMaxBy { it.width }?.width ?: 0
+        val fabHeight = fabPlaceable?.fastMaxBy { it.height }?.height ?: 0
         val fabOffsetX = when (floatingActionButtonPosition) {
             FabPosition.Center -> (layoutWidth - fabWidth) / 2
             else -> layoutWidth - fabWidth - FabSpacing.roundToPx()
@@ -515,20 +521,23 @@
         val fabOffsetY = if (sheetPeekHeight.toPx() < fabHeight / 2) {
             sheetOffsetY - fabHeight - FabSpacing.roundToPx()
         } else sheetOffsetY - (fabHeight / 2)
-        val snackbarPlaceable = subcompose(BottomSheetScaffoldLayoutSlot.Snackbar, snackbarHost)[0]
-            .measure(looseConstraints)
-        val snackbarOffsetX = (layoutWidth - snackbarPlaceable.width) / 2
+
+        val snackbarPlaceables = subcompose(BottomSheetScaffoldLayoutSlot.Snackbar, snackbarHost)
+            .map { it.measure(looseConstraints) }
+        val snackbarWidth = snackbarPlaceables.fastMaxBy { it.width }?.width ?: 0
+        val snackbarHeight = snackbarPlaceables.fastMaxBy { it.height }?.height ?: 0
+        val snackbarOffsetX = (layoutWidth - snackbarWidth) / 2
         val snackbarOffsetY = when (sheetState.currentValue) {
-            Collapsed -> fabOffsetY - snackbarPlaceable.height
-            Expanded -> layoutHeight - snackbarPlaceable.height
+            Collapsed -> fabOffsetY - snackbarHeight
+            Expanded -> layoutHeight - snackbarHeight
         }
         layout(layoutWidth, layoutHeight) {
             // Placement order is important for elevation
-            bodyPlaceable.placeRelative(0, topBarHeight)
-            topBarPlaceable?.placeRelative(0, 0)
-            sheetPlaceable.placeRelative(0, sheetOffsetY)
-            fabPlaceable?.placeRelative(fabOffsetX, fabOffsetY)
-            snackbarPlaceable.placeRelative(snackbarOffsetX, snackbarOffsetY)
+            bodyPlaceables.fastForEach { it.placeRelative(0, topBarHeight) }
+            topBarPlaceables?.fastForEach { it.placeRelative(0, 0) }
+            sheetPlaceables.fastForEach { it.placeRelative(0, sheetOffsetY) }
+            fabPlaceable?.fastForEach { it.placeRelative(fabOffsetX, fabOffsetY) }
+            snackbarPlaceables.fastForEach { it.placeRelative(snackbarOffsetX, snackbarOffsetY) }
         }
     }
 }
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/pullrefresh/PullRefreshIndicator.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/pullrefresh/PullRefreshIndicator.kt
index 082cd36..e8c1323 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/pullrefresh/PullRefreshIndicator.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/pullrefresh/PullRefreshIndicator.kt
@@ -21,14 +21,15 @@
 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
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.material.CircularProgressIndicator
 import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.LocalElevationOverlay
 import androidx.compose.material.MaterialTheme
-import androidx.compose.material.Surface
 import androidx.compose.material.contentColorFor
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
@@ -37,6 +38,7 @@
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.shadow
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.geometry.center
@@ -82,13 +84,19 @@
         derivedStateOf { refreshing || state.position > 0.5f }
     }
 
-    Surface(
+    // Apply an elevation overlay if needed. Note that we aren't using Surface here, as we do not
+    // want its input-blocking behaviour, since the indicator is typically displayed above other
+    // (possibly) interactive content.
+    val elevationOverlay = LocalElevationOverlay.current
+    val color = elevationOverlay?.apply(color = backgroundColor, elevation = Elevation)
+        ?: backgroundColor
+
+    Box(
         modifier = modifier
             .size(IndicatorSize)
-            .pullRefreshIndicatorTransform(state, scale),
-        shape = SpinnerShape,
-        color = backgroundColor,
-        elevation = if (showElevation) Elevation else 0.dp,
+            .pullRefreshIndicatorTransform(state, scale)
+            .shadow(if (showElevation) Elevation else 0.dp, SpinnerShape, clip = true)
+            .background(color = color, shape = SpinnerShape)
     ) {
         Crossfade(
             targetState = refreshing,
diff --git a/compose/material3/material3-window-size-class/api/1.1.0-beta01.txt b/compose/material3/material3-window-size-class/api/1.1.0-beta01.txt
new file mode 100644
index 0000000..cc66d9f
--- /dev/null
+++ b/compose/material3/material3-window-size-class/api/1.1.0-beta01.txt
@@ -0,0 +1,44 @@
+// Signature format: 4.0
+package androidx.compose.material3.windowsizeclass {
+
+  @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class WindowHeightSizeClass implements java.lang.Comparable<androidx.compose.material3.windowsizeclass.WindowHeightSizeClass> {
+    method public operator int compareTo(int other);
+    field public static final androidx.compose.material3.windowsizeclass.WindowHeightSizeClass.Companion Companion;
+  }
+
+  public static final class WindowHeightSizeClass.Companion {
+    method public int getCompact();
+    method public int getExpanded();
+    method public int getMedium();
+    property public final int Compact;
+    property public final int Expanded;
+    property public final int Medium;
+  }
+
+  @androidx.compose.runtime.Immutable public final class WindowSizeClass {
+    method public int getHeightSizeClass();
+    method public int getWidthSizeClass();
+    property public final int heightSizeClass;
+    property public final int widthSizeClass;
+    field public static final androidx.compose.material3.windowsizeclass.WindowSizeClass.Companion Companion;
+  }
+
+  public static final class WindowSizeClass.Companion {
+  }
+
+  @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class WindowWidthSizeClass implements java.lang.Comparable<androidx.compose.material3.windowsizeclass.WindowWidthSizeClass> {
+    method public operator int compareTo(int other);
+    field public static final androidx.compose.material3.windowsizeclass.WindowWidthSizeClass.Companion Companion;
+  }
+
+  public static final class WindowWidthSizeClass.Companion {
+    method public int getCompact();
+    method public int getExpanded();
+    method public int getMedium();
+    property public final int Compact;
+    property public final int Expanded;
+    property public final int Medium;
+  }
+
+}
+
diff --git a/compose/material3/material3-window-size-class/api/public_plus_experimental_1.1.0-beta01.txt b/compose/material3/material3-window-size-class/api/public_plus_experimental_1.1.0-beta01.txt
new file mode 100644
index 0000000..88ceafc
--- /dev/null
+++ b/compose/material3/material3-window-size-class/api/public_plus_experimental_1.1.0-beta01.txt
@@ -0,0 +1,52 @@
+// Signature format: 4.0
+package androidx.compose.material3.windowsizeclass {
+
+  public final class AndroidWindowSizeClass_androidKt {
+    method @androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi @androidx.compose.runtime.Composable public static androidx.compose.material3.windowsizeclass.WindowSizeClass calculateWindowSizeClass(android.app.Activity activity);
+  }
+
+  @kotlin.RequiresOptIn(message="This material3-window-size-class API is experimental and is likely to change or to " + "be removed in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalMaterial3WindowSizeClassApi {
+  }
+
+  @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class WindowHeightSizeClass implements java.lang.Comparable<androidx.compose.material3.windowsizeclass.WindowHeightSizeClass> {
+    method public operator int compareTo(int other);
+    field public static final androidx.compose.material3.windowsizeclass.WindowHeightSizeClass.Companion Companion;
+  }
+
+  public static final class WindowHeightSizeClass.Companion {
+    method public int getCompact();
+    method public int getExpanded();
+    method public int getMedium();
+    property public final int Compact;
+    property public final int Expanded;
+    property public final int Medium;
+  }
+
+  @androidx.compose.runtime.Immutable public final class WindowSizeClass {
+    method public int getHeightSizeClass();
+    method public int getWidthSizeClass();
+    property public final int heightSizeClass;
+    property public final int widthSizeClass;
+    field public static final androidx.compose.material3.windowsizeclass.WindowSizeClass.Companion Companion;
+  }
+
+  public static final class WindowSizeClass.Companion {
+    method @androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi @org.jetbrains.annotations.TestOnly public androidx.compose.material3.windowsizeclass.WindowSizeClass calculateFromSize(long size);
+  }
+
+  @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class WindowWidthSizeClass implements java.lang.Comparable<androidx.compose.material3.windowsizeclass.WindowWidthSizeClass> {
+    method public operator int compareTo(int other);
+    field public static final androidx.compose.material3.windowsizeclass.WindowWidthSizeClass.Companion Companion;
+  }
+
+  public static final class WindowWidthSizeClass.Companion {
+    method public int getCompact();
+    method public int getExpanded();
+    method public int getMedium();
+    property public final int Compact;
+    property public final int Expanded;
+    property public final int Medium;
+  }
+
+}
+
diff --git a/ads/ads-identifier-common/api/res-current.txt b/compose/material3/material3-window-size-class/api/res-1.1.0-beta01.txt
similarity index 100%
copy from ads/ads-identifier-common/api/res-current.txt
copy to compose/material3/material3-window-size-class/api/res-1.1.0-beta01.txt
diff --git a/compose/material3/material3-window-size-class/api/restricted_1.1.0-beta01.txt b/compose/material3/material3-window-size-class/api/restricted_1.1.0-beta01.txt
new file mode 100644
index 0000000..cc66d9f
--- /dev/null
+++ b/compose/material3/material3-window-size-class/api/restricted_1.1.0-beta01.txt
@@ -0,0 +1,44 @@
+// Signature format: 4.0
+package androidx.compose.material3.windowsizeclass {
+
+  @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class WindowHeightSizeClass implements java.lang.Comparable<androidx.compose.material3.windowsizeclass.WindowHeightSizeClass> {
+    method public operator int compareTo(int other);
+    field public static final androidx.compose.material3.windowsizeclass.WindowHeightSizeClass.Companion Companion;
+  }
+
+  public static final class WindowHeightSizeClass.Companion {
+    method public int getCompact();
+    method public int getExpanded();
+    method public int getMedium();
+    property public final int Compact;
+    property public final int Expanded;
+    property public final int Medium;
+  }
+
+  @androidx.compose.runtime.Immutable public final class WindowSizeClass {
+    method public int getHeightSizeClass();
+    method public int getWidthSizeClass();
+    property public final int heightSizeClass;
+    property public final int widthSizeClass;
+    field public static final androidx.compose.material3.windowsizeclass.WindowSizeClass.Companion Companion;
+  }
+
+  public static final class WindowSizeClass.Companion {
+  }
+
+  @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class WindowWidthSizeClass implements java.lang.Comparable<androidx.compose.material3.windowsizeclass.WindowWidthSizeClass> {
+    method public operator int compareTo(int other);
+    field public static final androidx.compose.material3.windowsizeclass.WindowWidthSizeClass.Companion Companion;
+  }
+
+  public static final class WindowWidthSizeClass.Companion {
+    method public int getCompact();
+    method public int getExpanded();
+    method public int getMedium();
+    property public final int Compact;
+    property public final int Expanded;
+    property public final int Medium;
+  }
+
+}
+
diff --git a/compose/material3/material3/api/1.1.0-beta01.txt b/compose/material3/material3/api/1.1.0-beta01.txt
new file mode 100644
index 0000000..7e2de6f
--- /dev/null
+++ b/compose/material3/material3/api/1.1.0-beta01.txt
@@ -0,0 +1,856 @@
+// Signature format: 4.0
+package androidx.compose.material3 {
+
+  public final class AlertDialogDefaults {
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method @androidx.compose.runtime.Composable public long getIconContentColor();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method @androidx.compose.runtime.Composable public long getTextContentColor();
+    method @androidx.compose.runtime.Composable public long getTitleContentColor();
+    method public float getTonalElevation();
+    property public final float TonalElevation;
+    property @androidx.compose.runtime.Composable public final long containerColor;
+    property @androidx.compose.runtime.Composable public final long iconContentColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    property @androidx.compose.runtime.Composable public final long textContentColor;
+    property @androidx.compose.runtime.Composable public final long titleContentColor;
+    field public static final androidx.compose.material3.AlertDialogDefaults INSTANCE;
+  }
+
+  public final class AndroidAlertDialog_androidKt {
+    method @androidx.compose.runtime.Composable public static void AlertDialog(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function0<kotlin.Unit> confirmButton, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? dismissButton, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? title, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long iconContentColor, optional long titleContentColor, optional long textContentColor, optional float tonalElevation, optional androidx.compose.ui.window.DialogProperties properties);
+  }
+
+  public final class AndroidMenu_androidKt {
+    method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean enabled, optional androidx.compose.material3.MenuItemColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  public final class AppBarKt {
+    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);
+    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);
+  }
+
+  public final class AssistChipDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipBorder assistChipBorder(optional long borderColor, optional long disabledBorderColor, optional float borderWidth);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipColors assistChipColors(optional long containerColor, optional long labelColor, optional long leadingIconContentColor, optional long trailingIconContentColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledLeadingIconContentColor, optional long disabledTrailingIconContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipElevation assistChipElevation(optional float elevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipColors elevatedAssistChipColors(optional long containerColor, optional long labelColor, optional long leadingIconContentColor, optional long trailingIconContentColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledLeadingIconContentColor, optional long disabledTrailingIconContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipElevation elevatedAssistChipElevation(optional float elevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method public float getHeight();
+    method public float getIconSize();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    property public final float Height;
+    property public final float IconSize;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    field public static final androidx.compose.material3.AssistChipDefaults INSTANCE;
+  }
+
+  public final class BottomAppBarDefaults {
+    method @androidx.compose.runtime.Composable public long getBottomAppBarFabColor();
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method public float getContainerElevation();
+    method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    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 @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;
+    field public static final androidx.compose.material3.BottomAppBarDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Immutable public final class ButtonColors {
+  }
+
+  public final class ButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonColors buttonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonElevation buttonElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float disabledElevation);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonColors elevatedButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonElevation elevatedButtonElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float disabledElevation);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonColors filledTonalButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonElevation filledTonalButtonElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float disabledElevation);
+    method public androidx.compose.foundation.layout.PaddingValues getButtonWithIconContentPadding();
+    method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getElevatedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFilledTonalShape();
+    method public float getIconSize();
+    method public float getIconSpacing();
+    method public float getMinHeight();
+    method public float getMinWidth();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke getOutlinedButtonBorder();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getOutlinedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method public androidx.compose.foundation.layout.PaddingValues getTextButtonContentPadding();
+    method public androidx.compose.foundation.layout.PaddingValues getTextButtonWithIconContentPadding();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getTextShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonColors outlinedButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonColors textButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    property public final androidx.compose.foundation.layout.PaddingValues ButtonWithIconContentPadding;
+    property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
+    property public final float IconSize;
+    property public final float IconSpacing;
+    property public final float MinHeight;
+    property public final float MinWidth;
+    property public final androidx.compose.foundation.layout.PaddingValues TextButtonContentPadding;
+    property public final androidx.compose.foundation.layout.PaddingValues TextButtonWithIconContentPadding;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape elevatedShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape filledTonalShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.BorderStroke outlinedButtonBorder;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape outlinedShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape textShape;
+    field public static final androidx.compose.material3.ButtonDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Stable public final class ButtonElevation {
+  }
+
+  public final class ButtonKt {
+    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ElevatedButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void FilledTonalButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void OutlinedButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void TextButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+  }
+
+  @androidx.compose.runtime.Immutable public final class CardColors {
+  }
+
+  public final class CardDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CardColors cardColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CardElevation cardElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CardColors elevatedCardColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CardElevation elevatedCardElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getElevatedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getOutlinedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke outlinedCardBorder(optional boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CardColors outlinedCardColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CardElevation outlinedCardElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape elevatedShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape outlinedShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    field public static final androidx.compose.material3.CardDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Immutable public final class CardElevation {
+  }
+
+  public final class CardKt {
+    method @androidx.compose.runtime.Composable public static void Card(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.CardColors colors, optional androidx.compose.material3.CardElevation elevation, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ElevatedCard(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.CardColors colors, optional androidx.compose.material3.CardElevation elevation, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void OutlinedCard(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.CardColors colors, optional androidx.compose.material3.CardElevation elevation, optional androidx.compose.foundation.BorderStroke border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+  }
+
+  @androidx.compose.runtime.Immutable public final class CheckboxColors {
+  }
+
+  public final class CheckboxDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CheckboxColors colors(optional long checkedColor, optional long uncheckedColor, optional long checkmarkColor, optional long disabledCheckedColor, optional long disabledUncheckedColor, optional long disabledIndeterminateColor);
+    field public static final androidx.compose.material3.CheckboxDefaults INSTANCE;
+  }
+
+  public final class CheckboxKt {
+    method @androidx.compose.runtime.Composable public static void Checkbox(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.CheckboxColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void TriStateCheckbox(androidx.compose.ui.state.ToggleableState state, kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.CheckboxColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  @androidx.compose.runtime.Immutable public final class ChipBorder {
+  }
+
+  @androidx.compose.runtime.Immutable public final class ChipColors {
+  }
+
+  @androidx.compose.runtime.Immutable public final class ChipElevation {
+  }
+
+  public final class ChipKt {
+    method @androidx.compose.runtime.Composable public static void AssistChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipColors colors, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void ElevatedAssistChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipColors colors, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void ElevatedSuggestionChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipColors colors, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void SuggestionChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipColors colors, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  @androidx.compose.runtime.Stable public final class ColorScheme {
+    ctor public ColorScheme(long primary, long onPrimary, long primaryContainer, long onPrimaryContainer, long inversePrimary, long secondary, long onSecondary, long secondaryContainer, long onSecondaryContainer, long tertiary, long onTertiary, long tertiaryContainer, long onTertiaryContainer, long background, long onBackground, long surface, long onSurface, long surfaceVariant, long onSurfaceVariant, long surfaceTint, long inverseSurface, long inverseOnSurface, long error, long onError, long errorContainer, long onErrorContainer, long outline, long outlineVariant, long scrim);
+    method public androidx.compose.material3.ColorScheme copy(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 long getBackground();
+    method public long getError();
+    method public long getErrorContainer();
+    method public long getInverseOnSurface();
+    method public long getInversePrimary();
+    method public long getInverseSurface();
+    method public long getOnBackground();
+    method public long getOnError();
+    method public long getOnErrorContainer();
+    method public long getOnPrimary();
+    method public long getOnPrimaryContainer();
+    method public long getOnSecondary();
+    method public long getOnSecondaryContainer();
+    method public long getOnSurface();
+    method public long getOnSurfaceVariant();
+    method public long getOnTertiary();
+    method public long getOnTertiaryContainer();
+    method public long getOutline();
+    method public long getOutlineVariant();
+    method public long getPrimary();
+    method public long getPrimaryContainer();
+    method public long getScrim();
+    method public long getSecondary();
+    method public long getSecondaryContainer();
+    method public long getSurface();
+    method public long getSurfaceTint();
+    method public long getSurfaceVariant();
+    method public long getTertiary();
+    method public long getTertiaryContainer();
+    property public final long background;
+    property public final long error;
+    property public final long errorContainer;
+    property public final long inverseOnSurface;
+    property public final long inversePrimary;
+    property public final long inverseSurface;
+    property public final long onBackground;
+    property public final long onError;
+    property public final long onErrorContainer;
+    property public final long onPrimary;
+    property public final long onPrimaryContainer;
+    property public final long onSecondary;
+    property public final long onSecondaryContainer;
+    property public final long onSurface;
+    property public final long onSurfaceVariant;
+    property public final long onTertiary;
+    property public final long onTertiaryContainer;
+    property public final long outline;
+    property public final long outlineVariant;
+    property public final long primary;
+    property public final long primaryContainer;
+    property public final long scrim;
+    property public final long secondary;
+    property public final long secondaryContainer;
+    property public final long surface;
+    property public final long surfaceTint;
+    property public final long surfaceVariant;
+    property public final long tertiary;
+    property public final long tertiaryContainer;
+  }
+
+  public final class ColorSchemeKt {
+    method public static long contentColorFor(androidx.compose.material3.ColorScheme, long backgroundColor);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static long contentColorFor(long backgroundColor);
+    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);
+    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);
+    method public static long surfaceColorAtElevation(androidx.compose.material3.ColorScheme, float elevation);
+  }
+
+  public final class ContentColorKt {
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> getLocalContentColor();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
+  }
+
+  public final class DividerDefaults {
+    method @androidx.compose.runtime.Composable public long getColor();
+    method public float getThickness();
+    property public final float Thickness;
+    property @androidx.compose.runtime.Composable public final long color;
+    field public static final androidx.compose.material3.DividerDefaults INSTANCE;
+  }
+
+  public final class DividerKt {
+    method @androidx.compose.runtime.Composable public static void Divider(optional androidx.compose.ui.Modifier modifier, optional float thickness, optional long color);
+  }
+
+  public final class DrawerDefaults {
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method public float getDismissibleDrawerElevation();
+    method public float getMaximumDrawerWidth();
+    method public float getModalDrawerElevation();
+    method public float getPermanentDrawerElevation();
+    method @androidx.compose.runtime.Composable public long getScrimColor();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
+    property public final float DismissibleDrawerElevation;
+    property public final float MaximumDrawerWidth;
+    property public final float ModalDrawerElevation;
+    property public final float PermanentDrawerElevation;
+    property @androidx.compose.runtime.Composable public final long containerColor;
+    property @androidx.compose.runtime.Composable public final long scrimColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets windowInsets;
+    field public static final androidx.compose.material3.DrawerDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Stable public final class DrawerState {
+    ctor public DrawerState(androidx.compose.material3.DrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DrawerValue,java.lang.Boolean> confirmStateChange);
+    method public suspend Object? animateTo(androidx.compose.material3.DrawerValue targetValue, androidx.compose.animation.core.AnimationSpec<java.lang.Float> anim, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public suspend Object? close(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public androidx.compose.material3.DrawerValue getCurrentValue();
+    method public androidx.compose.runtime.State<java.lang.Float> getOffset();
+    method public androidx.compose.material3.DrawerValue getTargetValue();
+    method public boolean isAnimationRunning();
+    method public boolean isClosed();
+    method public boolean isOpen();
+    method public suspend Object? open(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public suspend Object? snapTo(androidx.compose.material3.DrawerValue targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    property public final androidx.compose.material3.DrawerValue currentValue;
+    property public final boolean isAnimationRunning;
+    property public final boolean isClosed;
+    property public final boolean isOpen;
+    property public final androidx.compose.runtime.State<java.lang.Float> offset;
+    property public final androidx.compose.material3.DrawerValue targetValue;
+    field public static final androidx.compose.material3.DrawerState.Companion Companion;
+  }
+
+  public static final class DrawerState.Companion {
+    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.DrawerState,androidx.compose.material3.DrawerValue> Saver(kotlin.jvm.functions.Function1<? super androidx.compose.material3.DrawerValue,java.lang.Boolean> confirmStateChange);
+  }
+
+  public enum DrawerValue {
+    method public static androidx.compose.material3.DrawerValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.compose.material3.DrawerValue[] values();
+    enum_constant public static final androidx.compose.material3.DrawerValue Closed;
+    enum_constant public static final androidx.compose.material3.DrawerValue Open;
+  }
+
+  public final class DynamicTonalPaletteKt {
+    method @RequiresApi(android.os.Build.VERSION_CODES.S) public static androidx.compose.material3.ColorScheme dynamicDarkColorScheme(android.content.Context context);
+    method @RequiresApi(android.os.Build.VERSION_CODES.S) public static androidx.compose.material3.ColorScheme dynamicLightColorScheme(android.content.Context context);
+  }
+
+  @kotlin.jvm.JvmInline public final value class FabPosition {
+    field public static final androidx.compose.material3.FabPosition.Companion Companion;
+  }
+
+  public static final class FabPosition.Companion {
+    method public int getCenter();
+    method public int getEnd();
+    property public final int Center;
+    property public final int End;
+  }
+
+  public final class FloatingActionButtonDefaults {
+    method public androidx.compose.material3.FloatingActionButtonElevation bottomAppBarFabElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation);
+    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 public float getLargeIconSize();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getLargeShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    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 @androidx.compose.runtime.Composable public final long containerColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape extendedFabShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape largeShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape smallShape;
+    field public static final androidx.compose.material3.FloatingActionButtonDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Stable public class FloatingActionButtonElevation {
+  }
+
+  public final class FloatingActionButtonKt {
+    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 @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 @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);
+  }
+
+  @androidx.compose.runtime.Immutable public final class IconButtonColors {
+  }
+
+  public final class IconButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconButtonColors filledIconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconToggleButtonColors filledIconToggleButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor, optional long checkedContainerColor, optional long checkedContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconButtonColors filledTonalIconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconToggleButtonColors filledTonalIconToggleButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor, optional long checkedContainerColor, optional long checkedContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFilledShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getOutlinedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconButtonColors iconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconToggleButtonColors iconToggleButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor, optional long checkedContainerColor, optional long checkedContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke outlinedIconButtonBorder(boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconButtonColors outlinedIconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke? outlinedIconToggleButtonBorder(boolean enabled, boolean checked);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconToggleButtonColors outlinedIconToggleButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor, optional long checkedContainerColor, optional long checkedContentColor);
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape filledShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape outlinedShape;
+    field public static final androidx.compose.material3.IconButtonDefaults INSTANCE;
+  }
+
+  public final class IconButtonKt {
+    method @androidx.compose.runtime.Composable public static void FilledIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void FilledIconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.IconToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void FilledTonalIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void FilledTonalIconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.IconToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void IconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void IconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.IconToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void OutlinedIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void OutlinedIconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.IconToggleButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class IconKt {
+    method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.vector.ImageVector imageVector, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.ImageBitmap bitmap, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.painter.Painter painter, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+  }
+
+  @androidx.compose.runtime.Immutable public final class IconToggleButtonColors {
+  }
+
+  public final class InteractiveComponentSizeKt {
+    method public static androidx.compose.ui.Modifier minimumInteractiveComponentSize(androidx.compose.ui.Modifier);
+  }
+
+  @androidx.compose.runtime.Immutable public final class ListItemColors {
+  }
+
+  public final class ListItemDefaults {
+    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();
+    method public float getElevation();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.graphics.Shape getShape();
+    property public final float Elevation;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final long containerColor;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final long contentColor;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.ui.graphics.Shape shape;
+    field public static final androidx.compose.material3.ListItemDefaults INSTANCE;
+  }
+
+  public final class ListItemKt {
+    method @androidx.compose.runtime.Composable public static void ListItem(kotlin.jvm.functions.Function0<kotlin.Unit> headlineContent, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? overlineContent, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingContent, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingContent, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingContent, optional androidx.compose.material3.ListItemColors colors, optional float tonalElevation, optional float shadowElevation);
+  }
+
+  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.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 @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 @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);
+  }
+
+  public final class MenuDefaults {
+    method public androidx.compose.foundation.layout.PaddingValues getDropdownMenuItemContentPadding();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.MenuItemColors itemColors(optional long textColor, optional long leadingIconColor, optional long trailingIconColor, optional long disabledTextColor, optional long disabledLeadingIconColor, optional long disabledTrailingIconColor);
+    property public final androidx.compose.foundation.layout.PaddingValues DropdownMenuItemContentPadding;
+    field public static final androidx.compose.material3.MenuDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Immutable public final class MenuItemColors {
+  }
+
+  public final class NavigationBarDefaults {
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method public float getElevation();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
+    property public final float Elevation;
+    property @androidx.compose.runtime.Composable public final long containerColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets windowInsets;
+    field public static final androidx.compose.material3.NavigationBarDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Stable public final class NavigationBarItemColors {
+  }
+
+  public final class NavigationBarItemDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.NavigationBarItemColors colors(optional long selectedIconColor, optional long selectedTextColor, optional long indicatorColor, optional long unselectedIconColor, optional long unselectedTextColor, optional long disabledIconColor, optional long disabledTextColor);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material3.NavigationBarItemColors colors(optional long selectedIconColor, optional long selectedTextColor, optional long indicatorColor, optional long unselectedIconColor, optional long unselectedTextColor);
+    field public static final androidx.compose.material3.NavigationBarItemDefaults INSTANCE;
+  }
+
+  public final class NavigationBarKt {
+    method @androidx.compose.runtime.Composable public static void NavigationBar(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional float tonalElevation, 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 NavigationBarItem(androidx.compose.foundation.layout.RowScope, 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.NavigationBarItemColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  @androidx.compose.runtime.Stable public interface NavigationDrawerItemColors {
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> badgeColor(boolean selected);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> containerColor(boolean selected);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> iconColor(boolean selected);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> textColor(boolean selected);
+  }
+
+  public final class NavigationDrawerItemDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.NavigationDrawerItemColors colors(optional long selectedContainerColor, optional long unselectedContainerColor, optional long selectedIconColor, optional long unselectedIconColor, optional long selectedTextColor, optional long unselectedTextColor, optional long selectedBadgeColor, optional long unselectedBadgeColor);
+    method public androidx.compose.foundation.layout.PaddingValues getItemPadding();
+    property public final androidx.compose.foundation.layout.PaddingValues ItemPadding;
+    field public static final androidx.compose.material3.NavigationDrawerItemDefaults INSTANCE;
+  }
+
+  public final class NavigationDrawerKt {
+    method @androidx.compose.runtime.Composable public static void DismissibleDrawerSheet(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape drawerShape, optional long drawerContainerColor, optional long drawerContentColor, optional float drawerTonalElevation, optional androidx.compose.foundation.layout.WindowInsets windowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void DismissibleNavigationDrawer(kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.DrawerState drawerState, optional boolean gesturesEnabled, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ModalDrawerSheet(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape drawerShape, optional long drawerContainerColor, optional long drawerContentColor, optional float drawerTonalElevation, optional androidx.compose.foundation.layout.WindowInsets windowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ModalNavigationDrawer(kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.DrawerState drawerState, optional boolean gesturesEnabled, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void NavigationDrawerItem(kotlin.jvm.functions.Function0<kotlin.Unit> label, boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? badge, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.NavigationDrawerItemColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void PermanentDrawerSheet(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape drawerShape, optional long drawerContainerColor, optional long drawerContentColor, optional float drawerTonalElevation, optional androidx.compose.foundation.layout.WindowInsets windowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void PermanentNavigationDrawer(kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static androidx.compose.material3.DrawerState rememberDrawerState(androidx.compose.material3.DrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DrawerValue,java.lang.Boolean> confirmStateChange);
+  }
+
+  public final class NavigationRailDefaults {
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
+    property @androidx.compose.runtime.Composable public final long ContainerColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets windowInsets;
+    field public static final androidx.compose.material3.NavigationRailDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Stable public final class NavigationRailItemColors {
+  }
+
+  public final class NavigationRailItemDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.NavigationRailItemColors colors(optional long selectedIconColor, optional long selectedTextColor, optional long indicatorColor, optional long unselectedIconColor, optional long unselectedTextColor, optional long disabledIconColor, optional long disabledTextColor);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material3.NavigationRailItemColors colors(optional long selectedIconColor, optional long selectedTextColor, optional long indicatorColor, optional long unselectedIconColor, optional long unselectedTextColor);
+    field public static final androidx.compose.material3.NavigationRailItemDefaults INSTANCE;
+  }
+
+  public final class NavigationRailKt {
+    method @androidx.compose.runtime.Composable public static void NavigationRail(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? header, optional androidx.compose.foundation.layout.WindowInsets windowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    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 OutlinedTextFieldKt {
+    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);
+    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);
+  }
+
+  public final class ProgressIndicatorDefaults {
+    method @androidx.compose.runtime.Composable public long getCircularColor();
+    method public int getCircularDeterminateStrokeCap();
+    method public int getCircularIndeterminateStrokeCap();
+    method public float getCircularStrokeWidth();
+    method @androidx.compose.runtime.Composable public long getCircularTrackColor();
+    method @androidx.compose.runtime.Composable public long getLinearColor();
+    method public int getLinearStrokeCap();
+    method @androidx.compose.runtime.Composable public long getLinearTrackColor();
+    method public androidx.compose.animation.core.SpringSpec<java.lang.Float> getProgressAnimationSpec();
+    property public final int CircularDeterminateStrokeCap;
+    property public final int CircularIndeterminateStrokeCap;
+    property public final float CircularStrokeWidth;
+    property public final int LinearStrokeCap;
+    property public final androidx.compose.animation.core.SpringSpec<java.lang.Float> ProgressAnimationSpec;
+    property @androidx.compose.runtime.Composable public final long circularColor;
+    property @androidx.compose.runtime.Composable public final long circularTrackColor;
+    property @androidx.compose.runtime.Composable public final long linearColor;
+    property @androidx.compose.runtime.Composable public final long linearTrackColor;
+    field public static final androidx.compose.material3.ProgressIndicatorDefaults INSTANCE;
+  }
+
+  public final class ProgressIndicatorKt {
+    method @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 @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(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth);
+    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 LinearProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor, optional int strokeCap);
+    method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor, optional int strokeCap);
+    method @Deprecated @androidx.compose.runtime.Composable public static void LinearProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor);
+    method @Deprecated @androidx.compose.runtime.Composable public static void LinearProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor);
+  }
+
+  @androidx.compose.runtime.Immutable public final class RadioButtonColors {
+  }
+
+  public final class RadioButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.RadioButtonColors colors(optional long selectedColor, optional long unselectedColor, optional long disabledSelectedColor, optional long disabledUnselectedColor);
+    field public static final androidx.compose.material3.RadioButtonDefaults INSTANCE;
+  }
+
+  public final class RadioButtonKt {
+    method @androidx.compose.runtime.Composable public static void RadioButton(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.RadioButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  public final class ScaffoldDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getContentWindowInsets();
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets contentWindowInsets;
+    field public static final androidx.compose.material3.ScaffoldDefaults INSTANCE;
+  }
+
+  public final class ScaffoldKt {
+    method @androidx.compose.runtime.Composable public static void Scaffold(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional int floatingActionButtonPosition, optional long containerColor, optional long contentColor, optional androidx.compose.foundation.layout.WindowInsets contentWindowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
+  }
+
+  public final class ShapeDefaults {
+    method public androidx.compose.foundation.shape.CornerBasedShape getExtraLarge();
+    method public androidx.compose.foundation.shape.CornerBasedShape getExtraSmall();
+    method public androidx.compose.foundation.shape.CornerBasedShape getLarge();
+    method public androidx.compose.foundation.shape.CornerBasedShape getMedium();
+    method public androidx.compose.foundation.shape.CornerBasedShape getSmall();
+    property public final androidx.compose.foundation.shape.CornerBasedShape ExtraLarge;
+    property public final androidx.compose.foundation.shape.CornerBasedShape ExtraSmall;
+    property public final androidx.compose.foundation.shape.CornerBasedShape Large;
+    property public final androidx.compose.foundation.shape.CornerBasedShape Medium;
+    property public final androidx.compose.foundation.shape.CornerBasedShape Small;
+    field public static final androidx.compose.material3.ShapeDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Immutable public final class 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.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();
+    method public androidx.compose.foundation.shape.CornerBasedShape getExtraSmall();
+    method public androidx.compose.foundation.shape.CornerBasedShape getLarge();
+    method public androidx.compose.foundation.shape.CornerBasedShape getMedium();
+    method public androidx.compose.foundation.shape.CornerBasedShape getSmall();
+    property public final androidx.compose.foundation.shape.CornerBasedShape extraLarge;
+    property public final androidx.compose.foundation.shape.CornerBasedShape extraSmall;
+    property public final androidx.compose.foundation.shape.CornerBasedShape large;
+    property public final androidx.compose.foundation.shape.CornerBasedShape medium;
+    property public final androidx.compose.foundation.shape.CornerBasedShape small;
+  }
+
+  @androidx.compose.runtime.Immutable public final class SliderColors {
+  }
+
+  @androidx.compose.runtime.Stable public final class SliderDefaults {
+    method @androidx.compose.runtime.Composable public void Thumb(androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled, optional long thumbSize);
+    method @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.SliderPositions sliderPositions, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SliderColors colors(optional long thumbColor, optional long activeTrackColor, optional long activeTickColor, optional long inactiveTrackColor, optional long inactiveTickColor, optional long disabledThumbColor, optional long disabledActiveTrackColor, optional long disabledActiveTickColor, optional long disabledInactiveTrackColor, optional long disabledInactiveTickColor);
+    field public static final androidx.compose.material3.SliderDefaults INSTANCE;
+  }
+
+  public final class SliderKt {
+    method @androidx.compose.runtime.Composable public static void RangeSlider(kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> value, kotlin.jvm.functions.Function1<? super kotlin.ranges.ClosedFloatingPointRange<java.lang.Float>,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors);
+    method @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  @androidx.compose.runtime.Stable public final class SliderPositions {
+    ctor public SliderPositions(optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> initialActiveRange, optional float[] initialTickFractions);
+    method public kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> getActiveRange();
+    method public float[] getTickFractions();
+    property public final kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> activeRange;
+    property public final float[] tickFractions;
+  }
+
+  @androidx.compose.runtime.Stable public interface SnackbarData {
+    method public void dismiss();
+    method public androidx.compose.material3.SnackbarVisuals getVisuals();
+    method public void performAction();
+    property public abstract androidx.compose.material3.SnackbarVisuals visuals;
+  }
+
+  public final class SnackbarDefaults {
+    method @androidx.compose.runtime.Composable public long getActionColor();
+    method @androidx.compose.runtime.Composable public long getActionContentColor();
+    method @androidx.compose.runtime.Composable public long getColor();
+    method @androidx.compose.runtime.Composable public long getContentColor();
+    method @androidx.compose.runtime.Composable public long getDismissActionContentColor();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    property @androidx.compose.runtime.Composable public final long actionColor;
+    property @androidx.compose.runtime.Composable public final long actionContentColor;
+    property @androidx.compose.runtime.Composable public final long color;
+    property @androidx.compose.runtime.Composable public final long contentColor;
+    property @androidx.compose.runtime.Composable public final long dismissActionContentColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    field public static final androidx.compose.material3.SnackbarDefaults INSTANCE;
+  }
+
+  public enum SnackbarDuration {
+    method public static androidx.compose.material3.SnackbarDuration valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.compose.material3.SnackbarDuration[] values();
+    enum_constant public static final androidx.compose.material3.SnackbarDuration Indefinite;
+    enum_constant public static final androidx.compose.material3.SnackbarDuration Long;
+    enum_constant public static final androidx.compose.material3.SnackbarDuration Short;
+  }
+
+  public final class SnackbarHostKt {
+    method @androidx.compose.runtime.Composable public static void SnackbarHost(androidx.compose.material3.SnackbarHostState hostState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SnackbarData,kotlin.Unit> snackbar);
+  }
+
+  @androidx.compose.runtime.Stable public final class SnackbarHostState {
+    ctor public SnackbarHostState();
+    method public androidx.compose.material3.SnackbarData? getCurrentSnackbarData();
+    method public suspend Object? showSnackbar(String message, optional String? actionLabel, optional boolean withDismissAction, optional androidx.compose.material3.SnackbarDuration duration, optional kotlin.coroutines.Continuation<? super androidx.compose.material3.SnackbarResult>);
+    method public suspend Object? showSnackbar(androidx.compose.material3.SnackbarVisuals visuals, kotlin.coroutines.Continuation<? super androidx.compose.material3.SnackbarResult>);
+    property public final androidx.compose.material3.SnackbarData? currentSnackbarData;
+  }
+
+  public final class SnackbarKt {
+    method @androidx.compose.runtime.Composable public static void Snackbar(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? action, optional kotlin.jvm.functions.Function0<kotlin.Unit>? dismissAction, optional boolean actionOnNewLine, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional long actionContentColor, optional long dismissActionContentColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Snackbar(androidx.compose.material3.SnackbarData snackbarData, optional androidx.compose.ui.Modifier modifier, optional boolean actionOnNewLine, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional long actionColor, optional long actionContentColor, optional long dismissActionContentColor);
+  }
+
+  public enum SnackbarResult {
+    method public static androidx.compose.material3.SnackbarResult valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.compose.material3.SnackbarResult[] values();
+    enum_constant public static final androidx.compose.material3.SnackbarResult ActionPerformed;
+    enum_constant public static final androidx.compose.material3.SnackbarResult Dismissed;
+  }
+
+  @androidx.compose.runtime.Stable public interface SnackbarVisuals {
+    method public String? getActionLabel();
+    method public androidx.compose.material3.SnackbarDuration getDuration();
+    method public String getMessage();
+    method public boolean getWithDismissAction();
+    property public abstract String? actionLabel;
+    property public abstract androidx.compose.material3.SnackbarDuration duration;
+    property public abstract String message;
+    property public abstract boolean withDismissAction;
+  }
+
+  public final class SuggestionChipDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipColors elevatedSuggestionChipColors(optional long containerColor, optional long labelColor, optional long iconContentColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledIconContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipElevation elevatedSuggestionChipElevation(optional float elevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method public float getHeight();
+    method public float getIconSize();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipBorder suggestionChipBorder(optional long borderColor, optional long disabledBorderColor, optional float borderWidth);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipColors suggestionChipColors(optional long containerColor, optional long labelColor, optional long iconContentColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledIconContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipElevation suggestionChipElevation(optional float elevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    property public final float Height;
+    property public final float IconSize;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    field public static final androidx.compose.material3.SuggestionChipDefaults INSTANCE;
+  }
+
+  public final class SurfaceKt {
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> getLocalAbsoluteTonalElevation();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> LocalAbsoluteTonalElevation;
+  }
+
+  @androidx.compose.runtime.Immutable public final class SwitchColors {
+  }
+
+  public final class SwitchDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SwitchColors colors(optional long checkedThumbColor, optional long checkedTrackColor, optional long checkedBorderColor, optional long checkedIconColor, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional long uncheckedBorderColor, optional long uncheckedIconColor, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledCheckedBorderColor, optional long disabledCheckedIconColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor, optional long disabledUncheckedBorderColor, optional long disabledUncheckedIconColor);
+    method public float getIconSize();
+    property public final float IconSize;
+    field public static final androidx.compose.material3.SwitchDefaults INSTANCE;
+  }
+
+  public final class SwitchKt {
+    method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? thumbContent, optional boolean enabled, optional androidx.compose.material3.SwitchColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  public final class TabKt {
+    method @androidx.compose.runtime.Composable public static void LeadingIconTab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> icon, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional long selectedContentColor, optional long unselectedContentColor, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void Tab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional long selectedContentColor, optional long unselectedContentColor, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void Tab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional long selectedContentColor, optional long unselectedContentColor, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+  }
+
+  @androidx.compose.runtime.Immutable public final class TabPosition {
+    method public float getLeft();
+    method public float getRight();
+    method public float getWidth();
+    property public final float left;
+    property public final float right;
+    property public final float width;
+  }
+
+  public final class TabRowDefaults {
+    method @androidx.compose.runtime.Composable public void Indicator(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method @androidx.compose.runtime.Composable public long getContentColor();
+    method public androidx.compose.ui.Modifier tabIndicatorOffset(androidx.compose.ui.Modifier, androidx.compose.material3.TabPosition currentTabPosition);
+    property @androidx.compose.runtime.Composable public final long containerColor;
+    property @androidx.compose.runtime.Composable public final long contentColor;
+    field public static final androidx.compose.material3.TabRowDefaults INSTANCE;
+  }
+
+  public final class TabRowKt {
+    method @androidx.compose.runtime.Composable public static void ScrollableTabRow(int selectedTabIndex, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional float edgePadding, optional kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.material3.TabPosition>,kotlin.Unit> indicator, optional kotlin.jvm.functions.Function0<kotlin.Unit> divider, kotlin.jvm.functions.Function0<kotlin.Unit> tabs);
+    method @androidx.compose.runtime.Composable public static void TabRow(int selectedTabIndex, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.material3.TabPosition>,kotlin.Unit> indicator, optional kotlin.jvm.functions.Function0<kotlin.Unit> divider, kotlin.jvm.functions.Function0<kotlin.Unit> tabs);
+  }
+
+  @androidx.compose.runtime.Immutable public final class TextFieldColors {
+  }
+
+  @androidx.compose.runtime.Immutable public final class TextFieldDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFilledShape();
+    method public float getFocusedBorderThickness();
+    method public float getMinHeight();
+    method public float getMinWidth();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getOutlinedShape();
+    method public float getUnfocusedBorderThickness();
+    method public androidx.compose.foundation.layout.PaddingValues outlinedTextFieldPadding(optional float start, optional float top, optional float end, optional float bottom);
+    method public androidx.compose.foundation.layout.PaddingValues textFieldWithLabelPadding(optional float start, optional float end, optional float top, optional float bottom);
+    method public androidx.compose.foundation.layout.PaddingValues textFieldWithoutLabelPadding(optional float start, optional float top, optional float end, optional float bottom);
+    property public final float FocusedBorderThickness;
+    property public final float MinHeight;
+    property public final float MinWidth;
+    property public final float UnfocusedBorderThickness;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape filledShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape outlinedShape;
+    field public static final androidx.compose.material3.TextFieldDefaults INSTANCE;
+  }
+
+  public final class TextFieldKt {
+    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);
+    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);
+  }
+
+  public final class TextKt {
+    method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method @Deprecated @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method @Deprecated @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> getLocalTextStyle();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
+  }
+
+  @androidx.compose.runtime.Stable public final class TimePickerState {
+    ctor public TimePickerState(int initialHour, int initialMinute, boolean is24Hour);
+    method public int getHour();
+    method public int getMinute();
+    method public boolean is24hour();
+    method public suspend Object? settle(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    property public final int hour;
+    property public final boolean is24hour;
+    property public final int minute;
+    field public static final androidx.compose.material3.TimePickerState.Companion Companion;
+  }
+
+  public static final class TimePickerState.Companion {
+    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.TimePickerState,?> Saver();
+  }
+
+  @androidx.compose.runtime.Immutable public final class Typography {
+    ctor public Typography(optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
+    method public androidx.compose.material3.Typography copy(optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
+    method public androidx.compose.ui.text.TextStyle getBodyLarge();
+    method public androidx.compose.ui.text.TextStyle getBodyMedium();
+    method public androidx.compose.ui.text.TextStyle getBodySmall();
+    method public androidx.compose.ui.text.TextStyle getDisplayLarge();
+    method public androidx.compose.ui.text.TextStyle getDisplayMedium();
+    method public androidx.compose.ui.text.TextStyle getDisplaySmall();
+    method public androidx.compose.ui.text.TextStyle getHeadlineLarge();
+    method public androidx.compose.ui.text.TextStyle getHeadlineMedium();
+    method public androidx.compose.ui.text.TextStyle getHeadlineSmall();
+    method public androidx.compose.ui.text.TextStyle getLabelLarge();
+    method public androidx.compose.ui.text.TextStyle getLabelMedium();
+    method public androidx.compose.ui.text.TextStyle getLabelSmall();
+    method public androidx.compose.ui.text.TextStyle getTitleLarge();
+    method public androidx.compose.ui.text.TextStyle getTitleMedium();
+    method public androidx.compose.ui.text.TextStyle getTitleSmall();
+    property public final androidx.compose.ui.text.TextStyle bodyLarge;
+    property public final androidx.compose.ui.text.TextStyle bodyMedium;
+    property public final androidx.compose.ui.text.TextStyle bodySmall;
+    property public final androidx.compose.ui.text.TextStyle displayLarge;
+    property public final androidx.compose.ui.text.TextStyle displayMedium;
+    property public final androidx.compose.ui.text.TextStyle displaySmall;
+    property public final androidx.compose.ui.text.TextStyle headlineLarge;
+    property public final androidx.compose.ui.text.TextStyle headlineMedium;
+    property public final androidx.compose.ui.text.TextStyle headlineSmall;
+    property public final androidx.compose.ui.text.TextStyle labelLarge;
+    property public final androidx.compose.ui.text.TextStyle labelMedium;
+    property public final androidx.compose.ui.text.TextStyle labelSmall;
+    property public final androidx.compose.ui.text.TextStyle titleLarge;
+    property public final androidx.compose.ui.text.TextStyle titleMedium;
+    property public final androidx.compose.ui.text.TextStyle titleSmall;
+  }
+
+}
+
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index e6d7e92..7e2de6f 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -578,22 +578,6 @@
     method @androidx.compose.runtime.Composable public static void Scaffold(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional int floatingActionButtonPosition, optional long containerColor, optional long contentColor, optional androidx.compose.foundation.layout.WindowInsets contentWindowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
   }
 
-  public final class SearchBarDefaults {
-    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getDockedShape();
-    method public float getElevation();
-    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFullScreenShape();
-    method public float getInputFieldHeight();
-    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getInputFieldShape();
-    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
-    property public final float Elevation;
-    property public final float InputFieldHeight;
-    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape dockedShape;
-    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape fullScreenShape;
-    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape inputFieldShape;
-    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets windowInsets;
-    field public static final androidx.compose.material3.SearchBarDefaults INSTANCE;
-  }
-
   public final class ShapeDefaults {
     method public androidx.compose.foundation.shape.CornerBasedShape getExtraLarge();
     method public androidx.compose.foundation.shape.CornerBasedShape getExtraSmall();
diff --git a/compose/material3/material3/api/public_plus_experimental_1.1.0-beta01.txt b/compose/material3/material3/api/public_plus_experimental_1.1.0-beta01.txt
new file mode 100644
index 0000000..0b261b3
--- /dev/null
+++ b/compose/material3/material3/api/public_plus_experimental_1.1.0-beta01.txt
@@ -0,0 +1,1328 @@
+// Signature format: 4.0
+package androidx.compose.material3 {
+
+  public final class AlertDialogDefaults {
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method @androidx.compose.runtime.Composable public long getIconContentColor();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method @androidx.compose.runtime.Composable public long getTextContentColor();
+    method @androidx.compose.runtime.Composable public long getTitleContentColor();
+    method public float getTonalElevation();
+    property public final float TonalElevation;
+    property @androidx.compose.runtime.Composable public final long containerColor;
+    property @androidx.compose.runtime.Composable public final long iconContentColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    property @androidx.compose.runtime.Composable public final long textContentColor;
+    property @androidx.compose.runtime.Composable public final long titleContentColor;
+    field public static final androidx.compose.material3.AlertDialogDefaults INSTANCE;
+  }
+
+  public final class AndroidAlertDialog_androidKt {
+    method @androidx.compose.runtime.Composable public static void AlertDialog(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function0<kotlin.Unit> confirmButton, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? dismissButton, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? title, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long iconContentColor, optional long titleContentColor, optional long textContentColor, optional float tonalElevation, optional androidx.compose.ui.window.DialogProperties properties);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void AlertDialog(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.window.DialogProperties properties, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class AndroidMenu_androidKt {
+    method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean enabled, optional androidx.compose.material3.MenuItemColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  public final class AppBarKt {
+    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);
+    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.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void CenterAlignedTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void LargeTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void MediumTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
+    method @Deprecated @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void SmallTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void TopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.TopAppBarState rememberTopAppBarState(optional float initialHeightOffsetLimit, optional float initialHeightOffset, optional float initialContentOffset);
+  }
+
+  public final class AssistChipDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipBorder assistChipBorder(optional long borderColor, optional long disabledBorderColor, optional float borderWidth);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipColors assistChipColors(optional long containerColor, optional long labelColor, optional long leadingIconContentColor, optional long trailingIconContentColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledLeadingIconContentColor, optional long disabledTrailingIconContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipElevation assistChipElevation(optional float elevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipColors elevatedAssistChipColors(optional long containerColor, optional long labelColor, optional long leadingIconContentColor, optional long trailingIconContentColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledLeadingIconContentColor, optional long disabledTrailingIconContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipElevation elevatedAssistChipElevation(optional float elevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method public float getHeight();
+    method public float getIconSize();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    property public final float Height;
+    property public final float IconSize;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    field public static final androidx.compose.material3.AssistChipDefaults INSTANCE;
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api public final class BadgeDefaults {
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    property @androidx.compose.runtime.Composable public final long containerColor;
+    field public static final androidx.compose.material3.BadgeDefaults INSTANCE;
+  }
+
+  public final class BadgeKt {
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void Badge(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? content);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void BadgedBox(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> badge, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+  }
+
+  public final class BottomAppBarDefaults {
+    method @androidx.compose.runtime.Composable public long getBottomAppBarFabColor();
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method public float getContainerElevation();
+    method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    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 @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;
+    field public static final androidx.compose.material3.BottomAppBarDefaults INSTANCE;
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class BottomSheetDefaults {
+    method @androidx.compose.runtime.Composable public void DragHandle(optional androidx.compose.ui.Modifier modifier, optional float width, optional float height, optional androidx.compose.ui.graphics.Shape shape, optional long color);
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method public float getElevation();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getExpandedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getHiddenShape();
+    method @androidx.compose.runtime.Composable public long getScrimColor();
+    method public float getSheetPeekHeight();
+    property @androidx.compose.runtime.Composable public final long ContainerColor;
+    property public final float Elevation;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape ExpandedShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape HiddenShape;
+    property @androidx.compose.runtime.Composable public final long ScrimColor;
+    property public final float SheetPeekHeight;
+    field public static final androidx.compose.material3.BottomSheetDefaults INSTANCE;
+  }
+
+  public final class BottomSheetScaffoldKt {
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void BottomSheetScaffold(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.BottomSheetScaffoldState scaffoldState, optional float sheetPeekHeight, optional androidx.compose.ui.graphics.Shape sheetShape, optional long sheetContainerColor, optional long sheetContentColor, optional float sheetTonalElevation, optional float sheetShadowElevation, optional kotlin.jvm.functions.Function0<kotlin.Unit>? sheetDragHandle, optional boolean sheetSwipeEnabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? topBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SnackbarHostState,kotlin.Unit> snackbarHost, optional long containerColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.BottomSheetScaffoldState rememberBottomSheetScaffoldState(optional androidx.compose.material3.SheetState bottomSheetState, optional androidx.compose.material3.SnackbarHostState snackbarHostState);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.SheetState rememberStandardBottomSheetState(optional androidx.compose.material3.SheetValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHiddenState);
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class BottomSheetScaffoldState {
+    ctor public BottomSheetScaffoldState(androidx.compose.material3.SheetState bottomSheetState, androidx.compose.material3.SnackbarHostState snackbarHostState);
+    method public androidx.compose.material3.SheetState getBottomSheetState();
+    method public androidx.compose.material3.SnackbarHostState getSnackbarHostState();
+    property public final androidx.compose.material3.SheetState bottomSheetState;
+    property public final androidx.compose.material3.SnackbarHostState snackbarHostState;
+  }
+
+  @androidx.compose.runtime.Immutable public final class ButtonColors {
+  }
+
+  public final class ButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonColors buttonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonElevation buttonElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float disabledElevation);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonColors elevatedButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonElevation elevatedButtonElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float disabledElevation);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonColors filledTonalButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonElevation filledTonalButtonElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float disabledElevation);
+    method public androidx.compose.foundation.layout.PaddingValues getButtonWithIconContentPadding();
+    method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getElevatedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFilledTonalShape();
+    method public float getIconSize();
+    method public float getIconSpacing();
+    method public float getMinHeight();
+    method public float getMinWidth();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke getOutlinedButtonBorder();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getOutlinedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method public androidx.compose.foundation.layout.PaddingValues getTextButtonContentPadding();
+    method public androidx.compose.foundation.layout.PaddingValues getTextButtonWithIconContentPadding();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getTextShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonColors outlinedButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonColors textButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    property public final androidx.compose.foundation.layout.PaddingValues ButtonWithIconContentPadding;
+    property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
+    property public final float IconSize;
+    property public final float IconSpacing;
+    property public final float MinHeight;
+    property public final float MinWidth;
+    property public final androidx.compose.foundation.layout.PaddingValues TextButtonContentPadding;
+    property public final androidx.compose.foundation.layout.PaddingValues TextButtonWithIconContentPadding;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape elevatedShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape filledTonalShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.BorderStroke outlinedButtonBorder;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape outlinedShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape textShape;
+    field public static final androidx.compose.material3.ButtonDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Stable public final class ButtonElevation {
+  }
+
+  public final class ButtonKt {
+    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ElevatedButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void FilledTonalButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void OutlinedButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void TextButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+  }
+
+  @androidx.compose.runtime.Immutable public final class CardColors {
+  }
+
+  public final class CardDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CardColors cardColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CardElevation cardElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CardColors elevatedCardColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CardElevation elevatedCardElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getElevatedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getOutlinedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke outlinedCardBorder(optional boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CardColors outlinedCardColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CardElevation outlinedCardElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape elevatedShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape outlinedShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    field public static final androidx.compose.material3.CardDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Immutable public final class CardElevation {
+  }
+
+  public final class CardKt {
+    method @androidx.compose.runtime.Composable public static void Card(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.CardColors colors, optional androidx.compose.material3.CardElevation elevation, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void Card(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.CardColors colors, optional androidx.compose.material3.CardElevation elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ElevatedCard(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.CardColors colors, optional androidx.compose.material3.CardElevation elevation, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ElevatedCard(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.CardColors colors, optional androidx.compose.material3.CardElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void OutlinedCard(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.CardColors colors, optional androidx.compose.material3.CardElevation elevation, optional androidx.compose.foundation.BorderStroke border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void OutlinedCard(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.CardColors colors, optional androidx.compose.material3.CardElevation elevation, optional androidx.compose.foundation.BorderStroke border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+  }
+
+  @androidx.compose.runtime.Immutable public final class CheckboxColors {
+  }
+
+  public final class CheckboxDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CheckboxColors colors(optional long checkedColor, optional long uncheckedColor, optional long checkmarkColor, optional long disabledCheckedColor, optional long disabledUncheckedColor, optional long disabledIndeterminateColor);
+    field public static final androidx.compose.material3.CheckboxDefaults INSTANCE;
+  }
+
+  public final class CheckboxKt {
+    method @androidx.compose.runtime.Composable public static void Checkbox(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.CheckboxColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void TriStateCheckbox(androidx.compose.ui.state.ToggleableState state, kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.CheckboxColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  @androidx.compose.runtime.Immutable public final class ChipBorder {
+  }
+
+  @androidx.compose.runtime.Immutable public final class ChipColors {
+  }
+
+  @androidx.compose.runtime.Immutable public final class ChipElevation {
+  }
+
+  public final class ChipKt {
+    method @androidx.compose.runtime.Composable public static void AssistChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipColors colors, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void ElevatedAssistChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipColors colors, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ElevatedFilterChip(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.SelectableChipColors colors, optional androidx.compose.material3.SelectableChipElevation? elevation, optional androidx.compose.material3.SelectableChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void ElevatedSuggestionChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipColors colors, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void FilterChip(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.SelectableChipColors colors, optional androidx.compose.material3.SelectableChipElevation? elevation, optional androidx.compose.material3.SelectableChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void InputChip(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? avatar, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.SelectableChipColors colors, optional androidx.compose.material3.SelectableChipElevation? elevation, optional androidx.compose.material3.SelectableChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void SuggestionChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipColors colors, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  @androidx.compose.runtime.Stable public final class ColorScheme {
+    ctor public ColorScheme(long primary, long onPrimary, long primaryContainer, long onPrimaryContainer, long inversePrimary, long secondary, long onSecondary, long secondaryContainer, long onSecondaryContainer, long tertiary, long onTertiary, long tertiaryContainer, long onTertiaryContainer, long background, long onBackground, long surface, long onSurface, long surfaceVariant, long onSurfaceVariant, long surfaceTint, long inverseSurface, long inverseOnSurface, long error, long onError, long errorContainer, long onErrorContainer, long outline, long outlineVariant, long scrim);
+    method public androidx.compose.material3.ColorScheme copy(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 long getBackground();
+    method public long getError();
+    method public long getErrorContainer();
+    method public long getInverseOnSurface();
+    method public long getInversePrimary();
+    method public long getInverseSurface();
+    method public long getOnBackground();
+    method public long getOnError();
+    method public long getOnErrorContainer();
+    method public long getOnPrimary();
+    method public long getOnPrimaryContainer();
+    method public long getOnSecondary();
+    method public long getOnSecondaryContainer();
+    method public long getOnSurface();
+    method public long getOnSurfaceVariant();
+    method public long getOnTertiary();
+    method public long getOnTertiaryContainer();
+    method public long getOutline();
+    method public long getOutlineVariant();
+    method public long getPrimary();
+    method public long getPrimaryContainer();
+    method public long getScrim();
+    method public long getSecondary();
+    method public long getSecondaryContainer();
+    method public long getSurface();
+    method public long getSurfaceTint();
+    method public long getSurfaceVariant();
+    method public long getTertiary();
+    method public long getTertiaryContainer();
+    property public final long background;
+    property public final long error;
+    property public final long errorContainer;
+    property public final long inverseOnSurface;
+    property public final long inversePrimary;
+    property public final long inverseSurface;
+    property public final long onBackground;
+    property public final long onError;
+    property public final long onErrorContainer;
+    property public final long onPrimary;
+    property public final long onPrimaryContainer;
+    property public final long onSecondary;
+    property public final long onSecondaryContainer;
+    property public final long onSurface;
+    property public final long onSurfaceVariant;
+    property public final long onTertiary;
+    property public final long onTertiaryContainer;
+    property public final long outline;
+    property public final long outlineVariant;
+    property public final long primary;
+    property public final long primaryContainer;
+    property public final long scrim;
+    property public final long secondary;
+    property public final long secondaryContainer;
+    property public final long surface;
+    property public final long surfaceTint;
+    property public final long surfaceVariant;
+    property public final long tertiary;
+    property public final long tertiaryContainer;
+  }
+
+  public final class ColorSchemeKt {
+    method public static long contentColorFor(androidx.compose.material3.ColorScheme, long backgroundColor);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static long contentColorFor(long backgroundColor);
+    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);
+    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);
+    method public static long surfaceColorAtElevation(androidx.compose.material3.ColorScheme, float elevation);
+  }
+
+  public final class ContentColorKt {
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> getLocalContentColor();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class DatePickerColors {
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class DatePickerDefaults {
+    method @androidx.compose.runtime.Composable public void DatePickerHeadline(androidx.compose.material3.DatePickerState state, androidx.compose.material3.DatePickerFormatter dateFormatter, optional androidx.compose.ui.Modifier modifier);
+    method @androidx.compose.runtime.Composable public void DatePickerTitle(androidx.compose.material3.DatePickerState state, optional androidx.compose.ui.Modifier modifier);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.DatePickerColors colors(optional long containerColor, optional long titleContentColor, optional long headlineContentColor, optional long weekdayContentColor, optional long subheadContentColor, optional long yearContentColor, optional long currentYearContentColor, optional long selectedYearContentColor, optional long selectedYearContainerColor, optional long dayContentColor, optional long disabledDayContentColor, optional long selectedDayContentColor, optional long disabledSelectedDayContentColor, optional long selectedDayContainerColor, optional long disabledSelectedDayContainerColor, optional long todayContentColor, optional long todayDateBorderColor, optional long dayInSelectionRangeContentColor, optional long dayInSelectionRangeContainerColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method public float getTonalElevation();
+    method public kotlin.ranges.IntRange getYearRange();
+    property public final float TonalElevation;
+    property public final kotlin.ranges.IntRange YearRange;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    field public static final androidx.compose.material3.DatePickerDefaults INSTANCE;
+    field public static final String YearAbbrMonthDaySkeleton = "yMMMd";
+    field public static final String YearMonthSkeleton = "yMMMM";
+    field public static final String YearMonthWeekdayDaySkeleton = "yMMMMEEEEd";
+  }
+
+  public final class DatePickerDialog_androidKt {
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void DatePickerDialog(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function0<kotlin.Unit> confirmButton, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? dismissButton, optional androidx.compose.ui.graphics.Shape shape, optional float tonalElevation, optional androidx.compose.material3.DatePickerColors colors, optional androidx.compose.ui.window.DialogProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class DatePickerFormatter {
+    ctor public DatePickerFormatter(optional String yearSelectionSkeleton, optional String selectedDateSkeleton, optional String selectedDateDescriptionSkeleton);
+  }
+
+  public final class DatePickerKt {
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void DatePicker(androidx.compose.material3.DatePickerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.DatePickerFormatter dateFormatter, optional kotlin.jvm.functions.Function1<? super java.lang.Long,java.lang.Boolean> dateValidator, optional kotlin.jvm.functions.Function0<kotlin.Unit>? title, optional kotlin.jvm.functions.Function0<kotlin.Unit>? headline, optional boolean showModeToggle, optional androidx.compose.material3.DatePickerColors colors);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.DatePickerState rememberDatePickerState(optional Long? initialSelectedDateMillis, optional Long? initialDisplayedMonthMillis, optional kotlin.ranges.IntRange yearRange, optional int initialDisplayMode);
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class DatePickerState {
+    ctor public DatePickerState(Long? initialSelectedDateMillis, Long? initialDisplayedMonthMillis, kotlin.ranges.IntRange yearRange, int initialDisplayMode);
+    method public int getDisplayMode();
+    method public Long? getSelectedDateMillis();
+    method public void setDisplayMode(int);
+    method public void setSelection(Long? dateMillis);
+    property public final int displayMode;
+    property public final Long? selectedDateMillis;
+    field public static final androidx.compose.material3.DatePickerState.Companion Companion;
+  }
+
+  public static final class DatePickerState.Companion {
+    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.DatePickerState,?> Saver();
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class DateRangePickerDefaults {
+    method @androidx.compose.runtime.Composable public void DateRangePickerHeadline(androidx.compose.material3.DateRangePickerState state, androidx.compose.material3.DatePickerFormatter dateFormatter, optional androidx.compose.ui.Modifier modifier);
+    method @androidx.compose.runtime.Composable public void DateRangePickerTitle(androidx.compose.material3.DateRangePickerState state, optional androidx.compose.ui.Modifier modifier);
+    field public static final androidx.compose.material3.DateRangePickerDefaults INSTANCE;
+  }
+
+  public final class DateRangePickerKt {
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void DateRangePicker(androidx.compose.material3.DateRangePickerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.DatePickerFormatter dateFormatter, optional kotlin.jvm.functions.Function1<? super java.lang.Long,java.lang.Boolean> dateValidator, optional kotlin.jvm.functions.Function0<kotlin.Unit>? title, optional kotlin.jvm.functions.Function0<kotlin.Unit>? headline, optional boolean showModeToggle, optional androidx.compose.material3.DatePickerColors colors);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.DateRangePickerState rememberDateRangePickerState(optional Long? initialSelectedStartDateMillis, optional Long? initialSelectedEndDateMillis, optional Long? initialDisplayedMonthMillis, optional kotlin.ranges.IntRange yearRange, optional int initialDisplayMode);
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class DateRangePickerState {
+    ctor public DateRangePickerState(Long? initialSelectedStartDateMillis, Long? initialSelectedEndDateMillis, Long? initialDisplayedMonthMillis, kotlin.ranges.IntRange yearRange, int initialDisplayMode);
+    method public int getDisplayMode();
+    method public Long? getSelectedEndDateMillis();
+    method public Long? getSelectedStartDateMillis();
+    method public void setDisplayMode(int);
+    method public void setSelection(Long? startDateMillis, Long? endDateMillis);
+    property public final int displayMode;
+    property public final Long? selectedEndDateMillis;
+    property public final Long? selectedStartDateMillis;
+    field public static final androidx.compose.material3.DateRangePickerState.Companion Companion;
+  }
+
+  public static final class DateRangePickerState.Companion {
+    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.DateRangePickerState,?> Saver();
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api public enum DismissDirection {
+    method public static androidx.compose.material3.DismissDirection valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.compose.material3.DismissDirection[] values();
+    enum_constant public static final androidx.compose.material3.DismissDirection EndToStart;
+    enum_constant public static final androidx.compose.material3.DismissDirection StartToEnd;
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api public final class DismissState {
+    ctor public DismissState(androidx.compose.material3.DismissValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DismissValue,java.lang.Boolean> confirmValueChange, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super java.lang.Float,java.lang.Float> positionalThreshold);
+    method public suspend Object? dismiss(androidx.compose.material3.DismissDirection direction, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public androidx.compose.material3.DismissValue getCurrentValue();
+    method public androidx.compose.material3.DismissDirection? getDismissDirection();
+    method public float getProgress();
+    method public androidx.compose.material3.DismissValue getTargetValue();
+    method public boolean isDismissed(androidx.compose.material3.DismissDirection direction);
+    method public float requireOffset();
+    method public suspend Object? reset(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public suspend Object? snapTo(androidx.compose.material3.DismissValue targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    property public final androidx.compose.material3.DismissValue currentValue;
+    property public final androidx.compose.material3.DismissDirection? dismissDirection;
+    property public final float progress;
+    property public final androidx.compose.material3.DismissValue targetValue;
+    field public static final androidx.compose.material3.DismissState.Companion Companion;
+  }
+
+  public static final class DismissState.Companion {
+    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.DismissState,androidx.compose.material3.DismissValue> Saver(kotlin.jvm.functions.Function1<? super androidx.compose.material3.DismissValue,java.lang.Boolean> confirmValueChange, kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super java.lang.Float,java.lang.Float> positionalThreshold);
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api public enum DismissValue {
+    method public static androidx.compose.material3.DismissValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.compose.material3.DismissValue[] values();
+    enum_constant public static final androidx.compose.material3.DismissValue Default;
+    enum_constant public static final androidx.compose.material3.DismissValue DismissedToEnd;
+    enum_constant public static final androidx.compose.material3.DismissValue DismissedToStart;
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class DisplayMode {
+    field public static final androidx.compose.material3.DisplayMode.Companion Companion;
+  }
+
+  public static final class DisplayMode.Companion {
+    method public int getInput();
+    method public int getPicker();
+    property public final int Input;
+    property public final int Picker;
+  }
+
+  public final class DividerDefaults {
+    method @androidx.compose.runtime.Composable public long getColor();
+    method public float getThickness();
+    property public final float Thickness;
+    property @androidx.compose.runtime.Composable public final long color;
+    field public static final androidx.compose.material3.DividerDefaults INSTANCE;
+  }
+
+  public final class DividerKt {
+    method @androidx.compose.runtime.Composable public static void Divider(optional androidx.compose.ui.Modifier modifier, optional float thickness, optional long color);
+  }
+
+  public final class DrawerDefaults {
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method public float getDismissibleDrawerElevation();
+    method public float getMaximumDrawerWidth();
+    method public float getModalDrawerElevation();
+    method public float getPermanentDrawerElevation();
+    method @androidx.compose.runtime.Composable public long getScrimColor();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
+    property public final float DismissibleDrawerElevation;
+    property public final float MaximumDrawerWidth;
+    property public final float ModalDrawerElevation;
+    property public final float PermanentDrawerElevation;
+    property @androidx.compose.runtime.Composable public final long containerColor;
+    property @androidx.compose.runtime.Composable public final long scrimColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets windowInsets;
+    field public static final androidx.compose.material3.DrawerDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Stable public final class DrawerState {
+    ctor public DrawerState(androidx.compose.material3.DrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DrawerValue,java.lang.Boolean> confirmStateChange);
+    method public suspend Object? animateTo(androidx.compose.material3.DrawerValue targetValue, androidx.compose.animation.core.AnimationSpec<java.lang.Float> anim, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public suspend Object? close(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public androidx.compose.material3.DrawerValue getCurrentValue();
+    method public androidx.compose.runtime.State<java.lang.Float> getOffset();
+    method public androidx.compose.material3.DrawerValue getTargetValue();
+    method public boolean isAnimationRunning();
+    method public boolean isClosed();
+    method public boolean isOpen();
+    method public suspend Object? open(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public suspend Object? snapTo(androidx.compose.material3.DrawerValue targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    property public final androidx.compose.material3.DrawerValue currentValue;
+    property public final boolean isAnimationRunning;
+    property public final boolean isClosed;
+    property public final boolean isOpen;
+    property public final androidx.compose.runtime.State<java.lang.Float> offset;
+    property public final androidx.compose.material3.DrawerValue targetValue;
+    field public static final androidx.compose.material3.DrawerState.Companion Companion;
+  }
+
+  public static final class DrawerState.Companion {
+    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.DrawerState,androidx.compose.material3.DrawerValue> Saver(kotlin.jvm.functions.Function1<? super androidx.compose.material3.DrawerValue,java.lang.Boolean> confirmStateChange);
+  }
+
+  public enum DrawerValue {
+    method public static androidx.compose.material3.DrawerValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.compose.material3.DrawerValue[] values();
+    enum_constant public static final androidx.compose.material3.DrawerValue Closed;
+    enum_constant public static final androidx.compose.material3.DrawerValue Open;
+  }
+
+  public final class DynamicTonalPaletteKt {
+    method @RequiresApi(android.os.Build.VERSION_CODES.S) public static androidx.compose.material3.ColorScheme dynamicDarkColorScheme(android.content.Context context);
+    method @RequiresApi(android.os.Build.VERSION_CODES.S) public static androidx.compose.material3.ColorScheme dynamicLightColorScheme(android.content.Context context);
+  }
+
+  @kotlin.RequiresOptIn(message="This material API is experimental and is likely to change or to be removed in" + " the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalMaterial3Api {
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api public interface ExposedDropdownMenuBoxScope {
+    method @androidx.compose.runtime.Composable public default void ExposedDropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method public androidx.compose.ui.Modifier exposedDropdownSize(androidx.compose.ui.Modifier, optional boolean matchTextFieldWidth);
+    method public androidx.compose.ui.Modifier menuAnchor(androidx.compose.ui.Modifier);
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api public final class ExposedDropdownMenuDefaults {
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public void TrailingIcon(boolean expanded);
+    method public androidx.compose.foundation.layout.PaddingValues getItemContentPadding();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors outlinedTextFieldColors(optional long focusedTextColor, optional long unfocusedTextColor, optional long disabledTextColor, optional long errorTextColor, optional long containerColor, 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 focusedPrefixColor, optional long unfocusedPrefixColor, optional long disabledPrefixColor, optional long errorPrefixColor, optional long focusedSuffixColor, optional long unfocusedSuffixColor, optional long disabledSuffixColor, optional long errorSuffixColor);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors outlinedTextFieldColors(optional long textColor, optional long disabledTextColor, optional long containerColor, 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 placeholderColor, optional long disabledPlaceholderColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors textFieldColors(optional long focusedTextColor, optional long unfocusedTextColor, optional long disabledTextColor, optional long errorTextColor, optional long containerColor, 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 focusedPrefixColor, optional long unfocusedPrefixColor, optional long disabledPrefixColor, optional long errorPrefixColor, optional long focusedSuffixColor, optional long unfocusedSuffixColor, optional long disabledSuffixColor, optional long errorSuffixColor);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors textFieldColors(optional long textColor, optional long disabledTextColor, optional long containerColor, 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 placeholderColor, optional long disabledPlaceholderColor);
+    property public final androidx.compose.foundation.layout.PaddingValues ItemContentPadding;
+    field public static final androidx.compose.material3.ExposedDropdownMenuDefaults INSTANCE;
+  }
+
+  public final class ExposedDropdownMenuKt {
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ExposedDropdownMenuBox(boolean expanded, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onExpandedChange, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.material3.ExposedDropdownMenuBoxScope,kotlin.Unit> content);
+  }
+
+  @kotlin.jvm.JvmInline public final value class FabPosition {
+    field public static final androidx.compose.material3.FabPosition.Companion Companion;
+  }
+
+  public static final class FabPosition.Companion {
+    method public int getCenter();
+    method public int getEnd();
+    property public final int Center;
+    property public final int End;
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api public final class FilterChipDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SelectableChipColors elevatedFilterChipColors(optional long containerColor, optional long labelColor, optional long iconColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledLeadingIconColor, optional long disabledTrailingIconColor, optional long selectedContainerColor, optional long disabledSelectedContainerColor, optional long selectedLabelColor, optional long selectedLeadingIconColor, optional long selectedTrailingIconColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SelectableChipElevation elevatedFilterChipElevation(optional float elevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SelectableChipBorder filterChipBorder(optional long borderColor, optional long selectedBorderColor, optional long disabledBorderColor, optional long disabledSelectedBorderColor, optional float borderWidth, optional float selectedBorderWidth);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SelectableChipColors filterChipColors(optional long containerColor, optional long labelColor, optional long iconColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledLeadingIconColor, optional long disabledTrailingIconColor, optional long selectedContainerColor, optional long disabledSelectedContainerColor, optional long selectedLabelColor, optional long selectedLeadingIconColor, optional long selectedTrailingIconColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SelectableChipElevation filterChipElevation(optional float elevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method public float getHeight();
+    method public float getIconSize();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    property public final float Height;
+    property public final float IconSize;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    field public static final androidx.compose.material3.FilterChipDefaults INSTANCE;
+  }
+
+  public final class FloatingActionButtonDefaults {
+    method public androidx.compose.material3.FloatingActionButtonElevation bottomAppBarFabElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation);
+    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 public float getLargeIconSize();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getLargeShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    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 @androidx.compose.runtime.Composable public final long containerColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape extendedFabShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape largeShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape smallShape;
+    field public static final androidx.compose.material3.FloatingActionButtonDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Stable public class FloatingActionButtonElevation {
+  }
+
+  public final class FloatingActionButtonKt {
+    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 @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 @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);
+  }
+
+  @androidx.compose.runtime.Immutable public final class IconButtonColors {
+  }
+
+  public final class IconButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconButtonColors filledIconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconToggleButtonColors filledIconToggleButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor, optional long checkedContainerColor, optional long checkedContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconButtonColors filledTonalIconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconToggleButtonColors filledTonalIconToggleButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor, optional long checkedContainerColor, optional long checkedContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFilledShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getOutlinedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconButtonColors iconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconToggleButtonColors iconToggleButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor, optional long checkedContainerColor, optional long checkedContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke outlinedIconButtonBorder(boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconButtonColors outlinedIconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke? outlinedIconToggleButtonBorder(boolean enabled, boolean checked);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconToggleButtonColors outlinedIconToggleButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor, optional long checkedContainerColor, optional long checkedContentColor);
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape filledShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape outlinedShape;
+    field public static final androidx.compose.material3.IconButtonDefaults INSTANCE;
+  }
+
+  public final class IconButtonKt {
+    method @androidx.compose.runtime.Composable public static void FilledIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void FilledIconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.IconToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void FilledTonalIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void FilledTonalIconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.IconToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void IconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void IconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.IconToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void OutlinedIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void OutlinedIconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.IconToggleButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class IconKt {
+    method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.vector.ImageVector imageVector, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.ImageBitmap bitmap, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.painter.Painter painter, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+  }
+
+  @androidx.compose.runtime.Immutable public final class IconToggleButtonColors {
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api public final class InputChipDefaults {
+    method public float getAvatarSize();
+    method public float getHeight();
+    method public float getIconSize();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SelectableChipBorder inputChipBorder(optional long borderColor, optional long selectedBorderColor, optional long disabledBorderColor, optional long disabledSelectedBorderColor, optional float borderWidth, optional float selectedBorderWidth);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SelectableChipColors inputChipColors(optional long containerColor, optional long labelColor, optional long leadingIconColor, optional long trailingIconColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledLeadingIconColor, optional long disabledTrailingIconColor, optional long selectedContainerColor, optional long disabledSelectedContainerColor, optional long selectedLabelColor, optional long selectedLeadingIconColor, optional long selectedTrailingIconColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SelectableChipElevation inputChipElevation(optional float elevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    property public final float AvatarSize;
+    property public final float Height;
+    property public final float IconSize;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    field public static final androidx.compose.material3.InputChipDefaults INSTANCE;
+  }
+
+  public final class InteractiveComponentSizeKt {
+    method @androidx.compose.material3.ExperimentalMaterial3Api public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalMinimumInteractiveComponentEnforcement();
+    method @Deprecated @androidx.compose.material3.ExperimentalMaterial3Api public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalMinimumTouchTargetEnforcement();
+    method public static androidx.compose.ui.Modifier minimumInteractiveComponentSize(androidx.compose.ui.Modifier);
+    property @androidx.compose.material3.ExperimentalMaterial3Api public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> LocalMinimumInteractiveComponentEnforcement;
+    property @Deprecated @androidx.compose.material3.ExperimentalMaterial3Api public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> LocalMinimumTouchTargetEnforcement;
+  }
+
+  @androidx.compose.runtime.Immutable public final class ListItemColors {
+  }
+
+  public final class ListItemDefaults {
+    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();
+    method public float getElevation();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.graphics.Shape getShape();
+    property public final float Elevation;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final long containerColor;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final long contentColor;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.ui.graphics.Shape shape;
+    field public static final androidx.compose.material3.ListItemDefaults INSTANCE;
+  }
+
+  public final class ListItemKt {
+    method @androidx.compose.runtime.Composable public static void ListItem(kotlin.jvm.functions.Function0<kotlin.Unit> headlineContent, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? overlineContent, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingContent, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingContent, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingContent, optional androidx.compose.material3.ListItemColors colors, optional float tonalElevation, optional float shadowElevation);
+  }
+
+  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.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 @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 @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);
+  }
+
+  public final class MenuDefaults {
+    method public androidx.compose.foundation.layout.PaddingValues getDropdownMenuItemContentPadding();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.MenuItemColors itemColors(optional long textColor, optional long leadingIconColor, optional long trailingIconColor, optional long disabledTextColor, optional long disabledLeadingIconColor, optional long disabledTrailingIconColor);
+    property public final androidx.compose.foundation.layout.PaddingValues DropdownMenuItemContentPadding;
+    field public static final androidx.compose.material3.MenuDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Immutable public final class MenuItemColors {
+  }
+
+  public final class ModalBottomSheetKt {
+    method @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 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, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.SheetState rememberModalBottomSheetState(optional boolean skipPartiallyExpanded, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange);
+  }
+
+  public final class NavigationBarDefaults {
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method public float getElevation();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
+    property public final float Elevation;
+    property @androidx.compose.runtime.Composable public final long containerColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets windowInsets;
+    field public static final androidx.compose.material3.NavigationBarDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Stable public final class NavigationBarItemColors {
+  }
+
+  public final class NavigationBarItemDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.NavigationBarItemColors colors(optional long selectedIconColor, optional long selectedTextColor, optional long indicatorColor, optional long unselectedIconColor, optional long unselectedTextColor, optional long disabledIconColor, optional long disabledTextColor);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material3.NavigationBarItemColors colors(optional long selectedIconColor, optional long selectedTextColor, optional long indicatorColor, optional long unselectedIconColor, optional long unselectedTextColor);
+    field public static final androidx.compose.material3.NavigationBarItemDefaults INSTANCE;
+  }
+
+  public final class NavigationBarKt {
+    method @androidx.compose.runtime.Composable public static void NavigationBar(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional float tonalElevation, 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 NavigationBarItem(androidx.compose.foundation.layout.RowScope, 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.NavigationBarItemColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  @androidx.compose.runtime.Stable public interface NavigationDrawerItemColors {
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> badgeColor(boolean selected);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> containerColor(boolean selected);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> iconColor(boolean selected);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> textColor(boolean selected);
+  }
+
+  public final class NavigationDrawerItemDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.NavigationDrawerItemColors colors(optional long selectedContainerColor, optional long unselectedContainerColor, optional long selectedIconColor, optional long unselectedIconColor, optional long selectedTextColor, optional long unselectedTextColor, optional long selectedBadgeColor, optional long unselectedBadgeColor);
+    method public androidx.compose.foundation.layout.PaddingValues getItemPadding();
+    property public final androidx.compose.foundation.layout.PaddingValues ItemPadding;
+    field public static final androidx.compose.material3.NavigationDrawerItemDefaults INSTANCE;
+  }
+
+  public final class NavigationDrawerKt {
+    method @androidx.compose.runtime.Composable public static void DismissibleDrawerSheet(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape drawerShape, optional long drawerContainerColor, optional long drawerContentColor, optional float drawerTonalElevation, optional androidx.compose.foundation.layout.WindowInsets windowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void DismissibleNavigationDrawer(kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.DrawerState drawerState, optional boolean gesturesEnabled, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ModalDrawerSheet(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape drawerShape, optional long drawerContainerColor, optional long drawerContentColor, optional float drawerTonalElevation, optional androidx.compose.foundation.layout.WindowInsets windowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ModalNavigationDrawer(kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.DrawerState drawerState, optional boolean gesturesEnabled, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void NavigationDrawerItem(kotlin.jvm.functions.Function0<kotlin.Unit> label, boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? badge, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.NavigationDrawerItemColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void PermanentDrawerSheet(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape drawerShape, optional long drawerContainerColor, optional long drawerContentColor, optional float drawerTonalElevation, optional androidx.compose.foundation.layout.WindowInsets windowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void PermanentNavigationDrawer(kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static androidx.compose.material3.DrawerState rememberDrawerState(androidx.compose.material3.DrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DrawerValue,java.lang.Boolean> confirmStateChange);
+  }
+
+  public final class NavigationRailDefaults {
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
+    property @androidx.compose.runtime.Composable public final long ContainerColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets windowInsets;
+    field public static final androidx.compose.material3.NavigationRailDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Stable public final class NavigationRailItemColors {
+  }
+
+  public final class NavigationRailItemDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.NavigationRailItemColors colors(optional long selectedIconColor, optional long selectedTextColor, optional long indicatorColor, optional long unselectedIconColor, optional long unselectedTextColor, optional long disabledIconColor, optional long disabledTextColor);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material3.NavigationRailItemColors colors(optional long selectedIconColor, optional long selectedTextColor, optional long indicatorColor, optional long unselectedIconColor, optional long unselectedTextColor);
+    field public static final androidx.compose.material3.NavigationRailItemDefaults INSTANCE;
+  }
+
+  public final class NavigationRailKt {
+    method @androidx.compose.runtime.Composable public static void NavigationRail(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? header, optional androidx.compose.foundation.layout.WindowInsets windowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    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 OutlinedTextFieldKt {
+    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);
+    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 @Deprecated @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void OutlinedTextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,? extends 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<? extends kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<? extends 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 @Deprecated @androidx.compose.material3.ExperimentalMaterial3Api @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,? extends 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<? extends kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<? extends 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);
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class PlainTooltipState {
+    ctor public PlainTooltipState();
+    method public suspend Object? dismiss(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public boolean isVisible();
+    method public suspend Object? show(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    property public boolean isVisible;
+  }
+
+  public final class ProgressIndicatorDefaults {
+    method @androidx.compose.runtime.Composable public long getCircularColor();
+    method public int getCircularDeterminateStrokeCap();
+    method public int getCircularIndeterminateStrokeCap();
+    method public float getCircularStrokeWidth();
+    method @androidx.compose.runtime.Composable public long getCircularTrackColor();
+    method @androidx.compose.runtime.Composable public long getLinearColor();
+    method public int getLinearStrokeCap();
+    method @androidx.compose.runtime.Composable public long getLinearTrackColor();
+    method public androidx.compose.animation.core.SpringSpec<java.lang.Float> getProgressAnimationSpec();
+    property public final int CircularDeterminateStrokeCap;
+    property public final int CircularIndeterminateStrokeCap;
+    property public final float CircularStrokeWidth;
+    property public final int LinearStrokeCap;
+    property public final androidx.compose.animation.core.SpringSpec<java.lang.Float> ProgressAnimationSpec;
+    property @androidx.compose.runtime.Composable public final long circularColor;
+    property @androidx.compose.runtime.Composable public final long circularTrackColor;
+    property @androidx.compose.runtime.Composable public final long linearColor;
+    property @androidx.compose.runtime.Composable public final long linearTrackColor;
+    field public static final androidx.compose.material3.ProgressIndicatorDefaults INSTANCE;
+  }
+
+  public final class ProgressIndicatorKt {
+    method @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 @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(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth);
+    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 LinearProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor, optional int strokeCap);
+    method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor, optional int strokeCap);
+    method @Deprecated @androidx.compose.runtime.Composable public static void LinearProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor);
+    method @Deprecated @androidx.compose.runtime.Composable public static void LinearProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor);
+  }
+
+  @androidx.compose.runtime.Immutable public final class RadioButtonColors {
+  }
+
+  public final class RadioButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.RadioButtonColors colors(optional long selectedColor, optional long unselectedColor, optional long disabledSelectedColor, optional long disabledUnselectedColor);
+    field public static final androidx.compose.material3.RadioButtonDefaults INSTANCE;
+  }
+
+  public final class RadioButtonKt {
+    method @androidx.compose.runtime.Composable public static void RadioButton(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.RadioButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable @androidx.compose.runtime.Stable public final class RichTooltipColors {
+    ctor public RichTooltipColors(long containerColor, long contentColor, long titleContentColor, long actionContentColor);
+    method public long getActionContentColor();
+    method public long getContainerColor();
+    method public long getContentColor();
+    method public long getTitleContentColor();
+    property public final long actionContentColor;
+    property public final long containerColor;
+    property public final long contentColor;
+    property public final long titleContentColor;
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class RichTooltipState {
+    ctor public RichTooltipState();
+    method public suspend Object? dismiss(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public boolean isVisible();
+    method public suspend Object? show(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    property public boolean isVisible;
+  }
+
+  public final class ScaffoldDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getContentWindowInsets();
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets contentWindowInsets;
+    field public static final androidx.compose.material3.ScaffoldDefaults INSTANCE;
+  }
+
+  public final class ScaffoldKt {
+    method @androidx.compose.runtime.Composable public static void Scaffold(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional int floatingActionButtonPosition, optional long containerColor, optional long contentColor, optional androidx.compose.foundation.layout.WindowInsets contentWindowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class SearchBarColors {
+    method public long getContainerColor();
+    method public long getDividerColor();
+    method public androidx.compose.material3.TextFieldColors getInputFieldColors();
+    property public final long containerColor;
+    property public final long dividerColor;
+    property public final androidx.compose.material3.TextFieldColors inputFieldColors;
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api public final class SearchBarDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SearchBarColors colors(optional long containerColor, optional long dividerColor, optional androidx.compose.material3.TextFieldColors inputFieldColors);
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getDockedShape();
+    method public float getElevation();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFullScreenShape();
+    method public float getInputFieldHeight();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getInputFieldShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors inputFieldColors(optional long focusedTextColor, optional long unfocusedTextColor, optional long disabledTextColor, optional long cursorColor, optional androidx.compose.foundation.text.selection.TextSelectionColors selectionColors, optional long focusedLeadingIconColor, optional long unfocusedLeadingIconColor, optional long disabledLeadingIconColor, optional long focusedTrailingIconColor, optional long unfocusedTrailingIconColor, optional long disabledTrailingIconColor, optional long focusedPlaceholderColor, optional long unfocusedPlaceholderColor, optional long disabledPlaceholderColor);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors! inputFieldColors(optional long textColor, optional long disabledTextColor, optional long cursorColor, optional androidx.compose.foundation.text.selection.TextSelectionColors selectionColors, optional long focusedLeadingIconColor, optional long unfocusedLeadingIconColor, optional long disabledLeadingIconColor, optional long focusedTrailingIconColor, optional long unfocusedTrailingIconColor, optional long disabledTrailingIconColor, optional long placeholderColor, optional long disabledPlaceholderColor);
+    property public final float Elevation;
+    property public final float InputFieldHeight;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape dockedShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape fullScreenShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape inputFieldShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets windowInsets;
+    field public static final androidx.compose.material3.SearchBarDefaults INSTANCE;
+  }
+
+  public final class SearchBarKt {
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void DockedSearchBar(String query, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onQueryChange, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onSearch, boolean active, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onActiveChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, 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 androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.SearchBarColors colors, optional float tonalElevation, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void SearchBar(String query, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onQueryChange, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onSearch, boolean active, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onActiveChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, 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 androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.SearchBarColors colors, optional float tonalElevation, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class SelectableChipBorder {
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class SelectableChipColors {
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class SelectableChipElevation {
+  }
+
+  public final class ShapeDefaults {
+    method public androidx.compose.foundation.shape.CornerBasedShape getExtraLarge();
+    method public androidx.compose.foundation.shape.CornerBasedShape getExtraSmall();
+    method public androidx.compose.foundation.shape.CornerBasedShape getLarge();
+    method public androidx.compose.foundation.shape.CornerBasedShape getMedium();
+    method public androidx.compose.foundation.shape.CornerBasedShape getSmall();
+    property public final androidx.compose.foundation.shape.CornerBasedShape ExtraLarge;
+    property public final androidx.compose.foundation.shape.CornerBasedShape ExtraSmall;
+    property public final androidx.compose.foundation.shape.CornerBasedShape Large;
+    property public final androidx.compose.foundation.shape.CornerBasedShape Medium;
+    property public final androidx.compose.foundation.shape.CornerBasedShape Small;
+    field public static final androidx.compose.material3.ShapeDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Immutable public final class 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.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();
+    method public androidx.compose.foundation.shape.CornerBasedShape getExtraSmall();
+    method public androidx.compose.foundation.shape.CornerBasedShape getLarge();
+    method public androidx.compose.foundation.shape.CornerBasedShape getMedium();
+    method public androidx.compose.foundation.shape.CornerBasedShape getSmall();
+    property public final androidx.compose.foundation.shape.CornerBasedShape extraLarge;
+    property public final androidx.compose.foundation.shape.CornerBasedShape extraSmall;
+    property public final androidx.compose.foundation.shape.CornerBasedShape large;
+    property public final androidx.compose.foundation.shape.CornerBasedShape medium;
+    property public final androidx.compose.foundation.shape.CornerBasedShape small;
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class SheetState {
+    ctor public SheetState(boolean skipPartiallyExpanded, optional androidx.compose.material3.SheetValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHiddenState);
+    method public suspend Object? expand(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public androidx.compose.material3.SheetValue getCurrentValue();
+    method public boolean getHasExpandedState();
+    method public boolean getHasPartiallyExpandedState();
+    method public androidx.compose.material3.SheetValue getTargetValue();
+    method public suspend Object? hide(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public boolean isVisible();
+    method public suspend Object? partialExpand(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public float requireOffset();
+    method public suspend Object? show(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    property public final androidx.compose.material3.SheetValue currentValue;
+    property public final boolean hasExpandedState;
+    property public final boolean hasPartiallyExpandedState;
+    property public final boolean isVisible;
+    property public final androidx.compose.material3.SheetValue targetValue;
+    field public static final androidx.compose.material3.SheetState.Companion Companion;
+  }
+
+  public static final class SheetState.Companion {
+    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.SheetState,androidx.compose.material3.SheetValue> Saver(boolean skipPartiallyExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange);
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api public enum SheetValue {
+    method public static androidx.compose.material3.SheetValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.compose.material3.SheetValue[] values();
+    enum_constant public static final androidx.compose.material3.SheetValue Expanded;
+    enum_constant public static final androidx.compose.material3.SheetValue Hidden;
+    enum_constant public static final androidx.compose.material3.SheetValue PartiallyExpanded;
+  }
+
+  @androidx.compose.runtime.Immutable public final class SliderColors {
+  }
+
+  @androidx.compose.runtime.Stable public final class SliderDefaults {
+    method @androidx.compose.runtime.Composable public void Thumb(androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled, optional long thumbSize);
+    method @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.SliderPositions sliderPositions, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SliderColors colors(optional long thumbColor, optional long activeTrackColor, optional long activeTickColor, optional long inactiveTrackColor, optional long inactiveTickColor, optional long disabledThumbColor, optional long disabledActiveTrackColor, optional long disabledActiveTickColor, optional long disabledInactiveTrackColor, optional long disabledInactiveTickColor);
+    field public static final androidx.compose.material3.SliderDefaults INSTANCE;
+  }
+
+  public final class SliderKt {
+    method @androidx.compose.runtime.Composable public static void RangeSlider(kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> value, kotlin.jvm.functions.Function1<? super kotlin.ranges.ClosedFloatingPointRange<java.lang.Float>,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void RangeSlider(kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> value, kotlin.jvm.functions.Function1<? super kotlin.ranges.ClosedFloatingPointRange<java.lang.Float>,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource startInteractionSource, optional androidx.compose.foundation.interaction.MutableInteractionSource endInteractionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderPositions,kotlin.Unit> startThumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderPositions,kotlin.Unit> endThumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderPositions,kotlin.Unit> track, optional int steps);
+    method @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderPositions,kotlin.Unit> thumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderPositions,kotlin.Unit> track, optional int steps);
+  }
+
+  @androidx.compose.runtime.Stable public final class SliderPositions {
+    ctor public SliderPositions(optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> initialActiveRange, optional float[] initialTickFractions);
+    method public kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> getActiveRange();
+    method public float[] getTickFractions();
+    property public final kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> activeRange;
+    property public final float[] tickFractions;
+  }
+
+  @androidx.compose.runtime.Stable public interface SnackbarData {
+    method public void dismiss();
+    method public androidx.compose.material3.SnackbarVisuals getVisuals();
+    method public void performAction();
+    property public abstract androidx.compose.material3.SnackbarVisuals visuals;
+  }
+
+  public final class SnackbarDefaults {
+    method @androidx.compose.runtime.Composable public long getActionColor();
+    method @androidx.compose.runtime.Composable public long getActionContentColor();
+    method @androidx.compose.runtime.Composable public long getColor();
+    method @androidx.compose.runtime.Composable public long getContentColor();
+    method @androidx.compose.runtime.Composable public long getDismissActionContentColor();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    property @androidx.compose.runtime.Composable public final long actionColor;
+    property @androidx.compose.runtime.Composable public final long actionContentColor;
+    property @androidx.compose.runtime.Composable public final long color;
+    property @androidx.compose.runtime.Composable public final long contentColor;
+    property @androidx.compose.runtime.Composable public final long dismissActionContentColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    field public static final androidx.compose.material3.SnackbarDefaults INSTANCE;
+  }
+
+  public enum SnackbarDuration {
+    method public static androidx.compose.material3.SnackbarDuration valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.compose.material3.SnackbarDuration[] values();
+    enum_constant public static final androidx.compose.material3.SnackbarDuration Indefinite;
+    enum_constant public static final androidx.compose.material3.SnackbarDuration Long;
+    enum_constant public static final androidx.compose.material3.SnackbarDuration Short;
+  }
+
+  public final class SnackbarHostKt {
+    method @androidx.compose.runtime.Composable public static void SnackbarHost(androidx.compose.material3.SnackbarHostState hostState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SnackbarData,kotlin.Unit> snackbar);
+  }
+
+  @androidx.compose.runtime.Stable public final class SnackbarHostState {
+    ctor public SnackbarHostState();
+    method public androidx.compose.material3.SnackbarData? getCurrentSnackbarData();
+    method public suspend Object? showSnackbar(String message, optional String? actionLabel, optional boolean withDismissAction, optional androidx.compose.material3.SnackbarDuration duration, optional kotlin.coroutines.Continuation<? super androidx.compose.material3.SnackbarResult>);
+    method public suspend Object? showSnackbar(androidx.compose.material3.SnackbarVisuals visuals, kotlin.coroutines.Continuation<? super androidx.compose.material3.SnackbarResult>);
+    property public final androidx.compose.material3.SnackbarData? currentSnackbarData;
+  }
+
+  public final class SnackbarKt {
+    method @androidx.compose.runtime.Composable public static void Snackbar(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? action, optional kotlin.jvm.functions.Function0<kotlin.Unit>? dismissAction, optional boolean actionOnNewLine, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional long actionContentColor, optional long dismissActionContentColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Snackbar(androidx.compose.material3.SnackbarData snackbarData, optional androidx.compose.ui.Modifier modifier, optional boolean actionOnNewLine, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional long actionColor, optional long actionContentColor, optional long dismissActionContentColor);
+  }
+
+  public enum SnackbarResult {
+    method public static androidx.compose.material3.SnackbarResult valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.compose.material3.SnackbarResult[] values();
+    enum_constant public static final androidx.compose.material3.SnackbarResult ActionPerformed;
+    enum_constant public static final androidx.compose.material3.SnackbarResult Dismissed;
+  }
+
+  @androidx.compose.runtime.Stable public interface SnackbarVisuals {
+    method public String? getActionLabel();
+    method public androidx.compose.material3.SnackbarDuration getDuration();
+    method public String getMessage();
+    method public boolean getWithDismissAction();
+    property public abstract String? actionLabel;
+    property public abstract androidx.compose.material3.SnackbarDuration duration;
+    property public abstract String message;
+    property public abstract boolean withDismissAction;
+  }
+
+  public final class SuggestionChipDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipColors elevatedSuggestionChipColors(optional long containerColor, optional long labelColor, optional long iconContentColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledIconContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipElevation elevatedSuggestionChipElevation(optional float elevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method public float getHeight();
+    method public float getIconSize();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipBorder suggestionChipBorder(optional long borderColor, optional long disabledBorderColor, optional float borderWidth);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipColors suggestionChipColors(optional long containerColor, optional long labelColor, optional long iconContentColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledIconContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipElevation suggestionChipElevation(optional float elevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    property public final float Height;
+    property public final float IconSize;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    field public static final androidx.compose.material3.SuggestionChipDefaults INSTANCE;
+  }
+
+  public final class SurfaceKt {
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> getLocalAbsoluteTonalElevation();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> LocalAbsoluteTonalElevation;
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api public final class SwipeToDismissDefaults {
+    method public kotlin.jvm.functions.Function2<androidx.compose.ui.unit.Density,java.lang.Float,java.lang.Float> getFixedPositionalThreshold();
+    property public final kotlin.jvm.functions.Function2<androidx.compose.ui.unit.Density,java.lang.Float,java.lang.Float> FixedPositionalThreshold;
+    field public static final androidx.compose.material3.SwipeToDismissDefaults INSTANCE;
+  }
+
+  public final class SwipeToDismissKt {
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void SwipeToDismiss(androidx.compose.material3.DismissState state, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> background, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> dismissContent, optional androidx.compose.ui.Modifier modifier, optional java.util.Set<? extends androidx.compose.material3.DismissDirection> directions);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.DismissState rememberDismissState(optional androidx.compose.material3.DismissValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DismissValue,java.lang.Boolean> confirmValueChange, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super java.lang.Float,java.lang.Float> positionalThreshold);
+  }
+
+  @androidx.compose.runtime.Immutable public final class SwitchColors {
+  }
+
+  public final class SwitchDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SwitchColors colors(optional long checkedThumbColor, optional long checkedTrackColor, optional long checkedBorderColor, optional long checkedIconColor, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional long uncheckedBorderColor, optional long uncheckedIconColor, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledCheckedBorderColor, optional long disabledCheckedIconColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor, optional long disabledUncheckedBorderColor, optional long disabledUncheckedIconColor);
+    method public float getIconSize();
+    property public final float IconSize;
+    field public static final androidx.compose.material3.SwitchDefaults INSTANCE;
+  }
+
+  public final class SwitchKt {
+    method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? thumbContent, optional boolean enabled, optional androidx.compose.material3.SwitchColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  public final class TabKt {
+    method @androidx.compose.runtime.Composable public static void LeadingIconTab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> icon, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional long selectedContentColor, optional long unselectedContentColor, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void Tab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional long selectedContentColor, optional long unselectedContentColor, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void Tab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional long selectedContentColor, optional long unselectedContentColor, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+  }
+
+  @androidx.compose.runtime.Immutable public final class TabPosition {
+    method public float getLeft();
+    method public float getRight();
+    method public float getWidth();
+    property public final float left;
+    property public final float right;
+    property public final float width;
+  }
+
+  public final class TabRowDefaults {
+    method @androidx.compose.runtime.Composable public void Indicator(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method @androidx.compose.runtime.Composable public long getContentColor();
+    method public androidx.compose.ui.Modifier tabIndicatorOffset(androidx.compose.ui.Modifier, androidx.compose.material3.TabPosition currentTabPosition);
+    property @androidx.compose.runtime.Composable public final long containerColor;
+    property @androidx.compose.runtime.Composable public final long contentColor;
+    field public static final androidx.compose.material3.TabRowDefaults INSTANCE;
+  }
+
+  public final class TabRowKt {
+    method @androidx.compose.runtime.Composable public static void ScrollableTabRow(int selectedTabIndex, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional float edgePadding, optional kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.material3.TabPosition>,kotlin.Unit> indicator, optional kotlin.jvm.functions.Function0<kotlin.Unit> divider, kotlin.jvm.functions.Function0<kotlin.Unit> tabs);
+    method @androidx.compose.runtime.Composable public static void TabRow(int selectedTabIndex, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.material3.TabPosition>,kotlin.Unit> indicator, optional kotlin.jvm.functions.Function0<kotlin.Unit> divider, kotlin.jvm.functions.Function0<kotlin.Unit> tabs);
+  }
+
+  @androidx.compose.runtime.Immutable public final class TextFieldColors {
+  }
+
+  @androidx.compose.runtime.Immutable public final class TextFieldDefaults {
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public void FilledContainerBox(boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource, androidx.compose.material3.TextFieldColors colors, optional androidx.compose.ui.graphics.Shape shape);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public void OutlinedBorderContainerBox(boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource, androidx.compose.material3.TextFieldColors colors, optional androidx.compose.ui.graphics.Shape shape, optional float focusedBorderThickness, optional float unfocusedBorderThickness);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public void OutlinedTextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, 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 androidx.compose.material3.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit> container);
+    method @Deprecated @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public void OutlinedTextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<? extends kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? supportingText, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit> container);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public void TextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, 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 androidx.compose.ui.graphics.Shape shape, 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.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public void TextFieldDecorationBox(String value, kotlin.jvm.functions.Function0<? extends kotlin.Unit> innerTextField, boolean enabled, boolean singleLine, androidx.compose.ui.text.input.VisualTransformation visualTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional boolean isError, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? supportingText, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit> container);
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFilledShape();
+    method public float getFocusedBorderThickness();
+    method public float getMinHeight();
+    method public float getMinWidth();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getOutlinedShape();
+    method public float getUnfocusedBorderThickness();
+    method @androidx.compose.material3.ExperimentalMaterial3Api public androidx.compose.ui.Modifier indicatorLine(androidx.compose.ui.Modifier, boolean enabled, boolean isError, androidx.compose.foundation.interaction.InteractionSource interactionSource, androidx.compose.material3.TextFieldColors colors, optional float focusedIndicatorLineThickness, optional float unfocusedIndicatorLineThickness);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors outlinedTextFieldColors(optional long focusedTextColor, optional long unfocusedTextColor, optional long disabledTextColor, optional long errorTextColor, optional long containerColor, 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 @Deprecated @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors outlinedTextFieldColors(optional long textColor, optional long disabledTextColor, optional long containerColor, 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 placeholderColor, optional long disabledPlaceholderColor, 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 outlinedTextFieldPadding(optional float start, optional float top, optional float end, optional float bottom);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors textFieldColors(optional long focusedTextColor, optional long unfocusedTextColor, optional long disabledTextColor, optional long errorTextColor, optional long containerColor, 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 @Deprecated @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors textFieldColors(optional long textColor, optional long disabledTextColor, optional long containerColor, 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 placeholderColor, optional long disabledPlaceholderColor, 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 textFieldWithLabelPadding(optional float start, optional float end, optional float top, optional float bottom);
+    method public androidx.compose.foundation.layout.PaddingValues textFieldWithoutLabelPadding(optional float start, optional float top, optional float end, optional float bottom);
+    property public final float FocusedBorderThickness;
+    property public final float MinHeight;
+    property public final float MinWidth;
+    property public final float UnfocusedBorderThickness;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape filledShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape outlinedShape;
+    field public static final androidx.compose.material3.TextFieldDefaults INSTANCE;
+  }
+
+  public final class TextFieldKt {
+    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);
+    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 @Deprecated @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void TextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,? extends 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<? extends kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<? extends 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 @Deprecated @androidx.compose.material3.ExperimentalMaterial3Api @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,? extends 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<? extends kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<? extends kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<? extends 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 final class TextKt {
+    method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method @Deprecated @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method @Deprecated @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> getLocalTextStyle();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class TimePickerColors {
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class TimePickerDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.TimePickerColors colors(optional long clockDialColor, optional long clockDialSelectedContentColor, optional long clockDialUnselectedContentColor, optional long selectorColor, optional long containerColor, optional long periodSelectorBorderColor, optional long periodSelectorSelectedContainerColor, optional long periodSelectorUnselectedContainerColor, optional long periodSelectorSelectedContentColor, optional long periodSelectorUnselectedContentColor, optional long timeSelectorSelectedContainerColor, optional long timeSelectorUnselectedContainerColor, optional long timeSelectorSelectedContentColor, optional long timeSelectorUnselectedContentColor);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public int layoutType();
+    field public static final androidx.compose.material3.TimePickerDefaults INSTANCE;
+  }
+
+  public final class TimePickerKt {
+    method @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 @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 @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.TimePickerState rememberTimePickerState(optional int initialHour, optional int initialMinute, optional boolean is24Hour);
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class TimePickerLayoutType {
+    field public static final androidx.compose.material3.TimePickerLayoutType.Companion Companion;
+  }
+
+  public static final class TimePickerLayoutType.Companion {
+    method public int getHorizontal();
+    method public int getVertical();
+    property public final int Horizontal;
+    property public final int Vertical;
+  }
+
+  @androidx.compose.runtime.Stable public final class TimePickerState {
+    ctor public TimePickerState(int initialHour, int initialMinute, boolean is24Hour);
+    method public int getHour();
+    method public int getMinute();
+    method public boolean is24hour();
+    method public suspend Object? settle(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    property public final int hour;
+    property public final boolean is24hour;
+    property public final int minute;
+    field public static final androidx.compose.material3.TimePickerState.Companion Companion;
+  }
+
+  public static final class TimePickerState.Companion {
+    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.TimePickerState,?> Saver();
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api public interface TooltipBoxScope {
+    method public androidx.compose.ui.Modifier tooltipAnchor(androidx.compose.ui.Modifier);
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api public final class TooltipDefaults {
+    method @androidx.compose.runtime.Composable public long getPlainTooltipContainerColor();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getPlainTooltipContainerShape();
+    method @androidx.compose.runtime.Composable public long getPlainTooltipContentColor();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getRichTooltipContainerShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.RichTooltipColors richTooltipColors(optional long containerColor, optional long contentColor, optional long titleContentColor, optional long actionContentColor);
+    property @androidx.compose.runtime.Composable public final long plainTooltipContainerColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape plainTooltipContainerShape;
+    property @androidx.compose.runtime.Composable public final long plainTooltipContentColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape richTooltipContainerShape;
+    field public static final androidx.compose.material3.TooltipDefaults INSTANCE;
+  }
+
+  public final class TooltipKt {
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void PlainTooltipBox(kotlin.jvm.functions.Function0<kotlin.Unit> tooltip, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.PlainTooltipState tooltipState, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.material3.TooltipBoxScope,kotlin.Unit> content);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void RichTooltipBox(kotlin.jvm.functions.Function0<kotlin.Unit> text, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.RichTooltipState tooltipState, optional kotlin.jvm.functions.Function0<kotlin.Unit>? title, optional kotlin.jvm.functions.Function0<kotlin.Unit>? action, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.RichTooltipColors colors, kotlin.jvm.functions.Function1<? super androidx.compose.material3.TooltipBoxScope,kotlin.Unit> content);
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class TopAppBarColors {
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api public final class TopAppBarDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.TopAppBarColors centerAlignedTopAppBarColors(optional long containerColor, optional long scrolledContainerColor, optional long navigationIconContentColor, optional long titleContentColor, optional long actionIconContentColor);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public androidx.compose.material3.TopAppBarScrollBehavior enterAlwaysScrollBehavior(optional androidx.compose.material3.TopAppBarState state, optional kotlin.jvm.functions.Function0<java.lang.Boolean> canScroll, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float>? snapAnimationSpec, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float>? flingAnimationSpec);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public androidx.compose.material3.TopAppBarScrollBehavior exitUntilCollapsedScrollBehavior(optional androidx.compose.material3.TopAppBarState state, optional kotlin.jvm.functions.Function0<java.lang.Boolean> canScroll, 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 androidx.compose.foundation.layout.WindowInsets getWindowInsets();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.TopAppBarColors largeTopAppBarColors(optional long containerColor, optional long scrolledContainerColor, optional long navigationIconContentColor, optional long titleContentColor, optional long actionIconContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.TopAppBarColors mediumTopAppBarColors(optional long containerColor, optional long scrolledContainerColor, optional long navigationIconContentColor, optional long titleContentColor, optional long actionIconContentColor);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public androidx.compose.material3.TopAppBarScrollBehavior pinnedScrollBehavior(optional androidx.compose.material3.TopAppBarState state, optional kotlin.jvm.functions.Function0<java.lang.Boolean> canScroll);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material3.TopAppBarColors smallTopAppBarColors(optional long containerColor, optional long scrolledContainerColor, optional long navigationIconContentColor, optional long titleContentColor, optional long actionIconContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.TopAppBarColors topAppBarColors(optional long containerColor, optional long scrolledContainerColor, optional long navigationIconContentColor, optional long titleContentColor, optional long actionIconContentColor);
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets windowInsets;
+    field public static final androidx.compose.material3.TopAppBarDefaults INSTANCE;
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public interface TopAppBarScrollBehavior {
+    method public androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float>? getFlingAnimationSpec();
+    method public androidx.compose.ui.input.nestedscroll.NestedScrollConnection getNestedScrollConnection();
+    method public androidx.compose.animation.core.AnimationSpec<java.lang.Float>? getSnapAnimationSpec();
+    method public androidx.compose.material3.TopAppBarState getState();
+    method public boolean isPinned();
+    property public abstract androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float>? flingAnimationSpec;
+    property public abstract boolean isPinned;
+    property public abstract androidx.compose.ui.input.nestedscroll.NestedScrollConnection nestedScrollConnection;
+    property public abstract androidx.compose.animation.core.AnimationSpec<java.lang.Float>? snapAnimationSpec;
+    property public abstract androidx.compose.material3.TopAppBarState state;
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class TopAppBarState {
+    ctor public TopAppBarState(float initialHeightOffsetLimit, float initialHeightOffset, float initialContentOffset);
+    method public float getCollapsedFraction();
+    method public float getContentOffset();
+    method public float getHeightOffset();
+    method public float getHeightOffsetLimit();
+    method public float getOverlappedFraction();
+    method public void setContentOffset(float);
+    method public void setHeightOffset(float);
+    method public void setHeightOffsetLimit(float);
+    property public final float collapsedFraction;
+    property public final float contentOffset;
+    property public final float heightOffset;
+    property public final float heightOffsetLimit;
+    property public final float overlappedFraction;
+    field public static final androidx.compose.material3.TopAppBarState.Companion Companion;
+  }
+
+  public static final class TopAppBarState.Companion {
+    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.TopAppBarState,?> getSaver();
+    property public final androidx.compose.runtime.saveable.Saver<androidx.compose.material3.TopAppBarState,?> Saver;
+  }
+
+  @androidx.compose.runtime.Immutable public final class Typography {
+    ctor public Typography(optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
+    method public androidx.compose.material3.Typography copy(optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
+    method public androidx.compose.ui.text.TextStyle getBodyLarge();
+    method public androidx.compose.ui.text.TextStyle getBodyMedium();
+    method public androidx.compose.ui.text.TextStyle getBodySmall();
+    method public androidx.compose.ui.text.TextStyle getDisplayLarge();
+    method public androidx.compose.ui.text.TextStyle getDisplayMedium();
+    method public androidx.compose.ui.text.TextStyle getDisplaySmall();
+    method public androidx.compose.ui.text.TextStyle getHeadlineLarge();
+    method public androidx.compose.ui.text.TextStyle getHeadlineMedium();
+    method public androidx.compose.ui.text.TextStyle getHeadlineSmall();
+    method public androidx.compose.ui.text.TextStyle getLabelLarge();
+    method public androidx.compose.ui.text.TextStyle getLabelMedium();
+    method public androidx.compose.ui.text.TextStyle getLabelSmall();
+    method public androidx.compose.ui.text.TextStyle getTitleLarge();
+    method public androidx.compose.ui.text.TextStyle getTitleMedium();
+    method public androidx.compose.ui.text.TextStyle getTitleSmall();
+    property public final androidx.compose.ui.text.TextStyle bodyLarge;
+    property public final androidx.compose.ui.text.TextStyle bodyMedium;
+    property public final androidx.compose.ui.text.TextStyle bodySmall;
+    property public final androidx.compose.ui.text.TextStyle displayLarge;
+    property public final androidx.compose.ui.text.TextStyle displayMedium;
+    property public final androidx.compose.ui.text.TextStyle displaySmall;
+    property public final androidx.compose.ui.text.TextStyle headlineLarge;
+    property public final androidx.compose.ui.text.TextStyle headlineMedium;
+    property public final androidx.compose.ui.text.TextStyle headlineSmall;
+    property public final androidx.compose.ui.text.TextStyle labelLarge;
+    property public final androidx.compose.ui.text.TextStyle labelMedium;
+    property public final androidx.compose.ui.text.TextStyle labelSmall;
+    property public final androidx.compose.ui.text.TextStyle titleLarge;
+    property public final androidx.compose.ui.text.TextStyle titleMedium;
+    property public final androidx.compose.ui.text.TextStyle titleSmall;
+  }
+
+}
+
diff --git a/compose/material3/material3/api/public_plus_experimental_current.txt b/compose/material3/material3/api/public_plus_experimental_current.txt
index 2cc9b86..0b261b3 100644
--- a/compose/material3/material3/api/public_plus_experimental_current.txt
+++ b/compose/material3/material3/api/public_plus_experimental_current.txt
@@ -83,22 +83,22 @@
     method @androidx.compose.runtime.Composable public long getContainerColor();
     method public float getElevation();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getExpandedShape();
-    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getMinimizedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getHiddenShape();
     method @androidx.compose.runtime.Composable public long getScrimColor();
     method public float getSheetPeekHeight();
     property @androidx.compose.runtime.Composable public final long ContainerColor;
     property public final float Elevation;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape ExpandedShape;
-    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape MinimizedShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape HiddenShape;
     property @androidx.compose.runtime.Composable public final long ScrimColor;
     property public final float SheetPeekHeight;
     field public static final androidx.compose.material3.BottomSheetDefaults INSTANCE;
   }
 
   public final class BottomSheetScaffoldKt {
-    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void BottomSheetScaffold(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.BottomSheetScaffoldState scaffoldState, optional float sheetPeekHeight, optional androidx.compose.ui.graphics.Shape sheetShape, optional long sheetContainerColor, optional long sheetContentColor, optional float sheetTonalElevation, optional kotlin.jvm.functions.Function0<kotlin.Unit>? sheetDragHandle, optional boolean sheetSwipeEnabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? topBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SnackbarHostState,kotlin.Unit> snackbarHost, optional long containerColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void BottomSheetScaffold(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> sheetContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.BottomSheetScaffoldState scaffoldState, optional float sheetPeekHeight, optional androidx.compose.ui.graphics.Shape sheetShape, optional long sheetContainerColor, optional long sheetContentColor, optional float sheetTonalElevation, optional float sheetShadowElevation, optional kotlin.jvm.functions.Function0<kotlin.Unit>? sheetDragHandle, optional boolean sheetSwipeEnabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? topBar, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SnackbarHostState,kotlin.Unit> snackbarHost, optional long containerColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
     method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.BottomSheetScaffoldState rememberBottomSheetScaffoldState(optional androidx.compose.material3.SheetState bottomSheetState, optional androidx.compose.material3.SnackbarHostState snackbarHostState);
-    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.SheetState rememberStandardBottomSheetState(optional androidx.compose.material3.SheetValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.SheetState rememberStandardBottomSheetState(optional androidx.compose.material3.SheetValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHiddenState);
   }
 
   @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class BottomSheetScaffoldState {
@@ -858,16 +858,16 @@
     property public final androidx.compose.material3.TextFieldColors inputFieldColors;
   }
 
-  public final class SearchBarDefaults {
-    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public androidx.compose.material3.SearchBarColors colors(optional long containerColor, optional long dividerColor, optional androidx.compose.material3.TextFieldColors inputFieldColors);
+  @androidx.compose.material3.ExperimentalMaterial3Api public final class SearchBarDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SearchBarColors colors(optional long containerColor, optional long dividerColor, optional androidx.compose.material3.TextFieldColors inputFieldColors);
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getDockedShape();
     method public float getElevation();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFullScreenShape();
     method public float getInputFieldHeight();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getInputFieldShape();
     method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
-    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors inputFieldColors(optional long focusedTextColor, optional long unfocusedTextColor, optional long disabledTextColor, optional long cursorColor, optional androidx.compose.foundation.text.selection.TextSelectionColors selectionColors, optional long focusedLeadingIconColor, optional long unfocusedLeadingIconColor, optional long disabledLeadingIconColor, optional long focusedTrailingIconColor, optional long unfocusedTrailingIconColor, optional long disabledTrailingIconColor, optional long focusedPlaceholderColor, optional long unfocusedPlaceholderColor, optional long disabledPlaceholderColor);
-    method @Deprecated @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors! inputFieldColors(optional long textColor, optional long disabledTextColor, optional long cursorColor, optional androidx.compose.foundation.text.selection.TextSelectionColors selectionColors, optional long focusedLeadingIconColor, optional long unfocusedLeadingIconColor, optional long disabledLeadingIconColor, optional long focusedTrailingIconColor, optional long unfocusedTrailingIconColor, optional long disabledTrailingIconColor, optional long placeholderColor, optional long disabledPlaceholderColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors inputFieldColors(optional long focusedTextColor, optional long unfocusedTextColor, optional long disabledTextColor, optional long cursorColor, optional androidx.compose.foundation.text.selection.TextSelectionColors selectionColors, optional long focusedLeadingIconColor, optional long unfocusedLeadingIconColor, optional long disabledLeadingIconColor, optional long focusedTrailingIconColor, optional long unfocusedTrailingIconColor, optional long disabledTrailingIconColor, optional long focusedPlaceholderColor, optional long unfocusedPlaceholderColor, optional long disabledPlaceholderColor);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors! inputFieldColors(optional long textColor, optional long disabledTextColor, optional long cursorColor, optional androidx.compose.foundation.text.selection.TextSelectionColors selectionColors, optional long focusedLeadingIconColor, optional long unfocusedLeadingIconColor, optional long disabledLeadingIconColor, optional long focusedTrailingIconColor, optional long unfocusedTrailingIconColor, optional long disabledTrailingIconColor, optional long placeholderColor, optional long disabledPlaceholderColor);
     property public final float Elevation;
     property public final float InputFieldHeight;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape dockedShape;
@@ -921,7 +921,7 @@
   }
 
   @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class SheetState {
-    ctor public SheetState(boolean skipPartiallyExpanded, optional androidx.compose.material3.SheetValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange);
+    ctor public SheetState(boolean skipPartiallyExpanded, optional androidx.compose.material3.SheetValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHiddenState);
     method public suspend Object? expand(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public androidx.compose.material3.SheetValue getCurrentValue();
     method public boolean getHasExpandedState();
@@ -1177,15 +1177,27 @@
 
   @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class TimePickerDefaults {
     method @androidx.compose.runtime.Composable public androidx.compose.material3.TimePickerColors colors(optional long clockDialColor, optional long clockDialSelectedContentColor, optional long clockDialUnselectedContentColor, optional long selectorColor, optional long containerColor, optional long periodSelectorBorderColor, optional long periodSelectorSelectedContainerColor, optional long periodSelectorUnselectedContainerColor, optional long periodSelectorSelectedContentColor, optional long periodSelectorUnselectedContentColor, optional long timeSelectorSelectedContainerColor, optional long timeSelectorUnselectedContainerColor, optional long timeSelectorSelectedContentColor, optional long timeSelectorUnselectedContentColor);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public int layoutType();
     field public static final androidx.compose.material3.TimePickerDefaults INSTANCE;
   }
 
   public final class TimePickerKt {
     method @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 @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);
+    method @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 @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.TimePickerState rememberTimePickerState(optional int initialHour, optional int initialMinute, optional boolean is24Hour);
   }
 
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class TimePickerLayoutType {
+    field public static final androidx.compose.material3.TimePickerLayoutType.Companion Companion;
+  }
+
+  public static final class TimePickerLayoutType.Companion {
+    method public int getHorizontal();
+    method public int getVertical();
+    property public final int Horizontal;
+    property public final int Vertical;
+  }
+
   @androidx.compose.runtime.Stable public final class TimePickerState {
     ctor public TimePickerState(int initialHour, int initialMinute, boolean is24Hour);
     method public int getHour();
diff --git a/ads/ads-identifier-common/api/res-current.txt b/compose/material3/material3/api/res-1.1.0-beta01.txt
similarity index 100%
copy from ads/ads-identifier-common/api/res-current.txt
copy to compose/material3/material3/api/res-1.1.0-beta01.txt
diff --git a/compose/material3/material3/api/restricted_1.1.0-beta01.txt b/compose/material3/material3/api/restricted_1.1.0-beta01.txt
new file mode 100644
index 0000000..7e2de6f
--- /dev/null
+++ b/compose/material3/material3/api/restricted_1.1.0-beta01.txt
@@ -0,0 +1,856 @@
+// Signature format: 4.0
+package androidx.compose.material3 {
+
+  public final class AlertDialogDefaults {
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method @androidx.compose.runtime.Composable public long getIconContentColor();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method @androidx.compose.runtime.Composable public long getTextContentColor();
+    method @androidx.compose.runtime.Composable public long getTitleContentColor();
+    method public float getTonalElevation();
+    property public final float TonalElevation;
+    property @androidx.compose.runtime.Composable public final long containerColor;
+    property @androidx.compose.runtime.Composable public final long iconContentColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    property @androidx.compose.runtime.Composable public final long textContentColor;
+    property @androidx.compose.runtime.Composable public final long titleContentColor;
+    field public static final androidx.compose.material3.AlertDialogDefaults INSTANCE;
+  }
+
+  public final class AndroidAlertDialog_androidKt {
+    method @androidx.compose.runtime.Composable public static void AlertDialog(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function0<kotlin.Unit> confirmButton, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? dismissButton, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? title, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long iconContentColor, optional long titleContentColor, optional long textContentColor, optional float tonalElevation, optional androidx.compose.ui.window.DialogProperties properties);
+  }
+
+  public final class AndroidMenu_androidKt {
+    method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean enabled, optional androidx.compose.material3.MenuItemColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  public final class AppBarKt {
+    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);
+    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);
+  }
+
+  public final class AssistChipDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipBorder assistChipBorder(optional long borderColor, optional long disabledBorderColor, optional float borderWidth);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipColors assistChipColors(optional long containerColor, optional long labelColor, optional long leadingIconContentColor, optional long trailingIconContentColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledLeadingIconContentColor, optional long disabledTrailingIconContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipElevation assistChipElevation(optional float elevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipColors elevatedAssistChipColors(optional long containerColor, optional long labelColor, optional long leadingIconContentColor, optional long trailingIconContentColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledLeadingIconContentColor, optional long disabledTrailingIconContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipElevation elevatedAssistChipElevation(optional float elevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method public float getHeight();
+    method public float getIconSize();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    property public final float Height;
+    property public final float IconSize;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    field public static final androidx.compose.material3.AssistChipDefaults INSTANCE;
+  }
+
+  public final class BottomAppBarDefaults {
+    method @androidx.compose.runtime.Composable public long getBottomAppBarFabColor();
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method public float getContainerElevation();
+    method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    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 @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;
+    field public static final androidx.compose.material3.BottomAppBarDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Immutable public final class ButtonColors {
+  }
+
+  public final class ButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonColors buttonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonElevation buttonElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float disabledElevation);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonColors elevatedButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonElevation elevatedButtonElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float disabledElevation);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonColors filledTonalButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonElevation filledTonalButtonElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float disabledElevation);
+    method public androidx.compose.foundation.layout.PaddingValues getButtonWithIconContentPadding();
+    method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getElevatedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFilledTonalShape();
+    method public float getIconSize();
+    method public float getIconSpacing();
+    method public float getMinHeight();
+    method public float getMinWidth();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke getOutlinedButtonBorder();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getOutlinedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method public androidx.compose.foundation.layout.PaddingValues getTextButtonContentPadding();
+    method public androidx.compose.foundation.layout.PaddingValues getTextButtonWithIconContentPadding();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getTextShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonColors outlinedButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ButtonColors textButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    property public final androidx.compose.foundation.layout.PaddingValues ButtonWithIconContentPadding;
+    property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
+    property public final float IconSize;
+    property public final float IconSpacing;
+    property public final float MinHeight;
+    property public final float MinWidth;
+    property public final androidx.compose.foundation.layout.PaddingValues TextButtonContentPadding;
+    property public final androidx.compose.foundation.layout.PaddingValues TextButtonWithIconContentPadding;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape elevatedShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape filledTonalShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.BorderStroke outlinedButtonBorder;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape outlinedShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape textShape;
+    field public static final androidx.compose.material3.ButtonDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Stable public final class ButtonElevation {
+  }
+
+  public final class ButtonKt {
+    method @androidx.compose.runtime.Composable public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ElevatedButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void FilledTonalButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void OutlinedButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void TextButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+  }
+
+  @androidx.compose.runtime.Immutable public final class CardColors {
+  }
+
+  public final class CardDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CardColors cardColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CardElevation cardElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CardColors elevatedCardColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CardElevation elevatedCardElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getElevatedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getOutlinedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke outlinedCardBorder(optional boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CardColors outlinedCardColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CardElevation outlinedCardElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape elevatedShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape outlinedShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    field public static final androidx.compose.material3.CardDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Immutable public final class CardElevation {
+  }
+
+  public final class CardKt {
+    method @androidx.compose.runtime.Composable public static void Card(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.CardColors colors, optional androidx.compose.material3.CardElevation elevation, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ElevatedCard(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.CardColors colors, optional androidx.compose.material3.CardElevation elevation, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void OutlinedCard(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.CardColors colors, optional androidx.compose.material3.CardElevation elevation, optional androidx.compose.foundation.BorderStroke border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+  }
+
+  @androidx.compose.runtime.Immutable public final class CheckboxColors {
+  }
+
+  public final class CheckboxDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.CheckboxColors colors(optional long checkedColor, optional long uncheckedColor, optional long checkmarkColor, optional long disabledCheckedColor, optional long disabledUncheckedColor, optional long disabledIndeterminateColor);
+    field public static final androidx.compose.material3.CheckboxDefaults INSTANCE;
+  }
+
+  public final class CheckboxKt {
+    method @androidx.compose.runtime.Composable public static void Checkbox(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.CheckboxColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void TriStateCheckbox(androidx.compose.ui.state.ToggleableState state, kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.CheckboxColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  @androidx.compose.runtime.Immutable public final class ChipBorder {
+  }
+
+  @androidx.compose.runtime.Immutable public final class ChipColors {
+  }
+
+  @androidx.compose.runtime.Immutable public final class ChipElevation {
+  }
+
+  public final class ChipKt {
+    method @androidx.compose.runtime.Composable public static void AssistChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipColors colors, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void ElevatedAssistChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipColors colors, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void ElevatedSuggestionChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipColors colors, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void SuggestionChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipColors colors, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  @androidx.compose.runtime.Stable public final class ColorScheme {
+    ctor public ColorScheme(long primary, long onPrimary, long primaryContainer, long onPrimaryContainer, long inversePrimary, long secondary, long onSecondary, long secondaryContainer, long onSecondaryContainer, long tertiary, long onTertiary, long tertiaryContainer, long onTertiaryContainer, long background, long onBackground, long surface, long onSurface, long surfaceVariant, long onSurfaceVariant, long surfaceTint, long inverseSurface, long inverseOnSurface, long error, long onError, long errorContainer, long onErrorContainer, long outline, long outlineVariant, long scrim);
+    method public androidx.compose.material3.ColorScheme copy(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 long getBackground();
+    method public long getError();
+    method public long getErrorContainer();
+    method public long getInverseOnSurface();
+    method public long getInversePrimary();
+    method public long getInverseSurface();
+    method public long getOnBackground();
+    method public long getOnError();
+    method public long getOnErrorContainer();
+    method public long getOnPrimary();
+    method public long getOnPrimaryContainer();
+    method public long getOnSecondary();
+    method public long getOnSecondaryContainer();
+    method public long getOnSurface();
+    method public long getOnSurfaceVariant();
+    method public long getOnTertiary();
+    method public long getOnTertiaryContainer();
+    method public long getOutline();
+    method public long getOutlineVariant();
+    method public long getPrimary();
+    method public long getPrimaryContainer();
+    method public long getScrim();
+    method public long getSecondary();
+    method public long getSecondaryContainer();
+    method public long getSurface();
+    method public long getSurfaceTint();
+    method public long getSurfaceVariant();
+    method public long getTertiary();
+    method public long getTertiaryContainer();
+    property public final long background;
+    property public final long error;
+    property public final long errorContainer;
+    property public final long inverseOnSurface;
+    property public final long inversePrimary;
+    property public final long inverseSurface;
+    property public final long onBackground;
+    property public final long onError;
+    property public final long onErrorContainer;
+    property public final long onPrimary;
+    property public final long onPrimaryContainer;
+    property public final long onSecondary;
+    property public final long onSecondaryContainer;
+    property public final long onSurface;
+    property public final long onSurfaceVariant;
+    property public final long onTertiary;
+    property public final long onTertiaryContainer;
+    property public final long outline;
+    property public final long outlineVariant;
+    property public final long primary;
+    property public final long primaryContainer;
+    property public final long scrim;
+    property public final long secondary;
+    property public final long secondaryContainer;
+    property public final long surface;
+    property public final long surfaceTint;
+    property public final long surfaceVariant;
+    property public final long tertiary;
+    property public final long tertiaryContainer;
+  }
+
+  public final class ColorSchemeKt {
+    method public static long contentColorFor(androidx.compose.material3.ColorScheme, long backgroundColor);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static long contentColorFor(long backgroundColor);
+    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);
+    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);
+    method public static long surfaceColorAtElevation(androidx.compose.material3.ColorScheme, float elevation);
+  }
+
+  public final class ContentColorKt {
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> getLocalContentColor();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
+  }
+
+  public final class DividerDefaults {
+    method @androidx.compose.runtime.Composable public long getColor();
+    method public float getThickness();
+    property public final float Thickness;
+    property @androidx.compose.runtime.Composable public final long color;
+    field public static final androidx.compose.material3.DividerDefaults INSTANCE;
+  }
+
+  public final class DividerKt {
+    method @androidx.compose.runtime.Composable public static void Divider(optional androidx.compose.ui.Modifier modifier, optional float thickness, optional long color);
+  }
+
+  public final class DrawerDefaults {
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method public float getDismissibleDrawerElevation();
+    method public float getMaximumDrawerWidth();
+    method public float getModalDrawerElevation();
+    method public float getPermanentDrawerElevation();
+    method @androidx.compose.runtime.Composable public long getScrimColor();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
+    property public final float DismissibleDrawerElevation;
+    property public final float MaximumDrawerWidth;
+    property public final float ModalDrawerElevation;
+    property public final float PermanentDrawerElevation;
+    property @androidx.compose.runtime.Composable public final long containerColor;
+    property @androidx.compose.runtime.Composable public final long scrimColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets windowInsets;
+    field public static final androidx.compose.material3.DrawerDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Stable public final class DrawerState {
+    ctor public DrawerState(androidx.compose.material3.DrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DrawerValue,java.lang.Boolean> confirmStateChange);
+    method public suspend Object? animateTo(androidx.compose.material3.DrawerValue targetValue, androidx.compose.animation.core.AnimationSpec<java.lang.Float> anim, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public suspend Object? close(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public androidx.compose.material3.DrawerValue getCurrentValue();
+    method public androidx.compose.runtime.State<java.lang.Float> getOffset();
+    method public androidx.compose.material3.DrawerValue getTargetValue();
+    method public boolean isAnimationRunning();
+    method public boolean isClosed();
+    method public boolean isOpen();
+    method public suspend Object? open(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public suspend Object? snapTo(androidx.compose.material3.DrawerValue targetValue, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    property public final androidx.compose.material3.DrawerValue currentValue;
+    property public final boolean isAnimationRunning;
+    property public final boolean isClosed;
+    property public final boolean isOpen;
+    property public final androidx.compose.runtime.State<java.lang.Float> offset;
+    property public final androidx.compose.material3.DrawerValue targetValue;
+    field public static final androidx.compose.material3.DrawerState.Companion Companion;
+  }
+
+  public static final class DrawerState.Companion {
+    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.DrawerState,androidx.compose.material3.DrawerValue> Saver(kotlin.jvm.functions.Function1<? super androidx.compose.material3.DrawerValue,java.lang.Boolean> confirmStateChange);
+  }
+
+  public enum DrawerValue {
+    method public static androidx.compose.material3.DrawerValue valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.compose.material3.DrawerValue[] values();
+    enum_constant public static final androidx.compose.material3.DrawerValue Closed;
+    enum_constant public static final androidx.compose.material3.DrawerValue Open;
+  }
+
+  public final class DynamicTonalPaletteKt {
+    method @RequiresApi(android.os.Build.VERSION_CODES.S) public static androidx.compose.material3.ColorScheme dynamicDarkColorScheme(android.content.Context context);
+    method @RequiresApi(android.os.Build.VERSION_CODES.S) public static androidx.compose.material3.ColorScheme dynamicLightColorScheme(android.content.Context context);
+  }
+
+  @kotlin.jvm.JvmInline public final value class FabPosition {
+    field public static final androidx.compose.material3.FabPosition.Companion Companion;
+  }
+
+  public static final class FabPosition.Companion {
+    method public int getCenter();
+    method public int getEnd();
+    property public final int Center;
+    property public final int End;
+  }
+
+  public final class FloatingActionButtonDefaults {
+    method public androidx.compose.material3.FloatingActionButtonElevation bottomAppBarFabElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation);
+    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 public float getLargeIconSize();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getLargeShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    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 @androidx.compose.runtime.Composable public final long containerColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape extendedFabShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape largeShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape smallShape;
+    field public static final androidx.compose.material3.FloatingActionButtonDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Stable public class FloatingActionButtonElevation {
+  }
+
+  public final class FloatingActionButtonKt {
+    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 @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 @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);
+  }
+
+  @androidx.compose.runtime.Immutable public final class IconButtonColors {
+  }
+
+  public final class IconButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconButtonColors filledIconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconToggleButtonColors filledIconToggleButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor, optional long checkedContainerColor, optional long checkedContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconButtonColors filledTonalIconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconToggleButtonColors filledTonalIconToggleButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor, optional long checkedContainerColor, optional long checkedContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFilledShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getOutlinedShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconButtonColors iconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconToggleButtonColors iconToggleButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor, optional long checkedContainerColor, optional long checkedContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke outlinedIconButtonBorder(boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconButtonColors outlinedIconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke? outlinedIconToggleButtonBorder(boolean enabled, boolean checked);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.IconToggleButtonColors outlinedIconToggleButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor, optional long checkedContainerColor, optional long checkedContentColor);
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape filledShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape outlinedShape;
+    field public static final androidx.compose.material3.IconButtonDefaults INSTANCE;
+  }
+
+  public final class IconButtonKt {
+    method @androidx.compose.runtime.Composable public static void FilledIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void FilledIconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.IconToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void FilledTonalIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void FilledTonalIconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.IconToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void IconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void IconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.IconToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void OutlinedIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void OutlinedIconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.IconToggleButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class IconKt {
+    method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.vector.ImageVector imageVector, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.ImageBitmap bitmap, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.painter.Painter painter, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+  }
+
+  @androidx.compose.runtime.Immutable public final class IconToggleButtonColors {
+  }
+
+  public final class InteractiveComponentSizeKt {
+    method public static androidx.compose.ui.Modifier minimumInteractiveComponentSize(androidx.compose.ui.Modifier);
+  }
+
+  @androidx.compose.runtime.Immutable public final class ListItemColors {
+  }
+
+  public final class ListItemDefaults {
+    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();
+    method public float getElevation();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.ui.graphics.Shape getShape();
+    property public final float Elevation;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final long containerColor;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final long contentColor;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.compose.ui.graphics.Shape shape;
+    field public static final androidx.compose.material3.ListItemDefaults INSTANCE;
+  }
+
+  public final class ListItemKt {
+    method @androidx.compose.runtime.Composable public static void ListItem(kotlin.jvm.functions.Function0<kotlin.Unit> headlineContent, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? overlineContent, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingContent, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingContent, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingContent, optional androidx.compose.material3.ListItemColors colors, optional float tonalElevation, optional float shadowElevation);
+  }
+
+  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.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 @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 @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);
+  }
+
+  public final class MenuDefaults {
+    method public androidx.compose.foundation.layout.PaddingValues getDropdownMenuItemContentPadding();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.MenuItemColors itemColors(optional long textColor, optional long leadingIconColor, optional long trailingIconColor, optional long disabledTextColor, optional long disabledLeadingIconColor, optional long disabledTrailingIconColor);
+    property public final androidx.compose.foundation.layout.PaddingValues DropdownMenuItemContentPadding;
+    field public static final androidx.compose.material3.MenuDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Immutable public final class MenuItemColors {
+  }
+
+  public final class NavigationBarDefaults {
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method public float getElevation();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
+    property public final float Elevation;
+    property @androidx.compose.runtime.Composable public final long containerColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets windowInsets;
+    field public static final androidx.compose.material3.NavigationBarDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Stable public final class NavigationBarItemColors {
+  }
+
+  public final class NavigationBarItemDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.NavigationBarItemColors colors(optional long selectedIconColor, optional long selectedTextColor, optional long indicatorColor, optional long unselectedIconColor, optional long unselectedTextColor, optional long disabledIconColor, optional long disabledTextColor);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material3.NavigationBarItemColors colors(optional long selectedIconColor, optional long selectedTextColor, optional long indicatorColor, optional long unselectedIconColor, optional long unselectedTextColor);
+    field public static final androidx.compose.material3.NavigationBarItemDefaults INSTANCE;
+  }
+
+  public final class NavigationBarKt {
+    method @androidx.compose.runtime.Composable public static void NavigationBar(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional float tonalElevation, 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 NavigationBarItem(androidx.compose.foundation.layout.RowScope, 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.NavigationBarItemColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  @androidx.compose.runtime.Stable public interface NavigationDrawerItemColors {
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> badgeColor(boolean selected);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> containerColor(boolean selected);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> iconColor(boolean selected);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> textColor(boolean selected);
+  }
+
+  public final class NavigationDrawerItemDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.NavigationDrawerItemColors colors(optional long selectedContainerColor, optional long unselectedContainerColor, optional long selectedIconColor, optional long unselectedIconColor, optional long selectedTextColor, optional long unselectedTextColor, optional long selectedBadgeColor, optional long unselectedBadgeColor);
+    method public androidx.compose.foundation.layout.PaddingValues getItemPadding();
+    property public final androidx.compose.foundation.layout.PaddingValues ItemPadding;
+    field public static final androidx.compose.material3.NavigationDrawerItemDefaults INSTANCE;
+  }
+
+  public final class NavigationDrawerKt {
+    method @androidx.compose.runtime.Composable public static void DismissibleDrawerSheet(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape drawerShape, optional long drawerContainerColor, optional long drawerContentColor, optional float drawerTonalElevation, optional androidx.compose.foundation.layout.WindowInsets windowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void DismissibleNavigationDrawer(kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.DrawerState drawerState, optional boolean gesturesEnabled, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ModalDrawerSheet(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape drawerShape, optional long drawerContainerColor, optional long drawerContentColor, optional float drawerTonalElevation, optional androidx.compose.foundation.layout.WindowInsets windowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void ModalNavigationDrawer(kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.DrawerState drawerState, optional boolean gesturesEnabled, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void NavigationDrawerItem(kotlin.jvm.functions.Function0<kotlin.Unit> label, boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? badge, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.NavigationDrawerItemColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void PermanentDrawerSheet(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape drawerShape, optional long drawerContainerColor, optional long drawerContentColor, optional float drawerTonalElevation, optional androidx.compose.foundation.layout.WindowInsets windowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void PermanentNavigationDrawer(kotlin.jvm.functions.Function0<kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static androidx.compose.material3.DrawerState rememberDrawerState(androidx.compose.material3.DrawerValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DrawerValue,java.lang.Boolean> confirmStateChange);
+  }
+
+  public final class NavigationRailDefaults {
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
+    property @androidx.compose.runtime.Composable public final long ContainerColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets windowInsets;
+    field public static final androidx.compose.material3.NavigationRailDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Stable public final class NavigationRailItemColors {
+  }
+
+  public final class NavigationRailItemDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.NavigationRailItemColors colors(optional long selectedIconColor, optional long selectedTextColor, optional long indicatorColor, optional long unselectedIconColor, optional long unselectedTextColor, optional long disabledIconColor, optional long disabledTextColor);
+    method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.material3.NavigationRailItemColors colors(optional long selectedIconColor, optional long selectedTextColor, optional long indicatorColor, optional long unselectedIconColor, optional long unselectedTextColor);
+    field public static final androidx.compose.material3.NavigationRailItemDefaults INSTANCE;
+  }
+
+  public final class NavigationRailKt {
+    method @androidx.compose.runtime.Composable public static void NavigationRail(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? header, optional androidx.compose.foundation.layout.WindowInsets windowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+    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 OutlinedTextFieldKt {
+    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);
+    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);
+  }
+
+  public final class ProgressIndicatorDefaults {
+    method @androidx.compose.runtime.Composable public long getCircularColor();
+    method public int getCircularDeterminateStrokeCap();
+    method public int getCircularIndeterminateStrokeCap();
+    method public float getCircularStrokeWidth();
+    method @androidx.compose.runtime.Composable public long getCircularTrackColor();
+    method @androidx.compose.runtime.Composable public long getLinearColor();
+    method public int getLinearStrokeCap();
+    method @androidx.compose.runtime.Composable public long getLinearTrackColor();
+    method public androidx.compose.animation.core.SpringSpec<java.lang.Float> getProgressAnimationSpec();
+    property public final int CircularDeterminateStrokeCap;
+    property public final int CircularIndeterminateStrokeCap;
+    property public final float CircularStrokeWidth;
+    property public final int LinearStrokeCap;
+    property public final androidx.compose.animation.core.SpringSpec<java.lang.Float> ProgressAnimationSpec;
+    property @androidx.compose.runtime.Composable public final long circularColor;
+    property @androidx.compose.runtime.Composable public final long circularTrackColor;
+    property @androidx.compose.runtime.Composable public final long linearColor;
+    property @androidx.compose.runtime.Composable public final long linearTrackColor;
+    field public static final androidx.compose.material3.ProgressIndicatorDefaults INSTANCE;
+  }
+
+  public final class ProgressIndicatorKt {
+    method @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 @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(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth);
+    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 LinearProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor, optional int strokeCap);
+    method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor, optional int strokeCap);
+    method @Deprecated @androidx.compose.runtime.Composable public static void LinearProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor);
+    method @Deprecated @androidx.compose.runtime.Composable public static void LinearProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor);
+  }
+
+  @androidx.compose.runtime.Immutable public final class RadioButtonColors {
+  }
+
+  public final class RadioButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.RadioButtonColors colors(optional long selectedColor, optional long unselectedColor, optional long disabledSelectedColor, optional long disabledUnselectedColor);
+    field public static final androidx.compose.material3.RadioButtonDefaults INSTANCE;
+  }
+
+  public final class RadioButtonKt {
+    method @androidx.compose.runtime.Composable public static void RadioButton(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.RadioButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  public final class ScaffoldDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getContentWindowInsets();
+    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets contentWindowInsets;
+    field public static final androidx.compose.material3.ScaffoldDefaults INSTANCE;
+  }
+
+  public final class ScaffoldKt {
+    method @androidx.compose.runtime.Composable public static void Scaffold(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional int floatingActionButtonPosition, optional long containerColor, optional long contentColor, optional androidx.compose.foundation.layout.WindowInsets contentWindowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
+  }
+
+  public final class ShapeDefaults {
+    method public androidx.compose.foundation.shape.CornerBasedShape getExtraLarge();
+    method public androidx.compose.foundation.shape.CornerBasedShape getExtraSmall();
+    method public androidx.compose.foundation.shape.CornerBasedShape getLarge();
+    method public androidx.compose.foundation.shape.CornerBasedShape getMedium();
+    method public androidx.compose.foundation.shape.CornerBasedShape getSmall();
+    property public final androidx.compose.foundation.shape.CornerBasedShape ExtraLarge;
+    property public final androidx.compose.foundation.shape.CornerBasedShape ExtraSmall;
+    property public final androidx.compose.foundation.shape.CornerBasedShape Large;
+    property public final androidx.compose.foundation.shape.CornerBasedShape Medium;
+    property public final androidx.compose.foundation.shape.CornerBasedShape Small;
+    field public static final androidx.compose.material3.ShapeDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Immutable public final class 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.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();
+    method public androidx.compose.foundation.shape.CornerBasedShape getExtraSmall();
+    method public androidx.compose.foundation.shape.CornerBasedShape getLarge();
+    method public androidx.compose.foundation.shape.CornerBasedShape getMedium();
+    method public androidx.compose.foundation.shape.CornerBasedShape getSmall();
+    property public final androidx.compose.foundation.shape.CornerBasedShape extraLarge;
+    property public final androidx.compose.foundation.shape.CornerBasedShape extraSmall;
+    property public final androidx.compose.foundation.shape.CornerBasedShape large;
+    property public final androidx.compose.foundation.shape.CornerBasedShape medium;
+    property public final androidx.compose.foundation.shape.CornerBasedShape small;
+  }
+
+  @androidx.compose.runtime.Immutable public final class SliderColors {
+  }
+
+  @androidx.compose.runtime.Stable public final class SliderDefaults {
+    method @androidx.compose.runtime.Composable public void Thumb(androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled, optional long thumbSize);
+    method @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.SliderPositions sliderPositions, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SliderColors colors(optional long thumbColor, optional long activeTrackColor, optional long activeTickColor, optional long inactiveTrackColor, optional long inactiveTickColor, optional long disabledThumbColor, optional long disabledActiveTrackColor, optional long disabledActiveTickColor, optional long disabledInactiveTrackColor, optional long disabledInactiveTickColor);
+    field public static final androidx.compose.material3.SliderDefaults INSTANCE;
+  }
+
+  public final class SliderKt {
+    method @androidx.compose.runtime.Composable public static void RangeSlider(kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> value, kotlin.jvm.functions.Function1<? super kotlin.ranges.ClosedFloatingPointRange<java.lang.Float>,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors);
+    method @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  @androidx.compose.runtime.Stable public final class SliderPositions {
+    ctor public SliderPositions(optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> initialActiveRange, optional float[] initialTickFractions);
+    method public kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> getActiveRange();
+    method public float[] getTickFractions();
+    property public final kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> activeRange;
+    property public final float[] tickFractions;
+  }
+
+  @androidx.compose.runtime.Stable public interface SnackbarData {
+    method public void dismiss();
+    method public androidx.compose.material3.SnackbarVisuals getVisuals();
+    method public void performAction();
+    property public abstract androidx.compose.material3.SnackbarVisuals visuals;
+  }
+
+  public final class SnackbarDefaults {
+    method @androidx.compose.runtime.Composable public long getActionColor();
+    method @androidx.compose.runtime.Composable public long getActionContentColor();
+    method @androidx.compose.runtime.Composable public long getColor();
+    method @androidx.compose.runtime.Composable public long getContentColor();
+    method @androidx.compose.runtime.Composable public long getDismissActionContentColor();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    property @androidx.compose.runtime.Composable public final long actionColor;
+    property @androidx.compose.runtime.Composable public final long actionContentColor;
+    property @androidx.compose.runtime.Composable public final long color;
+    property @androidx.compose.runtime.Composable public final long contentColor;
+    property @androidx.compose.runtime.Composable public final long dismissActionContentColor;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    field public static final androidx.compose.material3.SnackbarDefaults INSTANCE;
+  }
+
+  public enum SnackbarDuration {
+    method public static androidx.compose.material3.SnackbarDuration valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.compose.material3.SnackbarDuration[] values();
+    enum_constant public static final androidx.compose.material3.SnackbarDuration Indefinite;
+    enum_constant public static final androidx.compose.material3.SnackbarDuration Long;
+    enum_constant public static final androidx.compose.material3.SnackbarDuration Short;
+  }
+
+  public final class SnackbarHostKt {
+    method @androidx.compose.runtime.Composable public static void SnackbarHost(androidx.compose.material3.SnackbarHostState hostState, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SnackbarData,kotlin.Unit> snackbar);
+  }
+
+  @androidx.compose.runtime.Stable public final class SnackbarHostState {
+    ctor public SnackbarHostState();
+    method public androidx.compose.material3.SnackbarData? getCurrentSnackbarData();
+    method public suspend Object? showSnackbar(String message, optional String? actionLabel, optional boolean withDismissAction, optional androidx.compose.material3.SnackbarDuration duration, optional kotlin.coroutines.Continuation<? super androidx.compose.material3.SnackbarResult>);
+    method public suspend Object? showSnackbar(androidx.compose.material3.SnackbarVisuals visuals, kotlin.coroutines.Continuation<? super androidx.compose.material3.SnackbarResult>);
+    property public final androidx.compose.material3.SnackbarData? currentSnackbarData;
+  }
+
+  public final class SnackbarKt {
+    method @androidx.compose.runtime.Composable public static void Snackbar(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? action, optional kotlin.jvm.functions.Function0<kotlin.Unit>? dismissAction, optional boolean actionOnNewLine, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional long actionContentColor, optional long dismissActionContentColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Snackbar(androidx.compose.material3.SnackbarData snackbarData, optional androidx.compose.ui.Modifier modifier, optional boolean actionOnNewLine, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional long actionColor, optional long actionContentColor, optional long dismissActionContentColor);
+  }
+
+  public enum SnackbarResult {
+    method public static androidx.compose.material3.SnackbarResult valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.compose.material3.SnackbarResult[] values();
+    enum_constant public static final androidx.compose.material3.SnackbarResult ActionPerformed;
+    enum_constant public static final androidx.compose.material3.SnackbarResult Dismissed;
+  }
+
+  @androidx.compose.runtime.Stable public interface SnackbarVisuals {
+    method public String? getActionLabel();
+    method public androidx.compose.material3.SnackbarDuration getDuration();
+    method public String getMessage();
+    method public boolean getWithDismissAction();
+    property public abstract String? actionLabel;
+    property public abstract androidx.compose.material3.SnackbarDuration duration;
+    property public abstract String message;
+    property public abstract boolean withDismissAction;
+  }
+
+  public final class SuggestionChipDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipColors elevatedSuggestionChipColors(optional long containerColor, optional long labelColor, optional long iconContentColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledIconContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipElevation elevatedSuggestionChipElevation(optional float elevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method public float getHeight();
+    method public float getIconSize();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipBorder suggestionChipBorder(optional long borderColor, optional long disabledBorderColor, optional float borderWidth);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipColors suggestionChipColors(optional long containerColor, optional long labelColor, optional long iconContentColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledIconContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipElevation suggestionChipElevation(optional float elevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    property public final float Height;
+    property public final float IconSize;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
+    field public static final androidx.compose.material3.SuggestionChipDefaults INSTANCE;
+  }
+
+  public final class SurfaceKt {
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> getLocalAbsoluteTonalElevation();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> LocalAbsoluteTonalElevation;
+  }
+
+  @androidx.compose.runtime.Immutable public final class SwitchColors {
+  }
+
+  public final class SwitchDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SwitchColors colors(optional long checkedThumbColor, optional long checkedTrackColor, optional long checkedBorderColor, optional long checkedIconColor, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional long uncheckedBorderColor, optional long uncheckedIconColor, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledCheckedBorderColor, optional long disabledCheckedIconColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor, optional long disabledUncheckedBorderColor, optional long disabledUncheckedIconColor);
+    method public float getIconSize();
+    property public final float IconSize;
+    field public static final androidx.compose.material3.SwitchDefaults INSTANCE;
+  }
+
+  public final class SwitchKt {
+    method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? thumbContent, optional boolean enabled, optional androidx.compose.material3.SwitchColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
+  public final class TabKt {
+    method @androidx.compose.runtime.Composable public static void LeadingIconTab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> icon, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional long selectedContentColor, optional long unselectedContentColor, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void Tab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional long selectedContentColor, optional long unselectedContentColor, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void Tab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional long selectedContentColor, optional long unselectedContentColor, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+  }
+
+  @androidx.compose.runtime.Immutable public final class TabPosition {
+    method public float getLeft();
+    method public float getRight();
+    method public float getWidth();
+    property public final float left;
+    property public final float right;
+    property public final float width;
+  }
+
+  public final class TabRowDefaults {
+    method @androidx.compose.runtime.Composable public void Indicator(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
+    method @androidx.compose.runtime.Composable public long getContainerColor();
+    method @androidx.compose.runtime.Composable public long getContentColor();
+    method public androidx.compose.ui.Modifier tabIndicatorOffset(androidx.compose.ui.Modifier, androidx.compose.material3.TabPosition currentTabPosition);
+    property @androidx.compose.runtime.Composable public final long containerColor;
+    property @androidx.compose.runtime.Composable public final long contentColor;
+    field public static final androidx.compose.material3.TabRowDefaults INSTANCE;
+  }
+
+  public final class TabRowKt {
+    method @androidx.compose.runtime.Composable public static void ScrollableTabRow(int selectedTabIndex, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional float edgePadding, optional kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.material3.TabPosition>,kotlin.Unit> indicator, optional kotlin.jvm.functions.Function0<kotlin.Unit> divider, kotlin.jvm.functions.Function0<kotlin.Unit> tabs);
+    method @androidx.compose.runtime.Composable public static void TabRow(int selectedTabIndex, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.material3.TabPosition>,kotlin.Unit> indicator, optional kotlin.jvm.functions.Function0<kotlin.Unit> divider, kotlin.jvm.functions.Function0<kotlin.Unit> tabs);
+  }
+
+  @androidx.compose.runtime.Immutable public final class TextFieldColors {
+  }
+
+  @androidx.compose.runtime.Immutable public final class TextFieldDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFilledShape();
+    method public float getFocusedBorderThickness();
+    method public float getMinHeight();
+    method public float getMinWidth();
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getOutlinedShape();
+    method public float getUnfocusedBorderThickness();
+    method public androidx.compose.foundation.layout.PaddingValues outlinedTextFieldPadding(optional float start, optional float top, optional float end, optional float bottom);
+    method public androidx.compose.foundation.layout.PaddingValues textFieldWithLabelPadding(optional float start, optional float end, optional float top, optional float bottom);
+    method public androidx.compose.foundation.layout.PaddingValues textFieldWithoutLabelPadding(optional float start, optional float top, optional float end, optional float bottom);
+    property public final float FocusedBorderThickness;
+    property public final float MinHeight;
+    property public final float MinWidth;
+    property public final float UnfocusedBorderThickness;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape filledShape;
+    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape outlinedShape;
+    field public static final androidx.compose.material3.TextFieldDefaults INSTANCE;
+  }
+
+  public final class TextFieldKt {
+    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);
+    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);
+  }
+
+  public final class TextKt {
+    method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method @Deprecated @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method @Deprecated @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,? extends androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,? extends kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> getLocalTextStyle();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
+  }
+
+  @androidx.compose.runtime.Stable public final class TimePickerState {
+    ctor public TimePickerState(int initialHour, int initialMinute, boolean is24Hour);
+    method public int getHour();
+    method public int getMinute();
+    method public boolean is24hour();
+    method public suspend Object? settle(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    property public final int hour;
+    property public final boolean is24hour;
+    property public final int minute;
+    field public static final androidx.compose.material3.TimePickerState.Companion Companion;
+  }
+
+  public static final class TimePickerState.Companion {
+    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.TimePickerState,?> Saver();
+  }
+
+  @androidx.compose.runtime.Immutable public final class Typography {
+    ctor public Typography(optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
+    method public androidx.compose.material3.Typography copy(optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
+    method public androidx.compose.ui.text.TextStyle getBodyLarge();
+    method public androidx.compose.ui.text.TextStyle getBodyMedium();
+    method public androidx.compose.ui.text.TextStyle getBodySmall();
+    method public androidx.compose.ui.text.TextStyle getDisplayLarge();
+    method public androidx.compose.ui.text.TextStyle getDisplayMedium();
+    method public androidx.compose.ui.text.TextStyle getDisplaySmall();
+    method public androidx.compose.ui.text.TextStyle getHeadlineLarge();
+    method public androidx.compose.ui.text.TextStyle getHeadlineMedium();
+    method public androidx.compose.ui.text.TextStyle getHeadlineSmall();
+    method public androidx.compose.ui.text.TextStyle getLabelLarge();
+    method public androidx.compose.ui.text.TextStyle getLabelMedium();
+    method public androidx.compose.ui.text.TextStyle getLabelSmall();
+    method public androidx.compose.ui.text.TextStyle getTitleLarge();
+    method public androidx.compose.ui.text.TextStyle getTitleMedium();
+    method public androidx.compose.ui.text.TextStyle getTitleSmall();
+    property public final androidx.compose.ui.text.TextStyle bodyLarge;
+    property public final androidx.compose.ui.text.TextStyle bodyMedium;
+    property public final androidx.compose.ui.text.TextStyle bodySmall;
+    property public final androidx.compose.ui.text.TextStyle displayLarge;
+    property public final androidx.compose.ui.text.TextStyle displayMedium;
+    property public final androidx.compose.ui.text.TextStyle displaySmall;
+    property public final androidx.compose.ui.text.TextStyle headlineLarge;
+    property public final androidx.compose.ui.text.TextStyle headlineMedium;
+    property public final androidx.compose.ui.text.TextStyle headlineSmall;
+    property public final androidx.compose.ui.text.TextStyle labelLarge;
+    property public final androidx.compose.ui.text.TextStyle labelMedium;
+    property public final androidx.compose.ui.text.TextStyle labelSmall;
+    property public final androidx.compose.ui.text.TextStyle titleLarge;
+    property public final androidx.compose.ui.text.TextStyle titleMedium;
+    property public final androidx.compose.ui.text.TextStyle titleSmall;
+  }
+
+}
+
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index e6d7e92..7e2de6f 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -578,22 +578,6 @@
     method @androidx.compose.runtime.Composable public static void Scaffold(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional int floatingActionButtonPosition, optional long containerColor, optional long contentColor, optional androidx.compose.foundation.layout.WindowInsets contentWindowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
   }
 
-  public final class SearchBarDefaults {
-    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getDockedShape();
-    method public float getElevation();
-    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFullScreenShape();
-    method public float getInputFieldHeight();
-    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getInputFieldShape();
-    method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
-    property public final float Elevation;
-    property public final float InputFieldHeight;
-    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape dockedShape;
-    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape fullScreenShape;
-    property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape inputFieldShape;
-    property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets windowInsets;
-    field public static final androidx.compose.material3.SearchBarDefaults INSTANCE;
-  }
-
   public final class ShapeDefaults {
     method public androidx.compose.foundation.shape.CornerBasedShape getExtraLarge();
     method public androidx.compose.foundation.shape.CornerBasedShape getExtraSmall();
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SearchBarSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SearchBarSamples.kt
index bebe797..96ec1a0 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SearchBarSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SearchBarSamples.kt
@@ -42,7 +42,6 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalFocusManager
 import androidx.compose.ui.semantics.isContainer
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.tooling.preview.Preview
@@ -56,12 +55,6 @@
 fun SearchBarSample() {
     var text by rememberSaveable { mutableStateOf("") }
     var active by rememberSaveable { mutableStateOf(false) }
-    val focusManager = LocalFocusManager.current
-
-    fun closeSearchBar() {
-        focusManager.clearFocus()
-        active = false
-    }
 
     Box(Modifier.fillMaxSize()) {
         // Talkback focus order sorts based on x and y position before considering z-index. The
@@ -72,11 +65,10 @@
                 modifier = Modifier.align(Alignment.TopCenter),
                 query = text,
                 onQueryChange = { text = it },
-                onSearch = { closeSearchBar() },
+                onSearch = { active = false },
                 active = active,
                 onActiveChange = {
                     active = it
-                    if (!active) focusManager.clearFocus()
                 },
                 placeholder = { Text("Hinted search text") },
                 leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
@@ -95,7 +87,7 @@
                             leadingContent = { Icon(Icons.Filled.Star, contentDescription = null) },
                             modifier = Modifier.clickable {
                                 text = resultText
-                                closeSearchBar()
+                                active = false
                             }
                         )
                     }
@@ -122,12 +114,6 @@
 fun DockedSearchBarSample() {
     var text by rememberSaveable { mutableStateOf("") }
     var active by rememberSaveable { mutableStateOf(false) }
-    val focusManager = LocalFocusManager.current
-
-    fun closeSearchBar() {
-        focusManager.clearFocus()
-        active = false
-    }
 
     Box(Modifier.fillMaxSize()) {
         // Talkback focus order sorts based on x and y position before considering z-index. The
@@ -138,12 +124,9 @@
                 modifier = Modifier.align(Alignment.TopCenter).padding(top = 8.dp),
                 query = text,
                 onQueryChange = { text = it },
-                onSearch = { closeSearchBar() },
+                onSearch = { active = false },
                 active = active,
-                onActiveChange = {
-                    active = it
-                    if (!active) focusManager.clearFocus()
-                },
+                onActiveChange = { active = it },
                 placeholder = { Text("Hinted search text") },
                 leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
                 trailingIcon = { Icon(Icons.Default.MoreVert, contentDescription = null) },
@@ -161,7 +144,7 @@
                             leadingContent = { Icon(Icons.Filled.Star, contentDescription = null) },
                             modifier = Modifier.clickable {
                                 text = resultText
-                                closeSearchBar()
+                                active = false
                             }
                         )
                     }
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TimePickerSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TimePickerSamples.kt
index 94a3911..e811231 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TimePickerSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TimePickerSamples.kt
@@ -22,17 +22,17 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.IntrinsicSize
 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.offset
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
-import androidx.compose.material.Button
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.Keyboard
 import androidx.compose.material.icons.outlined.Schedule
+import androidx.compose.material3.Button
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
@@ -54,10 +54,13 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.semantics.isContainer
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.window.Dialog
 import androidx.compose.ui.window.DialogProperties
+import androidx.compose.ui.zIndex
 import java.text.SimpleDateFormat
 import java.util.Calendar
 import java.util.Locale
@@ -162,11 +165,7 @@
 
     if (showTimePicker) {
         TimePickerDialog(
-            title = if (showingPicker.value) {
-                "Select Time "
-            } else {
-                "Enter Time"
-            },
+            title = if (showingPicker.value) { "Select Time " } else { "Enter Time" },
             onCancel = { showTimePicker = false },
             onConfirm = {
                 val cal = Calendar.getInstance()
@@ -179,28 +178,43 @@
                 showTimePicker = false
             },
             toggle = {
-                if (configuration.screenHeightDp > 540) {
-                    IconButton(
-                        modifier = Modifier.offset(x = -(8.dp)),
-                        onClick = { showingPicker.value = !showingPicker.value }) {
-                        val icon = if (showingPicker.value) {
-                            Icons.Outlined.Keyboard
-                        } else {
-                            Icons.Outlined.Schedule
-                        }
-                        Icon(
-                            icon,
-                            contentDescription = if (showingPicker.value) {
-                                "Switch to Text Input"
+                if (configuration.screenHeightDp > 400) {
+                    // Make this take the entire viewport. This will guarantee that Screen readers
+                    // focus the toggle first.
+                    Box(
+                        Modifier
+                            .fillMaxSize()
+                            .semantics { isContainer = true }
+                    ) {
+                        IconButton(
+                            modifier = Modifier
+                                // This is a workaround so that the Icon comes up first
+                                // in the talkback traversal order. So that users of a11y
+                                // services can use the text input. When talkback traversal
+                                // order is customizable we can remove this.
+                                .size(64.dp, 72.dp)
+                                .align(Alignment.BottomStart)
+                                .zIndex(5f),
+                            onClick = { showingPicker.value = !showingPicker.value }) {
+                            val icon = if (showingPicker.value) {
+                                Icons.Outlined.Keyboard
                             } else {
-                                "Switch to Touch Input"
+                                Icons.Outlined.Schedule
                             }
-                        )
+                            Icon(
+                                icon,
+                                contentDescription = if (showingPicker.value) {
+                                    "Switch to Text Input"
+                                } else {
+                                    "Switch to Touch Input"
+                                }
+                            )
+                        }
                     }
                 }
             }
         ) {
-            if (showingPicker.value && configuration.screenHeightDp > 540) {
+            if (showingPicker.value && configuration.screenHeightDp > 400) {
                 TimePicker(state = state)
             } else {
                 TimeInput(state = state)
@@ -214,23 +228,27 @@
     title: String = "Select Time",
     onCancel: () -> Unit,
     onConfirm: () -> Unit,
-    toggle: @Composable RowScope.() -> Unit = {},
+    toggle: @Composable () -> Unit = {},
     content: @Composable () -> Unit,
 ) {
     Dialog(
         onDismissRequest = onCancel,
-        properties = DialogProperties(usePlatformDefaultWidth = false),
+        properties = DialogProperties(
+            usePlatformDefaultWidth = false
+        ),
     ) {
         Surface(
             shape = MaterialTheme.shapes.extraLarge,
             tonalElevation = 6.dp,
             modifier = Modifier
                 .width(IntrinsicSize.Min)
+                .height(IntrinsicSize.Min)
                 .background(
                     shape = MaterialTheme.shapes.extraLarge,
                     color = MaterialTheme.colorScheme.surface
                 ),
         ) {
+            toggle()
             Column(
                 modifier = Modifier.padding(24.dp),
                 horizontalAlignment = Alignment.CenterHorizontally
@@ -248,7 +266,6 @@
                         .height(40.dp)
                         .fillMaxWidth()
                 ) {
-                    toggle()
                     Spacer(modifier = Modifier.weight(1f))
                     TextButton(
                         onClick = onCancel
diff --git a/compose/material3/material3/src/androidAndroidTest/AndroidManifest.xml b/compose/material3/material3/src/androidAndroidTest/AndroidManifest.xml
index e650e97..cf97ceb 100644
--- a/compose/material3/material3/src/androidAndroidTest/AndroidManifest.xml
+++ b/compose/material3/material3/src/androidAndroidTest/AndroidManifest.xml
@@ -16,7 +16,8 @@
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools">
-    <application>
+    <application
+        android:supportsRtl="true">
         <activity
             android:name="androidx.compose.material3.MaterialWindowInsetsActivity"
             android:windowSoftInputMode="adjustResize"
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AlertDialogScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AlertDialogScreenshotTest.kt
index b773b27..ec36cd9 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AlertDialogScreenshotTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AlertDialogScreenshotTest.kt
@@ -19,10 +19,13 @@
 import android.os.Build
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.isDialog
 import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.LayoutDirection
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
@@ -69,7 +72,7 @@
             )
         }
 
-        assertAppBarAgainstGolden(goldenIdentifier = "alertDialog_lightTheme")
+        assertAlertDialogAgainstGolden(goldenIdentifier = "alertDialog_lightTheme")
     }
 
     @Test
@@ -99,7 +102,7 @@
             )
         }
 
-        assertAppBarAgainstGolden(goldenIdentifier = "alertDialog_darkTheme")
+        assertAlertDialogAgainstGolden(goldenIdentifier = "alertDialog_darkTheme")
     }
 
     @Test
@@ -130,7 +133,7 @@
             )
         }
 
-        assertAppBarAgainstGolden(goldenIdentifier = "alertDialog_withIcon_lightTheme")
+        assertAlertDialogAgainstGolden(goldenIdentifier = "alertDialog_withIcon_lightTheme")
     }
 
     @Test
@@ -161,10 +164,41 @@
             )
         }
 
-        assertAppBarAgainstGolden(goldenIdentifier = "alertDialog_withIcon_darkTheme")
+        assertAlertDialogAgainstGolden(goldenIdentifier = "alertDialog_withIcon_darkTheme")
     }
 
-    private fun assertAppBarAgainstGolden(goldenIdentifier: String) {
+    @Test
+    fun alertDialog_rtl() {
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+                AlertDialog(
+                    onDismissRequest = {},
+                    title = {
+                        Text(text = "Title")
+                    },
+                    text = {
+                        Text(
+                            "This area typically contains the supportive text " +
+                                "which presents the details regarding the Dialog's purpose."
+                        )
+                    },
+                    confirmButton = {
+                        TextButton(onClick = { /* doSomething() */ }) {
+                            Text("Confirm")
+                        }
+                    },
+                    dismissButton = {
+                        TextButton(onClick = { /* doSomething() */ }) {
+                            Text("Dismiss")
+                        }
+                    }
+                )
+            }
+        }
+        assertAlertDialogAgainstGolden(goldenIdentifier = "alertDialog_rtl")
+    }
+
+    private fun assertAlertDialogAgainstGolden(goldenIdentifier: String) {
         composeTestRule.onNode(isDialog())
             .captureToImage()
             .assertAgainstGolden(screenshotRule, goldenIdentifier)
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/BottomSheetScaffoldTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/BottomSheetScaffoldTest.kt
index 0651b91..062609b 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/BottomSheetScaffoldTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/BottomSheetScaffoldTest.kt
@@ -21,7 +21,6 @@
 import android.content.res.Configuration
 import android.os.Build
 import androidx.activity.ComponentActivity
-import androidx.annotation.RequiresApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.PaddingValues
@@ -35,6 +34,7 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.material3.tokens.SheetBottomTokens
 import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.testutils.assertShape
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.shadow
@@ -79,6 +79,9 @@
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
 import junit.framework.TestCase
+import kotlin.IllegalStateException
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
 import org.junit.Rule
 import org.junit.Test
@@ -199,6 +202,76 @@
     }
 
     @Test
+    fun bottomSheetScaffold_testDismissAction_whenEnabled() {
+        rule.setContent {
+            BottomSheetScaffold(
+                sheetContent = {
+                    Box(
+                        Modifier
+                            .fillMaxWidth()
+                            .requiredHeight(sheetHeight)
+                            .testTag(sheetTag))
+                },
+                sheetDragHandle = { Box(
+                    Modifier
+                        .testTag(dragHandleTag)
+                        .size(dragHandleSize)) },
+                sheetPeekHeight = peekHeight,
+                scaffoldState = rememberBottomSheetScaffoldState(
+                    bottomSheetState = rememberStandardBottomSheetState(
+                        skipHiddenState = false
+                    )
+                )
+            ) {
+                Text("Content")
+            }
+        }
+
+        rule.onNodeWithTag(dragHandleTag).onParent()
+            .assert(SemanticsMatcher.keyNotDefined(SemanticsActions.Collapse))
+            .assert(SemanticsMatcher.keyIsDefined(SemanticsActions.Dismiss))
+            .assert(SemanticsMatcher.keyIsDefined(SemanticsActions.Expand))
+            .performSemanticsAction(SemanticsActions.Dismiss)
+
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(dragHandleTag)
+            .assertTopPositionInRootIsEqualTo(rule.rootHeight())
+    }
+
+    @Test
+    fun bottomSheetScaffold_testHideReturnsIllegalStateException() {
+        lateinit var scope: CoroutineScope
+        val bottomSheetState = SheetState(
+            skipPartiallyExpanded = false,
+            skipHiddenState = true,
+            initialValue = SheetValue.PartiallyExpanded,
+        )
+        rule.setContent {
+            scope = rememberCoroutineScope()
+            BottomSheetScaffold(
+                sheetContent = {
+                    Box(Modifier.fillMaxWidth().requiredHeight(sheetHeight))
+                },
+                scaffoldState = rememberBottomSheetScaffoldState(
+                    bottomSheetState = bottomSheetState
+                )
+            ) {
+                Text("Content")
+            }
+        }
+        scope.launch {
+            val exception = kotlin.runCatching { bottomSheetState.hide() }.exceptionOrNull()
+            assertThat(exception).isNotNull()
+            assertThat(exception).isInstanceOf(IllegalStateException::class.java)
+            assertThat(exception).hasMessageThat().containsMatch(
+                "Attempted to animate to hidden when skipHiddenState was enabled. Set " +
+                    "skipHiddenState to false to use this function."
+            )
+        }
+    }
+
+    @Test
     fun bottomSheetScaffold_testCollapseAction_whenExpanded() {
         rule.setContent {
             BottomSheetScaffold(
@@ -583,7 +656,7 @@
         }
     }
 
-    @RequiresApi(Build.VERSION_CODES.O)
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
     @Test
     fun bottomSheetScaffold_slotsPositionedAppropriately() {
         val topBarHeight = 56.dp
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DateRangePickerTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DateRangePickerTest.kt
index 8b38b9b..086476e 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DateRangePickerTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DateRangePickerTest.kt
@@ -110,18 +110,22 @@
         }
     }
 
-    @Test(expected = IllegalArgumentException::class)
     fun state_initWithEqualStartAndEndDates() {
+        lateinit var dateRangePickerState: DateRangePickerState
         rule.setMaterialContent(lightColorScheme()) {
-            // Expecting this to throw an exception when the start and end dates are the same.
-            rememberDateRangePickerState(
+            dateRangePickerState = rememberDateRangePickerState(
+                // 04/12/2022
+                initialSelectedStartDateMillis = 1649721600000L,
                 // 04/12/2022 + a few added milliseconds to ensure that the state is initialized
                 // with a canonical date.
-                initialSelectedStartDateMillis = 1649721600000L + 1000,
-                // 04/12/2022
-                initialSelectedEndDateMillis = 1649721600000L
+                initialSelectedEndDateMillis = 1649721600000L + 1000
             )
         }
+        with(dateRangePickerState) {
+            // Start and end are expected to be equal.
+            assertThat(selectedStartDateMillis).isEqualTo(1649721600000L)
+            assertThat(selectedEndDateMillis).isEqualTo(1649721600000L)
+        }
     }
 
     @Test(expected = IllegalArgumentException::class)
@@ -274,6 +278,61 @@
         }
     }
 
+    /**
+     * Tests that clicking the same date twice creates a single-day range, and that clicking it
+     * a third time resets the end date.
+     */
+    @Test
+    fun dateSelection_sameDateForStartAndEnd() {
+        lateinit var dateRangePickerState: DateRangePickerState
+        rule.setMaterialContent(lightColorScheme()) {
+            val monthInUtcMillis = dayInUtcMilliseconds(year = 2019, month = 3, dayOfMonth = 1)
+            dateRangePickerState = rememberDateRangePickerState(
+                initialDisplayedMonthMillis = monthInUtcMillis
+            )
+            DateRangePicker(state = dateRangePickerState)
+        }
+
+        val node = rule.onAllNodes(hasText("15", substring = true) and hasClickAction()).onFirst()
+        // First date selection: Select the 15th day of the first displayed month in the list.
+        node.performClick()
+        // Second date selection - click the same node.
+        node.performClick()
+
+        // Assert the state holds a valid start and end dates for the same date.
+        rule.runOnIdle {
+            assertThat(dateRangePickerState.selectedStartDateMillis).isEqualTo(
+                dayInUtcMilliseconds(
+                    year = 2019,
+                    month = 3,
+                    dayOfMonth = 15
+                )
+            )
+            assertThat(dateRangePickerState.selectedEndDateMillis).isEqualTo(
+                dayInUtcMilliseconds(
+                    year = 2019,
+                    month = 3,
+                    dayOfMonth = 15
+                )
+            )
+        }
+
+        // Click the node again to reset the end date.
+        node.performClick()
+
+        // Assert the state now holds just a start date.
+        rule.runOnIdle {
+            assertThat(dateRangePickerState.selectedStartDateMillis).isEqualTo(
+                dayInUtcMilliseconds(
+                    year = 2019,
+                    month = 3,
+                    dayOfMonth = 15
+                )
+            )
+            assertThat(dateRangePickerState.selectedEndDateMillis).isNull()
+        }
+    }
+
     @Test
     fun state_resetSelections() {
         lateinit var defaultStartSelectionHeadline: String
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt
index 744fd28..bbc0e46 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt
@@ -594,7 +594,10 @@
             ModalBottomSheet(
                 onDismissRequest = {},
                 sheetState = sheetState,
-                dragHandle = { Box(Modifier.testTag(dragHandleTag).size(dragHandleSize)) }
+                dragHandle = { Box(
+                    Modifier
+                        .testTag(dragHandleTag)
+                        .size(dragHandleSize)) }
             ) {
                 Box(
                     Modifier
@@ -716,11 +719,42 @@
     }
 
     @Test
+    fun modalBottomSheet_testParialExpandReturnsIllegalStateException_whenSkipPartialExpanded() {
+        lateinit var scope: CoroutineScope
+        val bottomSheetState = SheetState(
+            skipPartiallyExpanded = true,
+        )
+        rule.setContent {
+            scope = rememberCoroutineScope()
+            ModalBottomSheet(onDismissRequest = {}, sheetState = bottomSheetState) {
+                Box(
+                    Modifier
+                        .fillMaxSize()
+                        .testTag(sheetTag)
+                )
+            }
+        }
+        scope.launch {
+            val exception =
+                kotlin.runCatching { bottomSheetState.partialExpand() }.exceptionOrNull()
+            assertThat(exception).isNotNull()
+            assertThat(exception).isInstanceOf(IllegalStateException::class.java)
+            assertThat(exception).hasMessageThat().containsMatch(
+                "Attempted to animate to partial expanded when skipPartiallyExpanded was " +
+                    "enabled. Set skipPartiallyExpanded to false to use this function."
+            )
+        }
+    }
+
+    @Test
     fun modalBottomSheet_testDismissAction_tallBottomSheet_whenPartiallyExpanded() {
         rule.setContent {
             ModalBottomSheet(
                 onDismissRequest = {},
-                dragHandle = { Box(Modifier.testTag(dragHandleTag).size(dragHandleSize)) }
+                dragHandle = { Box(
+                    Modifier
+                        .testTag(dragHandleTag)
+                        .size(dragHandleSize)) }
             ) {
                 Box(
                     Modifier
@@ -745,7 +779,10 @@
             ModalBottomSheet(
                 onDismissRequest = {},
                 sheetState = sheetState,
-                dragHandle = { Box(Modifier.testTag(dragHandleTag).size(dragHandleSize)) }
+                dragHandle = { Box(
+                    Modifier
+                        .testTag(dragHandleTag)
+                        .size(dragHandleSize)) }
             ) {
                 Box(
                     Modifier
@@ -786,7 +823,10 @@
             ModalBottomSheet(
                 onDismissRequest = {},
                 sheetState = sheetState,
-                dragHandle = { Box(Modifier.testTag(dragHandleTag).size(dragHandleSize)) }
+                dragHandle = { Box(
+                    Modifier
+                        .testTag(dragHandleTag)
+                        .size(dragHandleSize)) }
             ) {
                 Box(
                     Modifier
@@ -831,7 +871,10 @@
             ModalBottomSheet(
                 onDismissRequest = {},
                 sheetState = sheetState,
-                dragHandle = { Box(Modifier.testTag(dragHandleTag).size(dragHandleSize)) }
+                dragHandle = { Box(
+                    Modifier
+                        .testTag(dragHandleTag)
+                        .size(dragHandleSize)) }
             ) {
                 Box(
                     Modifier
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SearchBarTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SearchBarTest.kt
index 8b99b74..54fcb26 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SearchBarTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SearchBarTest.kt
@@ -33,6 +33,8 @@
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertHeightIsEqualTo
 import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsFocused
+import androidx.compose.ui.test.assertIsNotFocused
 import androidx.compose.ui.test.assertWidthIsEqualTo
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
@@ -60,7 +62,7 @@
     private val BackTestTag = "Back"
 
     @Test
-    fun searchBar_becomesActiveOnClick_andInactiveOnBack() {
+    fun searchBar_becomesActiveAndFocusedOnClick_andInactiveAndUnfocusedOnBack() {
         rule.setMaterialContent(lightColorScheme()) {
             Box(Modifier.fillMaxSize()) {
                 val dispatcher = LocalOnBackPressedDispatcherOwner.current!!.onBackPressedDispatcher
@@ -88,9 +90,12 @@
 
         rule.onNodeWithTag(SearchBarTestTag).performClick()
         rule.onNodeWithTag(BackTestTag).assertIsDisplayed()
+        // onNodeWithText instead of onNodeWithTag to access the underlying text field
+        rule.onNodeWithText("Query").assertIsFocused()
 
         rule.onNodeWithTag(BackTestTag).performClick()
         rule.onNodeWithTag(BackTestTag).assertDoesNotExist()
+        rule.onNodeWithText("Query").assertIsNotFocused()
     }
 
     @Test
@@ -204,7 +209,7 @@
     }
 
     @Test
-    fun dockedSearchBar_becomesActiveOnClick_andInactiveOnBack() {
+    fun dockedSearchBar_becomesActiveAndFocusedOnClick_andInactiveAndUnfocusedOnBack() {
         rule.setMaterialContent(lightColorScheme()) {
             Column(Modifier.fillMaxSize()) {
                 val dispatcher = LocalOnBackPressedDispatcherOwner.current!!.onBackPressedDispatcher
@@ -232,9 +237,12 @@
 
         rule.onNodeWithTag(SearchBarTestTag).performClick()
         rule.onNodeWithTag(BackTestTag).assertIsDisplayed()
+        // onNodeWithText instead of onNodeWithTag to access the underlying text field
+        rule.onNodeWithText("Query").assertIsFocused()
 
         rule.onNodeWithTag(BackTestTag).performClick()
         rule.onNodeWithTag(BackTestTag).assertDoesNotExist()
+        rule.onNodeWithText("Query").assertIsNotFocused()
     }
 
     @Test
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SliderTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SliderTest.kt
index 24c2ae7..ee8d7f5 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SliderTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SliderTest.kt
@@ -39,6 +39,7 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.setValue
+import androidx.compose.testutils.expectAssertionError
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
@@ -720,6 +721,32 @@
         }
     }
 
+    @Test
+    fun slider_parentWithInfiniteWidth_minWidth() {
+        val state = mutableStateOf(0f)
+        rule.setMaterialContentForSizeAssertions {
+            Box(modifier = Modifier.requiredWidth(Int.MAX_VALUE.dp)) {
+                Slider(value = state.value, onValueChange = { state.value = it })
+            }
+        }.assertWidthIsEqualTo(48.dp)
+    }
+
+    @Test
+    fun slider_rowWithInfiniteWidth() {
+        val state = mutableStateOf(0f)
+        expectAssertionError(false) {
+            rule.setContent {
+                Row(modifier = Modifier.requiredWidth(Int.MAX_VALUE.dp)) {
+                    Slider(
+                        modifier = Modifier.weight(1f),
+                        value = state.value,
+                        onValueChange = { state.value = it }
+                    )
+                }
+            }
+        }
+    }
+
     @OptIn(ExperimentalMaterial3Api::class)
     @Test
     fun rangeSlider_dragThumb() {
@@ -1246,6 +1273,32 @@
             Truth.assertThat(recompositionCounter.innerRecomposition).isEqualTo(4)
         }
     }
+
+    @Test
+    fun rangeSlider_parentWithInfiniteWidth_minWidth() {
+        val state = mutableStateOf(0f..1f)
+        rule.setMaterialContentForSizeAssertions {
+            Box(modifier = Modifier.requiredWidth(Int.MAX_VALUE.dp)) {
+                RangeSlider(value = state.value, onValueChange = { state.value = it })
+            }
+        }.assertWidthIsEqualTo(48.dp)
+    }
+
+    @Test
+    fun rangeSlider_rowWithInfiniteWidth() {
+        val state = mutableStateOf(0f..1f)
+        expectAssertionError(false) {
+            rule.setContent {
+                Row(modifier = Modifier.requiredWidth(Int.MAX_VALUE.dp)) {
+                    RangeSlider(
+                        modifier = Modifier.weight(1f),
+                        value = state.value,
+                        onValueChange = { state.value = it }
+                    )
+                }
+            }
+        }
+    }
 }
 
 @Stable
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabTest.kt
index 6094e85..4ff880a 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabTest.kt
@@ -930,4 +930,17 @@
 
         rule.onNodeWithTag("Tabs").assertHeightIsEqualTo(height)
     }
+
+    @Test
+    fun tabRow_noTabsHasHeightZero() {
+        rule.setMaterialContent(lightColorScheme()) {
+            TabRow(
+                modifier = Modifier.testTag("tabRow"),
+                selectedTabIndex = 0
+            ) {}
+        }
+
+        val tabRowBounds = rule.onNodeWithTag("tabRow").getUnclippedBoundsInRoot()
+        tabRowBounds.height.assertIsEqualTo(0.dp)
+    }
 }
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TimePickerTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TimePickerTest.kt
index b26f237..fb31d63 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TimePickerTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TimePickerTest.kt
@@ -17,19 +17,26 @@
 package androidx.compose.material3
 
 import android.content.Context
+import android.content.res.Configuration
+import android.content.res.Resources
 import android.os.Build
 import android.text.format.DateFormat
-import androidx.compose.ui.platform.LocalContext
+import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.SemanticsProperties
 import androidx.compose.ui.semantics.SemanticsProperties.SelectableGroup
 import androidx.compose.ui.test.ExperimentalTestApi
 import androidx.compose.ui.test.SemanticsMatcher.Companion.expectValue
 import androidx.compose.ui.test.SemanticsMatcher.Companion.keyIsDefined
+import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
 import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.assertAll
+import androidx.compose.ui.test.assertContentDescriptionContains
 import androidx.compose.ui.test.assertCountEquals
 import androidx.compose.ui.test.assertHasClickAction
 import androidx.compose.ui.test.assertIsNotSelected
@@ -49,6 +56,7 @@
 import androidx.compose.ui.test.onAllNodesWithContentDescription
 import androidx.compose.ui.test.onAllNodesWithText
 import androidx.compose.ui.test.onChildren
+import androidx.compose.ui.test.onFirst
 import androidx.compose.ui.test.onLast
 import androidx.compose.ui.test.onNodeWithContentDescription
 import androidx.compose.ui.test.onNodeWithText
@@ -61,6 +69,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
 import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
 import com.android.dx.mockito.inline.extended.MockedMethod
@@ -79,15 +88,45 @@
     val rule = createComposeRule()
 
     @Test
+    fun timePicker_vertical_layout() {
+        rule.setMaterialContent(lightColorScheme()) {
+            val newConfiguration = Configuration(LocalConfiguration.current)
+            newConfiguration.screenHeightDp = 800
+            newConfiguration.screenWidthDp = 500
+            CompositionLocalProvider(LocalConfiguration provides newConfiguration) {
+                assertThat(defaultTimePickerLayoutType).isEqualTo(TimePickerLayoutType.Vertical)
+            }
+        }
+    }
+
+    @Test
+    fun timePicker_horizontal_layout() {
+        rule.setMaterialContent(lightColorScheme()) {
+            val newConfiguration = Configuration(LocalConfiguration.current)
+            newConfiguration.screenHeightDp = 500
+            newConfiguration.screenWidthDp = 800
+            CompositionLocalProvider(LocalConfiguration provides newConfiguration) {
+                assertThat(defaultTimePickerLayoutType).isEqualTo(TimePickerLayoutType.Horizontal)
+            }
+        }
+    }
+
+    @Test
     fun timePicker_initialState() {
         val state = TimePickerState(initialHour = 14, initialMinute = 23, is24Hour = false)
         rule.setMaterialContent(lightColorScheme()) {
             TimePicker(state)
         }
 
-        rule.onAllNodesWithText("23").assertCountEquals(1)
+       rule.onNodeWithTimeValue(
+           number = 2,
+           selection = Selection.Hour,
+       ).assertIsSelected()
 
-        rule.onNodeWithText("02").assertIsSelected()
+        rule.onNodeWithTimeValue(
+            number = 23,
+            selection = Selection.Minute,
+        ).assertExists()
 
         rule.onNodeWithText("AM").assertExists()
 
@@ -101,9 +140,14 @@
             TimePicker(state)
         }
 
-        rule.onNodeWithText("23").performClick()
+        rule.onNodeWithTimeValue(
+            number = 23,
+            selection = Selection.Minute,
+        ).performClick()
 
-        rule.onNodeWithText("55").assertExists()
+        rule.runOnIdle {
+            assertThat(state.selection).isEqualTo(Selection.Minute)
+        }
     }
 
     @Test
@@ -113,7 +157,7 @@
             TimePicker(state)
         }
 
-        rule.onNodeWithText("6").performClick()
+        rule.onNodeWithTimeValue(number = 6, selection = Selection.Hour).performClick()
 
         // shows 06 in display
         rule.onNodeWithText("06").assertExists()
@@ -327,12 +371,17 @@
 
         rule.onNodeWithText("14")
             .assert(isFocusable())
+            .assertContentDescriptionContains("for hour")
             .assert(hasImeAction(ImeAction.Next))
             .assert(isFocused())
 
         rule.onAllNodesWithText("23")
             .filterToOne(isSelectable())
             .assert(isNotSelected())
+            .performClick()
+
+        rule.onNodeWithText("23")
+            .assertContentDescriptionContains("for minutes")
     }
 
     @OptIn(ExperimentalComposeUiApi::class, ExperimentalTestApi::class)
@@ -479,7 +528,7 @@
         }
 
         repeat(24) { number ->
-            rule.onNodeWithText(number.toString()).performClick()
+            rule.onNodeWithTimeValue(number, Selection.Hour, is24Hour = true).performClick()
             rule.runOnIdle {
                 state.selection = Selection.Hour
                 assertThat(state.hour).isEqualTo(number)
@@ -506,11 +555,78 @@
                 else -> number
             }
 
-            rule.onNodeWithText("$hour").performClick()
+            rule.onNodeWithTimeValue(hour, Selection.Hour).performClick()
             rule.runOnIdle {
                 state.selection = Selection.Hour
                 assertThat(state.hour).isEqualTo(number)
             }
         }
     }
+
+    @Test
+    fun clockFace_24HourMinutes_everyValue() {
+        val state = TimePickerState(initialHour = 10, initialMinute = 23, is24Hour = true)
+        state.selection = Selection.Minute
+        rule.setMaterialContent(lightColorScheme()) {
+            ClockFace(state, TimePickerDefaults.colors())
+        }
+
+        repeat(11) { number ->
+            rule.onNodeWithTimeValue(
+                number * 5,
+                Selection.Minute,
+                is24Hour = true
+            ).performClick()
+            rule.runOnIdle {
+                assertThat(state.minute).isEqualTo(number * 5)
+            }
+        }
+    }
+
+    @Test
+    fun clockFace_12HourMinutes_everyValue() {
+        val state = TimePickerState(initialHour = 10, initialMinute = 23, is24Hour = false)
+        state.selection = Selection.Minute
+        rule.setMaterialContent(lightColorScheme()) {
+            ClockFace(state, TimePickerDefaults.colors())
+        }
+
+        repeat(11) { number ->
+            rule.onNodeWithTimeValue(number * 5, Selection.Minute).performClick()
+            rule.runOnIdle {
+                assertThat(state.minute).isEqualTo(number * 5)
+            }
+        }
+    }
+
+    private fun contentDescriptionForValue(
+        resources: Resources,
+        selection: Selection,
+        is24Hour: Boolean,
+        number: Int
+    ): String {
+
+        val id = if (selection == Selection.Minute) {
+            R.string.time_picker_minute_suffix
+        } else if (is24Hour) {
+            R.string.time_picker_hour_24h_suffix
+        } else {
+            R.string.time_picker_hour_suffix
+        }
+
+        return resources.getString(id, number)
+    }
+
+    private fun SemanticsNodeInteractionsProvider.onNodeWithTimeValue(
+        number: Int,
+        selection: Selection,
+        is24Hour: Boolean = false,
+    ): SemanticsNodeInteraction = onAllNodesWithContentDescription(
+        contentDescriptionForValue(
+            InstrumentationRegistry.getInstrumentation().context.resources,
+            selection,
+            is24Hour,
+            number
+        )
+    ).onFirst()
 }
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/SearchBar.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/SearchBar.kt
index 1c4cbf3..22914d0 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/SearchBar.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/SearchBar.kt
@@ -59,6 +59,7 @@
 import androidx.compose.material3.tokens.SearchViewTokens
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.State
 import androidx.compose.runtime.derivedStateOf
@@ -82,6 +83,7 @@
 import androidx.compose.ui.layout.layout
 import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalFocusManager
 import androidx.compose.ui.semantics.contentDescription
 import androidx.compose.ui.semantics.onClick
 import androidx.compose.ui.semantics.semantics
@@ -101,6 +103,7 @@
 import androidx.compose.ui.zIndex
 import kotlin.math.max
 import kotlin.math.min
+import kotlinx.coroutines.delay
 
 /**
  * <a href="https://m3.material.io/components/search/overview" class="external" target="_blank">Material Design search</a>.
@@ -177,6 +180,7 @@
         animationSpec = if (active) AnimationEnterFloatSpec else AnimationExitFloatSpec
     )
 
+    val focusManager = LocalFocusManager.current
     val density = LocalDensity.current
 
     val defaultInputFieldShape = SearchBarDefaults.inputFieldShape
@@ -276,6 +280,15 @@
         }
     }
 
+    LaunchedEffect(active) {
+        if (!active) {
+            // Not strictly needed according to the motion spec, but since the animation already has
+            // a delay, this works around b/261632544.
+            delay(AnimationDelayMillis.toLong())
+            focusManager.clearFocus()
+        }
+    }
+
     BackHandler(enabled = active) {
         onActiveChange(false)
     }
@@ -344,6 +357,8 @@
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
     content: @Composable ColumnScope.() -> Unit,
 ) {
+    val focusManager = LocalFocusManager.current
+
     Surface(
         shape = shape,
         color = colors.containerColor,
@@ -389,6 +404,15 @@
         }
     }
 
+    LaunchedEffect(active) {
+        if (!active) {
+            // Not strictly needed according to the motion spec, but since the animation already has
+            // a delay, this works around b/261632544.
+            delay(AnimationDelayMillis.toLong())
+            focusManager.clearFocus()
+        }
+    }
+
     BackHandler(enabled = active) {
         onActiveChange(false)
     }
@@ -469,6 +493,7 @@
 /**
  * Defaults used in [SearchBar] and [DockedSearchBar].
  */
+@ExperimentalMaterial3Api
 object SearchBarDefaults {
     /** Default elevation for a search bar. */
     val Elevation: Dp = SearchBarTokens.ContainerElevation
@@ -497,7 +522,6 @@
      * @param dividerColor the color of the divider between the input field and the search results
      * @param inputFieldColors the colors of the input field
      */
-    @ExperimentalMaterial3Api
     @Composable
     fun colors(
         containerColor: Color = SearchBarTokens.ContainerColor.toColor(),
@@ -533,7 +557,6 @@
      * @param unfocusedPlaceholderColor the placeholder color for this input field when not focused
      * @param disabledPlaceholderColor the placeholder color for this input field when disabled
      */
-    @ExperimentalMaterial3Api
     @Composable
     fun inputFieldColors(
         focusedTextColor: Color = SearchBarTokens.InputTextColor.toColor(),
@@ -573,7 +596,6 @@
         )
 
     @Deprecated("Maintained for binary compatibility", level = DeprecationLevel.HIDDEN)
-    @ExperimentalMaterial3Api
     @Composable
     fun inputFieldColors(
         textColor: Color = SearchBarTokens.InputTextColor.toColor(),
@@ -681,6 +703,7 @@
 }
 
 // Measurement specs
+@OptIn(ExperimentalMaterial3Api::class)
 private val SearchBarCornerRadius: Dp = InputFieldHeight / 2
 internal val DockedActiveTableMinHeight: Dp = 240.dp
 private const val DockedActiveTableMaxHeightScreenRatio: Float = 2f / 3f
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/Strings.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/Strings.android.kt
index 65f9107..07394fc 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/Strings.android.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/Strings.android.kt
@@ -156,7 +156,7 @@
         Strings.BottomSheetDragHandleDescription -> resources.getString(
             androidx.compose.material3.R.string.bottom_sheet_drag_handle_description
         )
-        Strings.BottomSheetCollapseDescription -> resources.getString(
+        Strings.BottomSheetPartialExpandDescription -> resources.getString(
             androidx.compose.material3.R.string.bottom_sheet_collapse_description
         )
         Strings.BottomSheetDismissDescription -> resources.getString(
@@ -188,6 +188,10 @@
             androidx.compose.material3.R.string.time_picker_hour)
         Strings.TimePickerMinute -> resources.getString(
             androidx.compose.material3.R.string.time_picker_minute)
+        Strings.TimePickerHourTextField -> resources.getString(
+            androidx.compose.material3.R.string.time_picker_hour_text_field)
+        Strings.TimePickerMinuteTextField -> resources.getString(
+            androidx.compose.material3.R.string.time_picker_minute_text_field)
         Strings.TooltipPaneDescription -> resources.getString(
             androidx.compose.material3.R.string.tooltip_pane_description)
         else -> ""
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/TimePicker.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/TimePicker.android.kt
new file mode 100644
index 0000000..002e5a2
--- /dev/null
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/TimePicker.android.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.ui.platform.LocalConfiguration
+
+@OptIn(ExperimentalMaterial3Api::class)
+internal actual val defaultTimePickerLayoutType: TimePickerLayoutType
+    @Composable
+    @ReadOnlyComposable get() = with(LocalConfiguration.current) {
+        if (screenHeightDp < screenWidthDp) {
+            TimePickerLayoutType.Horizontal
+        } else {
+            TimePickerLayoutType.Vertical
+        }
+    }
diff --git a/compose/material3/material3/src/androidMain/res/values-af/strings.xml b/compose/material3/material3/src/androidMain/res/values-af/strings.xml
index 580f969..beb91f8 100644
--- a/compose/material3/material3/src/androidMain/res/values-af/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-af/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Voer datums in"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Ongeldige datumreeksinvoer"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Sleephandvatsel"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Vou onderste blad in"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Maak onderste blad toe"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Vou onderste blad uit"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Nutswenk"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Wys nutswenk"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"nm."</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minute"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minuut"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Uur"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"vir minute"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"vir uur"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-am/strings.xml b/compose/material3/material3/src/androidMain/res/values-am/strings.xml
index 66ab384..c8749d6 100644
--- a/compose/material3/material3/src/androidMain/res/values-am/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-am/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"ቀናትን ያስገቡ"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"ልክ ያልሆነ የቀን ክልል ግቤት"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"መያዣ ይጎትቱ"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"የግርጌ ሉህን ይሰብስቡ"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"የግርጌ ሉህን ይሰብስቡ"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"የግርጌ ሉህ ይዘርጉ"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"የመሣሪያ ጥቆማ"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"መሣሪያ ጥቆማን አሳይ"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"ከሰዓት"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d ደቂቃዎች"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"ደቂቃ"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"ሰዓት"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"ለደቂቃዎች"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ለሰዓት"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ar/strings.xml b/compose/material3/material3/src/androidMain/res/values-ar/strings.xml
index 033383e..ae5ecc0 100644
--- a/compose/material3/material3/src/androidMain/res/values-ar/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-ar/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"إدخال التواريخ"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"إدخال نطاق زمني غير صالح"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"مقبض السحب"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"تصغير البطاقة السفلية"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"إغلاق البطاقة السفلية"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"توسيع البطاقة السفلية"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"تلميح"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"إظهار التلميح"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"م"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"‏%1$d دقيقة"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"دقيقة"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"ساعة"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"لمدة دقائق"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"لمدة ساعة"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-as/strings.xml b/compose/material3/material3/src/androidMain/res/values-as/strings.xml
index 4b34266..7bd785c 100644
--- a/compose/material3/material3/src/androidMain/res/values-as/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-as/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"তাৰিখ দিয়ক"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"অমান্য তাৰিখৰ পৰিসৰৰ ইনপুট"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ড্ৰেগ হেণ্ডেল"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"তলৰ শ্বীটখন সংকোচন কৰক"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"তলৰ শ্বীটখন অগ্ৰাহ্য কৰক"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"তলৰ শ্বীটখন বিস্তাৰ কৰক"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"টুলটিপ"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"টুলটিপ দেখুৱাওক"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"পৰাহ্ন"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%d মিনিট"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"মিনিট"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"ঘণ্টা"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"মিনিটৰ বাবে"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ঘণ্টাৰ বাবে"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-az/strings.xml b/compose/material3/material3/src/androidMain/res/values-az/strings.xml
index 0ca1afe..da185a0 100644
--- a/compose/material3/material3/src/androidMain/res/values-az/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-az/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Tarixləri daxil edin"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Yanlış tarix aralığı daxiletməsi"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Dəstəyi çəkin"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Aşağıdakı vərəqi yığcamlaşdırın"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Aşağıdakı vərəqi rədd edin"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Aşağıdakı vərəqi genişləndirin"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Alət izahı"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"İpucu göstərin"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d dəqiqə"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Dəqiqə"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Saat"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"dəqiqəlik"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"saatlıq"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-b+sr+Latn/strings.xml b/compose/material3/material3/src/androidMain/res/values-b+sr+Latn/strings.xml
index 0bc4a3a..be53fbf 100644
--- a/compose/material3/material3/src/androidMain/res/values-b+sr+Latn/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-b+sr+Latn/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Unesite datume"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Unos opsega datuma je nevažeći"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Identifikator za prevlačenje"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Skupi donju tabelu"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Odbaci donju tabelu"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Proširi donju tabelu"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Objašnjenje"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Prikaži objašnjenje"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"po"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d min"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minut"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Sat"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"za minute"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"za sate"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-be/strings.xml b/compose/material3/material3/src/androidMain/res/values-be/strings.xml
index 6f983e8..78697c3 100644
--- a/compose/material3/material3/src/androidMain/res/values-be/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-be/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Увядзіце даты"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Уведзены няправільны дыяпазон дат"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Маркер перацягвання"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Згарнуць ніжні аркуш"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Закрыць ніжні аркуш"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Разгарнуць ніжні аркуш"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Падказка"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Паказваць усплывальную падказку"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"пасля паўдня"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d хв"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Хвіліны"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Гадзіны"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"хвіліны"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"гадзіны"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-bg/strings.xml b/compose/material3/material3/src/androidMain/res/values-bg/strings.xml
index 9159c1d..63b1abf 100644
--- a/compose/material3/material3/src/androidMain/res/values-bg/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-bg/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Въведете дати"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Въведен е невалиден период от време"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Манипулатор за преместване с плъзгане"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Свиване на долния лист"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Отхвърляне на долния лист"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Разгъване на долния лист"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Подсказка"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Показване на подсказка"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d минути"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Минута"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Час"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"за минутите"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"за часа"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-bn/strings.xml b/compose/material3/material3/src/androidMain/res/values-bn/strings.xml
index 6afcc01..70935fe 100644
--- a/compose/material3/material3/src/androidMain/res/values-bn/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-bn/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"তারিখ লিখুন"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"তারিখের ব্যাপ্তি সম্পর্কিত ইনপুট ভুল দেওয়া আছে"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"টেনে আনার হ্যান্ডেল"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"স্ক্রিনের নিচে অ্যাটাচ করা শিট আড়াল করুন"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"স্ক্রিনের নিচে অ্যাটাচ করা শিট বাতিল করুন"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"স্ক্রিনের নিচে অ্যাটাচ করা শিট বড় করুন"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"টুলটিপ"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"টুলটিপ দেখান"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d মিনিট"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"মিনিট"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"ঘণ্টা"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"এত মিনিটের জন্য"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"এত ঘণ্টার জন্য"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-bs/strings.xml b/compose/material3/material3/src/androidMain/res/values-bs/strings.xml
index d02537d..afba3f0 100644
--- a/compose/material3/material3/src/androidMain/res/values-bs/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-bs/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Unesite datume"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Nevažeći unos raspona datuma"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Ručica za prevlačenje"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Sužavanje donje tabele"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Odbacivanje donje tabele"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Proširivanje donje tabele"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Skočni opis"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Prikaži skočni opis"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"poslijepodne"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d min"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minuta"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Sat"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"za minute"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"za sat"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ca/strings.xml b/compose/material3/material3/src/androidMain/res/values-ca/strings.xml
index 82bea54..a694aaf4 100644
--- a/compose/material3/material3/src/androidMain/res/values-ca/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-ca/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Introdueix les dates"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"S\'ha introduït un interval de dades no vàlid"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Ansa per arrossegar"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Replega el full inferior"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Ignora el full inferior"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Desplega el full inferior"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Descripció emergent"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Mostra la descripció emergent"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minuts"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minut"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Hora"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"per als minuts"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"per a l\'hora"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-cs/strings.xml b/compose/material3/material3/src/androidMain/res/values-cs/strings.xml
index 69cd262..a8ff6c0 100644
--- a/compose/material3/material3/src/androidMain/res/values-cs/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-cs/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Zadejte data"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Neplatné období"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Úchyt pro přetažení"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Sbalit spodní tabulku"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Zavřít spodní tabulku"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Rozbalit spodní tabulku"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Popisek"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Zobrazit popisek"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minut"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minuta"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Hodina"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"pro minuty"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"pro hodinu"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-da/strings.xml b/compose/material3/material3/src/androidMain/res/values-da/strings.xml
index 5a2d660..d87fd19 100644
--- a/compose/material3/material3/src/androidMain/res/values-da/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-da/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Angiv datoer"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Ugyldig angivelse af datainterval"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Håndtag"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Skjul felt i bunden"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Luk felt i bunden"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Udvid felt i bunden"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Værktøjstip"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Se værktøjstip"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutter"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minut"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Time"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"for minutter"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"for time"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-de/strings.xml b/compose/material3/material3/src/androidMain/res/values-de/strings.xml
index 1e0e158..4052a8f 100644
--- a/compose/material3/material3/src/androidMain/res/values-de/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-de/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Daten eingeben"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Angegebener Zeitraum ungültig"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Ziehpunkt"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Ansicht am unteren Rand minimieren"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Ansicht am unteren Rand schließen"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Ansicht am unteren Rand maximieren"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Kurzinfo"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Kurzinfo anzeigen"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d Minuten"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minute"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Stunde"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"für Minuten"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"für Stunde"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-el/strings.xml b/compose/material3/material3/src/androidMain/res/values-el/strings.xml
index 97b435b..06b90b8 100644
--- a/compose/material3/material3/src/androidMain/res/values-el/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-el/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Εισαγωγή ημερομηνιών"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Μη έγκυρη εισαγωγή εύρους ημερομηνιών"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Λαβή μεταφοράς"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Σύμπτυξη φύλλου κάτω μέρους"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Παράβλεψη φύλλου κάτω μέρους"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Ανάπτυξη φύλλου κάτω μέρους"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Επεξήγηση εργαλείου"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Προβολή επεξήγησης"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"ΜΜ"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d λεπτά"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Λεπτό"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Ώρα"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"για λεπτά"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"για ώρα"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-en-rAU/strings.xml b/compose/material3/material3/src/androidMain/res/values-en-rAU/strings.xml
index 7a7e021..7eef271 100644
--- a/compose/material3/material3/src/androidMain/res/values-en-rAU/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-en-rAU/strings.xml
@@ -70,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutes"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minute"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Hour"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"for minutes"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"for hour"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-en-rCA/strings.xml b/compose/material3/material3/src/androidMain/res/values-en-rCA/strings.xml
index af4dc76..04a4e1d 100644
--- a/compose/material3/material3/src/androidMain/res/values-en-rCA/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-en-rCA/strings.xml
@@ -70,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutes"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minute"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Hour"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"for minutes"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"for hour"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-en-rGB/strings.xml b/compose/material3/material3/src/androidMain/res/values-en-rGB/strings.xml
index 7a7e021..7eef271 100644
--- a/compose/material3/material3/src/androidMain/res/values-en-rGB/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-en-rGB/strings.xml
@@ -70,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutes"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minute"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Hour"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"for minutes"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"for hour"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-en-rIN/strings.xml b/compose/material3/material3/src/androidMain/res/values-en-rIN/strings.xml
index 7a7e021..7eef271 100644
--- a/compose/material3/material3/src/androidMain/res/values-en-rIN/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-en-rIN/strings.xml
@@ -70,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutes"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minute"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Hour"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"for minutes"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"for hour"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-en-rXC/strings.xml b/compose/material3/material3/src/androidMain/res/values-en-rXC/strings.xml
index 0d341cb7..dea2fdc 100644
--- a/compose/material3/material3/src/androidMain/res/values-en-rXC/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-en-rXC/strings.xml
@@ -70,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‎‏‎‏‎‎‎‏‏‎‏‎‏‎‎‏‎‎‎‏‎‏‎‎‏‏‏‎‏‎%1$d minutes‎‏‎‎‏‎"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‎‎‏‎‎‏‎‎‎‏‏‎‎‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎Minute‎‏‎‎‏‎"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎‎‏‏‎‏‏‏‎‏‏‎‏‏‎‎‎‏‏‎‏‎‏‎‏‏‏‎‎‏‎‎‏‏‏‏‎‏‎‏‎‏‏‎Hour‎‏‎‎‏‎"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‏‏‎‎‎‎‎‎‎‏‎‎‎‏‎‎‏‎‏‎‎‏‎‏‏‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎‏‎‏‏‎‏‎‏‎for minutes‎‏‎‎‏‎"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‎‎‏‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‎‏‎‏‎‏‎‎‏‏‏‏‏‎‏‎‎for hour‎‏‎‎‏‎"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-es-rUS/strings.xml b/compose/material3/material3/src/androidMain/res/values-es-rUS/strings.xml
index 98f1ffc..db156dd 100644
--- a/compose/material3/material3/src/androidMain/res/values-es-rUS/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-es-rUS/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Ingresar fechas"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Se introdujo un período no válido"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Controlador de arrastre"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Contraer la hoja inferior"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Descartar la hoja inferior"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Expandir la hoja inferior"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Información"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Mostrar información"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"p.m."</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutos"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minuto"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Hora"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"por minutos"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"por hora"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-es/strings.xml b/compose/material3/material3/src/androidMain/res/values-es/strings.xml
index 48f642b..6226ef2 100644
--- a/compose/material3/material3/src/androidMain/res/values-es/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-es/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Introducir fechas"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"El intervalo de fechas no es válido"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Controlador de arrastre"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Contrae la hoja inferior"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Cierra la hoja inferior"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Despliega la hoja inferior"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Descripción emergente"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Mostrar descripción emergente"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutos"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minutos"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Hora"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"por minutos"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"por hora"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-et/strings.xml b/compose/material3/material3/src/androidMain/res/values-et/strings.xml
index 1a05d680..85ea055 100644
--- a/compose/material3/material3/src/androidMain/res/values-et/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-et/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Sisestage kuupäevad"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Sisestati sobimatu kuupäevavahemik"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Lohistamispide"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Alumise lehe ahendamine"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Alumisest lehest loobumine"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Alumise lehe laiendamine"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Kohtspikker"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Kuva kohtspikker"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutit"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minutid"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Tunnid"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"minutite jaoks"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"tundide jaoks"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-eu/strings.xml b/compose/material3/material3/src/androidMain/res/values-eu/strings.xml
index 43d79354..2f2f09d 100644
--- a/compose/material3/material3/src/androidMain/res/values-eu/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-eu/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Idatzi datak"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Idatzitako data tarteak ez du balio"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Arrastatzeko kontrol-puntua"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Tolestu pantailaren behealdean ainguratutako orria"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Baztertu pantailaren behealdean ainguratutako orria"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Zabaldu pantailaren behealdean ainguratutako orria"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Aholkua"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Erakutsi aholkua"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutu"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minutuak"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Orduak"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"minutuetarako"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ordurako"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-fa/strings.xml b/compose/material3/material3/src/androidMain/res/values-fa/strings.xml
index 0ba4c72..aaab237 100644
--- a/compose/material3/material3/src/androidMain/res/values-fa/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-fa/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"تاریخ‌ها را وارد کنید"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"محدوده تاریخ واردشده نامعتبر است"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"دستگیره کشاندن"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"جمع کردن برگه زیرین"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"رد کردن برگه زیرین"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"ازهم باز کردن برگه زیرین"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"نکته‌ابزار"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"نمایش نکته‌ابزار"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"ب.ظ."</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"‏%1$d دقیقه"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"دقیقه"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"ساعت"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"برای دقیقه"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"برای ساعت"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-fi/strings.xml b/compose/material3/material3/src/androidMain/res/values-fi/strings.xml
index efea19e..f4fa38d 100644
--- a/compose/material3/material3/src/androidMain/res/values-fi/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-fi/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Lisää päivämäärät"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Virheellinen ajanjakso"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Vetokahva"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Tiivistä alapaneeli"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Hylkää alapaneeli"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Laajenna alapaneeli"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Vihjeteksti"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Näytä vihjeteksti"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"IP"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minuuttia"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minuutti"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Tunti"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"minuuttien ajan"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"tunnin ajan"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-fr-rCA/strings.xml b/compose/material3/material3/src/androidMain/res/values-fr-rCA/strings.xml
index 2ce849d..61d15f1 100644
--- a/compose/material3/material3/src/androidMain/res/values-fr-rCA/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-fr-rCA/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Entrer les dates"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Entrée de période incorrecte"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Poignée de déplacement"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Réduire la zone de contenu dans le bas de l\'écran"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Fermer la zone de contenu dans le bas de l\'écran"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Développer la zone de contenu dans le bas de l\'écran"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Infobulle"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Afficher une infobulle"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutes"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minute"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Heure"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"pour les minutes"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"pour l\'heure"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-fr/strings.xml b/compose/material3/material3/src/androidMain/res/values-fr/strings.xml
index 5f7e380..d0ec15d 100644
--- a/compose/material3/material3/src/androidMain/res/values-fr/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-fr/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Saisir des dates"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Plage de dates non valide"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Poignée de déplacement"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Réduire la bottom sheet"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Fermer la bottom sheet"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Développer la bottom sheet"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Info-bulle"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Afficher l\'info-bulle"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutes"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minute"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Heure"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"en minutes"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"en heures"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-gl/strings.xml b/compose/material3/material3/src/androidMain/res/values-gl/strings.xml
index a22ec51..0913a02 100644
--- a/compose/material3/material3/src/androidMain/res/values-gl/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-gl/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Indica as datas"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Indicouse un intervalo de datas que non é válido"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Controlador de arrastre"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Contraer panel inferior"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Pechar panel inferior"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Despregar panel inferior"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Cadro de información"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Mostrar cadro de información"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"pm"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutos"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minuto"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Hora"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"por minuto"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"por hora"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-gu/strings.xml b/compose/material3/material3/src/androidMain/res/values-gu/strings.xml
index b45aabf..b4a3309 100644
--- a/compose/material3/material3/src/androidMain/res/values-gu/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-gu/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"તારીખો દાખલ કરો"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"તારીખની શ્રેણીનું અમાન્ય ઇનપુટ"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ઑબ્જેક્ટ ખેંચવાનું હૅન્ડલ"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"બોટમ શીટ નાની કરો"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"બોટમ શીટ છોડી દો"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"બોટમ શીટ મોટી કરો"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"ટૂલટિપ"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"ટૂલટિપ બતાવો"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d મિનિટ"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"મિનિટ"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"કલાક"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"મિનિટ માટે"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"કલાક માટે"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-hi/strings.xml b/compose/material3/material3/src/androidMain/res/values-hi/strings.xml
index 35d585e..e0ad0f1 100644
--- a/compose/material3/material3/src/androidMain/res/values-hi/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-hi/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"तारीखें डालें"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"तारीख की दी गई सीमा गलत है"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"खींचकर छोड़ने वाला हैंडल"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"बॉटम शीट को छोटा करें"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"बॉटम शीट को खारिज करें"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"बॉटम शीट को बड़ा करें"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"टूलटिप"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"टूलटिप देखें"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d मिनट"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"मिनट"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"घंटा"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"मिनट के लिए"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"घंटे के लिए"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-hr/strings.xml b/compose/material3/material3/src/androidMain/res/values-hr/strings.xml
index 2f9c3cb..12bf78f 100644
--- a/compose/material3/material3/src/androidMain/res/values-hr/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-hr/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Unos datuma"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Unos datumskog raspona nije važeći"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Marker za povlačenje"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Sažimanje donje tablice"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Odbacivanje donje tablice"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Proširivanje donje tablice"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Opis"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Prikaži opis"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"Poslijepodne"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d min"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minuta"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Sat"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"minutama"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"na jedan sat"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-hu/strings.xml b/compose/material3/material3/src/androidMain/res/values-hu/strings.xml
index 500c866..9c8ccf6 100644
--- a/compose/material3/material3/src/androidMain/res/values-hu/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-hu/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Dátumok megadása"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Érvénytelen a megadott dátum"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Fogópont"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Az alsó lap összecsukása"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Az alsó lap elvetése"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Az alsó lap kibontása"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Elemleírás"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Elemleírás megjelenítése"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"du."</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d perc"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Perc"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Óra"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"percre"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"órára"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-hy/strings.xml b/compose/material3/material3/src/androidMain/res/values-hy/strings.xml
index 50d319f..d964992 100644
--- a/compose/material3/material3/src/androidMain/res/values-hy/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-hy/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Մուտքագրեք ամսաթվերը"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Մուտքագրված ամսաթվերի միջակայքն անվավեր է"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Տեղափոխման նշիչ"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Ծալել ներքևի էկրանը"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Փակել ներքևի էկրանը"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Ծավալել ներքևի էկրանը"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Հուշակ"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Ցուցադրել հուշում"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d րոպե"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Րոպե"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Ժամ"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"րոպեներ"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ժամեր"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-in/strings.xml b/compose/material3/material3/src/androidMain/res/values-in/strings.xml
index cf6645c..36ba535 100644
--- a/compose/material3/material3/src/androidMain/res/values-in/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-in/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Masukkan tanggal"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Input rentang tanggal tidak valid"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Handel geser"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Menciutkan sheet bawah"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Menutup sheet bawah"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Meluaskan sheet bawah"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Tooltip"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Tampilkan tooltip"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d menit"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Menit"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Jam"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"untuk menit"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"untuk jam"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-is/strings.xml b/compose/material3/material3/src/androidMain/res/values-is/strings.xml
index f090921..ff5ec27 100644
--- a/compose/material3/material3/src/androidMain/res/values-is/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-is/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Sláðu inn dagsetningar"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Ógilt tímabil fært inn"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Dragkló"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Minnka blað neðst"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Hunsa blað neðst"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Stækka blað neðst"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Ábending"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Sýna ábendingu"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"eh"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d mínútur"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Mínúta"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Klukkustund"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"fyrir mínútur"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"fyrir klukkustund"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-it/strings.xml b/compose/material3/material3/src/androidMain/res/values-it/strings.xml
index a841077..6959ff1 100644
--- a/compose/material3/material3/src/androidMain/res/values-it/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-it/strings.xml
@@ -70,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minuti"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minuto"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Ora"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"per minuti"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"per ora"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-iw/strings.xml b/compose/material3/material3/src/androidMain/res/values-iw/strings.xml
index 095cd13..a0750c6 100644
--- a/compose/material3/material3/src/androidMain/res/values-iw/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-iw/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"הזנת תאריכים"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"קלט טווח תאריכים לא חוקי"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"נקודת אחיזה לגרירה"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"כיווץ הגיליון התחתון"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"סגירת הגיליון התחתון"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"הרחבת הגיליון התחתון"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"הסבר קצר"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"הצגת הסבר קצר"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"‏%1$d דקות"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"דקות"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"שעות"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"דקות"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"שעות"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ja/strings.xml b/compose/material3/material3/src/androidMain/res/values-ja/strings.xml
index 750d905..3ff0d0c 100644
--- a/compose/material3/material3/src/androidMain/res/values-ja/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-ja/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"日付の入力"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"入力された期間は無効です"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ドラッグ ハンドル"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"ボトムシートを折りたたみます"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"ボトムシートを閉じます"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"ボトムシートを開きます"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"ツールチップ"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"ツールチップを表示します"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d 分"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"分"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"時間"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"(分単位)"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"(時間単位)"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ka/strings.xml b/compose/material3/material3/src/androidMain/res/values-ka/strings.xml
index f59ba7d..0a0e223 100644
--- a/compose/material3/material3/src/androidMain/res/values-ka/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-ka/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"თარიღების შეყვანა"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"შეყვანილია თარიღების არასწორი დიაპაზონი"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"სახელური ჩავლებისთვის"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"ქვედა ფურცლის ჩაკეცვა"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"ქვედა ფურცლის უარყოფა"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"ქვედა ფურცლის გაშლა"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"მინიშნება"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"მინიშნების ჩვენება"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d წთ"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"წუთი"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"საათი"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"რამდენიმე წუთით"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ერთი საათით"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-kk/strings.xml b/compose/material3/material3/src/androidMain/res/values-kk/strings.xml
index 0a65d47..7ba95aa 100644
--- a/compose/material3/material3/src/androidMain/res/values-kk/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-kk/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Күндерді енгізіңіз"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Жарамсыз күндер аралығы енгізілген."</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Сүйрейтін тетік"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Төменгі парақшаны жию"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Төменгі парақшаны жабу"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Төменгі парақшаны жаю"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Қалқыма көмек"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Қалқыма көмекті көрсету"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"түстен кейін"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d минут"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Mинут"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Сағат"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"минут"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"сағат"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-km/strings.xml b/compose/material3/material3/src/androidMain/res/values-km/strings.xml
index b247f86..3a087c7 100644
--- a/compose/material3/material3/src/androidMain/res/values-km/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-km/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"បញ្ចូល​កាលបរិច្ឆេទ"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"ការបញ្ចូលចន្លោះកាលបរិច្ឆេទមិនត្រឹមត្រូវ"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ដង​អូស"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"បង្រួម​សន្លឹកខាងក្រោម"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"ច្រានចោល​សន្លឹកខាងក្រោម"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"ពង្រីក​សន្លឹកខាងក្រោម"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"កំណត់​ពន្យល់"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"បង្ហាញ​កំណត់​ពន្យល់"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d នាទី"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"នាទី​"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"ម៉ោង"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"រយៈពេលប៉ុន្មាននាទី"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"រយៈពេលប៉ុន្មានម៉ោង"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-kn/strings.xml b/compose/material3/material3/src/androidMain/res/values-kn/strings.xml
index eaf20df..7e4f82e 100644
--- a/compose/material3/material3/src/androidMain/res/values-kn/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-kn/strings.xml
@@ -70,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d ನಿಮಿಷಗಳು"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"ನಿಮಿಷ"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"ಗಂಟೆ"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"ನಿಮಿಷಗಳವರೆಗೆ"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ಗಂಟೆಯವರೆಗೆ"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ko/strings.xml b/compose/material3/material3/src/androidMain/res/values-ko/strings.xml
index 24cc091..67735f6 100644
--- a/compose/material3/material3/src/androidMain/res/values-ko/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-ko/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"날짜 입력"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"잘못된 기간 입력"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"드래그 핸들"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"하단 시트 접기"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"하단 시트 닫기"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"하단 시트 펼치기"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"도움말"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"도움말 표시"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"오후"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d분"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"분"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"시간"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"기간(분)"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"기간(시간)"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ky/strings.xml b/compose/material3/material3/src/androidMain/res/values-ky/strings.xml
index d9e1152..e4d6c16 100644
--- a/compose/material3/material3/src/androidMain/res/values-ky/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-ky/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Күндөрдү киргизүү"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Даталар диапазону туура эмес тандалды"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Тизменин керектүү жерине сүйрөп баруу"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Ылдыйкы экранды жыйыштыруу"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Ылдыйкы экранды жабуу"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Ылдыйкы экранды жайып көрсөтүү"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Калкып чыгуучу кеңеш"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Калкып чыгуучу кеңешти көрсөтүү"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"түштөн кийин"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d мүнөт"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Мүнөт"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Саат"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"мүнөткө"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"саатка"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-lo/strings.xml b/compose/material3/material3/src/androidMain/res/values-lo/strings.xml
index ebb96d9..c3cc53a 100644
--- a/compose/material3/material3/src/androidMain/res/values-lo/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-lo/strings.xml
@@ -70,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d ນາທີ"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"ນາທີ"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"ຊົ່ວໂມງ"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"ສຳລັບນາທີ"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ສຳລັບຊົ່ວໂມງ"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-lt/strings.xml b/compose/material3/material3/src/androidMain/res/values-lt/strings.xml
index 2f2b031..2d449ad 100644
--- a/compose/material3/material3/src/androidMain/res/values-lt/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-lt/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Įvesti datas"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Netinkama dienų sekos įvestis"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Vilkimo rankenėlė"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Sutraukti apatinį lapą"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Atsisakyti apatinio lapo"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Išskleisti apatinį lapą"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Patarimas"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Rodyti patarimą"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"popiet"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%d min."</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minutė"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Valanda"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"minutės"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"valandos"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-lv/strings.xml b/compose/material3/material3/src/androidMain/res/values-lv/strings.xml
index 288096b..a0e0c58 100644
--- a/compose/material3/material3/src/androidMain/res/values-lv/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-lv/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Ievadiet datumus"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Ievadīts nederīgs datumu diapazons."</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Vilkšanas turis"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Sakļaut ekrāna apakšdaļas lapu"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Noraidīt ekrāna apakšdaļas lapu"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Izvērst ekrāna apakšdaļas lapu"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Rīka padoms"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Rādīt rīka padomu"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"Minūtes: %1$d"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minūtes"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Stundas"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"(minūtes)"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"(stundas)"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-mk/strings.xml b/compose/material3/material3/src/androidMain/res/values-mk/strings.xml
index 634be65..66bfb69 100644
--- a/compose/material3/material3/src/androidMain/res/values-mk/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-mk/strings.xml
@@ -70,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d минути"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Минута"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Час"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"за минути"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"за час"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ml/strings.xml b/compose/material3/material3/src/androidMain/res/values-ml/strings.xml
index cf41cc2..1e0fea1 100644
--- a/compose/material3/material3/src/androidMain/res/values-ml/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-ml/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"തീയതികൾ നൽകുക"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"തീയതി ശ്രേണി ഇൻപുട്ട് അസാധുവാണ്"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"വലിച്ചിടുന്നതിനുള്ള ഹാൻഡിൽ"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"ബോട്ടം ഷീറ്റ് ചുരുക്കുക"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"ബോട്ടം ഷീറ്റ് ഡിസ്മിസ് ചെയ്യുക"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"ബോട്ടം ഷീറ്റ് വികസിപ്പിക്കുക"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"ടൂൾടിപ്പ്"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"ടൂൾടിപ്പ് കാണിക്കുക"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d മിനിറ്റ്"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"മിനിറ്റ്"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"മണിക്കൂർ"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"മിനിറ്റ് നേരത്തേക്ക്"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"മണിക്കൂർ നേരത്തേക്ക്"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-mn/strings.xml b/compose/material3/material3/src/androidMain/res/values-mn/strings.xml
index a55fdf6..e5b4acb 100644
--- a/compose/material3/material3/src/androidMain/res/values-mn/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-mn/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Огноо оруулах"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Хугацааны интервалын оролт буруу байна"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Чирэх бариул"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Доод хүснэгтийг хураах"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Доод хүснэгтийг хаах"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Доод хүснэгтийг дэлгэх"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Зөвлөмж"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Зөвлөмж харуулах"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"ҮХ"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d минут"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Минут"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Цаг"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"минутын турш"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"цагийн турш"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-mr/strings.xml b/compose/material3/material3/src/androidMain/res/values-mr/strings.xml
index 7dd8357..ef3edc5 100644
--- a/compose/material3/material3/src/androidMain/res/values-mr/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-mr/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"तारखा एंटर करा"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"तारीख रेंजचे इनपुट चुकीचे आहे"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ड्रॅग हॅंडल"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"तळाशी असलेली शीट कोलॅप्स करा"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"तळाशी असलेली शीट डिसमिस करा"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"तळाशी असलेली शीट विस्तारीत करा"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"टूलटिप"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"टूलटिप दाखवा"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d मिनिटे"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"मिनिट"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"तास"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"मिनिटांसाठी"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"तासासाठी"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ms/strings.xml b/compose/material3/material3/src/androidMain/res/values-ms/strings.xml
index 9dcc19d..bf121e9 100644
--- a/compose/material3/material3/src/androidMain/res/values-ms/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-ms/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Masukkan tarikh"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Input julat tarikh tidak sah"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Pemegang seret"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Kuncupkan helaian bawah"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Ketepikan helaian bawah"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Kembangkan helaian bawah"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Tip alat"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Tunjukkan tip alat"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"P/M"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minit"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minit"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Jam"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"selama # minit"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"selama # jam"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-my/strings.xml b/compose/material3/material3/src/androidMain/res/values-my/strings.xml
index 09f9c67..5131a8f 100644
--- a/compose/material3/material3/src/androidMain/res/values-my/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-my/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"ရက်စွဲများထည့်ပါ"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"ဒေတာအပိုင်းအခြား ထည့်သွင်းမှု မမှန်ပါ"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ဖိဆွဲအထိန်း"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"အောက်ခြေအပိုဆောင်း စာမျက်နှာကို ချုံ့သည်"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"အောက်ခြေအပိုဆောင်း စာမျက်နှာကို ပယ်သည်"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"အောက်ခြေအပိုဆောင်း စာမျက်နှာကို ချဲ့သည်"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"အကြံပြုချက်ပြ ပေါ့အပ် ဝင်းဒိုး"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"အကြံပြုချက်ပြ ပေါ့အပ်ဝင်းဒိုး ပြရန်"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d မိနစ်"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"မိနစ်"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"နာရီ"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"မိနစ်ကြာ"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"နာရီကြာ"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-nb/strings.xml b/compose/material3/material3/src/androidMain/res/values-nb/strings.xml
index 3ba360f..4274c3f 100644
--- a/compose/material3/material3/src/androidMain/res/values-nb/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-nb/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Legg inn datoer"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"En ugyldig dataperiode er skrevet inn"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Håndtak"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Skjul feltet nederst"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Lukk feltet nederst"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Vis feltet nederst"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Verktøytips"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Vis verktøytips"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutter"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minutt"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Time"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"for minutter"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"for timer"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ne/strings.xml b/compose/material3/material3/src/androidMain/res/values-ne/strings.xml
index 9abf195..f6180e9 100644
--- a/compose/material3/material3/src/androidMain/res/values-ne/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-ne/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"मितिहरू हाल्नुहोस्"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"मितिको अवैध दायरा तोकियो"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ड्र्याग ह्यान्डल"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"पुछारको पाना कोल्याप्स गर्नुहोस्"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"पुछारको पाना हटाउनुहोस्"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"पुछारको पाना एक्स्पान्ड गर्नुहोस्"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"टुलटिप"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"टुलटिप देखाइयोस्"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d मिनेट"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"मिनेट"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"घण्टा"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"मिनेटका लागि"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"घण्टाका लागि"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-nl/strings.xml b/compose/material3/material3/src/androidMain/res/values-nl/strings.xml
index 2ecf2a7..ca8d0ec 100644
--- a/compose/material3/material3/src/androidMain/res/values-nl/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-nl/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Datums opgeven"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Ongeldige invoer voor periode"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Handgreep voor slepen"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Blad onderaan samenvouwen"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Blad onderaan sluiten"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Blad onderaan uitvouwen"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Tooltip"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Tooltip tonen"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minuten"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minuut"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Uur"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"voor minuten"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"voor uur"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-or/strings.xml b/compose/material3/material3/src/androidMain/res/values-or/strings.xml
index fc09715..b8af623 100644
--- a/compose/material3/material3/src/androidMain/res/values-or/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-or/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"ତାରିଖଗୁଡ଼ିକ ଲେଖନ୍ତୁ"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"ଅବୈଧ ତାରିଖ ରେଞ୍ଜ ଇନପୁଟ"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ଡ୍ରାଗ ହେଣ୍ଡେଲ"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"ବଟମ ସିଟକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"ବଟମ ସିଟକୁ ଖାରଜ କରନ୍ତୁ"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"ବଟମ ସିଟକୁ ବିସ୍ତାର କରନ୍ତୁ"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"ଟୁଲଟିପ"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"ଟୁଲଟିପ ଦେଖାନ୍ତୁ"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d ମିନିଟ"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"ମିନିଟ"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"ଘଣ୍ଟା"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"ମିନିଟ ପାଇଁ"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ଘଣ୍ଟା ପାଇଁ"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-pa/strings.xml b/compose/material3/material3/src/androidMain/res/values-pa/strings.xml
index f69dd51..30a8002 100644
--- a/compose/material3/material3/src/androidMain/res/values-pa/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-pa/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"ਤਾਰੀਖਾਂ ਦਾਖਲ ਕਰੋ"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"ਇਨਪੁੱਟ ਕੀਤੀ ਗਈ ਤਾਰੀਖ ਦੀ ਰੇਂਜ ਅਵੈਧ ਹੈ"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ਘਸੀਟਣ ਵਾਲਾ ਹੈਂਡਲ"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"ਹੇਠਲੀ ਸ਼ੀਟ ਨੂੰ ਸਮੇਟੋ"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"ਹੇਠਲੀ ਸ਼ੀਟ ਨੂੰ ਖਾਰਜ ਕਰੋ"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"ਹੇਠਲੀ ਸ਼ੀਟ ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"ਟੂਲ-ਟਿੱਪ"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"ਟੂਲ-ਟਿੱਪ ਦਿਖਾਓ"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d ਮਿੰਟ"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"ਮਿੰਟ"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"ਘੰਟੇ"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"ਮਿੰਟਾਂ ਲਈ"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ਘੰਟੇ ਲਈ"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-pl/strings.xml b/compose/material3/material3/src/androidMain/res/values-pl/strings.xml
index 2ecf3ea..e2e9c8c 100644
--- a/compose/material3/material3/src/androidMain/res/values-pl/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-pl/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Wprowadź daty"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Nieprawidłowy zakres dat"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Uchwyt do przeciągania"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Zwiń planszę dolną"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Zamknij planszę dolną"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Rozwiń planszę dolną"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Etykietka"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Pokaż etykietkę"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minut"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minuta"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Godzina"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"na minuty"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"na godzinę"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-pt-rBR/strings.xml b/compose/material3/material3/src/androidMain/res/values-pt-rBR/strings.xml
index 1ebeb8e..d54b942 100644
--- a/compose/material3/material3/src/androidMain/res/values-pt-rBR/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-pt-rBR/strings.xml
@@ -70,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutos"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minuto"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Hora"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"por minutos"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"por hora"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-pt-rPT/strings.xml b/compose/material3/material3/src/androidMain/res/values-pt-rPT/strings.xml
index ce146b4..ab06fd9 100644
--- a/compose/material3/material3/src/androidMain/res/values-pt-rPT/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-pt-rPT/strings.xml
@@ -70,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutos"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minuto"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Hora"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"para minutos"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"para hora"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-pt/strings.xml b/compose/material3/material3/src/androidMain/res/values-pt/strings.xml
index 1ebeb8e..d54b942 100644
--- a/compose/material3/material3/src/androidMain/res/values-pt/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-pt/strings.xml
@@ -70,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minutos"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minuto"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Hora"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"por minutos"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"por hora"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ro/strings.xml b/compose/material3/material3/src/androidMain/res/values-ro/strings.xml
index 1bbe278..1280988 100644
--- a/compose/material3/material3/src/androidMain/res/values-ro/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-ro/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Introdu datele"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Intervalul de date introdus nu este valid"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Ghidaj de tragere"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Restrânge foaia din partea de jos"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Închide foaia din partea de jos"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Extinde foaia din partea de jos"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Balon explicativ"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Afișează balonul explicativ"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"p.m."</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minute"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minut"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Oră"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"timp de câteva minute"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"timp de o oră"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ru/strings.xml b/compose/material3/material3/src/androidMain/res/values-ru/strings.xml
index 64e4e01..8e3b21c 100644
--- a/compose/material3/material3/src/androidMain/res/values-ru/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-ru/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Введите даты"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Указан недопустимый диапазон дат."</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Маркер перемещения"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Свернуть нижний экран"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Закрыть нижний экран"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Развернуть нижний экран"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Подсказка"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Показать подсказку"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d мин."</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Минуты"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Часы"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"минуты"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"часы"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-si/strings.xml b/compose/material3/material3/src/androidMain/res/values-si/strings.xml
index b0b144f..e77dc29 100644
--- a/compose/material3/material3/src/androidMain/res/values-si/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-si/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"දින ඇතුළු කරන්න"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"අවලංගු දින පරාස ආදානය"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"ඇදීම් හැඬලය"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"පහළම පත්‍රය හකුළන්න"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"පහළම පත්‍රය අස් කරන්න"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"පහළම පත්‍රය දිග හරින්න"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"මෙවලම් ඉඟිය"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"මෙවලම් ඉඟිය පෙන්වන්න"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"ප.ව."</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"මිනිත්තු %1$d"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"විනාඩි"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"පැය"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"මිනිත්තු ගණනක් සඳහා"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"පැයක් සඳහා"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-sk/strings.xml b/compose/material3/material3/src/androidMain/res/values-sk/strings.xml
index a9adb7e..baee341 100644
--- a/compose/material3/material3/src/androidMain/res/values-sk/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-sk/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Zadajte dátumy"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Neplatný vstup obdobia"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Presúvadlo"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Zbaliť dolný hárok"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Zavrieť dolný hárok"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Rozbaliť dolný hárok"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Popis"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Zobraziť opis"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d min"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minúty"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Hodina"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"minúty"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"hodina"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-sl/strings.xml b/compose/material3/material3/src/androidMain/res/values-sl/strings.xml
index 3bbf720..b01d1ca 100644
--- a/compose/material3/material3/src/androidMain/res/values-sl/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-sl/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Vnesite datume"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Neveljaven vnos obdobja."</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Ročica za vlečenje"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Strnitev razdelka na dnu zaslona"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Opustitev razdelka na dnu zaslona"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Razširitev razdelka na dnu zaslona"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Opis orodja"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Pokaži opis orodja"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"pop."</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d min"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minute"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Ura"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"za minute"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"za uro"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-sq/strings.xml b/compose/material3/material3/src/androidMain/res/values-sq/strings.xml
index 5c58dff..f0161e9 100644
--- a/compose/material3/material3/src/androidMain/res/values-sq/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-sq/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Fut datat"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Hyrje e pavlefshme e diapazonit të datave"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Doreza e zvarritjes"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Palos fletën e poshtme"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Hiq fletën e poshtme"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Zgjero fletën e poshtme"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Këshilla për veglën"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Shfaq këshillat për veglën"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"MD"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minuta"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minuta"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Ora"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"për minuta"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"për orë"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-sr/strings.xml b/compose/material3/material3/src/androidMain/res/values-sr/strings.xml
index c253b45..2b0fe74 100644
--- a/compose/material3/material3/src/androidMain/res/values-sr/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-sr/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Унесите датуме"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Унос опсега датума је неважећи"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Идентификатор за превлачење"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Скупи доњу табелу"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Одбаци доњу табелу"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Прошири доњу табелу"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Објашњење"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Прикажи објашњење"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"по"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d мин"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Минут"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Сат"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"за минуте"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"за сате"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-sv/strings.xml b/compose/material3/material3/src/androidMain/res/values-sv/strings.xml
index 8613182..212db21 100644
--- a/compose/material3/material3/src/androidMain/res/values-sv/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-sv/strings.xml
@@ -54,14 +54,10 @@
     <string name="date_range_picker_day_in_range" msgid="9048690781645835833">"Inom intervall"</string>
     <string name="date_range_input_title" msgid="2366412111888449406">"Ange datum"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Ett ogiltigt datumintervall har angetts"</string>
-    <!-- no translation found for bottom_sheet_drag_handle_description (7772321844937772780) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Handtag"</string>
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Komprimera arket på nedre delen av skärmen"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Stäng arket på nedre delen av skärmen"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Utöka arket på nedre delen av skärmen"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Beskrivning"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Visa beskrivning"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"EM"</string>
@@ -74,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d minuter"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minut"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Timme"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"för minuter"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"för timmar"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-sw/strings.xml b/compose/material3/material3/src/androidMain/res/values-sw/strings.xml
index e38417d..6aa23aa 100644
--- a/compose/material3/material3/src/androidMain/res/values-sw/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-sw/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Weka tarehe"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Kipindi kilichowekwa si sahihi"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Aikoni ya buruta"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Kunja safu ya chini"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Ondoa safu ya chini"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Panua safu ya chini"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Kidirisha cha vidokezo"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Onyesha kidirisha cha vidokezo"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"Mchana"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"Dakika %1$d"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Dakika"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Saa"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"kwa dakika"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"kwa saa moja"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ta/strings.xml b/compose/material3/material3/src/androidMain/res/values-ta/strings.xml
index 0580cfe..59cac2f 100644
--- a/compose/material3/material3/src/androidMain/res/values-ta/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-ta/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"தேதிகளை உள்ளிடுங்கள்"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"தவறான தேதி வரம்பை உள்ளிட்டுள்ளீர்கள்"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"இழுப்பதற்கான ஹேண்டில்"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"கீழ்ப்புறச் சீட்டைச் சுருக்கும்"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"கீழ்ப்புறச் சீட்டை நிராகரிக்கும்"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"கீழ்ப்புறச் சீட்டை விரிவாக்கும்"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"உதவிக்குறிப்பு"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"உதவிக்குறிப்பைக் காட்டு"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d நிமிடங்கள்"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"நிமிடம்"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"மணிநேரம்"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"நிமிடங்களுக்கு"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"மணிநேரத்திற்கு"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-te/strings.xml b/compose/material3/material3/src/androidMain/res/values-te/strings.xml
index 9cfd074..aab4e0f 100644
--- a/compose/material3/material3/src/androidMain/res/values-te/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-te/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"తేదీలను ఎంటర్ చేయండి"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"తేదీల పరిధి ఇన్‌పుట్ చెల్లదు"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"లాగే హ్యాండిల్"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"దిగువున ఉన్న షీట్‌ను కుదిస్తుంది"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"దిగువున ఉన్న షీట్‌ను విస్మరిస్తుంది"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"దిగువున ఉన్న షీట్‌ను విస్తరిస్తుంది"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"టూల్‌టిప్"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"టూల్‌టిప్‌ను చూపించు"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d నిమిషాలు"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"నిమిషం"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"గంట"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"నిమిషాలకు"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ఒక గంట పాటు"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-th/strings.xml b/compose/material3/material3/src/androidMain/res/values-th/strings.xml
index 1c1e122..5369807 100644
--- a/compose/material3/material3/src/androidMain/res/values-th/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-th/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"ป้อนวันที่"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"การป้อนข้อมูลช่วงวันที่ไม่ถูกต้อง"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"แฮนเดิลการลาก"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"ยุบ Bottom Sheet"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"ปิด Bottom Sheet"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"ขยาย Bottom Sheet"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"เคล็ดลับเครื่องมือ"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"แสดงเคล็ดลับเครื่องมือ"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d นาที"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"นาที"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"ชั่วโมง"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"สำหรับนาที"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"สำหรับชั่วโมง"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-tl/strings.xml b/compose/material3/material3/src/androidMain/res/values-tl/strings.xml
index 4ec31b1..cdbe629 100644
--- a/compose/material3/material3/src/androidMain/res/values-tl/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-tl/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Maglagay ng mga petsa"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Invalid ang input na hanay ng petsa"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Handle sa pag-drag"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"I-collapse ang bottom sheet"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"I-dismiss ang bottom sheet"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Palawakin ang bottom sheet"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Tooltip"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Ipakita ang tooltip"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d (na) minuto"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Minuto"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Oras"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"nang ilang minuto"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"nang ilang oras"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-tr/strings.xml b/compose/material3/material3/src/androidMain/res/values-tr/strings.xml
index 61bc807..795e820 100644
--- a/compose/material3/material3/src/androidMain/res/values-tr/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-tr/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Tarihleri girin"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Geçersiz tarih aralığı girişi"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Sürükleme tutamacı"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Alt sayfayı daralt"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Alt sayfayı kapat"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Alt sayfayı genişlet"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"İpucu"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Araç ipucunu göster"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"ÖS"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d dakika"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Dakika"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Saat"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"dakika"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"saat"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-uk/strings.xml b/compose/material3/material3/src/androidMain/res/values-uk/strings.xml
index 433711fa..46376ba 100644
--- a/compose/material3/material3/src/androidMain/res/values-uk/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-uk/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Введіть дати"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Указано недійсний діапазон дат"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Маркер переміщення"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Згорнути нижній екран"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Закрити нижній екран"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Розгорнути нижній екран"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Спливаюча підказка"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Показати підказку"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"ПП"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d хв"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Хвилина"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Година"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"для хвилин"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"для годин"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-ur/strings.xml b/compose/material3/material3/src/androidMain/res/values-ur/strings.xml
index 045d61d..b2bbeaf 100644
--- a/compose/material3/material3/src/androidMain/res/values-ur/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-ur/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"تواریخ درج کریں"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"تاریخ کی حد کا غلط ان پٹ"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"گھسیٹنے کا ہینڈل"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"نیچے کی شیٹ کو سکیڑیں"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"نیچے کی شیٹ کو برخاست کریں"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"نیچے کی شیٹ کو پھیلائیں"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"ٹول ٹپ"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"ٹول ٹپ دکھائیں"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"بعد از دوپہر"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"‏%1$d منٹس"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"منٹ"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"گھنٹہ"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"منٹ کے لیے"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"گھنٹے کے لیے"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-uz/strings.xml b/compose/material3/material3/src/androidMain/res/values-uz/strings.xml
index 2ffa0a5..489ed71 100644
--- a/compose/material3/material3/src/androidMain/res/values-uz/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-uz/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Sanalarni kiriting"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Kiritilgan muddat yaroqsiz"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Surish dastagi"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Quyi ekranni yigʻish"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Quyi ekranni yopish"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Quyi ekranni yoyish"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Maslahat oynasi"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Maslahat oynasini koʻrsatish"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"TK"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d daqiqa"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Daqiqa"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Soat"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"bir daqiqa"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"bir soat"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-vi/strings.xml b/compose/material3/material3/src/androidMain/res/values-vi/strings.xml
index 2f65eb9..59d238e0 100644
--- a/compose/material3/material3/src/androidMain/res/values-vi/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-vi/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Nhập ngày"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Phạm vi ngày đã nhập không hợp lệ"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Nút kéo"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Thu gọn bảng dưới cùng"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Bỏ qua bảng dưới cùng"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Mở rộng bảng dưới cùng"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Chú giải công cụ"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Hiển thị chú giải công cụ"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"CH"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d phút"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Phút"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Giờ"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"về số phút"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"về số giờ"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-zh-rCN/strings.xml b/compose/material3/material3/src/androidMain/res/values-zh-rCN/strings.xml
index 30ebb15..f2e1a9b 100644
--- a/compose/material3/material3/src/androidMain/res/values-zh-rCN/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-zh-rCN/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"输入日期"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"输入的日期范围无效"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"拖动手柄"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"收起底部动作条"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"关闭底部动作条"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"展开底部动作条"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"提示"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"显示提示"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"下午"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d 分钟"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"分"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"时"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"表示分钟"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"表示小时"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-zh-rHK/strings.xml b/compose/material3/material3/src/androidMain/res/values-zh-rHK/strings.xml
index e906fe1c..939c4ca 100644
--- a/compose/material3/material3/src/androidMain/res/values-zh-rHK/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-zh-rHK/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"輸入日期"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"輸入的日期範圍無效"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"拖曳控點"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"收合底部功能表"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"關閉底部功能表"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"展開底部功能表"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"提示"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"顯示提示"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"下午"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d 分鐘"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"分鐘"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"小時"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"分鐘"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"小時"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-zh-rTW/strings.xml b/compose/material3/material3/src/androidMain/res/values-zh-rTW/strings.xml
index c7fb463..b17c4cf 100644
--- a/compose/material3/material3/src/androidMain/res/values-zh-rTW/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-zh-rTW/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"輸入日期"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"輸入的日期範圍無效"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"拖曳控點"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"收合底部功能表"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"關閉底部功能表"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"展開底部功能表"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"工具提示"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"顯示工具提示"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"下午"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"%1$d 分鐘"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"分鐘"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"小時"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"分鐘"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"小時"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values-zu/strings.xml b/compose/material3/material3/src/androidMain/res/values-zu/strings.xml
index c3c5857..d1f05ad 100644
--- a/compose/material3/material3/src/androidMain/res/values-zu/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-zu/strings.xml
@@ -55,12 +55,9 @@
     <string name="date_range_input_title" msgid="2366412111888449406">"Faka izinsuku"</string>
     <string name="date_range_input_invalid_range_input" msgid="1891592555781755601">"Okokufaka kwebanga losuku okungavumelekile"</string>
     <string name="bottom_sheet_drag_handle_description" msgid="7772321844937772780">"Hudula isibambi"</string>
-    <!-- no translation found for bottom_sheet_collapse_description (6128938260108474660) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_dismiss_description (1918297411568599192) -->
-    <skip />
-    <!-- no translation found for bottom_sheet_expand_description (4324434199045499117) -->
-    <skip />
+    <string name="bottom_sheet_collapse_description" msgid="6128938260108474660">"Goqa ishidi eliphansi"</string>
+    <string name="bottom_sheet_dismiss_description" msgid="1918297411568599192">"Chitha ishidi eliphansi"</string>
+    <string name="bottom_sheet_expand_description" msgid="4324434199045499117">"Nweba ishidi eliphansi"</string>
     <string name="tooltip_pane_description" msgid="8191239805703103845">"Ithulithiphu"</string>
     <string name="tooltip_long_press_label" msgid="2732804537909054941">"Bonisa ithulithiphu"</string>
     <string name="time_picker_pm" msgid="2232702812657998674">"PM"</string>
@@ -73,4 +70,6 @@
     <string name="time_picker_minute_suffix" msgid="3206486707779478173">"Imizuzu engu-%1$d"</string>
     <string name="time_picker_minute" msgid="6116528647594005945">"Umzuzu"</string>
     <string name="time_picker_hour" msgid="7241191970823415723">"Ihora"</string>
+    <string name="time_picker_minute_text_field" msgid="994099543833979061">"ngemizuzu"</string>
+    <string name="time_picker_hour_text_field" msgid="5298761125390275834">"ngehora"</string>
 </resources>
diff --git a/compose/material3/material3/src/androidMain/res/values/strings.xml b/compose/material3/material3/src/androidMain/res/values/strings.xml
index ac0c807..dbd7ceb 100644
--- a/compose/material3/material3/src/androidMain/res/values/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values/strings.xml
@@ -103,4 +103,8 @@
     <string name="time_picker_minute">Minute</string>
     <!-- Label for a textField that allows the user to the hour to set the time -->
     <string name="time_picker_hour">Hour</string>
+    <!-- A label for a textfield that allows the user to input minutes. It reads: Edit Box, for minutes -->
+    <string name="time_picker_minute_text_field">for minutes</string>
+    <!-- A label for a textfield that allows the user to input hours. It reads: Edit Box, for hour -->
+    <string name="time_picker_hour_text_field">for hour</string>
 </resources>
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AlertDialog.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AlertDialog.kt
index 5bfdc87..21cd441 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AlertDialog.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AlertDialog.kt
@@ -193,12 +193,11 @@
                     placeables[j].width +
                         if (j < placeables.lastIndex) mainAxisSpacing.roundToPx() else 0
                 }
-                val arrangement = Arrangement.Bottom
-                // TODO(soboleva): rtl support
-                // Handle vertical direction
+                val arrangement = Arrangement.End
                 val mainAxisPositions = IntArray(childrenMainAxisSizes.size) { 0 }
                 with(arrangement) {
-                    arrange(mainAxisLayoutSize, childrenMainAxisSizes, mainAxisPositions)
+                    arrange(mainAxisLayoutSize, childrenMainAxisSizes,
+                        layoutDirection, mainAxisPositions)
                 }
                 placeables.forEachIndexed { j, placeable ->
                     placeable.place(
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 da9bf4d..ad35d3ed 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
@@ -39,6 +39,7 @@
 import androidx.compose.ui.layout.SubcomposeLayout
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.semantics.collapse
+import androidx.compose.ui.semantics.dismiss
 import androidx.compose.ui.semantics.expand
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.Dp
@@ -74,6 +75,7 @@
  * children. Defaults to the matching content color for [sheetContainerColor], or if that is
  * not a color from the theme, this will keep the same content color set above the bottom sheet.
  * @param sheetTonalElevation the tonal elevation of the bottom sheet
+ * @param sheetShadowElevation the shadow elevation of the bottom sheet
  * @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
@@ -101,6 +103,7 @@
     sheetContainerColor: Color = BottomSheetDefaults.ContainerColor,
     sheetContentColor: Color = contentColorFor(sheetContainerColor),
     sheetTonalElevation: Dp = BottomSheetDefaults.Elevation,
+    sheetShadowElevation: Dp = BottomSheetDefaults.Elevation,
     sheetDragHandle: @Composable (() -> Unit)? = { BottomSheetDefaults.DragHandle() },
     sheetSwipeEnabled: Boolean = true,
     topBar: @Composable (() -> Unit)? = null,
@@ -129,6 +132,7 @@
                     containerColor = sheetContainerColor,
                     contentColor = sheetContentColor,
                     tonalElevation = sheetTonalElevation,
+                    shadowElevation = sheetShadowElevation,
                     dragHandle = sheetDragHandle,
                     content = sheetContent
                 )
@@ -175,15 +179,17 @@
  * Create and [remember] a [SheetState] for [BottomSheetScaffold].
  *
  * @param initialValue the initial value of the state. Should be either [PartiallyExpanded] or
- * [Expanded]
+ * [Expanded] if [skipHiddenState] is true
  * @param confirmValueChange optional callback invoked to confirm or veto a pending state change
+ * @param [skipHiddenState] whether Hidden state is skipped for [BottomSheetScaffold]
  */
 @Composable
 @ExperimentalMaterial3Api
 fun rememberStandardBottomSheetState(
     initialValue: SheetValue = PartiallyExpanded,
     confirmValueChange: (SheetValue) -> Boolean = { true },
-) = rememberSheetState(false, confirmValueChange, initialValue)
+    skipHiddenState: Boolean = true,
+) = rememberSheetState(false, confirmValueChange, initialValue, skipHiddenState)
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
@@ -196,6 +202,7 @@
     containerColor: Color,
     contentColor: Color,
     tonalElevation: Dp,
+    shadowElevation: Dp,
     dragHandle: @Composable (() -> Unit)?,
     content: @Composable ColumnScope.() -> Unit
 ) {
@@ -244,24 +251,27 @@
                 anchorChangeHandler = anchorChangeHandler
             ) { value, sheetSize ->
                 when (value) {
-                    PartiallyExpanded -> layoutHeight - peekHeightPx
+                    PartiallyExpanded -> if (state.skipPartiallyExpanded)
+                        null else layoutHeight - peekHeightPx
                     Expanded -> if (sheetSize.height == peekHeightPx.roundToInt()) {
                         null
                     } else {
                         max(0f, layoutHeight - sheetSize.height)
                     }
-
-                    Hidden -> null
+                    Hidden -> if (state.skipHiddenState) null else layoutHeight
                 }
             },
         shape = shape,
         color = containerColor,
         contentColor = contentColor,
-        tonalElevation = tonalElevation
+        tonalElevation = tonalElevation,
+        shadowElevation = shadowElevation,
     ) {
         Column(Modifier.fillMaxWidth()) {
             if (dragHandle != null) {
-                val collapseActionLabel = getString(Strings.BottomSheetCollapseDescription)
+                val partialExpandActionLabel =
+                    getString(Strings.BottomSheetPartialExpandDescription)
+                val dismissActionLabel = getString(Strings.BottomSheetDismissDescription)
                 val expandActionLabel = getString(Strings.BottomSheetExpandDescription)
                 Box(Modifier
                     .align(CenterHorizontally)
@@ -278,11 +288,17 @@
                                     }
                                 } else {
                                     if (swipeableState.confirmValueChange(PartiallyExpanded)) {
-                                        collapse(collapseActionLabel) {
+                                        collapse(partialExpandActionLabel) {
                                             scope.launch { partialExpand() }; true
                                         }
                                     }
                                 }
+                                if (!state.skipHiddenState) {
+                                    dismiss(dismissActionLabel) {
+                                        scope.launch { hide() }
+                                        true
+                                    }
+                                }
                             }
                         }
                     },
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 47e1162..f3e5f68 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
@@ -914,8 +914,9 @@
      * Sets a start and end selection dates.
      *
      * The function expects the dates to be within the state's year-range, and for the start date to
-     * appear before the end date. Also, if an end date is provided (e.g. not `null`), a start date
-     * is also expected to be provided. In any other case, an [IllegalArgumentException] is thrown.
+     * appear before, or be equal, the end date. Also, if an end date is provided (e.g. not `null`),
+     * a start date is also expected to be provided. In any other case, an
+     * [IllegalArgumentException] is thrown.
      *
      * @param startDateMillis timestamp in _UTC_ milliseconds from the epoch that represents the
      * start date selection. Provide a `null` to indicate no selection.
@@ -951,8 +952,8 @@
             requireNotNull(startDate) {
                 "An end date was provided without a start date."
             }
-            // Validate that the end date appears after the start date.
-            require(startDate.utcTimeMillis < endDate.utcTimeMillis) {
+            // Validate that the end date appears on or after the start date.
+            require(startDate.utcTimeMillis <= endDate.utcTimeMillis) {
                 "The provided end date appears before the start date."
             }
         }
@@ -1042,7 +1043,11 @@
                     verticalAlignment = Alignment.CenterVertically
                 ) {
                     if (headline != null) {
-                        ProvideTextStyle(value = headlineTextStyle, content = headline)
+                        ProvideTextStyle(value = headlineTextStyle) {
+                            Box(modifier = Modifier.weight(1f)) {
+                                headline()
+                            }
+                        }
                     }
                     modeToggleButton?.invoke()
                 }
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 2962286..f2e199c 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
@@ -603,7 +603,7 @@
             // Reset the selection to "start" only.
             selectedStartDate.value = date
             selectedEndDate.value = null
-        } else if (currentStart != null && date > currentStart) {
+        } else if (currentStart != null && date >= currentStart) {
             selectedEndDate.value = date
         }
     }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/InternalMutatorMutex.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/InternalMutatorMutex.kt
new file mode 100644
index 0000000..e336c3e
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/InternalMutatorMutex.kt
@@ -0,0 +1,170 @@
+/*
+ * 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.foundation.MutatePriority
+import androidx.compose.runtime.Stable
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+
+/*** This is an internal copy of androidx.compose.foundation.MutatorMutex with an additional
+ * tryMutate method. Do not modify, except for tryMutate. ***/
+
+expect class InternalAtomicReference<V>(value: V) {
+    fun get(): V
+    fun set(value: V)
+    fun getAndSet(value: V): V
+    fun compareAndSet(expect: V, newValue: V): Boolean
+}
+
+/**
+ * Mutual exclusion for UI state mutation over time.
+ *
+ * [mutate] permits interruptible state mutation over time using a standard [MutatePriority].
+ * A [InternalMutatorMutex] enforces that only a single writer can be active at a time for a particular
+ * state resource. Instead of queueing callers that would acquire the lock like a traditional
+ * [Mutex], new attempts to [mutate] the guarded state will either cancel the current mutator or
+ * if the current mutator has a higher priority, the new caller will throw [CancellationException].
+ *
+ * [InternalMutatorMutex] should be used for implementing hoisted state objects that many mutators may
+ * want to manipulate over time such that those mutators can coordinate with one another. The
+ * [InternalMutatorMutex] instance should be hidden as an implementation detail. For example:
+ *
+ */
+@Stable
+internal class InternalMutatorMutex {
+    private class Mutator(val priority: MutatePriority, val job: Job) {
+        fun canInterrupt(other: Mutator) = priority >= other.priority
+
+        fun cancel() = job.cancel()
+    }
+
+    private val currentMutator = InternalAtomicReference<Mutator?>(null)
+    private val mutex = Mutex()
+
+    private fun tryMutateOrCancel(mutator: Mutator) {
+        while (true) {
+            val oldMutator = currentMutator.get()
+            if (oldMutator == null || mutator.canInterrupt(oldMutator)) {
+                if (currentMutator.compareAndSet(oldMutator, mutator)) {
+                    oldMutator?.cancel()
+                    break
+                }
+            } else throw CancellationException("Current mutation had a higher priority")
+        }
+    }
+
+    /**
+     * Enforce that only a single caller may be active at a time.
+     *
+     * If [mutate] is called while another call to [mutate] or [mutateWith] is in progress, their
+     * [priority] values are compared. If the new caller has a [priority] equal to or higher than
+     * the call in progress, the call in progress will be cancelled, throwing
+     * [CancellationException] and the new caller's [block] will be invoked. If the call in
+     * progress had a higher [priority] than the new caller, the new caller will throw
+     * [CancellationException] without invoking [block].
+     *
+     * @param priority the priority of this mutation; [MutatePriority.Default] by default.
+     * Higher priority mutations will interrupt lower priority mutations.
+     * @param block mutation code to run mutually exclusive with any other call to [mutate],
+     * [mutateWith] or [tryMutate].
+     */
+    suspend fun <R> mutate(
+        priority: MutatePriority = MutatePriority.Default,
+        block: suspend () -> R
+    ) = coroutineScope {
+        val mutator = Mutator(priority, coroutineContext[Job]!!)
+
+        tryMutateOrCancel(mutator)
+
+        mutex.withLock {
+            try {
+                block()
+            } finally {
+                currentMutator.compareAndSet(mutator, null)
+            }
+        }
+    }
+
+    /**
+     * Enforce that only a single caller may be active at a time.
+     *
+     * If [mutateWith] is called while another call to [mutate] or [mutateWith] is in progress,
+     * their [priority] values are compared. If the new caller has a [priority] equal to or
+     * higher than the call in progress, the call in progress will be cancelled, throwing
+     * [CancellationException] and the new caller's [block] will be invoked. If the call in
+     * progress had a higher [priority] than the new caller, the new caller will throw
+     * [CancellationException] without invoking [block].
+     *
+     * This variant of [mutate] calls its [block] with a [receiver], removing the need to create
+     * an additional capturing lambda to invoke it with a receiver object. This can be used to
+     * expose a mutable scope to the provided [block] while leaving the rest of the state object
+     * read-only. For example:
+     *
+     * @param receiver the receiver `this` that [block] will be called with
+     * @param priority the priority of this mutation; [MutatePriority.Default] by default.
+     * Higher priority mutations will interrupt lower priority mutations.
+     * @param block mutation code to run mutually exclusive with any other call to [mutate],
+     * [mutateWith] or [tryMutate].
+     */
+    suspend fun <T, R> mutateWith(
+        receiver: T,
+        priority: MutatePriority = MutatePriority.Default,
+        block: suspend T.() -> R
+    ) = coroutineScope {
+        val mutator = Mutator(priority, coroutineContext[Job]!!)
+
+        tryMutateOrCancel(mutator)
+
+        mutex.withLock {
+            try {
+                receiver.block()
+            } finally {
+                currentMutator.compareAndSet(mutator, null)
+            }
+        }
+    }
+
+    /**
+     * Attempt to mutate synchronously if there is no other active caller.
+     * If there is no other active caller, the [block] will be executed in a lock. If there is
+     * another active caller, this method will return false, indicating that the active caller
+     * needs to be cancelled through a [mutate] or [mutateWith] call with an equal or higher
+     * mutation priority.
+     *
+     * Calls to [mutate] and [mutateWith] will suspend until execution of the [block] has finished.
+     *
+     * @param block mutation code to run mutually exclusive with any other call to [mutate],
+     * [mutateWith] or [tryMutate].
+     * @return true if the [block] was executed, false if there was another active caller and the
+     * [block] was not executed.
+     */
+    fun tryMutate(block: () -> Unit): Boolean {
+        val didLock = mutex.tryLock()
+        if (didLock) {
+            try {
+                block()
+            } finally {
+                mutex.unlock()
+            }
+        }
+        return didLock
+    }
+}
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 31776bd..dec70d5 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
@@ -183,7 +183,8 @@
             ) {
                 Column(Modifier.fillMaxWidth()) {
                     if (dragHandle != null) {
-                        val collapseActionLabel = getString(Strings.BottomSheetCollapseDescription)
+                        val collapseActionLabel =
+                            getString(Strings.BottomSheetPartialExpandDescription)
                         val dismissActionLabel = getString(Strings.BottomSheetDismissDescription)
                         val expandActionLabel = getString(Strings.BottomSheetExpandDescription)
                         Box(Modifier
@@ -285,7 +286,7 @@
     bottomPadding: Float,
     onDragStopped: CoroutineScope.(velocity: Float) -> Unit,
 ) = draggable(
-            state = sheetState.swipeableState.draggableState,
+            state = sheetState.swipeableState.swipeDraggableState,
             orientation = Orientation.Vertical,
             enabled = sheetState.isVisible,
             startDragImmediately = sheetState.swipeableState.isAnimationRunning,
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 f66216c..c0fcd04 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
@@ -152,6 +152,11 @@
     return copy(topStart = CornerSize(0.0.dp), topEnd = CornerSize(0.0.dp))
 }
 
+/** Helper function for component shape tokens. Used to grab the start values of a shape parameter. */
+internal fun CornerBasedShape.start(): CornerBasedShape {
+    return copy(topEnd = CornerSize(0.0.dp), bottomEnd = CornerSize(0.0.dp))
+}
+
 /** Helper function for component shape tokens. Used to grab the end values of a shape parameter. */
 internal fun CornerBasedShape.end(): CornerBasedShape {
     return copy(topStart = CornerSize(0.0.dp), bottomStart = CornerSize(0.0.dp))
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 fca009d..71dd117 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
@@ -47,24 +47,34 @@
  *
  * Contains states relating to it's swipe position as well as animations between state values.
  *
- * @param skipPartiallyExpanded Whether the partially expanded state state, if the sheet is large
+ * @param skipPartiallyExpanded Whether the partially expanded state, if the sheet is large
  * enough, should be skipped. If true, the sheet will always expand to the [Expanded] state and move
- * to the [Hidden] state when hiding the sheet, either programmatically or by user interaction.
+ * to the [Hidden] state if available when hiding the sheet, either programmatically or by user
+ * interaction.
  * @param initialValue The initial value of the state.
  * @param confirmValueChange Optional callback invoked to confirm or veto a pending state change.
+ * @param skipHiddenState Whether the hidden state should be skipped. If true, the sheet will always
+ * expand to the [Expanded] state and move to the [PartiallyExpanded] if available, either
+ * programmatically or by user interaction.
  */
 @Stable
 @ExperimentalMaterial3Api
 class SheetState(
     internal val skipPartiallyExpanded: Boolean,
     initialValue: SheetValue = Hidden,
-    confirmValueChange: (SheetValue) -> Boolean = { true }
+    confirmValueChange: (SheetValue) -> Boolean = { true },
+    internal val skipHiddenState: Boolean = false,
 ) {
     init {
         if (skipPartiallyExpanded) {
             require(initialValue != PartiallyExpanded) {
-                "The initial value must not be set to HalfExpanded if skipHalfExpanded is set to" +
-                    " true."
+                "The initial value must not be set to PartiallyExpanded if skipPartiallyExpanded " +
+                    "is set to true."
+            }
+        }
+        if (skipHiddenState) {
+            require(initialValue != Hidden) {
+                "The initial value must not be set to Hidden if skipHiddenState is set to true."
             }
         }
     }
@@ -97,10 +107,21 @@
     /**
      * Require the current offset (in pixels) of the bottom sheet.
      *
+     * The offset will be initialized during the first measurement phase of the provided sheet
+     * content.
+     *
+     * These are the phases:
+     * Composition { -> Effects } -> Layout { Measurement -> Placement } -> Drawing
+     *
+     * During the first composition, an [IllegalStateException] is thrown. In subsequent
+     * compositions, the offset will be derived from the anchors of the previous pass. Always prefer
+     * accessing the offset from a LaunchedEffect as it will be scheduled to be executed the next
+     * frame, after layout.
+     *
      * @throws IllegalStateException If the offset has not been initialized yet
      */
-
     fun requireOffset(): Float = swipeableState.requireOffset()
+
     /**
      * Whether the sheet has an expanded state defined.
      */
@@ -131,13 +152,11 @@
      * @throws [IllegalStateException] if [skipPartiallyExpanded] is set to true
      */
     suspend fun partialExpand() {
-        if (skipPartiallyExpanded) {
-            check(skipPartiallyExpanded) {
-                "Attempted to animate to partial expanded when skipPartiallyExpanded was enabled." +
-                    " Set skipPartiallyExpanded to false to use this function."
-            }
+        check(!skipPartiallyExpanded) {
+            "Attempted to animate to partial expanded when skipPartiallyExpanded was enabled. Set" +
+                " skipPartiallyExpanded to false to use this function."
         }
-        swipeableState.animateTo(PartiallyExpanded)
+        animateTo(PartiallyExpanded)
     }
 
     /**
@@ -150,7 +169,7 @@
             hasPartiallyExpandedState -> PartiallyExpanded
             else -> Expanded
         }
-        swipeableState.animateTo(targetValue)
+        animateTo(targetValue)
     }
 
     /**
@@ -159,6 +178,10 @@
      * @throws [CancellationException] if the animation is interrupted
      */
     suspend fun hide() {
+        check(!skipHiddenState) {
+            "Attempted to animate to hidden when skipHiddenState was enabled. Set skipHiddenState" +
+                " to false to use this function."
+        }
         animateTo(Hidden)
     }
 
@@ -250,7 +273,7 @@
 @ExperimentalMaterial3Api
 object BottomSheetDefaults {
     /** The default shape for bottom sheets in a [Hidden] state. */
-    val MinimizedShape: Shape
+    val HiddenShape: Shape
         @Composable get() =
         SheetBottomTokens.DockedMinimizedContainerShape.toShape()
 
@@ -338,7 +361,7 @@
     override suspend fun onPreFling(available: Velocity): Velocity {
         val toFling = available.toFloat()
         val currentOffset = sheetState.requireOffset()
-        return if (toFling < 0 && currentOffset > sheetState.swipeableState.minBound) {
+        return if (toFling < 0 && currentOffset > sheetState.swipeableState.minOffset) {
             onFling(toFling)
             // since we go to the anchor with tween settling, consume all for the best UX
             available
@@ -369,7 +392,8 @@
 internal fun rememberSheetState(
     skipPartiallyExpanded: Boolean = false,
     confirmValueChange: (SheetValue) -> Boolean = { true },
-    initialValue: SheetValue
+    initialValue: SheetValue = Hidden,
+    skipHiddenState: Boolean = false,
 ): SheetState {
     return rememberSaveable(
         skipPartiallyExpanded, confirmValueChange,
@@ -378,7 +402,7 @@
             confirmValueChange = confirmValueChange
         )
     ) {
-        SheetState(skipPartiallyExpanded, initialValue, confirmValueChange)
+        SheetState(skipPartiallyExpanded, initialValue, confirmValueChange, skipHiddenState)
     }
 }
 
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 a2b17cf..1547cab 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
@@ -88,6 +88,7 @@
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.offset
 import androidx.compose.ui.util.lerp
 import kotlin.math.abs
 import kotlin.math.floor
@@ -588,15 +589,12 @@
             it.layoutId == SliderComponents.THUMB
         }.measure(constraints)
 
-        val maxTrackWidth = constraints.maxWidth - thumbPlaceable.width
         val trackPlaceable = measurables.first {
             it.layoutId == SliderComponents.TRACK
         }.measure(
-            constraints.copy(
-                minWidth = 0,
-                maxWidth = maxTrackWidth,
-                minHeight = 0
-            )
+            constraints.offset(
+                horizontal = - thumbPlaceable.width
+            ).copy(minHeight = 0)
         )
 
         val sliderWidth = thumbPlaceable.width + trackPlaceable.width
@@ -792,16 +790,12 @@
             constraints
         )
 
-        val maxTrackWidth =
-            constraints.maxWidth - (startThumbPlaceable.width + endThumbPlaceable.width) / 2
         val trackPlaceable = measurables.first {
             it.layoutId == RangeSliderComponents.TRACK
         }.measure(
-            constraints.copy(
-                minWidth = 0,
-                maxWidth = maxTrackWidth,
-                minHeight = 0
-            )
+            constraints.offset(
+                horizontal = - (startThumbPlaceable.width + endThumbPlaceable.width) / 2
+            ).copy(minHeight = 0)
         )
 
         val sliderWidth = trackPlaceable.width +
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Strings.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Strings.kt
index 54a96ae..dd8f671 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Strings.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Strings.kt
@@ -74,7 +74,7 @@
         val DateRangeInputTitle = Strings()
         val DateRangeInputInvalidRangeInput = Strings()
         val BottomSheetDragHandleDescription = Strings()
-        val BottomSheetCollapseDescription = Strings()
+        val BottomSheetPartialExpandDescription = Strings()
         val BottomSheetDismissDescription = Strings()
         val BottomSheetExpandDescription = Strings()
         val TooltipLongPressLabel = Strings()
@@ -88,6 +88,8 @@
         val TimePickerMinuteSuffix = Strings()
         val TimePickerHour = Strings()
         val TimePickerMinute = Strings()
+        val TimePickerHourTextField = Strings()
+        val TimePickerMinuteTextField = Strings()
         val TooltipPaneDescription = Strings()
     }
 }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeableV2.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeableV2.kt
index d57af0c..77bdf92 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeableV2.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeableV2.kt
@@ -21,6 +21,8 @@
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.SpringSpec
 import androidx.compose.animation.core.animate
+import androidx.compose.foundation.MutatePriority
+import androidx.compose.foundation.gestures.DragScope
 import androidx.compose.foundation.gestures.DraggableState
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.draggable
@@ -50,6 +52,7 @@
 import androidx.compose.ui.unit.dp
 import kotlin.math.abs
 import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
 
 /**
@@ -79,7 +82,7 @@
     reverseDirection: Boolean = false,
     interactionSource: MutableInteractionSource? = null
 ) = draggable(
-    state = state.draggableState,
+    state = state.swipeDraggableState,
     orientation = orientation,
     enabled = enabled,
     interactionSource = interactionSource,
@@ -122,7 +125,11 @@
             val previousTarget = state.targetValue
             val stateRequiresCleanup = state.updateAnchors(newAnchors)
             if (stateRequiresCleanup) {
-                anchorChangeHandler?.onAnchorsChanged(previousTarget, previousAnchors, newAnchors)
+                anchorChangeHandler?.onAnchorsChanged(
+                    previousTarget,
+                    previousAnchors,
+                    newAnchors
+                )
             }
         }
     },
@@ -165,6 +172,27 @@
     internal val velocityThreshold: Dp = SwipeableV2Defaults.VelocityThreshold,
 ) {
 
+    private val swipeMutex = InternalMutatorMutex()
+
+    internal val swipeDraggableState = object : DraggableState {
+        private val dragScope = object : DragScope {
+            override fun dragBy(pixels: Float) {
+                this@SwipeableV2State.dispatchRawDelta(pixels)
+            }
+        }
+
+        override suspend fun drag(
+            dragPriority: MutatePriority,
+            block: suspend DragScope.() -> Unit
+        ) {
+            swipe(dragPriority) { dragScope.block() }
+        }
+
+        override fun dispatchRawDelta(delta: Float) {
+            this@SwipeableV2State.dispatchRawDelta(delta)
+        }
+    }
+
     /**
      * The current value of the [SwipeableV2State].
      */
@@ -242,13 +270,19 @@
     var lastVelocity: Float by mutableStateOf(0f)
         private set
 
-    val minBound by derivedStateOf { anchors.minOrNull() ?: Float.NEGATIVE_INFINITY }
-    val maxBound by derivedStateOf { anchors.maxOrNull() ?: Float.POSITIVE_INFINITY }
+    /**
+     * The minimum offset this state can reach. This will be the smallest anchor, or
+     * [Float.NEGATIVE_INFINITY] if the anchors are not initialized yet.
+     */
+    val minOffset by derivedStateOf { anchors.minOrNull() ?: Float.NEGATIVE_INFINITY }
+
+    /**
+     * The maximum offset this state can reach. This will be the biggest anchor, or
+     * [Float.POSITIVE_INFINITY] if the anchors are not initialized yet.
+     */
+    val maxOffset by derivedStateOf { anchors.maxOrNull() ?: Float.POSITIVE_INFINITY }
 
     private var animationTarget: T? by mutableStateOf(null)
-    internal val draggableState = DraggableState {
-        offset = ((offset ?: 0f) + it).coerceIn(minBound, maxBound)
-    }
 
     internal var anchors by mutableStateOf(emptyMap<T, Float>())
 
@@ -267,9 +301,10 @@
         val previousAnchorsEmpty = anchors.isEmpty()
         anchors = newAnchors
         val initialValueHasAnchor = if (previousAnchorsEmpty) {
-            val initialValueAnchor = anchors[currentValue]
+            val initialValue = currentValue
+            val initialValueAnchor = anchors[initialValue]
             val initialValueHasAnchor = initialValueAnchor != null
-            if (initialValueHasAnchor) offset = initialValueAnchor
+            if (initialValueHasAnchor) trySnapTo(initialValue)
             initialValueHasAnchor
         } else true
         return !initialValueHasAnchor || !previousAnchorsEmpty
@@ -282,6 +317,8 @@
 
     /**
      * Snap to a [targetValue] without any animation.
+     * If the [targetValue] is not in the set of anchors, the [currentValue] will be updated to the
+     * [targetValue] without updating the offset.
      *
      * @throws CancellationException if the interaction interrupted by another interaction like a
      * gesture interaction or another programmatic interaction like a [animateTo] or [snapTo] call.
@@ -289,16 +326,7 @@
      * @param targetValue The target value of the animation
      */
     suspend fun snapTo(targetValue: T) {
-        val targetOffset = anchors.requireAnchor(targetValue)
-        try {
-            draggableState.drag {
-                animationTarget = targetValue
-                dragBy(targetOffset - requireOffset())
-            }
-            this.currentValue = targetValue
-        } finally {
-            animationTarget = null
-        }
+        swipe { snap(targetValue) }
     }
 
     /**
@@ -319,7 +347,7 @@
         val targetOffset = anchors[targetValue]
         if (targetOffset != null) {
             try {
-                draggableState.drag {
+                swipe {
                     animationTarget = targetValue
                     var prev = offset ?: 0f
                     animate(prev, targetOffset, velocity, animationSpec) { value, velocity ->
@@ -366,17 +394,17 @@
     }
 
     /**
-     * Swipe by the [delta], coerce it in the bounds and dispatch it to the [draggableState].
+     * Swipe by the [delta], coerce it in the bounds and dispatch it to the [SwipeableV2State].
      *
-     * @return The delta the [draggableState] will consume
+     * @return The delta the consumed by the [SwipeableV2State]
      */
     fun dispatchRawDelta(delta: Float): Float {
         val currentDragPosition = offset ?: 0f
         val potentiallyConsumed = currentDragPosition + delta
-        val clamped = potentiallyConsumed.coerceIn(minBound, maxBound)
+        val clamped = potentiallyConsumed.coerceIn(minOffset, maxOffset)
         val deltaToConsume = clamped - currentDragPosition
-        if (abs(deltaToConsume) > 0) {
-            draggableState.dispatchRawDelta(deltaToConsume)
+        if (abs(deltaToConsume) >= 0) {
+            offset = ((offset ?: 0f) + deltaToConsume).coerceIn(minOffset, maxOffset)
         }
         return deltaToConsume
     }
@@ -428,6 +456,31 @@
             "this=$this SwipeableState?"
     }
 
+    private suspend fun swipe(
+        swipePriority: MutatePriority = MutatePriority.Default,
+        action: suspend () -> Unit
+    ): Unit = coroutineScope { swipeMutex.mutate(swipePriority, action) }
+
+    /**
+     * Attempt to snap synchronously. Snapping can happen synchronously when there is no other swipe
+     * transaction like a drag or an animation is progress. If there is another interaction in
+     * progress, the suspending [snapTo] overload needs to be used.
+     *
+     * @return true if the synchronous snap was successful, or false if we couldn't snap synchronous
+     */
+    internal fun trySnapTo(targetValue: T): Boolean = swipeMutex.tryMutate { snap(targetValue) }
+
+    private fun snap(targetValue: T) {
+        val targetOffset = anchors[targetValue]
+        if (targetOffset != null) {
+            dispatchRawDelta(targetOffset - (offset ?: 0f))
+            currentValue = targetValue
+            animationTarget = null
+        } else {
+            currentValue = targetValue
+        }
+    }
+
     companion object {
         /**
          * The default [Saver] implementation for [SwipeableV2State].
@@ -636,6 +689,3 @@
 
 private fun <T> Map<T, Float>.minOrNull() = minOfOrNull { (_, offset) -> offset }
 private fun <T> Map<T, Float>.maxOrNull() = maxOfOrNull { (_, offset) -> offset }
-private fun <T> Map<T, Float>.requireAnchor(value: T) = requireNotNull(this[value]) {
-    "Required anchor $value was not found in anchors. Current anchors: ${this.toMap()}"
-}
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 043b2aa..6480116 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
@@ -129,9 +129,11 @@
     containerColor: Color = TabRowDefaults.containerColor,
     contentColor: Color = TabRowDefaults.contentColor,
     indicator: @Composable (tabPositions: List<TabPosition>) -> Unit = @Composable { tabPositions ->
-        TabRowDefaults.Indicator(
-            Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex])
-        )
+        if (selectedTabIndex < tabPositions.size) {
+            TabRowDefaults.Indicator(
+                Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex])
+            )
+        }
     },
     divider: @Composable () -> Unit = @Composable {
         Divider()
@@ -147,7 +149,10 @@
             val tabRowWidth = constraints.maxWidth
             val tabMeasurables = subcompose(TabSlots.Tabs, tabs)
             val tabCount = tabMeasurables.size
-            val tabWidth = (tabRowWidth / tabCount)
+            var tabWidth = 0
+            if (tabCount > 0) {
+                tabWidth = (tabRowWidth / tabCount)
+            }
             val tabRowHeight = tabMeasurables.fold(initial = 0) { max, curr ->
                 maxOf(curr.maxIntrinsicHeight(tabWidth), max)
             }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimeFormat.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimeFormat.kt
index 01f635a..d1941c51 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimeFormat.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimeFormat.kt
@@ -21,4 +21,4 @@
 
 internal expect val is24HourFormat: Boolean
   @Composable
-  @ReadOnlyComposable get
+  @ReadOnlyComposable get
\ No newline at end of file
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 665d9fe..d2c7319 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
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+@file:OptIn(ExperimentalMaterial3Api::class)
 
 package androidx.compose.material3
 
@@ -31,16 +32,16 @@
 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.PaddingValues
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.selection.selectableGroup
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.foundation.shape.CornerBasedShape
@@ -66,6 +67,8 @@
 import androidx.compose.material3.tokens.TimePickerTokens.ClockDialUnselectedLabelTextColor
 import androidx.compose.material3.tokens.TimePickerTokens.ContainerColor
 import androidx.compose.material3.tokens.TimePickerTokens.PeriodSelectorContainerShape
+import androidx.compose.material3.tokens.TimePickerTokens.PeriodSelectorHorizontalContainerHeight
+import androidx.compose.material3.tokens.TimePickerTokens.PeriodSelectorHorizontalContainerWidth
 import androidx.compose.material3.tokens.TimePickerTokens.PeriodSelectorOutlineColor
 import androidx.compose.material3.tokens.TimePickerTokens.PeriodSelectorSelectedContainerColor
 import androidx.compose.material3.tokens.TimePickerTokens.PeriodSelectorSelectedLabelTextColor
@@ -114,16 +117,22 @@
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.layout.LayoutModifier
 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.boundsInParent
 import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.layout.onSizeChanged
 import androidx.compose.ui.platform.InspectorInfo
 import androidx.compose.ui.platform.InspectorValueInfo
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.clearAndSetSemantics
 import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.isContainer
+import androidx.compose.ui.semantics.onClick
 import androidx.compose.ui.semantics.role
 import androidx.compose.ui.semantics.selectableGroup
 import androidx.compose.ui.semantics.selected
@@ -140,6 +149,7 @@
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.center
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.zIndex
 import java.text.NumberFormat
 import kotlin.math.PI
 import kotlin.math.abs
@@ -171,6 +181,8 @@
  * @param modifier the [Modifier] to be applied to this time input
  * @param colors colors [TimePickerColors] that will be used to resolve the colors used for this
  * time picker in different states. See [TimePickerDefaults.colors].
+ * @param layoutType, the different [TimePickerLayoutType] supported by this time picker,
+ * it will change the position and sizing of different components of the timepicker.
  */
 @Composable
 @ExperimentalMaterial3Api
@@ -178,12 +190,20 @@
     state: TimePickerState,
     modifier: Modifier = Modifier,
     colors: TimePickerColors = TimePickerDefaults.colors(),
+    layoutType: TimePickerLayoutType = TimePickerDefaults.layoutType(),
 ) {
-    Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) {
-        ClockDisplay(state, colors)
-        Spacer(modifier = Modifier.height(ClockDisplayBottomMargin))
-        ClockFace(state, colors)
-        Spacer(modifier = Modifier.height(ClockFaceBottomMargin))
+    if (layoutType == TimePickerLayoutType.Vertical) {
+        VerticalTimePicker(
+            state = state,
+            modifier = modifier,
+            colors = colors,
+        )
+    } else {
+        HorizontalTimePicker(
+            state = state,
+            modifier = modifier,
+            colors = colors
+        )
     }
 }
 
@@ -209,103 +229,7 @@
     modifier: Modifier = Modifier,
     colors: TimePickerColors = TimePickerDefaults.colors(),
 ) {
-    var hourValue by rememberSaveable(stateSaver = TextFieldValue.Saver) {
-        mutableStateOf(TextFieldValue(text = state.hourForDisplay.toLocalString(2)))
-    }
-    var minuteValue by rememberSaveable(stateSaver = TextFieldValue.Saver) {
-        mutableStateOf(TextFieldValue(text = state.minute.toLocalString(2)))
-    }
-
-    Row(
-        modifier = modifier.padding(bottom = TimeInputBottomPadding),
-        verticalAlignment = Alignment.Top
-    ) {
-        val textStyle = MaterialTheme.typography.fromToken(TimeInputTokens.TimeFieldLabelTextFont)
-            .copy(
-                textAlign = TextAlign.Center,
-                color = colors.timeSelectorContentColor(true)
-            )
-
-        CompositionLocalProvider(LocalTextStyle provides textStyle) {
-            TimePickerTextField(
-                modifier = Modifier
-                    .onKeyEvent { event ->
-                        // Zero == 48, Nine == 57
-                        val switchFocus = event.utf16CodePoint in 48..57 &&
-                            hourValue.selection.start == 2 && hourValue.text.length == 2
-
-                        if (switchFocus) {
-                            state.selection = Selection.Minute
-                        }
-
-                        false
-                    },
-                value = hourValue,
-                onValueChange = { newValue ->
-                    timeInputOnChange(
-                        selection = Selection.Hour,
-                        state = state,
-                        value = newValue,
-                        prevValue = hourValue,
-                        max = if (state.is24hour) 23 else 12,
-                    ) { hourValue = it }
-                },
-                state = state,
-                selection = Selection.Hour,
-                keyboardOptions = KeyboardOptions(
-                    imeAction = ImeAction.Next,
-                    keyboardType = KeyboardType.Number
-                ),
-                keyboardActions = KeyboardActions(onNext = { state.selection = Selection.Minute }),
-                colors = colors,
-            )
-            DisplaySeparator(Modifier.size(DisplaySeparatorWidth, PeriodSelectorContainerHeight))
-            TimePickerTextField(
-                modifier = Modifier
-                    .onPreviewKeyEvent { event ->
-                        // 0 == KEYCODE_DEL
-                        val switchFocus = event.utf16CodePoint == 0 &&
-                            minuteValue.selection.start == 0
-
-                        if (switchFocus) {
-                            state.selection = Selection.Hour
-                        }
-
-                        switchFocus
-                    },
-
-                value = minuteValue,
-                onValueChange = { newValue ->
-                    timeInputOnChange(
-                        selection = Selection.Minute,
-                        state = state,
-                        value = newValue,
-                        prevValue = minuteValue,
-                        max = 59,
-                    ) { minuteValue = it }
-                },
-                state = state,
-                selection = Selection.Minute,
-                keyboardOptions = KeyboardOptions(
-                    imeAction = ImeAction.Done,
-                    keyboardType = KeyboardType.Number
-                ),
-                keyboardActions = KeyboardActions(onNext = { state.selection = Selection.Minute }),
-                colors = colors,
-            )
-        }
-
-        if (!state.is24hour) {
-            PeriodToggle(
-                modifier = Modifier.size(
-                    PeriodSelectorContainerWidth,
-                    PeriodSelectorContainerHeight
-                ),
-                state = state,
-                colors = colors
-            )
-        }
-    }
+    TimeInputImpl(modifier, colors, state)
 }
 
 /**
@@ -382,6 +306,11 @@
         timeSelectorSelectedContentColor = timeSelectorSelectedContentColor,
         timeSelectorUnselectedContentColor = timeSelectorUnselectedContentColor
     )
+
+    /** Default layout type, uses the screen dimensions to choose an appropriate layout. */
+    @ReadOnlyComposable
+    @Composable
+    fun layoutType(): TimePickerLayoutType = defaultTimePickerLayoutType
 }
 
 /**
@@ -518,6 +447,29 @@
 }
 
 /**
+ * Represents the different configurations for the layout of the Time Picker
+ */
+@Immutable
+@JvmInline
+@ExperimentalMaterial3Api
+value class TimePickerLayoutType internal constructor(internal val value: Int) {
+
+    companion object {
+        /** Displays the Time picker with a horizontal layout. Should be used in landscape mode. */
+        val Horizontal = TimePickerLayoutType(0)
+
+        /** Displays the Time picker with a vertical layout. Should be used in portrait mode.*/
+        val Vertical = TimePickerLayoutType(1)
+    }
+
+    override fun toString() = when (this) {
+        Horizontal -> "Horizontal"
+        Vertical -> "Vertical"
+        else -> "Unknown"
+    }
+}
+
+/**
  * A class to handle state changes in a [TimePicker]
  *
  * @sample androidx.compose.material3.samples.TimePickerSample
@@ -563,9 +515,8 @@
         DpOffset(offsetX, offsetY)
     }
 
-    internal val values by derivedStateOf {
-        if (selection == Selection.Minute) Minutes else Hours
-    }
+    internal var center by mutableStateOf(IntOffset.Zero)
+    internal val values get() = if (selection == Selection.Minute) Minutes else Hours
 
     internal var selection by mutableStateOf(Selection.Hour)
     internal var isAfternoonToggle by mutableStateOf(initialHour > 12 && !is24Hour)
@@ -588,6 +539,12 @@
         hourAngle = RadiansPerHour * hour % 12 - FullCircle / 4
     }
 
+    internal fun moveSelector(x: Float, y: Float, maxDist: Float) {
+        if (selection == Selection.Hour && is24hour) {
+            isInnerCircle = dist(x, y, center.x, center.y) < maxDist
+        }
+    }
+
     internal fun isSelected(value: Int): Boolean =
         if (selection == Selection.Minute) {
             value == minute
@@ -655,6 +612,17 @@
         currentAngle.animateTo(targetValue.second, tween(200))
     }
 
+    internal suspend fun onTap(x: Float, y: Float, maxDist: Float) {
+        update(atan(y - center.y, x - center.x), true)
+        moveSelector(x, y, maxDist)
+
+        if (selection == Selection.Hour) {
+            selection = Selection.Minute
+        } else {
+            settle()
+        }
+    }
+
     companion object {
         /**
          * The default [Saver] implementation for [TimePickerState].
@@ -678,13 +646,191 @@
     }
 }
 
-@OptIn(ExperimentalMaterial3Api::class)
 @Composable
-private fun ClockDisplay(state: TimePickerState, colors: TimePickerColors) {
+@ExperimentalMaterial3Api
+internal fun VerticalTimePicker(
+    state: TimePickerState,
+    modifier: Modifier = Modifier,
+    colors: TimePickerColors = TimePickerDefaults.colors(),
+) {
+    Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) {
+        VerticalClockDisplay(state, colors)
+        Spacer(modifier = Modifier.height(ClockDisplayBottomMargin))
+        ClockFace(state, colors)
+        Spacer(modifier = Modifier.height(ClockFaceBottomMargin))
+    }
+}
+
+@Composable
+internal fun HorizontalTimePicker(
+    state: TimePickerState,
+    modifier: Modifier = Modifier,
+    colors: TimePickerColors = TimePickerDefaults.colors(),
+) {
+    Row(
+        modifier = modifier.padding(bottom = ClockFaceBottomMargin),
+        verticalAlignment = Alignment.CenterVertically
+    ) {
+        HorizontalClockDisplay(state, colors)
+        Spacer(modifier = Modifier.width(ClockDisplayBottomMargin))
+        ClockFace(state, colors)
+    }
+}
+
+@Composable
+private fun TimeInputImpl(
+    modifier: Modifier,
+    colors: TimePickerColors,
+    state: TimePickerState,
+) {
+    var hourValue by rememberSaveable(stateSaver = TextFieldValue.Saver) {
+        mutableStateOf(TextFieldValue(text = state.hourForDisplay.toLocalString(2)))
+    }
+    var minuteValue by rememberSaveable(stateSaver = TextFieldValue.Saver) {
+        mutableStateOf(TextFieldValue(text = state.minute.toLocalString(2)))
+    }
+
+    Row(
+        modifier = modifier.padding(bottom = TimeInputBottomPadding),
+        verticalAlignment = Alignment.Top
+    ) {
+        val textStyle = MaterialTheme.typography.fromToken(TimeInputTokens.TimeFieldLabelTextFont)
+            .copy(
+                textAlign = TextAlign.Center,
+                color = colors.timeSelectorContentColor(true)
+            )
+
+        CompositionLocalProvider(LocalTextStyle provides textStyle) {
+            TimePickerTextField(
+                modifier = Modifier
+                    .onKeyEvent { event ->
+                        // Zero == 48, Nine == 57
+                        val switchFocus = event.utf16CodePoint in 48..57 &&
+                            hourValue.selection.start == 2 && hourValue.text.length == 2
+
+                        if (switchFocus) {
+                            state.selection = Selection.Minute
+                        }
+
+                        false
+                    },
+                value = hourValue,
+                onValueChange = { newValue ->
+                    timeInputOnChange(
+                        selection = Selection.Hour,
+                        state = state,
+                        value = newValue,
+                        prevValue = hourValue,
+                        max = if (state.is24hour) 23 else 12,
+                    ) { hourValue = it }
+                },
+                state = state,
+                selection = Selection.Hour,
+                keyboardOptions = KeyboardOptions(
+                    imeAction = ImeAction.Next,
+                    keyboardType = KeyboardType.Number
+                ),
+                keyboardActions = KeyboardActions(onNext = { state.selection = Selection.Minute }),
+                colors = colors,
+            )
+            DisplaySeparator(Modifier.size(DisplaySeparatorWidth, PeriodSelectorContainerHeight))
+            TimePickerTextField(
+                modifier = Modifier
+                    .onPreviewKeyEvent { event ->
+                        // 0 == KEYCODE_DEL
+                        val switchFocus = event.utf16CodePoint == 0 &&
+                            minuteValue.selection.start == 0
+
+                        if (switchFocus) {
+                            state.selection = Selection.Hour
+                        }
+
+                        switchFocus
+                    },
+
+                value = minuteValue,
+                onValueChange = { newValue ->
+                    timeInputOnChange(
+                        selection = Selection.Minute,
+                        state = state,
+                        value = newValue,
+                        prevValue = minuteValue,
+                        max = 59,
+                    ) { minuteValue = it }
+                },
+                state = state,
+                selection = Selection.Minute,
+                keyboardOptions = KeyboardOptions(
+                    imeAction = ImeAction.Done,
+                    keyboardType = KeyboardType.Number
+                ),
+                keyboardActions = KeyboardActions(onNext = { state.selection = Selection.Minute }),
+                colors = colors,
+            )
+        }
+
+        if (!state.is24hour) {
+            Box(modifier.padding(start = PeriodToggleMargin)) {
+                VerticalPeriodToggle(
+                    modifier = Modifier.size(
+                        PeriodSelectorContainerWidth,
+                        PeriodSelectorContainerHeight
+                    ),
+                    state = state,
+                    colors = colors,
+                )
+            }
+        }
+    }
+}
+
+@Composable
+private fun HorizontalClockDisplay(state: TimePickerState, colors: TimePickerColors) {
+    Column(verticalArrangement = Arrangement.Center) {
+        ClockDisplayNumbers(state, colors)
+        if (!state.is24hour) {
+            Box(modifier = Modifier.padding(top = PeriodToggleMargin)) {
+                HorizontalPeriodToggle(
+                    modifier = Modifier.size(
+                        PeriodSelectorHorizontalContainerWidth,
+                        PeriodSelectorHorizontalContainerHeight
+                    ),
+                    state = state,
+                    colors = colors,
+                )
+            }
+        }
+    }
+}
+
+@Composable
+private fun VerticalClockDisplay(state: TimePickerState, colors: TimePickerColors) {
     Row(horizontalArrangement = Arrangement.Center) {
-        CompositionLocalProvider(
-            LocalTextStyle provides MaterialTheme.typography.fromToken(TimeSelectorLabelTextFont)
-        ) {
+        ClockDisplayNumbers(state, colors)
+        if (!state.is24hour) {
+            Box(modifier = Modifier.padding(start = PeriodToggleMargin)) {
+                VerticalPeriodToggle(
+                    modifier = Modifier.size(
+                        PeriodSelectorVerticalContainerWidth,
+                        PeriodSelectorVerticalContainerHeight
+                    ),
+                    state = state,
+                    colors = colors,
+                )
+            }
+        }
+    }
+}
+
+@Composable
+private fun ClockDisplayNumbers(
+    state: TimePickerState,
+    colors: TimePickerColors
+) {
+    CompositionLocalProvider(
+        LocalTextStyle provides MaterialTheme.typography.fromToken(TimeSelectorLabelTextFont)
+    ) {
+        Row {
             TimeSelector(
                 modifier = Modifier.size(
                     TimeSelectorContainerWidth,
@@ -711,26 +857,104 @@
                 selection = Selection.Minute,
                 colors = colors,
             )
-            if (!state.is24hour) {
-                PeriodToggle(
-                    Modifier.size(
-                        PeriodSelectorVerticalContainerWidth,
-                        PeriodSelectorVerticalContainerHeight
-                    ),
-                    state,
-                    colors
-                )
-            }
         }
     }
 }
 
-@OptIn(ExperimentalMaterial3Api::class)
 @Composable
-private fun PeriodToggle(
+private fun HorizontalPeriodToggle(
     modifier: Modifier,
     state: TimePickerState,
-    colors: TimePickerColors
+    colors: TimePickerColors,
+) {
+    val measurePolicy = remember {
+        MeasurePolicy { measurables, constraints ->
+            val spacer = measurables.first { it.layoutId == "Spacer" }
+            val spacerPlaceable = spacer.measure(
+                constraints.copy(
+                    minWidth = 0,
+                    maxWidth = TimePickerTokens.PeriodSelectorOutlineWidth.toPx().roundToInt(),
+                )
+            )
+
+            val items = measurables.filter { it.layoutId != "Spacer" }.map { item ->
+                item.measure(constraints.copy(
+                    minWidth = 0,
+                    maxWidth = constraints.maxWidth / 2
+                ))
+            }
+
+            layout(constraints.maxWidth, constraints.maxHeight) {
+                items[0].place(0, 0)
+                items[1].place(items[0].width, 0)
+                spacerPlaceable.place(items[0].width - spacerPlaceable.width / 2, 0)
+            }
+        }
+    }
+
+    val shape = PeriodSelectorContainerShape.toShape() as CornerBasedShape
+
+    PeriodToggleImpl(
+        modifier = modifier,
+        state = state,
+        colors = colors,
+        measurePolicy = measurePolicy,
+        startShape = shape.start(),
+        endShape = shape.end()
+    )
+}
+
+@Composable
+private fun VerticalPeriodToggle(
+    modifier: Modifier,
+    state: TimePickerState,
+    colors: TimePickerColors,
+) {
+    val measurePolicy = remember {
+        MeasurePolicy { measurables, constraints ->
+            val spacer = measurables.first { it.layoutId == "Spacer" }
+            val spacerPlaceable = spacer.measure(
+                constraints.copy(
+                    minHeight = 0,
+                    maxHeight = TimePickerTokens.PeriodSelectorOutlineWidth.toPx().roundToInt()
+                )
+            )
+
+            val items = measurables.filter { it.layoutId != "Spacer" }.map { item ->
+                item.measure(constraints.copy(
+                    minHeight = 0,
+                    maxHeight = constraints.maxHeight / 2
+                ))
+            }
+
+            layout(constraints.maxWidth, constraints.maxHeight) {
+                items[0].place(0, 0)
+                items[1].place(0, items[0].height)
+                spacerPlaceable.place(0, items[0].height - spacerPlaceable.height / 2)
+            }
+        }
+    }
+
+    val shape = PeriodSelectorContainerShape.toShape() as CornerBasedShape
+
+    PeriodToggleImpl(
+        modifier = modifier,
+        state = state,
+        colors = colors,
+        measurePolicy = measurePolicy,
+        startShape = shape.top(),
+        endShape = shape.bottom()
+    )
+}
+
+@Composable
+private fun PeriodToggleImpl(
+    modifier: Modifier,
+    state: TimePickerState,
+    colors: TimePickerColors,
+    measurePolicy: MeasurePolicy,
+    startShape: Shape,
+    endShape: Shape,
 ) {
     val borderStroke = BorderStroke(
         TimePickerTokens.PeriodSelectorOutlineWidth,
@@ -739,42 +963,47 @@
 
     val shape = PeriodSelectorContainerShape.toShape() as CornerBasedShape
     val contentDescription = getString(Strings.TimePickerPeriodToggle)
-    Column(Modifier
-        .padding(start = PeriodToggleMargin)
-        .semantics { this.contentDescription = contentDescription }
-        .selectableGroup()
-        .then(modifier)
-        .border(border = borderStroke, shape = shape)
-    ) {
-        ToggleItem(
-            checked = !state.isAfternoonToggle,
-            shape = shape.top(),
-            onClick = {
-                state.isAfternoonToggle = false
-            },
-            colors = colors,
-        ) { Text(text = getString(string = Strings.TimePickerAM)) }
-        Spacer(
-            Modifier
-                .fillMaxWidth()
-                .height(TimePickerTokens.PeriodSelectorOutlineWidth)
-                .background(color = PeriodSelectorOutlineColor.toColor())
-        )
-        ToggleItem(
-            checked =
-            state.isAfternoonToggle,
-            shape = shape.bottom(),
-            onClick = {
-                state.isAfternoonToggle = true
-            },
-            colors = colors,
-        ) { Text(getString(string = Strings.TimePickerPM)) }
-    }
+    Layout(
+        modifier = modifier
+            .semantics {
+                isContainer = true
+                this.contentDescription = contentDescription
+            }
+            .selectableGroup()
+            .then(modifier)
+            .border(border = borderStroke, shape = shape),
+        measurePolicy = measurePolicy,
+        content = {
+            ToggleItem(
+                checked = !state.isAfternoonToggle,
+                shape = startShape,
+                onClick = {
+                    state.isAfternoonToggle = false
+                },
+                colors = colors,
+            ) { Text(text = getString(string = Strings.TimePickerAM)) }
+            Spacer(
+                Modifier
+                    .layoutId("Spacer")
+                    .zIndex(SeparatorZIndex)
+                    .fillMaxSize()
+                    .background(color = PeriodSelectorOutlineColor.toColor())
+            )
+            ToggleItem(
+                checked =
+                state.isAfternoonToggle,
+                shape = endShape,
+                onClick = {
+                    state.isAfternoonToggle = true
+                },
+                colors = colors,
+            ) { Text(getString(string = Strings.TimePickerPM)) }
+        }
+    )
 }
 
-@OptIn(ExperimentalMaterial3Api::class)
 @Composable
-private fun ColumnScope.ToggleItem(
+private fun ToggleItem(
     checked: Boolean,
     shape: Shape,
     onClick: () -> Unit,
@@ -786,7 +1015,8 @@
 
     TextButton(
         modifier = Modifier
-            .weight(1f)
+            .zIndex(if (checked) 0f else 1f)
+            .fillMaxSize()
             .semantics { selected = checked },
         contentPadding = PaddingValues(0.dp),
         shape = shape,
@@ -811,7 +1041,7 @@
     )
 
     Box(
-        modifier = modifier,
+        modifier = modifier.clearAndSetSemantics { },
         contentAlignment = Alignment.Center
     ) {
         Text(
@@ -861,13 +1091,13 @@
         shape = TimeSelectorContainerShape.toShape(),
         color = containerColor,
     ) {
-        val valueContentDescription = getString(
+        val valueContentDescription =
             numberContentDescription(
                 selection = selection,
-                is24Hour = state.is24hour
-            ),
-            value
-        )
+                is24Hour = state.is24hour,
+                number = value
+            )
+
         Box(contentAlignment = Alignment.Center) {
             Text(
                 modifier = Modifier.semantics { contentDescription = valueContentDescription },
@@ -878,7 +1108,6 @@
     }
 }
 
-@OptIn(ExperimentalMaterial3Api::class)
 @Composable
 internal fun ClockFace(state: TimePickerState, colors: TimePickerColors) {
     Crossfade(
@@ -886,6 +1115,7 @@
             .background(shape = CircleShape, color = colors.clockDialColor)
             .size(ClockDialContainerSize)
             .semantics {
+                isContainer = false
                 selectableGroup()
             },
         targetState = state.values,
@@ -902,13 +1132,12 @@
                 LocalContentColor provides colors.clockDialContentColor(false)
             ) {
                 repeat(screen.size) {
-                    val outerValue = if (!state.is24hour) screen[it] else screen[it] % 12
-                    ClockText(
-                        is24Hour = state.is24hour,
-                        selection = state.selection,
-                        value = outerValue,
-                        selected = state.isSelected(it)
-                    )
+                    val outerValue = if (!state.is24hour || state.selection == Selection.Minute) {
+                        screen[it]
+                    } else {
+                        screen[it] % 12
+                    }
+                    ClockText(state = state, value = outerValue)
                 }
 
                 if (state.selection == Selection.Hour && state.is24hour) {
@@ -921,12 +1150,7 @@
                     ) {
                         repeat(ExtraHours.size) {
                             val innerValue = ExtraHours[it]
-                            ClockText(
-                                is24Hour = true,
-                                selection = state.selection,
-                                value = innerValue,
-                                selected = state.isSelected(it % 11)
-                            )
+                            ClockText(state = state, value = innerValue)
                         }
                     }
                 }
@@ -935,7 +1159,6 @@
     }
 }
 
-@OptIn(ExperimentalMaterial3Api::class)
 private fun Modifier.drawSelector(
     state: TimePickerState,
     colors: TimePickerColors,
@@ -1003,37 +1226,24 @@
 }) {
     var offsetX by remember { mutableStateOf(0f) }
     var offsetY by remember { mutableStateOf(0f) }
-    var center by remember { mutableStateOf(IntOffset.Zero) }
+    val center by remember { mutableStateOf(IntOffset.Zero) }
     val scope = rememberCoroutineScope()
     val maxDist = with(LocalDensity.current) { MaxDistance.toPx() }
-    fun moveSelector(x: Float, y: Float) {
-        if (state.selection == Selection.Hour && state.is24hour) {
-            state.isInnerCircle = dist(x, y, center.x, center.y) < maxDist
-        }
-    }
+
     Modifier
-        .onSizeChanged { center = it.center }
-        .pointerInput(state, maxDist, center) {
+        .onSizeChanged { state.center = it.center }
+        .pointerInput(state, center, maxDist) {
             detectTapGestures(
                 onPress = {
                     offsetX = it.x
                     offsetY = it.y
                 },
                 onTap = {
-                    scope.launch {
-                        state.update(atan(it.y - center.y, it.x - center.x), true)
-                        moveSelector(it.x, it.y)
-
-                        if (state.selection == Selection.Hour) {
-                            state.selection = Selection.Minute
-                        } else {
-                            state.settle()
-                        }
-                    }
+                    scope.launch { state.onTap(it.x, it.y, maxDist) }
                 },
             )
         }
-        .pointerInput(state, maxDist, center) {
+        .pointerInput(state, center, maxDist) {
             detectDragGestures(onDragEnd = {
                 scope.launch {
                     if (state.selection == Selection.Hour) {
@@ -1047,47 +1257,56 @@
                 scope.launch {
                     offsetX += dragAmount.x
                     offsetY += dragAmount.y
-                    state.update(atan(offsetY - center.y, offsetX - center.x))
+                    state.update(atan(offsetY - state.center.y, offsetX - state.center.x))
                 }
-                moveSelector(offsetX, offsetY)
+                state.moveSelector(offsetX, offsetY, maxDist)
             }
         }
 }
 
 @Composable
-private fun ClockText(
-    is24Hour: Boolean,
-    selected: Boolean,
-    selection: Selection,
-    value: Int
-) {
+private fun ClockText(state: TimePickerState, value: Int) {
     val style = MaterialTheme.typography.fromToken(ClockDialLabelTextFont).let {
-        remember(it) {
-            copyAndSetFontPadding(style = it, false)
-        }
+        copyAndSetFontPadding(style = it, false)
     }
 
-    val contentDescription = getString(
+    val maxDist = with(LocalDensity.current) { MaxDistance.toPx() }
+    var center by remember { mutableStateOf(Offset.Zero) }
+    val scope = rememberCoroutineScope()
+    val contentDescription =
         numberContentDescription(
-            selection = selection,
-            is24Hour = is24Hour
-        ),
-        value
-    )
+            selection = state.selection,
+            is24Hour = state.is24hour,
+            number = value
+        )
+
+    val text = value.toLocalString(minDigits = 1)
+    val selected = if (state.selection == Selection.Minute) {
+        state.minute.toLocalString(minDigits = 1) == text
+    } else {
+        state.hour.toLocalString(minDigits = 1) == text
+    }
 
     Box(
         contentAlignment = Alignment.Center,
         modifier = Modifier
             .minimumInteractiveComponentSize()
             .size(MinimumInteractiveSize)
+            .onGloballyPositioned { center = it.boundsInParent().center }
             .focusable()
-            .semantics {
+            .semantics(mergeDescendants = true) {
+                onClick {
+                    scope.launch { state.onTap(center.x, center.y, maxDist) }
+                    true
+                }
                 this.selected = selected
-                this.contentDescription = contentDescription
             }
     ) {
         Text(
-            text = value.toLocalString(minDigits = 1),
+            modifier = Modifier.clearAndSetSemantics {
+                this.contentDescription = contentDescription
+            },
+            text = text,
             style = style,
         )
     }
@@ -1174,13 +1393,24 @@
             )
         }
 
+        val contentDescription = getString(
+            if (selection == Selection.Minute) {
+                Strings.TimePickerMinuteTextField
+            } else {
+                Strings.TimePickerHourTextField
+            }
+        )
+
         Box(Modifier.visible(selected)) {
             BasicTextField(
                 value = value,
                 onValueChange = onValueChange,
                 modifier = Modifier
                     .focusRequester(focusRequester)
-                    .size(TimeFieldContainerWidth, TimeFieldContainerHeight),
+                    .size(TimeFieldContainerWidth, TimeFieldContainerHeight)
+                    .semantics {
+                        this.contentDescription = contentDescription
+                    },
                 interactionSource = interactionSource,
                 keyboardOptions = keyboardOptions,
                 keyboardActions = keyboardActions,
@@ -1219,7 +1449,9 @@
         }
 
         Text(
-            modifier = Modifier.offset(y = SupportLabelTop),
+            modifier = Modifier
+                .offset(y = SupportLabelTop)
+                .clearAndSetSemantics {},
             text = getString(
                 if (selection == Selection.Hour) {
                     Strings.TimePickerHour
@@ -1288,16 +1520,20 @@
 
 @Composable
 @ReadOnlyComposable
-private fun numberContentDescription(selection: Selection, is24Hour: Boolean): Strings {
-    if (selection == Selection.Minute) {
-        return Strings.TimePickerMinuteSuffix
+internal fun numberContentDescription(
+    selection: Selection,
+    is24Hour: Boolean,
+    number: Int
+): String {
+    val id = if (selection == Selection.Minute) {
+        Strings.TimePickerMinuteSuffix
+    } else if (is24Hour) {
+        Strings.TimePicker24HourSuffix
+    } else {
+        Strings.TimePickerHourSuffix
     }
 
-    if (is24Hour) {
-        return Strings.TimePicker24HourSuffix
-    }
-
-    return Strings.TimePickerHourSuffix
+    return getString(id, number)
 }
 
 private fun valuesForAnimation(current: Float, new: Float): Pair<Float, Float> {
@@ -1331,6 +1567,11 @@
     Selector, InnerCircle,
 }
 
+@OptIn(ExperimentalMaterial3Api::class)
+internal expect val defaultTimePickerLayoutType: TimePickerLayoutType
+    @Composable
+    @ReadOnlyComposable get
+
 @JvmInline
 internal value class Selection private constructor(val value: Int) {
     companion object {
@@ -1343,6 +1584,7 @@
 private const val QuarterCircle = PI / 2
 private const val RadiansPerMinute: Float = FullCircle / 60
 private const val RadiansPerHour: Float = FullCircle / 12f
+private const val SeparatorZIndex = 2f
 
 private val OuterCircleSizeRadius = 101.dp
 private val InnerCircleRadius = 69.dp
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/compose-material-3-documentation.md b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/compose-material-3-documentation.md
index e797721..5b59689 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/compose-material-3-documentation.md
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/compose-material-3-documentation.md
@@ -29,74 +29,86 @@
 
 ### Components
 
-|      | **APIs** | **Description** |
-| ---- | -------- | --------------- |
-| **Badge** | [Badge] | M3 badge |
-|  | [BadgedBox] | M3 badged box |
-| **Bottom app bar** | [BottomAppBar] | M3 bottom app bar |
-| **Buttons** | [Button] | M3 filled button |
-|  | [ElevatedButton] | M3 elevated button |
-|  | [FilledTonalButton] | M3 filled tonal button |
-|  | [OutlinedButton] | M3 outlined button |
-|  | [TextButton] | M3 text button |
-| **Cards** | [Card] | M3 filled card |
-|  | [ElevatedCard] | M3 elevated card |
-|  | [OutlinedCard] | M3 outlined card |
-| **Checkbox** | [Checkbox] | M3 checkbox |
-|  | [TriStateCheckbox] | M3 indeterminate checkbox |
-| **Chips** | [AssistChip] | M3 assist chip |
-|  | [ElevatedAssistChip] | M3 elevated assist chip |
-|  | [FilterChip] | M3 filter chip |
-|  | [ElevatedFilterChip] | M3 elevated filter chip |
-|  | [InputChip] | M3 input chip |
-|  | [SuggestionChip] | M3 suggestion chip |
-|  | [ElevatedSuggestionChip] | M3 elevated suggestion chip |
-| **Dialogs** | [AlertDialog] | M3 basic dialog |
-| **Dividers** | [Divider] | M3 divider |
-| **Extended FAB** | [ExtendedFloatingActionButton] | M3 extended FAB |
-| **FAB** | [FloatingActionButton] | M3 FAB |
-|  | [SmallFloatingActionButton] | M3 small FAB |
-|  | [LargeFloatingActionButton] | M3 large FAB |
-| **Icon button** | [IconButton] | M3 standard icon button |
-|  | [IconToggleButton] | M3 standard icon toggle button |
-|  | [FilledIconButton] | M3 filled icon button |
-|  | [FilledIconToggleButton] | M3 filled icon toggle button |
-|  | [FilledTonalIconButton] | M3 filled tonal icon button |
-|  | [FilledTonalIconToggleButton] | M3 filled tonal icon toggle button |
-|  | [OutlinedIconButton] | M3 outlined icon button |
-|  | [OutlinedIconToggleButton] | M3 outlined icon toggle button |
-| **Lists** | [ListItem] | M3 list item |
-| **Menus** | [DropdownMenu] | M3 menu |
-|  | [DropdownMenuItem] | M3 menu item |
-|  | [ExposedDropdownMenuBox] | M3 exposed dropdown menu |
-| **Navigation bar** | [NavigationBar] | M3 navigation bar |
-|  | [NavigationBarItem] | M3 navigation bar item |
-| **Navigation drawer** | [ModalNavigationDrawer] | M3 modal navigation drawer |
-|  | [ModalDrawerSheet] | M3 modal drawer sheet |
-|  | [PermanentNavigationDrawer] | M3 permanent standard navigation drawer |
-|  | [PermanentDrawerSheet] | M3 permanent standard drawer sheet |
-|  | [DismissibleNavigationDrawer] | M3 dismissible standard navigation drawer |
-|  | [DismissibleDrawerSheet] | M3 dismissible standard drawer sheet |
-|  | [NavigationDrawerItem] | M3 navigation drawer item |
-| **Navigation rail** | [NavigationRail] | M3 navigation rail |
-|  | [NavigationRailItem] | M3 navigation rail item |
-| **Progress indicators** | [LinearProgressIndicator] | M3 linear progress indicator |
-|  | [CircularProgressIndicator] | M3 circular progress indicator |
-| **Radio button** | [RadioButton] | M3 radio button |
-| **Sliders** | [Slider] | M3 slider |
-|  | [RangeSlider] | M3 range slider |
-| **Snackbars** | [Snackbar] | M3 snackbar |
-| **Switch** | [Switch] | M3 switch |
-| **Tabs** | [Tab] | M3 tab |
-|  | [LeadingIconTab] | M3 leading icon tab |
-|  | [TabRow] | M3 tab row |
-|  | [ScrollableTabRow] | M3 scrollable tab row |
-| **Text fields** | [TextField] | M3 filled text field |
-|  | [OutlinedTextField] | M3 outlined text field |
-| **Top app bar** | [TopAppBar] | M3 small top app bar |
-|  | [CenterAlignedTopAppBar] | M3 center-aligned top app bar |
-|  | [MediumTopAppBar] | M3 medium top app bar |
-|  | [LargeTopAppBar] | M3 large top app bar |
+|                         | **APIs**                       | **Description**                           |
+|-------------------------|--------------------------------|-------------------------------------------|
+| **Badge**               | [Badge]                        | M3 badge                                  |
+|                         | [BadgedBox]                    | M3 badged box                             |
+| **Bottom app bar**      | [BottomAppBar]                 | M3 bottom app bar                         |
+| **Bottom sheet**        | [BottomSheetScaffold]          | M3 bottom sheet                           |
+|                         | [ModalBottomSheet]             | M3 modal bottom sheet                     |
+| **Buttons**             | [Button]                       | M3 filled button                          |
+|                         | [ElevatedButton]               | M3 elevated button                        |
+|                         | [FilledTonalButton]            | M3 filled tonal button                    |
+|                         | [OutlinedButton]               | M3 outlined button                        |
+|                         | [TextButton]                   | M3 text button                            |
+| **Cards**               | [Card]                         | M3 filled card                            |
+|                         | [ElevatedCard]                 | M3 elevated card                          |
+|                         | [OutlinedCard]                 | M3 outlined card                          |
+| **Checkbox**            | [Checkbox]                     | M3 checkbox                               |
+|                         | [TriStateCheckbox]             | M3 indeterminate checkbox                 |
+| **Chips**               | [AssistChip]                   | M3 assist chip                            |
+|                         | [ElevatedAssistChip]           | M3 elevated assist chip                   |
+|                         | [FilterChip]                   | M3 filter chip                            |
+|                         | [ElevatedFilterChip]           | M3 elevated filter chip                   |
+|                         | [InputChip]                    | M3 input chip                             |
+|                         | [SuggestionChip]               | M3 suggestion chip                        |
+|                         | [ElevatedSuggestionChip]       | M3 elevated suggestion chip               |
+| **Date Picker**         | [DatePicker]                   | M3 date picker                            |
+|                         | [DatePickerDialog]             | M3 date picker embeeded in dialog         |
+|                         | [DateRangePicker]              | M3 date range picker                      |
+| **Dialogs**             | [AlertDialog]                  | M3 basic dialog                           |
+| **Dividers**            | [Divider]                      | M3 divider                                |
+| **Extended FAB**        | [ExtendedFloatingActionButton] | M3 extended FAB                           |
+| **FAB**                 | [FloatingActionButton]         | M3 FAB                                    |
+|                         | [SmallFloatingActionButton]    | M3 small FAB                              |
+|                         | [LargeFloatingActionButton]    | M3 large FAB                              |
+| **Icon button**         | [IconButton]                   | M3 standard icon button                   |
+|                         | [IconToggleButton]             | M3 standard icon toggle button            |
+|                         | [FilledIconButton]             | M3 filled icon button                     |
+|                         | [FilledIconToggleButton]       | M3 filled icon toggle button              |
+|                         | [FilledTonalIconButton]        | M3 filled tonal icon button               |
+|                         | [FilledTonalIconToggleButton]  | M3 filled tonal icon toggle button        |
+|                         | [OutlinedIconButton]           | M3 outlined icon button                   |
+|                         | [OutlinedIconToggleButton]     | M3 outlined icon toggle button            |
+| **Lists**               | [ListItem]                     | M3 list item                              |
+| **Menus**               | [DropdownMenu]                 | M3 menu                                   |
+|                         | [DropdownMenuItem]             | M3 menu item                              |
+|                         | [ExposedDropdownMenuBox]       | M3 exposed dropdown menu                  |
+| **Navigation bar**      | [NavigationBar]                | M3 navigation bar                         |
+|                         | [NavigationBarItem]            | M3 navigation bar item                    |
+| **Navigation drawer**   | [ModalNavigationDrawer]        | M3 modal navigation drawer                |
+|                         | [ModalDrawerSheet]             | M3 modal drawer sheet                     |
+|                         | [PermanentNavigationDrawer]    | M3 permanent standard navigation drawer   |
+|                         | [PermanentDrawerSheet]         | M3 permanent standard drawer sheet        |
+|                         | [DismissibleNavigationDrawer]  | M3 dismissible standard navigation drawer |
+|                         | [DismissibleDrawerSheet]       | M3 dismissible standard drawer sheet      |
+|                         | [NavigationDrawerItem]         | M3 navigation drawer item                 |
+| **Navigation rail**     | [NavigationRail]               | M3 navigation rail                        |
+|                         | [NavigationRailItem]           | M3 navigation rail item                   |
+| **Progress indicators** | [LinearProgressIndicator]      | M3 linear progress indicator              |
+|                         | [CircularProgressIndicator]    | M3 circular progress indicator            |
+| **Radio button**        | [RadioButton]                  | M3 radio button                           |
+| **Search Bar**          | [SearchBar]                    | M3 search bar                             |
+|                         | [DockedSearchBar]              | M3 docked search bar                      |
+| **Sliders**             | [Slider]                       | M3 slider                                 |
+|                         | [RangeSlider]                  | M3 range slider                           |
+| **Snackbars**           | [Snackbar]                     | M3 snackbar                               |
+| **Swipe to Dismiss**    | [SwipeToDismiss]               | M3 swipe to dismiss                       |
+| **Switch**              | [Switch]                       | M3 switch                                 |
+| **Tabs**                | [Tab]                          | M3 tab                                    |
+|                         | [LeadingIconTab]               | M3 leading icon tab                       |
+|                         | [TabRow]                       | M3 tab row                                |
+|                         | [ScrollableTabRow]             | M3 scrollable tab row                     |
+| **Text fields**         | [TextField]                    | M3 filled text field                      |
+|                         | [OutlinedTextField]            | M3 outlined text field                    |
+| **Time Picker**         | [TimePicker]                   | M3 time picker                            |
+|                         | [TimeInput]                    | M3 time input                             |
+| **Tool tip**            | [PlainTooltipBox]              | M3 plain tool tip                         |
+|                         | [RichTooltipBox]               | M3 rich tool tip                          |
+| **Top app bar**         | [TopAppBar]                    | M3 small top app bar                      |
+|                         | [CenterAlignedTopAppBar]       | M3 center-aligned top app bar             |
+|                         | [MediumTopAppBar]              | M3 medium top app bar                     |
+|                         | [LargeTopAppBar]               | M3 large top app bar                      |
 
 ### Surfaces and layout
 
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/RichTooltipTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/RichTooltipTokens.kt
index 2344a15..e6d6f79 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/RichTooltipTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/RichTooltipTokens.kt
@@ -13,6 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// VERSION: v0_162
+// GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
 
diff --git a/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/Strings.desktop.kt b/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/Strings.desktop.kt
deleted file mode 100644
index f58f0d5..0000000
--- a/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/Strings.desktop.kt
+++ /dev/null
@@ -1,94 +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
-
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ReadOnlyComposable
-
-import java.util.Locale
-
-@Composable
-@ReadOnlyComposable
-internal actual fun getString(string: Strings): String {
-    return when (string) {
-        Strings.NavigationMenu -> "Navigation menu"
-        Strings.CloseDrawer -> "Close navigation menu"
-        Strings.CloseSheet -> "Close sheet"
-        Strings.DefaultErrorMessage -> "Invalid input"
-        Strings.SliderRangeStart -> "Range Start"
-        Strings.SliderRangeEnd -> "Range End"
-        Strings.Dialog -> "Dialog"
-        Strings.MenuExpanded -> "Expanded"
-        Strings.MenuCollapsed -> "Collapsed"
-        Strings.SnackbarDismiss -> "Dismiss"
-        Strings.SearchBarSearch -> "Search"
-        Strings.SuggestionsAvailable -> "Suggestions below"
-        Strings.DatePickerTitle -> "Select date"
-        Strings.DatePickerHeadline -> "Selected date"
-        Strings.DatePickerYearPickerPaneTitle -> "Year picker visible"
-        Strings.DatePickerSwitchToYearSelection -> "Switch to selecting a year"
-        Strings.DatePickerSwitchToDaySelection -> "Swipe to select a year, or tap to switch " +
-            "back to selecting a day"
-        Strings.DatePickerSwitchToNextMonth -> "Change to next month"
-        Strings.DatePickerSwitchToPreviousMonth -> "Change to previous month"
-        Strings.DatePickerNavigateToYearDescription -> "Navigate to year %1$"
-        Strings.DatePickerHeadlineDescription -> "Current selection: %1$"
-        Strings.DatePickerNoSelectionDescription -> "None"
-        Strings.DatePickerTodayDescription -> "Today"
-        Strings.DatePickerScrollToShowLaterYears -> "Scroll to show later years"
-        Strings.DatePickerScrollToShowEarlierYears -> "Scroll to show earlier years"
-        Strings.DateInputTitle -> "Select date"
-        Strings.DateInputHeadline -> "Entered date"
-        Strings.DateInputLabel -> "Date"
-        Strings.DateInputHeadlineDescription -> "Entered date: %1$"
-        Strings.DateInputNoInputDescription -> "None"
-        Strings.DateInputInvalidNotAllowed -> "Date not allowed: %1$"
-        Strings.DateInputInvalidForPattern -> "Date does not match expected pattern: %1$"
-        Strings.DateInputInvalidYearRange -> "Date out of expected year range %1$ - %2$"
-        Strings.DatePickerSwitchToCalendarMode -> "Switch to calendar input mode"
-        Strings.DatePickerSwitchToInputMode -> "Switch to text input mode"
-        Strings.DateRangePickerTitle -> "Select dates"
-        Strings.DateRangePickerStartHeadline -> "Start date"
-        Strings.DateRangePickerEndHeadline -> "End date"
-        Strings.DateRangePickerScrollToShowNextMonth -> "Scroll to show the next month"
-        Strings.DateRangePickerScrollToShowPreviousMonth -> "Scroll to show the previous month"
-        Strings.DateRangePickerDayInRange -> "In range"
-        Strings.DateRangeInputTitle -> "Enter dates"
-        Strings.DateRangeInputInvalidRangeInput -> "Invalid date range input"
-        Strings.BottomSheetDragHandleDescription -> "Drag Handle"
-        Strings.BottomSheetCollapseDescription -> "Collapse bottom sheet"
-        Strings.BottomSheetDismissDescription -> "Dismiss bottom sheet"
-        Strings.BottomSheetExpandDescription -> "Expand bottom sheet"
-        Strings.TooltipLongPressLabel -> "Show tooltip"
-        Strings.TimePickerAM -> "AM"
-        Strings.TimePickerPM -> "PM"
-        Strings.TimePickerPeriodToggle -> "Select AM or PM"
-        Strings.TimePickerMinuteSelection -> "Select minutes"
-        Strings.TimePickerHourSelection -> "Select hour"
-        Strings.TimePickerHourSuffix -> "%1$ o\\'clock"
-        Strings.TimePickerMinuteSuffix -> "%1$ minutes"
-        Strings.TimePicker24HourSuffix -> "%1$ hours"
-        Strings.TimePickerMinute -> "Minute"
-        Strings.TimePickerHour -> "Hour"
-        Strings.TooltipPaneDescription -> "Tooltip"
-        else -> ""
-    }
-}
-@Composable
-@ReadOnlyComposable
-internal actual fun getString(string: Strings, vararg formatArgs: Any): String =
-    String.format(getString(string), Locale.getDefault(), *formatArgs)
diff --git a/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/TimeFormat.desktop.kt b/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/TimeFormat.desktop.kt
deleted file mode 100644
index 6f365ff..0000000
--- a/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/TimeFormat.desktop.kt
+++ /dev/null
@@ -1,24 +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
-
-internal actual val is24HourFormat: Boolean
-    @Composable
-    @ReadOnlyComposable get() = false
\ No newline at end of file
diff --git a/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/CalendarModel.desktop.kt b/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/CalendarModel.desktop.kt
similarity index 100%
rename from compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/CalendarModel.desktop.kt
rename to compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/CalendarModel.desktop.kt
diff --git a/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/DefaultPlatformTextStyle.desktop.kt b/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/DefaultPlatformTextStyle.desktop.kt
similarity index 100%
rename from compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/DefaultPlatformTextStyle.desktop.kt
rename to compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/DefaultPlatformTextStyle.desktop.kt
diff --git a/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/IncludeFontPaddingHelper.desktop.kt b/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/IncludeFontPaddingHelper.desktop.kt
similarity index 100%
rename from compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/IncludeFontPaddingHelper.desktop.kt
rename to compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/IncludeFontPaddingHelper.desktop.kt
diff --git a/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/ModalBottomSheetPopup.desktop.kt b/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/ModalBottomSheetPopup.desktop.kt
similarity index 100%
rename from compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/ModalBottomSheetPopup.desktop.kt
rename to compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/ModalBottomSheetPopup.desktop.kt
diff --git a/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/Strings.desktop.kt b/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/Strings.desktop.kt
new file mode 100644
index 0000000..2de7b31
--- /dev/null
+++ b/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/Strings.desktop.kt
@@ -0,0 +1,96 @@
+/*
+ * 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
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReadOnlyComposable
+
+import java.util.Locale
+
+@Composable
+@ReadOnlyComposable
+internal actual fun getString(string: Strings): String {
+    return when (string) {
+        Strings.NavigationMenu -> "Navigation menu"
+        Strings.CloseDrawer -> "Close navigation menu"
+        Strings.CloseSheet -> "Close sheet"
+        Strings.DefaultErrorMessage -> "Invalid input"
+        Strings.SliderRangeStart -> "Range Start"
+        Strings.SliderRangeEnd -> "Range End"
+        Strings.Dialog -> "Dialog"
+        Strings.MenuExpanded -> "Expanded"
+        Strings.MenuCollapsed -> "Collapsed"
+        Strings.SnackbarDismiss -> "Dismiss"
+        Strings.SearchBarSearch -> "Search"
+        Strings.SuggestionsAvailable -> "Suggestions below"
+        Strings.DatePickerTitle -> "Select date"
+        Strings.DatePickerHeadline -> "Selected date"
+        Strings.DatePickerYearPickerPaneTitle -> "Year picker visible"
+        Strings.DatePickerSwitchToYearSelection -> "Switch to selecting a year"
+        Strings.DatePickerSwitchToDaySelection -> "Swipe to select a year, or tap to switch " +
+            "back to selecting a day"
+        Strings.DatePickerSwitchToNextMonth -> "Change to next month"
+        Strings.DatePickerSwitchToPreviousMonth -> "Change to previous month"
+        Strings.DatePickerNavigateToYearDescription -> "Navigate to year %1$"
+        Strings.DatePickerHeadlineDescription -> "Current selection: %1$"
+        Strings.DatePickerNoSelectionDescription -> "None"
+        Strings.DatePickerTodayDescription -> "Today"
+        Strings.DatePickerScrollToShowLaterYears -> "Scroll to show later years"
+        Strings.DatePickerScrollToShowEarlierYears -> "Scroll to show earlier years"
+        Strings.DateInputTitle -> "Select date"
+        Strings.DateInputHeadline -> "Entered date"
+        Strings.DateInputLabel -> "Date"
+        Strings.DateInputHeadlineDescription -> "Entered date: %1$"
+        Strings.DateInputNoInputDescription -> "None"
+        Strings.DateInputInvalidNotAllowed -> "Date not allowed: %1$"
+        Strings.DateInputInvalidForPattern -> "Date does not match expected pattern: %1$"
+        Strings.DateInputInvalidYearRange -> "Date out of expected year range %1$ - %2$"
+        Strings.DatePickerSwitchToCalendarMode -> "Switch to calendar input mode"
+        Strings.DatePickerSwitchToInputMode -> "Switch to text input mode"
+        Strings.DateRangePickerTitle -> "Select dates"
+        Strings.DateRangePickerStartHeadline -> "Start date"
+        Strings.DateRangePickerEndHeadline -> "End date"
+        Strings.DateRangePickerScrollToShowNextMonth -> "Scroll to show the next month"
+        Strings.DateRangePickerScrollToShowPreviousMonth -> "Scroll to show the previous month"
+        Strings.DateRangePickerDayInRange -> "In range"
+        Strings.DateRangeInputTitle -> "Enter dates"
+        Strings.DateRangeInputInvalidRangeInput -> "Invalid date range input"
+        Strings.BottomSheetDragHandleDescription -> "Drag Handle"
+        Strings.BottomSheetPartialExpandDescription -> "Collapse bottom sheet"
+        Strings.BottomSheetDismissDescription -> "Dismiss bottom sheet"
+        Strings.BottomSheetExpandDescription -> "Expand bottom sheet"
+        Strings.TooltipLongPressLabel -> "Show tooltip"
+        Strings.TimePickerAM -> "AM"
+        Strings.TimePickerPM -> "PM"
+        Strings.TimePickerPeriodToggle -> "Select AM or PM"
+        Strings.TimePickerMinuteSelection -> "Select minutes"
+        Strings.TimePickerHourSelection -> "Select hour"
+        Strings.TimePickerHourSuffix -> "%1$ o\\'clock"
+        Strings.TimePickerMinuteSuffix -> "%1$ minutes"
+        Strings.TimePicker24HourSuffix -> "%1$ hours"
+        Strings.TimePickerMinute -> "Minute"
+        Strings.TimePickerHour -> "Hour"
+        Strings.TimePickerMinuteTextField -> "for minutes"
+        Strings.TimePickerHourTextField -> "for hour"
+        Strings.TooltipPaneDescription -> "Tooltip"
+        else -> ""
+    }
+}
+@Composable
+@ReadOnlyComposable
+internal actual fun getString(string: Strings, vararg formatArgs: Any): String =
+    String.format(getString(string), Locale.getDefault(), *formatArgs)
diff --git a/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/SystemBarsDefaultInsets.desktop.kt b/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/SystemBarsDefaultInsets.desktop.kt
similarity index 100%
rename from compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/SystemBarsDefaultInsets.desktop.kt
rename to compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/SystemBarsDefaultInsets.desktop.kt
diff --git a/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/TimeFormat.desktop.kt b/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/TimeFormat.desktop.kt
new file mode 100644
index 0000000..f1c70f9
--- /dev/null
+++ b/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/TimeFormat.desktop.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.material3
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReadOnlyComposable
+
+internal actual val is24HourFormat: Boolean
+    @Composable
+    @ReadOnlyComposable get() = false
diff --git a/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/TimePicker.desktop.kt b/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/TimePicker.desktop.kt
new file mode 100644
index 0000000..530eca7
--- /dev/null
+++ b/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/TimePicker.desktop.kt
@@ -0,0 +1,21 @@
+/*
+ * 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
+
+@OptIn(ExperimentalMaterial3Api::class)
+internal actual val defaultTimePickerLayoutType: TimePickerLayoutType =
+    TimePickerLayoutType.Vertical
diff --git a/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/TooltipPopup.desktop.kt b/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/TooltipPopup.desktop.kt
similarity index 100%
rename from compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/TooltipPopup.desktop.kt
rename to compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/TooltipPopup.desktop.kt
diff --git a/compose/material3/material3/src/jvmMain/kotlin/androidx/compose/material3/ActualJvm.kt b/compose/material3/material3/src/jvmMain/kotlin/androidx/compose/material3/ActualJvm.kt
new file mode 100644
index 0000000..65a109b
--- /dev/null
+++ b/compose/material3/material3/src/jvmMain/kotlin/androidx/compose/material3/ActualJvm.kt
@@ -0,0 +1,25 @@
+// ktlint-disable filename
+
+/*
+ * 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
+
+/* Copy of androidx.compose.material.ActualJvm, mirrored from Foundation. This is used for the
+   M2/M3-internal copy of MutatorMutex.
+ */
+internal actual typealias InternalAtomicReference<V> =
+    java.util.concurrent.atomic.AtomicReference<V>
\ No newline at end of file
diff --git a/compose/runtime/runtime-livedata/build.gradle b/compose/runtime/runtime-livedata/build.gradle
index da51dd8..3dbc1e9 100644
--- a/compose/runtime/runtime-livedata/build.gradle
+++ b/compose/runtime/runtime-livedata/build.gradle
@@ -30,13 +30,13 @@
     implementation(libs.kotlinStdlib)
 
     api(project(":compose:runtime:runtime"))
-    api("androidx.lifecycle:lifecycle-livedata:2.6.0-rc01")
-    api("androidx.lifecycle:lifecycle-runtime:2.6.0-rc01")
+    api("androidx.lifecycle:lifecycle-livedata:2.6.1")
+    api("androidx.lifecycle:lifecycle-runtime:2.6.1")
     implementation("androidx.compose.ui:ui:1.2.1")
 
     androidTestImplementation(projectOrArtifact(":compose:ui:ui-test-junit4"))
     androidTestImplementation(projectOrArtifact(":compose:test-utils"))
-    androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.5.1")
+    androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.6.1")
     androidTestImplementation(libs.testRunner)
     androidTestImplementation(libs.junit)
     androidTestImplementation(libs.truth)
diff --git a/compose/runtime/runtime/api/current.ignore b/compose/runtime/runtime/api/current.ignore
index e28d869..1589837 100644
--- a/compose/runtime/runtime/api/current.ignore
+++ b/compose/runtime/runtime/api/current.ignore
@@ -3,3 +3,7 @@
     Added method androidx.compose.runtime.Composer.getCurrentCompositionLocalMap()
 AddedAbstractMethod: androidx.compose.runtime.CompositionContext#getEffectCoroutineContext():
     Added method androidx.compose.runtime.CompositionContext.getEffectCoroutineContext()
+
+
+RemovedClass: androidx.compose.runtime.SnapshotStateKt:
+    Removed class androidx.compose.runtime.SnapshotStateKt
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index e6b9f52..1291a3c 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -448,110 +448,6 @@
     method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> toMutableStateMap(Iterable<? extends kotlin.Pair<? extends K,? extends V>>);
   }
 
-  public final class SnapshotStateKt {
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
-    method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R? initial, optional kotlin.coroutines.CoroutineContext context);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static inline operator <T> T! getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
-    method public static <T> androidx.compose.runtime.MutableState<T> mutableStateOf(T? value, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> neverEqualPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object![]? keys, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> referentialEqualityPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> rememberUpdatedState(T? newValue);
-    method public static inline operator <T> void setValue(androidx.compose.runtime.MutableState<T>, Object? thisObj, kotlin.reflect.KProperty<?> property, T? value);
-    method public static <T> kotlinx.coroutines.flow.Flow<T> snapshotFlow(kotlin.jvm.functions.Function0<? extends T> block);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> structuralEqualityPolicy();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> toMutableStateList(java.util.Collection<? extends T>);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> toMutableStateMap(Iterable<? extends kotlin.Pair<? extends K,? extends V>>);
-  }
-
-  public final class SnapshotStateKt {
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
-    method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R? initial, optional kotlin.coroutines.CoroutineContext context);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static inline operator <T> T! getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
-    method public static <T> androidx.compose.runtime.MutableState<T> mutableStateOf(T? value, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> neverEqualPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object![]? keys, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> referentialEqualityPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> rememberUpdatedState(T? newValue);
-    method public static inline operator <T> void setValue(androidx.compose.runtime.MutableState<T>, Object? thisObj, kotlin.reflect.KProperty<?> property, T? value);
-    method public static <T> kotlinx.coroutines.flow.Flow<T> snapshotFlow(kotlin.jvm.functions.Function0<? extends T> block);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> structuralEqualityPolicy();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> toMutableStateList(java.util.Collection<? extends T>);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> toMutableStateMap(Iterable<? extends kotlin.Pair<? extends K,? extends V>>);
-  }
-
-  public final class SnapshotStateKt {
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
-    method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R? initial, optional kotlin.coroutines.CoroutineContext context);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static inline operator <T> T! getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
-    method public static <T> androidx.compose.runtime.MutableState<T> mutableStateOf(T? value, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> neverEqualPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object![]? keys, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> referentialEqualityPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> rememberUpdatedState(T? newValue);
-    method public static inline operator <T> void setValue(androidx.compose.runtime.MutableState<T>, Object? thisObj, kotlin.reflect.KProperty<?> property, T? value);
-    method public static <T> kotlinx.coroutines.flow.Flow<T> snapshotFlow(kotlin.jvm.functions.Function0<? extends T> block);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> structuralEqualityPolicy();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> toMutableStateList(java.util.Collection<? extends T>);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> toMutableStateMap(Iterable<? extends kotlin.Pair<? extends K,? extends V>>);
-  }
-
-  public final class SnapshotStateKt {
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
-    method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R? initial, optional kotlin.coroutines.CoroutineContext context);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static inline operator <T> T! getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
-    method public static <T> androidx.compose.runtime.MutableState<T> mutableStateOf(T? value, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> neverEqualPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object![]? keys, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> referentialEqualityPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> rememberUpdatedState(T? newValue);
-    method public static inline operator <T> void setValue(androidx.compose.runtime.MutableState<T>, Object? thisObj, kotlin.reflect.KProperty<?> property, T? value);
-    method public static <T> kotlinx.coroutines.flow.Flow<T> snapshotFlow(kotlin.jvm.functions.Function0<? extends T> block);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> structuralEqualityPolicy();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> toMutableStateList(java.util.Collection<? extends T>);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> toMutableStateMap(Iterable<? extends kotlin.Pair<? extends K,? extends V>>);
-  }
-
   @androidx.compose.runtime.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.PROPERTY}) public @interface Stable {
   }
 
diff --git a/compose/runtime/runtime/api/public_plus_experimental_current.txt b/compose/runtime/runtime/api/public_plus_experimental_current.txt
index 892930c..b6000ab 100644
--- a/compose/runtime/runtime/api/public_plus_experimental_current.txt
+++ b/compose/runtime/runtime/api/public_plus_experimental_current.txt
@@ -497,110 +497,6 @@
     method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> toMutableStateMap(Iterable<? extends kotlin.Pair<? extends K,? extends V>>);
   }
 
-  public final class SnapshotStateKt {
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
-    method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R? initial, optional kotlin.coroutines.CoroutineContext context);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static inline operator <T> T! getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
-    method public static <T> androidx.compose.runtime.MutableState<T> mutableStateOf(T? value, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> neverEqualPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object![]? keys, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> referentialEqualityPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> rememberUpdatedState(T? newValue);
-    method public static inline operator <T> void setValue(androidx.compose.runtime.MutableState<T>, Object? thisObj, kotlin.reflect.KProperty<?> property, T? value);
-    method public static <T> kotlinx.coroutines.flow.Flow<T> snapshotFlow(kotlin.jvm.functions.Function0<? extends T> block);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> structuralEqualityPolicy();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> toMutableStateList(java.util.Collection<? extends T>);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> toMutableStateMap(Iterable<? extends kotlin.Pair<? extends K,? extends V>>);
-  }
-
-  public final class SnapshotStateKt {
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
-    method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R? initial, optional kotlin.coroutines.CoroutineContext context);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static inline operator <T> T! getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
-    method public static <T> androidx.compose.runtime.MutableState<T> mutableStateOf(T? value, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> neverEqualPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object![]? keys, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> referentialEqualityPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> rememberUpdatedState(T? newValue);
-    method public static inline operator <T> void setValue(androidx.compose.runtime.MutableState<T>, Object? thisObj, kotlin.reflect.KProperty<?> property, T? value);
-    method public static <T> kotlinx.coroutines.flow.Flow<T> snapshotFlow(kotlin.jvm.functions.Function0<? extends T> block);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> structuralEqualityPolicy();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> toMutableStateList(java.util.Collection<? extends T>);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> toMutableStateMap(Iterable<? extends kotlin.Pair<? extends K,? extends V>>);
-  }
-
-  public final class SnapshotStateKt {
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
-    method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R? initial, optional kotlin.coroutines.CoroutineContext context);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static inline operator <T> T! getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
-    method public static <T> androidx.compose.runtime.MutableState<T> mutableStateOf(T? value, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> neverEqualPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object![]? keys, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> referentialEqualityPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> rememberUpdatedState(T? newValue);
-    method public static inline operator <T> void setValue(androidx.compose.runtime.MutableState<T>, Object? thisObj, kotlin.reflect.KProperty<?> property, T? value);
-    method public static <T> kotlinx.coroutines.flow.Flow<T> snapshotFlow(kotlin.jvm.functions.Function0<? extends T> block);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> structuralEqualityPolicy();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> toMutableStateList(java.util.Collection<? extends T>);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> toMutableStateMap(Iterable<? extends kotlin.Pair<? extends K,? extends V>>);
-  }
-
-  public final class SnapshotStateKt {
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
-    method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R? initial, optional kotlin.coroutines.CoroutineContext context);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static inline operator <T> T! getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
-    method public static <T> androidx.compose.runtime.MutableState<T> mutableStateOf(T? value, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> neverEqualPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object![]? keys, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> referentialEqualityPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> rememberUpdatedState(T? newValue);
-    method public static inline operator <T> void setValue(androidx.compose.runtime.MutableState<T>, Object? thisObj, kotlin.reflect.KProperty<?> property, T? value);
-    method public static <T> kotlinx.coroutines.flow.Flow<T> snapshotFlow(kotlin.jvm.functions.Function0<? extends T> block);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> structuralEqualityPolicy();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> toMutableStateList(java.util.Collection<? extends T>);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> toMutableStateMap(Iterable<? extends kotlin.Pair<? extends K,? extends V>>);
-  }
-
   @androidx.compose.runtime.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.PROPERTY}) public @interface Stable {
   }
 
diff --git a/compose/runtime/runtime/api/restricted_current.ignore b/compose/runtime/runtime/api/restricted_current.ignore
index e28d869..1589837 100644
--- a/compose/runtime/runtime/api/restricted_current.ignore
+++ b/compose/runtime/runtime/api/restricted_current.ignore
@@ -3,3 +3,7 @@
     Added method androidx.compose.runtime.Composer.getCurrentCompositionLocalMap()
 AddedAbstractMethod: androidx.compose.runtime.CompositionContext#getEffectCoroutineContext():
     Added method androidx.compose.runtime.CompositionContext.getEffectCoroutineContext()
+
+
+RemovedClass: androidx.compose.runtime.SnapshotStateKt:
+    Removed class androidx.compose.runtime.SnapshotStateKt
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index 39b95b6..8af600b 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -484,110 +484,6 @@
     method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> toMutableStateMap(Iterable<? extends kotlin.Pair<? extends K,? extends V>>);
   }
 
-  public final class SnapshotStateKt {
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
-    method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R? initial, optional kotlin.coroutines.CoroutineContext context);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static inline operator <T> T! getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
-    method public static <T> androidx.compose.runtime.MutableState<T> mutableStateOf(T? value, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> neverEqualPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object![]? keys, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> referentialEqualityPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> rememberUpdatedState(T? newValue);
-    method public static inline operator <T> void setValue(androidx.compose.runtime.MutableState<T>, Object? thisObj, kotlin.reflect.KProperty<?> property, T? value);
-    method public static <T> kotlinx.coroutines.flow.Flow<T> snapshotFlow(kotlin.jvm.functions.Function0<? extends T> block);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> structuralEqualityPolicy();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> toMutableStateList(java.util.Collection<? extends T>);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> toMutableStateMap(Iterable<? extends kotlin.Pair<? extends K,? extends V>>);
-  }
-
-  public final class SnapshotStateKt {
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
-    method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R? initial, optional kotlin.coroutines.CoroutineContext context);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static inline operator <T> T! getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
-    method public static <T> androidx.compose.runtime.MutableState<T> mutableStateOf(T? value, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> neverEqualPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object![]? keys, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> referentialEqualityPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> rememberUpdatedState(T? newValue);
-    method public static inline operator <T> void setValue(androidx.compose.runtime.MutableState<T>, Object? thisObj, kotlin.reflect.KProperty<?> property, T? value);
-    method public static <T> kotlinx.coroutines.flow.Flow<T> snapshotFlow(kotlin.jvm.functions.Function0<? extends T> block);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> structuralEqualityPolicy();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> toMutableStateList(java.util.Collection<? extends T>);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> toMutableStateMap(Iterable<? extends kotlin.Pair<? extends K,? extends V>>);
-  }
-
-  public final class SnapshotStateKt {
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
-    method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R? initial, optional kotlin.coroutines.CoroutineContext context);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static inline operator <T> T! getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
-    method public static <T> androidx.compose.runtime.MutableState<T> mutableStateOf(T? value, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> neverEqualPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object![]? keys, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> referentialEqualityPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> rememberUpdatedState(T? newValue);
-    method public static inline operator <T> void setValue(androidx.compose.runtime.MutableState<T>, Object? thisObj, kotlin.reflect.KProperty<?> property, T? value);
-    method public static <T> kotlinx.coroutines.flow.Flow<T> snapshotFlow(kotlin.jvm.functions.Function0<? extends T> block);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> structuralEqualityPolicy();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> toMutableStateList(java.util.Collection<? extends T>);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> toMutableStateMap(Iterable<? extends kotlin.Pair<? extends K,? extends V>>);
-  }
-
-  public final class SnapshotStateKt {
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsState(kotlinx.coroutines.flow.StateFlow<? extends T>, optional kotlin.coroutines.CoroutineContext context);
-    method @androidx.compose.runtime.Composable public static <T extends R, R> androidx.compose.runtime.State<R> collectAsState(kotlinx.coroutines.flow.Flow<? extends T>, R? initial, optional kotlin.coroutines.CoroutineContext context);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static <T> androidx.compose.runtime.State<T> derivedStateOf(androidx.compose.runtime.SnapshotMutationPolicy<T> policy, kotlin.jvm.functions.Function0<? extends T> calculation);
-    method public static inline operator <T> T! getValue(androidx.compose.runtime.State<? extends T>, Object? thisObj, kotlin.reflect.KProperty<?> property);
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> mutableStateListOf(T?... elements);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf();
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> mutableStateMapOf(kotlin.Pair<? extends K,? extends V>... pairs);
-    method public static <T> androidx.compose.runtime.MutableState<T> mutableStateOf(T? value, optional androidx.compose.runtime.SnapshotMutationPolicy<T> policy);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> neverEqualPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object? key1, Object? key2, Object? key3, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> produceState(T? initialValue, Object![]? keys, kotlin.jvm.functions.Function2<? super androidx.compose.runtime.ProduceStateScope<T>,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> producer);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> referentialEqualityPolicy();
-    method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> rememberUpdatedState(T? newValue);
-    method public static inline operator <T> void setValue(androidx.compose.runtime.MutableState<T>, Object? thisObj, kotlin.reflect.KProperty<?> property, T? value);
-    method public static <T> kotlinx.coroutines.flow.Flow<T> snapshotFlow(kotlin.jvm.functions.Function0<? extends T> block);
-    method public static <T> androidx.compose.runtime.SnapshotMutationPolicy<T> structuralEqualityPolicy();
-    method public static <T> androidx.compose.runtime.snapshots.SnapshotStateList<T> toMutableStateList(java.util.Collection<? extends T>);
-    method public static <K, V> androidx.compose.runtime.snapshots.SnapshotStateMap<K,V> toMutableStateMap(Iterable<? extends kotlin.Pair<? extends K,? extends V>>);
-  }
-
   @androidx.compose.runtime.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER, kotlin.annotation.AnnotationTarget.PROPERTY}) public @interface Stable {
   }
 
diff --git a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/AndroidMovableContentTests.kt b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/AndroidMovableContentTests.kt
new file mode 100644
index 0000000..c17130f
--- /dev/null
+++ b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/AndroidMovableContentTests.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.runtime
+
+import androidx.compose.foundation.layout.BoxWithConstraints
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import kotlin.test.assertEquals
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class AndroidMovableContentTests : BaseComposeTest() {
+    @get:Rule
+    override val activityRule = makeTestActivityRule()
+
+    @Test
+    fun testMovableContentParameterInBoxWithConstraints() {
+        var state by mutableStateOf(false)
+        var lastSeen: Boolean? = null
+        val content = movableContentOf { parameter: Boolean ->
+            val content = @Composable {
+                lastSeen = parameter
+            }
+            Container(content)
+        }
+
+        // Infrastructure to avoid throwing a failure on the UI thread.
+        val failureReasons = mutableListOf<String>()
+        fun failed(reason: String) {
+            failureReasons.add(reason)
+        }
+
+        fun phase(description: String): Phase {
+            return object : Phase {
+                override fun expect(actual: Any?): Result {
+                    return object : Result {
+                        override fun toEqual(expected: Any?) {
+                            if (expected != actual) {
+                                failed("$description, expected $actual to be $expected")
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        compose {
+            if (state) {
+                content(true)
+            } else {
+                BoxWithConstraints {
+                    content(false)
+                }
+            }
+        }.then {
+            phase("In initial composition").expect(lastSeen).toEqual(state)
+            state = true
+        }.then {
+            phase("When setting state to true").expect(lastSeen).toEqual(state)
+            state = false
+        }.then {
+            phase("When setting state to false").expect(lastSeen).toEqual(state)
+        }.done()
+
+        assertEquals("", failureReasons.joinToString())
+    }
+}
+
+@Composable
+fun Container(content: @Composable () -> Unit) {
+    content()
+}
+
+interface Phase {
+    fun expect(actual: Any?): Result
+}
+
+interface Result {
+    fun toEqual(expected: Any?)
+}
\ No newline at end of file
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 319bccf..7df2c189 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
@@ -320,7 +320,7 @@
     internal val composition: ControlledComposition,
     internal val slotTable: SlotTable,
     internal val anchor: Anchor,
-    internal val invalidations: List<Pair<RecomposeScopeImpl, IdentityArraySet<Any>?>>,
+    internal var invalidations: List<Pair<RecomposeScopeImpl, IdentityArraySet<Any>?>>,
     internal val locals: PersistentCompositionLocalMap
 )
 
@@ -1261,6 +1261,15 @@
     private var snapshot = currentSnapshot()
     private var compositionToken: Int = 0
     private var sourceInformationEnabled = true
+    private val derivedStateObserver = object : DerivedStateObserver {
+        override fun start(derivedState: DerivedState<*>) {
+            childrenComposing++
+        }
+
+        override fun done(derivedState: DerivedState<*>) {
+            childrenComposing--
+        }
+    }
 
     private val invalidateStack = Stack<RecomposeScopeImpl>()
 
@@ -1479,6 +1488,7 @@
     }
 
     internal fun changesApplied() {
+        createFreshInsertTable()
         providerUpdates.clear()
     }
 
@@ -1663,10 +1673,31 @@
 
     override fun endToMarker(marker: Int) {
         if (marker < 0) {
+            // If the marker is negative then the marker is for the writer
             val writerLocation = -marker
-            while (writer.parent > writerLocation) end(false)
+            val writer = writer
+            while (true) {
+                val parent = writer.parent
+                if (parent <= writerLocation) break
+                end(writer.isNode(parent))
+            }
         } else {
-            while (reader.parent > marker) end(false)
+            // If the marker is positive then the marker is for the reader. However, if we are
+            // inserting then we need to close the inserting groups first.
+            if (inserting) {
+                // We might be inserting, we need to close all the groups until we are no longer
+                // inserting.
+                val writer = writer
+                while (inserting) {
+                    end(writer.isNode(writer.parent))
+                }
+            }
+            val reader = reader
+            while (true) {
+                val parent = reader.parent
+                if (parent <= marker) break
+                end(reader.isNode(parent))
+            }
         }
     }
 
@@ -1864,13 +1895,7 @@
                 when (val previous = slots.set(groupSlotIndex, value)) {
                     is RememberObserver ->
                         rememberManager.forgetting(previous)
-                    is RecomposeScopeImpl -> {
-                        val composition = previous.composition
-                        if (composition != null) {
-                            previous.release()
-                            composition.pendingInvalidScopes = true
-                        }
-                    }
+                    is RecomposeScopeImpl -> previous.release()
                 }
             }
         }
@@ -2673,6 +2698,7 @@
 
     internal fun tryImminentInvalidation(scope: RecomposeScopeImpl, instance: Any?): Boolean {
         val anchor = scope.anchor ?: return false
+        val slotTable = reader.table
         val location = anchor.toIndexFor(slotTable)
         if (isComposing && location >= reader.currentGroup) {
             // if we are invalidating a scope that is going to be traversed during this
@@ -2760,7 +2786,7 @@
                         is RememberObserver -> {
                             reader.reposition(group)
                             recordSlotTableOperation { _, slots, rememberManager ->
-                                runtimeCheck(data == slots.slot(group, index)) {
+                                runtimeCheck(data == slots.slot(slots.currentGroup, index)) {
                                     "Slot table is out of sync"
                                 }
                                 rememberManager.forgetting(data)
@@ -2768,14 +2794,10 @@
                             }
                         }
                         is RecomposeScopeImpl -> {
-                            val composition = data.composition
-                            if (composition != null) {
-                                composition.pendingInvalidScopes = true
-                                data.release()
-                            }
+                            data.release()
                             reader.reposition(group)
                             recordSlotTableOperation { _, slots, _ ->
-                                runtimeCheck(data == slots.slot(group, index)) {
+                                runtimeCheck(data == slots.slot(slots.currentGroup, index)) {
                                     "Slot table is out of sync"
                                 }
                                 slots.set(index, Composer.Empty)
@@ -3107,15 +3129,11 @@
 
                         // For all the anchors that moved, if the anchor is tracking a recompose
                         // scope, update it to reference its new composer.
-                        if (anchors.isNotEmpty()) {
-                            val toComposition = to.composition as CompositionImpl
-                            anchors.fastForEach { anchor ->
-                                // The recompose scope is always at slot 0 of a restart group.
-                                val recomposeScope = slots.slot(anchor, 0) as? RecomposeScopeImpl
-                                // Check for null as the anchor might not be for a recompose scope
-                                recomposeScope?.adoptedBy(toComposition)
-                            }
-                        }
+                        RecomposeScopeImpl.adoptAnchoredScopes(
+                            slots = slots,
+                            anchors = anchors,
+                            newOwner = to.composition as RecomposeScopeOwner
+                        )
                     }
 
                     fromTable.read { reader ->
@@ -3316,14 +3334,7 @@
                 // ^^ Experimental for forced
 
                 // Ignore reads of derivedStateOf recalculations
-                observeDerivedStateRecalculations(
-                    start = {
-                        childrenComposing++
-                    },
-                    done = {
-                        childrenComposing--
-                    },
-                ) {
+                observeDerivedStateRecalculations(derivedStateObserver) {
                     if (content != null) {
                         startGroup(invocationKey, invocation)
                         invokeComposable(this, content)
@@ -3516,7 +3527,11 @@
             val insertTable = insertTable
             recordSlotEditingOperation { _, slots, _ ->
                 slots.beginInsert()
-                slots.moveFrom(insertTable, anchor.toIndexFor(insertTable))
+                slots.moveFrom(
+                    table = insertTable,
+                    index = anchor.toIndexFor(insertTable),
+                    removeSourceGroup = false
+                )
                 slots.endInsert()
             }
         } else {
@@ -3532,7 +3547,11 @@
                     }
                 }
                 slots.beginInsert()
-                slots.moveFrom(insertTable, anchor.toIndexFor(insertTable))
+                slots.moveFrom(
+                    table = insertTable,
+                    index = anchor.toIndexFor(insertTable),
+                    removeSourceGroup = false
+                )
                 slots.endInsert()
             }
         }
@@ -3690,7 +3709,7 @@
         // composition before the new composition can be composed to receive it. When
         // the new composition receives the state it must recompose over the state by
         // calling invokeMovableContentLambda.
-        slotTable.write { writer ->
+        val anchors = slotTable.write { writer ->
             writer.beginInsert()
 
             // This is the prefix created by invokeMovableContentLambda
@@ -3699,7 +3718,7 @@
             writer.update(reference.parameter)
 
             // Move the content into current location
-            slots.moveTo(reference.anchor, 1, writer)
+            val anchors = slots.moveTo(reference.anchor, 1, writer)
 
             // skip the group that was just inserted.
             writer.skipGroup()
@@ -3708,8 +3727,63 @@
             writer.endGroup()
 
             writer.endInsert()
+
+            anchors
         }
+
         val state = MovableContentState(slotTable)
+        if (RecomposeScopeImpl.hasAnchoredRecomposeScopes(slotTable, anchors)) {
+            // Copy this into a local so the `movableContentRecomposeScopeOwner` captures
+            // only the value of `composition` not the composer not the composer's reference
+            // to the composition, which might change if we move to a composer per thread model
+            // instead of the current composer per composition, and the owner of the recompose
+            // scope is the composition, not the composer.
+            val composition = composition
+
+            // If any recompose scopes are invalidated while the movable content is outside
+            // a composition, ensure the reference is updated to contain the invalidation.
+            val movableContentRecomposeScopeOwner = object : RecomposeScopeOwner {
+                override fun invalidate(
+                    scope: RecomposeScopeImpl,
+                    instance: Any?
+                ): InvalidationResult {
+                    // Try sending this to the original owner first.
+                    val result = (composition as? RecomposeScopeOwner)?.invalidate(scope, instance)
+                        ?: InvalidationResult.IGNORED
+
+                    // If the original owner ignores this then we need to record it in the
+                    // reference
+                    if (result == InvalidationResult.IGNORED) {
+                        reference.invalidations += scope to instance?.let {
+                            IdentityArraySet<Any>().also { it.add(it) }
+                        }
+                        return InvalidationResult.SCHEDULED
+                    }
+                    return result
+                }
+
+                // The only reason [recomposeScopeReleased] is called is when the recompose scope is
+                // removed from the table. First, this never happens for content that is moving, and
+                // 2) even if it did the only reason we tell the composer is to clear tracking
+                // tables that contain this information which is not relevant here.
+                override fun recomposeScopeReleased(scope: RecomposeScopeImpl) {
+                    // Nothing to do
+                }
+
+                // [recordReadOf] this is also something that would happen only during active
+                // recomposition which doesn't happened to a slot table that is moving.
+                override fun recordReadOf(value: Any) {
+                    // Nothing to do
+                }
+            }
+            slotTable.write { writer ->
+                RecomposeScopeImpl.adoptAnchoredScopes(
+                    slots = writer,
+                    anchors = anchors,
+                    newOwner = movableContentRecomposeScopeOwner
+                )
+            }
+        }
         parentContext.movableContentStateReleased(reference, state)
     }
 
@@ -4193,11 +4267,7 @@
             rememberManager.forgetting(slot)
         }
         if (slot is RecomposeScopeImpl) {
-            val composition = slot.composition
-            if (composition != null) {
-                composition.pendingInvalidScopes = true
-                slot.release()
-            }
+            slot.release()
         }
     }
 
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
index 165ab21..ddbb91e 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
@@ -20,6 +20,7 @@
 import androidx.compose.runtime.collection.IdentityArrayMap
 import androidx.compose.runtime.collection.IdentityArraySet
 import androidx.compose.runtime.collection.IdentityScopeMap
+import androidx.compose.runtime.collection.fastForEach
 import androidx.compose.runtime.snapshots.fastAll
 import androidx.compose.runtime.snapshots.fastAny
 import androidx.compose.runtime.snapshots.fastForEach
@@ -348,7 +349,7 @@
     private val applier: Applier<*>,
 
     recomposeContext: CoroutineContext? = null
-) : ControlledComposition {
+) : ControlledComposition, RecomposeScopeOwner {
     /**
      * `null` if a composition isn't pending to apply.
      * `Set<Any>` or `Array<Set<Any>>` if there are modifications to record
@@ -693,7 +694,7 @@
             }
         }
 
-        for (value in values) {
+        values.fastForEach { value ->
             if (value is RecomposeScopeImpl) {
                 value.invalidateForResult(null)
             } else {
@@ -928,20 +929,33 @@
         } else block()
     }
 
-    fun invalidate(scope: RecomposeScopeImpl, instance: Any?): InvalidationResult {
+    override fun invalidate(scope: RecomposeScopeImpl, instance: Any?): InvalidationResult {
         if (scope.defaultsInScope) {
             scope.defaultsInvalid = true
         }
         val anchor = scope.anchor
-        if (anchor == null || !slotTable.ownsAnchor(anchor) || !anchor.valid)
-            return InvalidationResult.IGNORED // The scope has not yet entered the composition
-        if (!anchor.valid)
+        if (anchor == null || !anchor.valid)
             return InvalidationResult.IGNORED // The scope was removed from the composition
+        if (!slotTable.ownsAnchor(anchor)) {
+            // The scope might be owned by the delegate
+            val delegate = synchronized(lock) { invalidationDelegate }
+            if (delegate?.tryImminentInvalidation(scope, instance) == true)
+                return InvalidationResult.IMMINENT // The scope was owned by the delegate
+
+            return InvalidationResult.IGNORED // The scope has not yet entered the composition
+        }
         if (!scope.canRecompose)
             return InvalidationResult.IGNORED // The scope isn't able to be recomposed/invalidated
         return invalidateChecked(scope, anchor, instance)
     }
 
+    override fun recomposeScopeReleased(scope: RecomposeScopeImpl) {
+        pendingInvalidScopes = true
+    }
+
+    private fun tryImminentInvalidation(scope: RecomposeScopeImpl, instance: Any?): Boolean =
+        isComposing && composer.tryImminentInvalidation(scope, instance)
+
     private fun invalidateChecked(
         scope: RecomposeScopeImpl,
         anchor: Anchor,
@@ -959,7 +973,7 @@
                 } else null
             }
             if (delegate == null) {
-                if (isComposing && composer.tryImminentInvalidation(scope, instance)) {
+                if (tryImminentInvalidation(scope, instance)) {
                     // The invalidation was redirected to the composer.
                     return InvalidationResult.IMMINENT
                 }
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt
index 59a68e6..0d74edd 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt
@@ -20,7 +20,6 @@
 
 import androidx.compose.runtime.collection.IdentityArrayMap
 import androidx.compose.runtime.collection.MutableVector
-import androidx.compose.runtime.collection.mutableVectorOf
 import androidx.compose.runtime.snapshots.Snapshot
 import androidx.compose.runtime.snapshots.StateObject
 import androidx.compose.runtime.snapshots.StateRecord
@@ -303,39 +302,50 @@
     calculation: () -> T,
 ): State<T> = DerivedSnapshotState(calculation, policy)
 
-private typealias DerivedStateObservers = Pair<(DerivedState<*>) -> Unit, (DerivedState<*>) -> Unit>
+/**
+ * Observe the recalculations performed by derived states.
+ */
+internal interface DerivedStateObserver {
+    /**
+     * Called before a calculation starts.
+     */
+    fun start(derivedState: DerivedState<*>)
 
-private val derivedStateObservers = SnapshotThreadLocal<MutableVector<DerivedStateObservers>>()
+    /**
+     * Called after the started calculation is complete.
+     */
+    fun done(derivedState: DerivedState<*>)
+}
+
+private val derivedStateObservers = SnapshotThreadLocal<MutableVector<DerivedStateObserver>>()
+
+internal fun derivedStateObservers(): MutableVector<DerivedStateObserver> =
+    derivedStateObservers.get() ?: MutableVector<DerivedStateObserver>(0).also {
+        derivedStateObservers.set(it)
+    }
 
 private inline fun <R> notifyObservers(derivedState: DerivedState<*>, block: () -> R): R {
-    val observers = derivedStateObservers.get() ?: MutableVector(0)
-    observers.forEach { (start, _) -> start(derivedState) }
+    val observers = derivedStateObservers()
+    observers.forEach { it.start(derivedState) }
     return try {
         block()
     } finally {
-        observers.forEach { (_, done) -> done(derivedState) }
+        observers.forEach { it.done(derivedState) }
     }
 }
 
 /**
  * Observe the recalculations performed by any derived state that is recalculated during the
- * execution of [block]. [start] is called before a calculation starts and [done] is called
- * after the started calculation is complete.
+ * execution of [block].
  *
- * @param start a lambda called before every calculation of a derived state is in [block].
- * @param done a lambda that is called after the state passed to [start] is recalculated.
+ * @param observer called for every calculation of a derived state in the [block].
  * @param block the block of code to observe.
  */
-internal fun <R> observeDerivedStateRecalculations(
-    start: (derivedState: State<*>) -> Unit,
-    done: (derivedState: State<*>) -> Unit,
+internal inline fun <R> observeDerivedStateRecalculations(
+    observer: DerivedStateObserver,
     block: () -> R
 ) {
-    val observers = derivedStateObservers.get() ?: mutableVectorOf<DerivedStateObservers>().also {
-        derivedStateObservers.set(it)
-    }
-
-    val observer = start to done
+    val observers = derivedStateObservers()
     try {
         observers.add(observer)
         block()
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/RecomposeScopeImpl.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/RecomposeScopeImpl.kt
index b5b8aac..8f72d6ad8 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/RecomposeScopeImpl.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/RecomposeScopeImpl.kt
@@ -19,6 +19,8 @@
 import androidx.compose.runtime.collection.IdentityArrayIntMap
 import androidx.compose.runtime.collection.IdentityArrayMap
 import androidx.compose.runtime.collection.IdentityArraySet
+import androidx.compose.runtime.snapshots.fastAny
+import androidx.compose.runtime.snapshots.fastForEach
 
 /**
  * Represents a recomposable scope or section of the composition hierarchy. Can be used to
@@ -57,6 +59,12 @@
 private const val SkippedFlag = 0x10
 private const val RereadingFlag = 0x20
 
+internal interface RecomposeScopeOwner {
+    fun invalidate(scope: RecomposeScopeImpl, instance: Any?): InvalidationResult
+    fun recomposeScopeReleased(scope: RecomposeScopeImpl)
+    fun recordReadOf(value: Any)
+}
+
 /**
  * A RecomposeScope is created for a region of the composition that can be recomposed independently
  * of the rest of the composition. The composer will position the slot table to the location
@@ -64,13 +72,12 @@
  * [Composer.startRestartGroup] and is used to track how to restart the group.
  */
 internal class RecomposeScopeImpl(
-    composition: CompositionImpl?
+    owner: RecomposeScopeOwner?
 ) : ScopeUpdateScope, RecomposeScope {
 
     private var flags: Int = 0
 
-    var composition: CompositionImpl? = composition
-        private set
+    private var owner: RecomposeScopeOwner? = owner
 
     /**
      * An anchor to the location in the slot table that start the group associated with this
@@ -83,7 +90,7 @@
      * removed from the slot table. For example, if the scope is in the then clause of an if
      * statement that later becomes false.
      */
-    val valid: Boolean get() = composition != null && anchor?.valid ?: false
+    val valid: Boolean get() = owner != null && anchor?.valid ?: false
 
     val canRecompose: Boolean get() = block != null
 
@@ -163,18 +170,19 @@
     }
 
     /**
-     * Invalidate the group which will cause [composition] to request this scope be recomposed,
+     * Invalidate the group which will cause [owner] to request this scope be recomposed,
      * and an [InvalidationResult] will be returned.
      */
     fun invalidateForResult(value: Any?): InvalidationResult =
-        composition?.invalidate(this, value) ?: InvalidationResult.IGNORED
+        owner?.invalidate(this, value) ?: InvalidationResult.IGNORED
 
     /**
      * Release the recompose scope. This is called when the recompose scope has been removed by the
      * compostion because the part of the composition it was tracking was removed.
      */
     fun release() {
-        composition = null
+        owner?.recomposeScopeReleased(this)
+        owner = null
         trackedInstances = null
         trackedDependencies = null
     }
@@ -183,18 +191,18 @@
      * Called when the data tracked by this recompose scope moves to a different composition when
      * for example, the movable content it is part of has moved.
      */
-    fun adoptedBy(composition: CompositionImpl) {
-        this.composition = composition
+    fun adoptedBy(owner: RecomposeScopeOwner) {
+        this.owner = owner
     }
 
     /**
-     * Invalidate the group which will cause [composition] to request this scope be recomposed.
+     * Invalidate the group which will cause [owner] to request this scope be recomposed.
      *
      * Unlike [invalidateForResult], this method is thread safe and calls the thread safe
      * invalidate on the composer.
      */
     override fun invalidate() {
-        composition?.invalidate(this, null)
+        owner?.invalidate(this, null)
     }
 
     /**
@@ -291,12 +299,12 @@
     }
 
     fun rereadTrackedInstances() {
-        composition?.let { composition ->
+        owner?.let { owner ->
             trackedInstances?.let { trackedInstances ->
                 rereading = true
                 try {
                     trackedInstances.forEach { value, _ ->
-                        composition.recordReadOf(value)
+                        owner.recordReadOf(value)
                     }
                 } finally {
                     rereading = false
@@ -345,4 +353,26 @@
             } else null
         }
     }
+
+    companion object {
+        internal fun adoptAnchoredScopes(
+            slots: SlotWriter,
+            anchors: List<Anchor>,
+            newOwner: RecomposeScopeOwner
+        ) {
+            if (anchors.isNotEmpty()) {
+                anchors.fastForEach { anchor ->
+                    // The recompose scope is always at slot 0 of a restart group.
+                    val recomposeScope = slots.slot(anchor, 0) as? RecomposeScopeImpl
+                    // Check for null as the anchor might not be for a recompose scope
+                    recomposeScope?.adoptedBy(newOwner)
+                }
+            }
+        }
+
+        internal fun hasAnchoredRecomposeScopes(slots: SlotTable, anchors: List<Anchor>) =
+            anchors.isNotEmpty() && anchors.fastAny {
+                slots.ownsAnchor(it) && slots.slot(slots.anchorIndex(it), 0) is RecomposeScopeImpl
+            }
+    }
 }
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
index b9d3905..8569cbf 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
@@ -199,7 +199,7 @@
     private var runnerJob: Job? = null
     private var closeCause: Throwable? = null
     private val knownCompositions = mutableListOf<ControlledComposition>()
-    private var snapshotInvalidations = mutableSetOf<Any>()
+    private var snapshotInvalidations = IdentityArraySet<Any>()
     private val compositionInvalidations = mutableListOf<ControlledComposition>()
     private val compositionsAwaitingApply = mutableListOf<ControlledComposition>()
     private val compositionValuesAwaitingInsert = mutableListOf<MovableContentStateReference>()
@@ -280,7 +280,7 @@
     private fun deriveStateLocked(): CancellableContinuation<Unit>? {
         if (_state.value <= State.ShuttingDown) {
             knownCompositions.clear()
-            snapshotInvalidations = mutableSetOf()
+            snapshotInvalidations = IdentityArraySet()
             compositionInvalidations.clear()
             compositionsAwaitingApply.clear()
             compositionValuesAwaitingInsert.clear()
@@ -296,7 +296,7 @@
                 State.Inactive
             }
             runnerJob == null -> {
-                snapshotInvalidations = mutableSetOf()
+                snapshotInvalidations = IdentityArraySet()
                 compositionInvalidations.clear()
                 if (broadcastFrameClock.hasAwaiters) State.InactivePendingWork else State.Inactive
             }
@@ -420,7 +420,7 @@
                     if (_state.value <= State.ShuttingDown) return@run
                 }
             }
-            snapshotInvalidations = mutableSetOf()
+            snapshotInvalidations = IdentityArraySet()
             if (deriveStateLocked() != null) {
                 error("called outside of runRecomposeAndApplyChanges")
             }
@@ -435,7 +435,7 @@
             knownCompositions.fastForEach { composition ->
                 composition.recordModificationsOf(changes)
             }
-            snapshotInvalidations = mutableSetOf()
+            snapshotInvalidations = IdentityArraySet()
         }
         compositionInvalidations.fastForEach(onEachInvalidComposition)
         compositionInvalidations.clear()
@@ -657,7 +657,7 @@
 
                 compositionsAwaitingApply.clear()
                 compositionInvalidations.clear()
-                snapshotInvalidations = mutableSetOf()
+                snapshotInvalidations = IdentityArraySet()
 
                 compositionValuesAwaitingInsert.clear()
                 compositionValuesRemoved.clear()
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
index 3ed0e5a2..02a9933 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
@@ -472,6 +472,13 @@
             }
         }
 
+        // Verify that slot gap contains all nulls
+        for (index in slotsSize until slots.size) {
+            check(slots[index] == null) {
+                "Non null value in the slot gap at index $index"
+            }
+        }
+
         // Verify anchors are well-formed
         var lastLocation = -1
         anchors.fastForEach { anchor ->
@@ -599,6 +606,13 @@
         return slots.toList().subList(start, end)
     }
 
+    internal fun slot(group: Int, slotIndex: Int): Any? {
+        val start = groups.slotAnchor(group)
+        val end = if (group + 1 < groupsSize) groups.dataAnchor(group + 1) else slots.size
+        val len = end - start
+        return if (slotIndex in 0 until len) return slots[start + slotIndex] else Composer.Empty
+    }
+
     override val compositionGroups: Iterable<CompositionGroup> get() = this
 
     override fun iterator(): Iterator<CompositionGroup> =
@@ -1325,6 +1339,7 @@
             // Only reset the writer if it closes normally.
             moveGroupGapTo(size)
             moveSlotGapTo(slots.size - slotsGapLen, groupGapStart)
+            clearSlotGap()
             recalculateMarks()
         }
         table.close(
@@ -1949,7 +1964,8 @@
             fromIndex: Int,
             toWriter: SlotWriter,
             updateFromCursor: Boolean,
-            updateToCursor: Boolean
+            updateToCursor: Boolean,
+            removeSourceGroup: Boolean = true
         ): List<Anchor> {
             val groupsToMove = fromWriter.groupSize(fromIndex)
             val sourceGroupsEnd = fromIndex + groupsToMove
@@ -2057,7 +2073,11 @@
             } else emptyList()
 
             val parentGroup = fromWriter.parent(fromIndex)
-            val anchorsRemoved = if (updateFromCursor) {
+            val anchorsRemoved = if (!removeSourceGroup) {
+                // e.g.: we can skip groups removal for insertTable of Composer because
+                // it's going to be disposed anyway after changes applied
+                false
+            } else if (updateFromCursor) {
                 // Remove the group using the sequence the writer expects when removing a group, that
                 // is the root group and the group's parent group must be correctly started and ended
                 // when it is not a root group.
@@ -2164,7 +2184,7 @@
      *
      * @return a list of the anchors that were moved
      */
-    fun moveFrom(table: SlotTable, index: Int): List<Anchor> {
+    fun moveFrom(table: SlotTable, index: Int, removeSourceGroup: Boolean = true): List<Anchor> {
         runtimeCheck(insertCount > 0)
 
         if (index == 0 && currentGroup == 0 && this.table.groupsSize == 0) {
@@ -2196,7 +2216,8 @@
                 index,
                 this,
                 updateFromCursor = true,
-                updateToCursor = true
+                updateToCursor = true,
+                removeSourceGroup = removeSourceGroup
             )
         }
     }
@@ -2482,9 +2503,6 @@
                     endIndex = index + gapLen
                 )
             }
-
-            // Clear the gap in the data array
-            slots.fill(null, index, index + gapLen)
         }
 
         // Update the data anchors affected by the move
@@ -2522,6 +2540,12 @@
         this.slotsGapStart = index
     }
 
+    private fun clearSlotGap() {
+        val slotsGapStart = slotsGapStart
+        val slotsGapEnd = slotsGapStart + slotsGapLen
+        slots.fill(null, slotsGapStart, slotsGapEnd)
+    }
+
     /**
      * Insert [size] number of groups in front of [currentGroup]. These groups are implicitly a
      * child of [parent].
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMap.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMap.kt
index 272450c..5644c62 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMap.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMap.kt
@@ -17,18 +17,14 @@
 package androidx.compose.runtime.collection
 
 import androidx.compose.runtime.identityHashCode
-import kotlin.contracts.ExperimentalContracts
 
-@OptIn(ExperimentalContracts::class)
 internal class IdentityArrayIntMap {
-    @PublishedApi
     internal var size = 0
-
-    @PublishedApi
+        private set
     internal var keys: Array<Any?> = arrayOfNulls(4)
-
-    @PublishedApi
+        private set
     internal var values: IntArray = IntArray(4)
+        private set
 
     operator fun get(key: Any): Int {
         val index = find(key)
@@ -38,6 +34,8 @@
      * Add [value] to the map and return `-1` if it was added or previous value if it already existed.
      */
     fun add(key: Any, value: Int): Int {
+        val values = values
+
         val index: Int
         if (size > 0) {
             index = find(key)
@@ -52,6 +50,8 @@
 
         val insertIndex = -(index + 1)
 
+        val keys = keys
+        val size = size
         if (size == keys.size) {
             val newKeys = arrayOfNulls<Any>(keys.size * 2)
             val newValues = IntArray(keys.size * 2)
@@ -75,8 +75,8 @@
                 destination = newValues,
                 endIndex = insertIndex
             )
-            keys = newKeys
-            values = newValues
+            this.keys = newKeys
+            this.values = newValues
         } else {
             keys.copyInto(
                 destination = keys,
@@ -91,9 +91,9 @@
                 endIndex = size
             )
         }
-        keys[insertIndex] = key
-        values[insertIndex] = value
-        size++
+        this.keys[insertIndex] = key
+        this.values[insertIndex] = value
+        this.size++
 
         return -1
     }
@@ -103,6 +103,10 @@
      */
     fun remove(key: Any): Boolean {
         val index = find(key)
+
+        val keys = keys
+        val values = values
+        val size = size
         if (index >= 0) {
             if (index < size - 1) {
                 keys.copyInto(
@@ -118,8 +122,9 @@
                     endIndex = size
                 )
             }
-            size--
-            keys[size] = null
+            val newSize = size - 1
+            keys[newSize] = null
+            this.size = newSize
             return true
         }
         return false
@@ -129,6 +134,10 @@
      * Removes all values that match [predicate].
      */
     inline fun removeValueIf(predicate: (Any, Int) -> Boolean) {
+        val keys = keys
+        val values = values
+        val size = size
+
         var destinationIndex = 0
         for (i in 0 until size) {
             @Suppress("UNCHECKED_CAST")
@@ -145,10 +154,14 @@
         for (i in destinationIndex until size) {
             keys[i] = null
         }
-        size = destinationIndex
+        this.size = destinationIndex
     }
 
     inline fun any(predicate: (Any, Int) -> Boolean): Boolean {
+        val keys = keys
+        val values = values
+        val size = size
+
         for (i in 0 until size) {
             if (predicate(keys[i] as Any, values[i])) return true
         }
@@ -156,6 +169,10 @@
     }
 
     inline fun forEach(block: (Any, Int) -> Unit) {
+        val keys = keys
+        val values = values
+        val size = size
+
         for (i in 0 until size) {
             block(keys[i] as Any, values[i])
         }
@@ -170,6 +187,7 @@
         var high = size - 1
         val valueIdentity = identityHashCode(key)
 
+        val keys = keys
         while (low <= high) {
             val mid = (low + high).ushr(1)
             val midVal = keys[mid]
@@ -192,6 +210,9 @@
      * be returned, which is always after the last item with the same [identityHashCode].
      */
     private fun findExactIndex(midIndex: Int, value: Any?, valueHash: Int): Int {
+        val keys = keys
+        val size = size
+
         // hunt down first
         for (i in midIndex - 1 downTo 0) {
             val v = keys[i]
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArrayMap.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArrayMap.kt
index 13d31df..cb6d619 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArrayMap.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArrayMap.kt
@@ -20,8 +20,11 @@
 
 internal class IdentityArrayMap<Key : Any, Value : Any?>(capacity: Int = 16) {
     internal var keys = arrayOfNulls<Any?>(capacity)
+        private set
     internal var values = arrayOfNulls<Any?>(capacity)
+        private set
     internal var size = 0
+        private set
 
     fun isEmpty() = size == 0
     fun isNotEmpty() = size > 0
@@ -35,6 +38,10 @@
     }
 
     operator fun set(key: Key, value: Value) {
+        val keys = keys
+        val values = values
+        val size = size
+
         val index = find(key)
         if (index >= 0) {
             values[index] = value
@@ -57,7 +64,7 @@
                 )
             }
             destKeys[insertIndex] = key
-            keys = destKeys
+            this.keys = destKeys
             val destValues = if (resize) {
                 arrayOfNulls(size * 2)
             } else values
@@ -74,8 +81,8 @@
                 )
             }
             destValues[insertIndex] = value
-            values = destValues
-            size++
+            this.values = destValues
+            this.size++
         }
     }
 
@@ -158,6 +165,7 @@
         var low = 0
         var high = size - 1
 
+        val keys = keys
         while (low <= high) {
             val mid = (low + high).ushr(1)
             val midKey = keys[mid]
@@ -180,6 +188,9 @@
      * be returned, which is always after the last key with the same [identityHashCode].
      */
     private fun findExactIndex(midIndex: Int, key: Any?, keyHash: Int): Int {
+        val keys = keys
+        val size = size
+
         // hunt down first
         for (i in midIndex - 1 downTo 0) {
             val k = keys[i]
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArraySet.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArraySet.kt
index 1f2404b..29721ca 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArraySet.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArraySet.kt
@@ -27,9 +27,11 @@
 @OptIn(ExperimentalContracts::class)
 internal class IdentityArraySet<T : Any> : Set<T> {
     override var size = 0
+        private set
 
     @PublishedApi
     internal var values: Array<Any?> = arrayOfNulls(16)
+        private set
 
     /**
      * Returns true if the set contains [element]
@@ -51,6 +53,9 @@
      */
     fun add(value: T): Boolean {
         val index: Int
+        val size = size
+        val values = values
+
         if (size > 0) {
             index = find(value)
 
@@ -75,7 +80,7 @@
                 destination = newSorted,
                 endIndex = insertIndex
             )
-            values = newSorted
+            this.values = newSorted
         } else {
             values.copyInto(
                 destination = values,
@@ -84,8 +89,8 @@
                 endIndex = size
             )
         }
-        values[insertIndex] = value
-        size++
+        this.values[insertIndex] = value
+        this.size++
         return true
     }
 
@@ -94,7 +99,6 @@
      */
     fun clear() {
         values.fill(null)
-
         size = 0
     }
 
@@ -103,8 +107,125 @@
      */
     inline fun fastForEach(block: (T) -> Unit) {
         contract { callsInPlace(block) }
+        val values = values
         for (i in 0 until size) {
-            block(this[i])
+            @Suppress("UNCHECKED_CAST")
+            block(values[i] as T)
+        }
+    }
+
+    fun addAll(collection: Collection<T>) {
+        if (collection.isEmpty()) return
+
+        if (collection !is IdentityArraySet<T>) {
+            // Unknown collection, just add repeatedly
+            for (value in collection) {
+                add(value)
+            }
+        } else {
+            // Identity set, merge sorted arrays
+            val thisValues = values
+            val otherValues = collection.values
+            val thisSize = size
+            val otherSize = collection.size
+            val combinedSize = thisSize + otherSize
+
+            val needsResize = values.size < combinedSize
+            val elementsInOrder = thisSize == 0 ||
+                identityHashCode(thisValues[thisSize - 1]) < identityHashCode(otherValues[0])
+
+            if (!needsResize && elementsInOrder) {
+                // fast path, just copy target values
+                otherValues.copyInto(
+                    destination = thisValues,
+                    destinationOffset = thisSize,
+                    startIndex = 0,
+                    endIndex = otherSize
+                )
+                size += otherSize
+            } else {
+                // slow path, merge this and other values
+                val newArray = if (needsResize) {
+                    arrayOfNulls(if (thisSize > otherSize) thisSize * 2 else otherSize * 2)
+                } else {
+                    thisValues
+                }
+                var thisIndex = thisSize - 1
+                var otherIndex = otherSize - 1
+                var nextInsertIndex = combinedSize - 1
+                while (thisIndex >= 0 || otherIndex >= 0) {
+                    val nextValue = when {
+                        thisIndex < 0 -> otherValues[otherIndex--]
+                        otherIndex < 0 -> thisValues[thisIndex--]
+                        else -> {
+                            val thisValue = thisValues[thisIndex]
+                            val otherValue = otherValues[otherIndex]
+
+                            val thisHash = identityHashCode(thisValue)
+                            val otherHash = identityHashCode(otherValue)
+                            when {
+                                thisHash > otherHash -> {
+                                    thisIndex--
+                                    thisValue
+                                }
+                                thisHash < otherHash -> {
+                                    otherIndex--
+                                    otherValue
+                                }
+                                thisValue === otherValue -> {
+                                    // hash and the value are the same, advance both pointers
+                                    thisIndex--
+                                    otherIndex--
+                                    thisValue
+                                }
+                                else -> {
+                                    // collision, lookup if the same item is in the array
+                                    var i = thisIndex - 1
+                                    var foundDuplicate = false
+                                    while (i >= 0) {
+                                        val value = thisValues[i--]
+                                        if (identityHashCode(value) != otherHash) break
+                                        if (otherValue === value) {
+                                            foundDuplicate = true
+                                            break
+                                        }
+                                    }
+
+                                    if (foundDuplicate) {
+                                        // advance pointer and continue next iteration of outer
+                                        // merge loop.
+                                        otherIndex--
+                                        continue
+                                    } else {
+                                        // didn't find the duplicate, put other item in array.
+                                        otherIndex--
+                                        otherValue
+                                    }
+                                }
+                            }
+                        }
+                    }
+
+                    // insert value and continue
+                    newArray[nextInsertIndex--] = nextValue
+                }
+
+                if (nextInsertIndex >= 0) {
+                    // some values were duplicated, copy the merged part
+                    newArray.copyInto(
+                        newArray,
+                        destinationOffset = 0,
+                        startIndex = nextInsertIndex + 1,
+                        endIndex = combinedSize
+                    )
+                }
+                // newSize = endOffset - startOffset of copy above
+                val newSize = combinedSize - (nextInsertIndex + 1)
+                newArray.fill(null, fromIndex = newSize, toIndex = combinedSize)
+
+                values = newArray
+                size = newSize
+            }
         }
     }
 
@@ -123,6 +244,9 @@
      */
     fun remove(value: T): Boolean {
         val index = find(value)
+        val values = values
+        val size = size
+
         if (index >= 0) {
             if (index < size - 1) {
                 values.copyInto(
@@ -132,8 +256,8 @@
                     endIndex = size
                 )
             }
-            size--
-            values[size] = null
+            values[size - 1] = null
+            this.size--
             return true
         }
         return false
@@ -143,6 +267,9 @@
      * Removes all values that match [predicate].
      */
     inline fun removeValueIf(predicate: (T) -> Boolean) {
+        val values = values
+        val size = size
+
         var destinationIndex = 0
         for (i in 0 until size) {
             @Suppress("UNCHECKED_CAST")
@@ -157,7 +284,7 @@
         for (i in destinationIndex until size) {
             values[i] = null
         }
-        size = destinationIndex
+        this.size = destinationIndex
     }
 
     /**
@@ -168,10 +295,11 @@
         var low = 0
         var high = size - 1
         val valueIdentity = identityHashCode(value)
+        val values = values
 
         while (low <= high) {
             val mid = (low + high).ushr(1)
-            val midVal = get(mid)
+            val midVal = values[mid]
             val midIdentity = identityHashCode(midVal)
             when {
                 midIdentity < valueIdentity -> low = mid + 1
@@ -190,7 +318,14 @@
      * If no match is found, the negative index - 1 of the position in which it would be will
      * be returned, which is always after the last item with the same [identityHashCode].
      */
-    private fun findExactIndex(midIndex: Int, value: Any?, valueHash: Int): Int {
+    private fun findExactIndex(
+        midIndex: Int,
+        value: Any?,
+        valueHash: Int
+    ): Int {
+        val values = values
+        val size = size
+
         // hunt down first
         for (i in midIndex - 1 downTo 0) {
             val v = values[i]
@@ -240,4 +375,16 @@
         override fun hasNext(): Boolean = index < size
         override fun next(): T = this@IdentityArraySet.values[index++] as T
     }
+
+    override fun toString(): String {
+        return joinToString(prefix = "[", postfix = "]") { it.toString() }
+    }
 }
+
+internal inline fun <T : Any> Set<T>.fastForEach(block: (T) -> Unit) {
+    if (this is IdentityArraySet<T>) {
+        fastForEach(block)
+    } else {
+        forEach(block)
+    }
+}
\ No newline at end of file
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityScopeMap.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityScopeMap.kt
index cf102a2..f94b85f 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityScopeMap.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityScopeMap.kt
@@ -54,14 +54,6 @@
     internal var size = 0
 
     /**
-     * Returns the value at the given [index] order in the map.
-     */
-    @Suppress("NOTHING_TO_INLINE")
-    private inline fun valueAt(index: Int): Any {
-        return values[valueOrder[index]]!!
-    }
-
-    /**
      * Returns the [IdentityArraySet] for the value at the given [index] order in the map.
      */
     private fun scopeSetAt(index: Int): IdentityArraySet<T> {
@@ -97,6 +89,11 @@
      * and insertes it into the map and returns it.
      */
     private fun getOrCreateIdentitySet(value: Any): IdentityArraySet<T> {
+        val size = size
+        val valueOrder = valueOrder
+        val values = values
+        val scopeSets = scopeSets
+
         val index: Int
         if (size > 0) {
             index = find(value)
@@ -127,18 +124,18 @@
                 )
             }
             valueOrder[insertIndex] = valueIndex
-            size++
+            this.size++
             return scopeSet
         }
 
         // We have to increase the size of all arrays
         val newSize = valueOrder.size * 2
         val valueIndex = size
-        scopeSets = scopeSets.copyOf(newSize)
+        val newScopeSets = scopeSets.copyOf(newSize)
         val scopeSet = IdentityArraySet<T>()
-        scopeSets[valueIndex] = scopeSet
-        values = values.copyOf(newSize)
-        values[valueIndex] = value
+        newScopeSets[valueIndex] = scopeSet
+        val newValues = values.copyOf(newSize)
+        newValues[valueIndex] = value
 
         val newKeyOrder = IntArray(newSize)
         for (i in size + 1 until newSize) {
@@ -160,8 +157,10 @@
                 endIndex = insertIndex
             )
         }
-        valueOrder = newKeyOrder
-        size++
+        this.scopeSets = newScopeSets
+        this.values = newValues
+        this.valueOrder = newKeyOrder
+        this.size++
         return scopeSet
     }
 
@@ -169,7 +168,11 @@
      * Removes all values and scopes from the map
      */
     fun clear() {
-        for (i in 0 until scopeSets.size) {
+        val scopeSets = scopeSets
+        val valueOrder = valueOrder
+        val values = values
+
+        for (i in scopeSets.indices) {
             scopeSets[i]?.clear()
             valueOrder[i] = i
             values[i] = null
@@ -188,6 +191,11 @@
      */
     fun remove(value: Any, scope: T): Boolean {
         val index = find(value)
+
+        val valueOrder = valueOrder
+        val scopeSets = scopeSets
+        val values = values
+        val size = size
         if (index >= 0) {
             val valueOrderIndex = valueOrder[index]
             val set = scopeSets[valueOrderIndex] ?: return false
@@ -203,9 +211,10 @@
                         endIndex = endIndex
                     )
                 }
-                valueOrder[size - 1] = valueOrderIndex
+                val newSize = size - 1
+                valueOrder[newSize] = valueOrderIndex
                 values[valueOrderIndex] = null
-                size--
+                this.size = newSize
             }
             return removed
         }
@@ -233,6 +242,9 @@
     }
 
     private inline fun removingScopes(removalOperation: (IdentityArraySet<T>) -> Unit) {
+        val valueOrder = valueOrder
+        val scopeSets = scopeSets
+        val values = values
         var destinationIndex = 0
         for (i in 0 until size) {
             val valueIndex = valueOrder[i]
@@ -265,9 +277,11 @@
         var low = 0
         var high = size - 1
 
+        val values = values
+        val valueOrder = valueOrder
         while (low <= high) {
             val mid = (low + high).ushr(1)
-            val midValue = valueAt(mid)
+            val midValue = values[valueOrder[mid]]
             val midValHash = identityHashCode(midValue)
             when {
                 midValHash < valueIdentity -> low = mid + 1
@@ -287,9 +301,12 @@
      * be returned, which is always after the last item with the same [identityHashCode].
      */
     private fun findExactIndex(midIndex: Int, value: Any?, valueHash: Int): Int {
+        val values = values
+        val valueOrder = valueOrder
+
         // hunt down first
         for (i in midIndex - 1 downTo 0) {
-            val v = valueAt(i)
+            val v = values[valueOrder[i]]
             if (v === value) {
                 return i
             }
@@ -299,7 +316,7 @@
         }
 
         for (i in midIndex + 1 until size) {
-            val v = valueAt(i)
+            val v = values[valueOrder[i]]
             if (v === value) {
                 return i
             }
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
index 94957ba..d66cff4 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
@@ -24,6 +24,7 @@
 import androidx.compose.runtime.ExperimentalComposeApi
 import androidx.compose.runtime.InternalComposeApi
 import androidx.compose.runtime.SnapshotThreadLocal
+import androidx.compose.runtime.collection.IdentityArraySet
 import androidx.compose.runtime.synchronized
 import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.InvocationKind
@@ -211,7 +212,7 @@
     /**
      * The set of state objects that have been modified in this snapshot.
      */
-    internal abstract val modified: MutableSet<StateObject>?
+    internal abstract val modified: IdentityArraySet<StateObject>?
 
     /**
      * Notify the snapshot that all objects created in this snapshot to this point should be
@@ -449,11 +450,11 @@
                 val snapshot =
                     if (currentSnapshot == null || currentSnapshot is MutableSnapshot)
                         TransparentObserverMutableSnapshot(
-                            previousSnapshot = currentSnapshot as? MutableSnapshot,
+                            parentSnapshot = currentSnapshot as? MutableSnapshot,
                             specifiedReadObserver = readObserver,
                             specifiedWriteObserver = writeObserver,
                             mergeParentObservers = true,
-                            ownsPreviousSnapshot = false
+                            ownsParentSnapshot = false
                         )
                     else if (readObserver == null) return block()
                     else currentSnapshot.takeNestedSnapshot(readObserver)
@@ -905,21 +906,21 @@
                             mergedRecords ?: mutableListOf<Pair<StateObject, StateRecord>>().also {
                                 mergedRecords = it
                             }
-                            ).add(state to current.create())
+                        ).add(state to current.create())
 
                         // If we revert to current then the state is no longer modified.
                         (
                             statesToRemove ?: mutableListOf<StateObject>().also {
                                 statesToRemove = it
                             }
-                            ).add(state)
+                        ).add(state)
                     }
                     else -> {
                         (
                             mergedRecords ?: mutableListOf<Pair<StateObject, StateRecord>>().also {
                                 mergedRecords = it
                             }
-                            ).add(
+                        ).add(
                             if (merged != previous) state to merged
                             else state to previous.create()
                         )
@@ -943,9 +944,9 @@
             }
         }
 
-        statesToRemove?.let {
+        statesToRemove?.fastForEach {
             // Remove from modified any state objects that have reverted to the parent value.
-            modified.removeAll(it)
+            modified.remove(it)
         }
 
         return SnapshotApplyResult.Success
@@ -1003,10 +1004,10 @@
     }
 
     override fun recordModified(state: StateObject) {
-        (modified ?: HashSet<StateObject>().also { modified = it }).add(state)
+        (modified ?: IdentityArraySet<StateObject>().also { modified = it }).add(state)
     }
 
-    override var modified: MutableSet<StateObject>? = null
+    override var modified: IdentityArraySet<StateObject>? = null
 
     /**
      * A set of the id's previously associated with this snapshot. When this snapshot closes
@@ -1017,7 +1018,7 @@
     /**
      * A list of the pinned snapshots handles that must be released by this snapshot
      */
-    internal var previousPinnedSnapshots: IntArray = IntArray(0)
+    internal var previousPinnedSnapshots: IntArray = EmptyIntArray
 
     /**
      * The number of pending nested snapshots of this snapshot. To simplify the code, this
@@ -1029,6 +1030,10 @@
      * Tracks whether the snapshot has been applied.
      */
     internal var applied = false
+
+    private companion object {
+        private val EmptyIntArray = IntArray(0)
+    }
 }
 
 /**
@@ -1202,7 +1207,7 @@
     override fun hasPendingChanges(): Boolean = false
     override val writeObserver: ((Any) -> Unit)? get() = null
 
-    override var modified: HashSet<StateObject>?
+    override var modified: IdentityArraySet<StateObject>?
         get() = null
         @Suppress("UNUSED_PARAMETER")
         set(value) = unsupported()
@@ -1273,7 +1278,7 @@
         }
     }
 
-    override val modified: HashSet<StateObject>? get() = null
+    override val modified: IdentityArraySet<StateObject>? get() = null
     override val writeObserver: ((Any) -> Unit)? get() = null
     override fun recordModified(state: StateObject) = reportReadonlySnapshotWrite()
 
@@ -1397,10 +1402,10 @@
 
                 // Add all modified objects in this set to the parent
                 (
-                    parent.modified ?: HashSet<StateObject>().also {
+                    parent.modified ?: IdentityArraySet<StateObject>().also {
                         parent.modified = it
                     }
-                    ).addAll(modified)
+                ).addAll(modified)
             }
 
             // Ensure the parent is newer than the current snapshot
@@ -1435,32 +1440,32 @@
  * A pseudo snapshot that doesn't introduce isolation but does introduce observers.
  */
 internal class TransparentObserverMutableSnapshot(
-    private val previousSnapshot: MutableSnapshot?,
-    internal val specifiedReadObserver: ((Any) -> Unit)?,
-    internal val specifiedWriteObserver: ((Any) -> Unit)?,
+    private val parentSnapshot: MutableSnapshot?,
+    specifiedReadObserver: ((Any) -> Unit)?,
+    specifiedWriteObserver: ((Any) -> Unit)?,
     private val mergeParentObservers: Boolean,
-    private val ownsPreviousSnapshot: Boolean
+    private val ownsParentSnapshot: Boolean
 ) : MutableSnapshot(
     INVALID_SNAPSHOT,
     SnapshotIdSet.EMPTY,
     mergedReadObserver(
         specifiedReadObserver,
-        previousSnapshot?.readObserver ?: currentGlobalSnapshot.get().readObserver,
+        parentSnapshot?.readObserver ?: currentGlobalSnapshot.get().readObserver,
         mergeParentObservers
     ),
     mergedWriteObserver(
         specifiedWriteObserver,
-        previousSnapshot?.writeObserver ?: currentGlobalSnapshot.get().writeObserver
+        parentSnapshot?.writeObserver ?: currentGlobalSnapshot.get().writeObserver
     )
 ) {
     private val currentSnapshot: MutableSnapshot
-        get() = previousSnapshot ?: currentGlobalSnapshot.get()
+        get() = parentSnapshot ?: currentGlobalSnapshot.get()
 
     override fun dispose() {
         // Explicitly don't call super.dispose()
         disposed = true
-        if (ownsPreviousSnapshot) {
-            previousSnapshot?.dispose()
+        if (ownsParentSnapshot) {
+            parentSnapshot?.dispose()
         }
     }
 
@@ -1475,7 +1480,7 @@
 
     override fun hasPendingChanges(): Boolean = currentSnapshot.hasPendingChanges()
 
-    override var modified: MutableSet<StateObject>?
+    override var modified: IdentityArraySet<StateObject>?
         get() = currentSnapshot.modified
         @Suppress("UNUSED_PARAMETER")
         set(value) = unsupported()
@@ -1514,11 +1519,11 @@
                 writeObserver = mergedWriteObserver
             )
             TransparentObserverMutableSnapshot(
-                previousSnapshot = nestedSnapshot,
+                parentSnapshot = nestedSnapshot,
                 specifiedReadObserver = mergedReadObserver,
                 specifiedWriteObserver = mergedWriteObserver,
                 mergeParentObservers = false,
-                ownsPreviousSnapshot = true
+                ownsParentSnapshot = true
             )
         } else {
             currentSnapshot.takeNestedMutableSnapshot(
@@ -1579,7 +1584,7 @@
 
     override fun hasPendingChanges(): Boolean = currentSnapshot.hasPendingChanges()
 
-    override var modified: MutableSet<StateObject>?
+    override var modified: IdentityArraySet<StateObject>?
         get() = currentSnapshot.modified
         @Suppress("UNUSED_PARAMETER")
         set(value) = unsupported()
@@ -1617,11 +1622,11 @@
     ownsPreviousSnapshot: Boolean = false
 ): Snapshot = if (previousSnapshot is MutableSnapshot || previousSnapshot == null) {
     TransparentObserverMutableSnapshot(
-        previousSnapshot = previousSnapshot as? MutableSnapshot,
+        parentSnapshot = previousSnapshot as? MutableSnapshot,
         specifiedReadObserver = readObserver,
         specifiedWriteObserver = null,
         mergeParentObservers = false,
-        ownsPreviousSnapshot = ownsPreviousSnapshot
+        ownsParentSnapshot = ownsPreviousSnapshot
     )
 } else {
     TransparentObserverSnapshot(
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserver.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserver.kt
index 216c438..ad78a63 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserver.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserver.kt
@@ -18,12 +18,13 @@
 
 import androidx.compose.runtime.AtomicReference
 import androidx.compose.runtime.DerivedState
-import androidx.compose.runtime.State
+import androidx.compose.runtime.DerivedStateObserver
 import androidx.compose.runtime.TestOnly
 import androidx.compose.runtime.collection.IdentityArrayIntMap
 import androidx.compose.runtime.collection.IdentityArrayMap
 import androidx.compose.runtime.collection.IdentityArraySet
 import androidx.compose.runtime.collection.IdentityScopeMap
+import androidx.compose.runtime.collection.fastForEach
 import androidx.compose.runtime.collection.mutableVectorOf
 import androidx.compose.runtime.composeRuntimeError
 import androidx.compose.runtime.observeDerivedStateRecalculations
@@ -226,14 +227,7 @@
             isPaused = false
             currentMap = scopeMap
 
-            scopeMap.observe(scope) {
-                observeDerivedStateRecalculations(
-                    start = scopeMap.derivedStateEnterObserver,
-                    done = scopeMap.derivedStateExitObserver
-                ) {
-                    Snapshot.observe(readObserver, null, block)
-                }
-            }
+            scopeMap.observe(scope, readObserver, block)
         } finally {
             currentMap = oldMap
             isPaused = oldPaused
@@ -371,14 +365,17 @@
         // derived state handling
 
         /**
-         * Start observer for derived state recalculation
+         * Observer for derived state recalculation
          */
-        val derivedStateEnterObserver: (State<*>) -> Unit = { deriveStateScopeCount++ }
+        val derivedStateObserver = object : DerivedStateObserver {
+            override fun start(derivedState: DerivedState<*>) {
+                deriveStateScopeCount++
+            }
 
-        /**
-         * Exit observer for derived state recalculation
-         */
-        val derivedStateExitObserver: (State<*>) -> Unit = { deriveStateScopeCount-- }
+            override fun done(derivedState: DerivedState<*>) {
+                deriveStateScopeCount--
+            }
+        }
 
         /**
          * Counter for skipping reads inside derived states. If count is > 0, read happens inside
@@ -432,7 +429,7 @@
         /**
          * Setup new scope for state read observation, observe them, and cleanup afterwards
          */
-        inline fun observe(scope: Any, block: () -> Unit) {
+        fun observe(scope: Any, readObserver: (Any) -> Unit, block: () -> Unit) {
             val previousScope = currentScope
             val previousReads = currentScopeReads
             val previousToken = currentToken
@@ -443,7 +440,9 @@
                 currentToken = currentSnapshot().id
             }
 
-            block()
+            observeDerivedStateRecalculations(derivedStateObserver) {
+                Snapshot.observe(readObserver, null, block)
+            }
 
             clearObsoleteStateReads(currentScope!!)
 
@@ -511,7 +510,7 @@
          */
         fun recordInvalidation(changes: Set<Any>): Boolean {
             var hasValues = false
-            for (value in changes) {
+            changes.fastForEach { value ->
                 if (value in dependencyToDerivedStates) {
                     // Find derived state that is invalidated by this change
                     dependencyToDerivedStates.forEachScopeOf(value) { derivedState ->
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionReusingTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionReusingTests.kt
index 625b369..97569db 100644
--- a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionReusingTests.kt
+++ b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionReusingTests.kt
@@ -292,6 +292,72 @@
     }
 
     @Test
+    fun reusableContentHost_deactivates_whenRecomposedWithOtherContent() = compositionTest {
+        var reuseKey by mutableStateOf(0)
+        var active by mutableStateOf(true)
+
+        val rememberedState = object : RememberObserver {
+            var currentlyRemembered = false
+
+            override fun toString(): String = "Some text"
+
+            override fun onRemembered() {
+                currentlyRemembered = true
+            }
+
+            override fun onForgotten() {
+                currentlyRemembered = false
+            }
+
+            override fun onAbandoned() {
+                currentlyRemembered = false
+            }
+        }
+
+        compose {
+            if (!active) {
+                Text("Not active")
+            }
+
+            ReusableContentHost(active) {
+                ReusableContent(reuseKey) {
+                    Linear {
+                        val state = remember { rememberedState }
+                        Text(state.toString())
+                    }
+                }
+            }
+        }
+
+        validate {
+            if (!active) {
+                Text("Not active")
+            }
+
+            Linear {
+                Text(rememberedState.toString())
+            }
+        }
+
+        assertTrue(rememberedState.currentlyRemembered)
+
+        active = false
+        expectChanges()
+        revalidate()
+        assertFalse(rememberedState.currentlyRemembered)
+
+        active = true
+        expectChanges()
+        revalidate()
+        assertTrue(rememberedState.currentlyRemembered)
+
+        reuseKey++
+        expectChanges()
+        revalidate()
+        assertTrue(rememberedState.currentlyRemembered)
+    }
+
+    @Test
     fun reusableContentHostCanDisableRecompose() = compositionTest {
         var active by mutableStateOf(true)
         var outer by mutableStateOf("Outer")
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
index 2916991..9c514dc 100644
--- a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
+++ b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
@@ -21,6 +21,7 @@
 import androidx.compose.runtime.mock.ContactModel
 import androidx.compose.runtime.mock.Edit
 import androidx.compose.runtime.mock.EmptyApplier
+import androidx.compose.runtime.mock.InlineLinear
 import androidx.compose.runtime.mock.Linear
 import androidx.compose.runtime.mock.MockViewValidator
 import androidx.compose.runtime.mock.Point
@@ -3390,6 +3391,199 @@
         revalidate()
     }
 
+    @Test // regression test for 264467571
+    fun test_returnConditionally_fromNodeLambda_local_initial_return() = compositionTest {
+        var condition by mutableStateOf(true)
+        compose {
+            Text("Before outer")
+            InlineLinear {
+                Text("Before inner")
+                InlineLinear inner@{
+                    Text("Before return")
+                    if (condition) return@inner
+                    Text("After return")
+                }
+                Text("After inner")
+            }
+            Text("Before outer")
+        }
+
+        validate {
+            Text("Before outer")
+            InlineLinear {
+                Text("Before inner")
+                InlineLinear inner@{
+                    Text("Before return")
+                    if (condition) return@inner
+                    Text("After return")
+                }
+                Text("After inner")
+            }
+            Text("Before outer")
+        }
+
+        repeat(4) {
+            condition = !condition
+            expectChanges()
+            revalidate()
+        }
+    }
+
+    @Test // regression test for 264467571
+    fun test_returnConditionally_fromNodeLambda_local_initial_no_return() = compositionTest {
+        var condition by mutableStateOf(true)
+        compose {
+            Text("Before outer")
+            InlineLinear {
+                Text("Before inner")
+                InlineLinear inner@{
+                    Text("Before return")
+                    if (condition) return@inner
+                    Text("After return")
+                }
+                Text("After inner")
+            }
+            Text("Before outer")
+        }
+
+        validate {
+            Text("Before outer")
+            InlineLinear {
+                Text("Before inner")
+                InlineLinear inner@{
+                    Text("Before return")
+                    if (condition) return@inner
+                    Text("After return")
+                }
+                Text("After inner")
+            }
+            Text("Before outer")
+        }
+
+        repeat(4) {
+            condition = !condition
+            expectChanges()
+            revalidate()
+        }
+    }
+
+    @Test // regression test for 264467571
+    fun test_returnConditionally_fromNodeLambda_nonLocal_initial_return() = compositionTest {
+        var condition by mutableStateOf(true)
+        compose {
+            Text("Before outer")
+            InlineLinear outer@{
+                Text("Before inner")
+                InlineLinear {
+                    Text("Before return")
+                    if (condition) return@outer
+                    Text("After return")
+                }
+                Text("After inner")
+            }
+            Text("Before outer")
+        }
+
+        validate {
+            Text("Before outer")
+            InlineLinear outer@{
+                Text("Before inner")
+                InlineLinear {
+                    Text("Before return")
+                    if (condition) return@outer
+                    Text("After return")
+                }
+                Text("After inner")
+            }
+            Text("Before outer")
+        }
+
+        repeat(4) {
+            condition = !condition
+            expectChanges()
+            revalidate()
+        }
+    }
+
+    @Test // regression test for 264467571
+    fun test_returnConditionally_fromNodeLambda_nonLocal_initial_no_return() = compositionTest {
+        var condition by mutableStateOf(true)
+        compose {
+            Text("Before outer")
+            InlineLinear outer@{
+                Text("Before inner")
+                InlineLinear {
+                    Text("Before return")
+                    if (condition) return@outer
+                    Text("After return")
+                }
+                Text("After inner")
+            }
+            Text("Before outer")
+        }
+
+        validate {
+            Text("Before outer")
+            InlineLinear outer@{
+                Text("Before inner")
+                InlineLinear {
+                    Text("Before return")
+                    if (condition) return@outer
+                    Text("After return")
+                }
+                Text("After inner")
+            }
+            Text("Before outer")
+        }
+
+        repeat(4) {
+            condition = !condition
+            expectChanges()
+            revalidate()
+        }
+    }
+
+    @Test
+    fun test_returnConditionally_fromConditionalNodeLambda_nonLocal_initial_no_return() =
+        compositionTest {
+            var condition by mutableStateOf(true)
+            compose {
+                Text("Before outer")
+                InlineLinear outer@{
+                    Text("Before inner")
+                    if (condition) {
+                        InlineLinear {
+                            Text("Before return")
+                            return@outer
+                        }
+                    }
+                    Text("After inner")
+                }
+                Text("Before outer")
+            }
+
+            validate {
+                Text("Before outer")
+                InlineLinear outer@{
+                    Text("Before inner")
+                    if (condition) {
+                        InlineLinear {
+                            Text("Before return")
+                            return@outer
+                        }
+                    }
+                    Text("After inner")
+                }
+                Text("Before outer")
+            }
+
+            repeat(4) {
+                condition = !condition
+                expectChanges()
+                revalidate()
+            }
+        }
+
     @Test
     fun test_returnConditionally_fromFunction_nonLocal() = compositionTest {
         val text = mutableStateOf<String?>(null)
@@ -3411,6 +3605,45 @@
         revalidate()
     }
 
+    @Test // regression test for 274889428
+    fun test_returnConditionally_simulatedIf() = compositionTest {
+        val condition1 = mutableStateOf(true)
+        val condition2 = mutableStateOf(true)
+        val condition3 = mutableStateOf(true)
+
+        compose block@{
+            Text("A")
+            simulatedIf(condition1.value) { return@block }
+            Text("B")
+            simulatedIf(condition2.value) { return@block }
+            Text("C")
+            simulatedIf(condition3.value) { return@block }
+            Text("D")
+        }
+
+        validate block@{
+            Text("A")
+            this.simulatedIf(condition1.value) { return@block }
+            Text("B")
+            this.simulatedIf(condition2.value) { return@block }
+            Text("C")
+            this.simulatedIf(condition3.value) { return@block }
+            Text("D")
+        }
+
+        condition1.value = false
+        expectChanges()
+        revalidate()
+
+        condition2.value = false
+        expectChanges()
+        revalidate()
+
+        condition3.value = false
+        expectChanges()
+        revalidate()
+    }
+
     @Test // regression test for 267586102
     fun test_remember_in_a_loop() = compositionTest {
         var i1 = 0
@@ -3457,6 +3690,30 @@
         assertEquals(2, i2)
         assertEquals(3, i3)
     }
+
+    @Test
+    fun test_crossinline_differentComposition() = compositionTest {
+        var branch by mutableStateOf(false)
+        compose {
+            if (branch) {
+                Text("Content")
+            }
+            InlineSubcomposition {
+                if (false) {
+                    remember { "Something" }
+                }
+            }
+        }
+        validate {
+            if (branch) {
+                Text("Content")
+            }
+        }
+
+        branch = true
+        expectChanges()
+        revalidate()
+    }
 }
 
 class SomeUnstableClass(val a: Any = "abc")
@@ -3694,4 +3951,18 @@
         if (text == null) return
         Text(text)
     }
-}
\ No newline at end of file
+}
+
+@Composable
+private inline fun simulatedIf(condition: Boolean, block: () -> Unit) {
+    if (condition) block()
+}
+
+private inline fun MockViewValidator.simulatedIf(condition: Boolean, block: () -> Unit) {
+    if (condition) block()
+}
+
+@Composable
+private inline fun InlineSubcomposition(
+    crossinline content: @Composable () -> Unit
+) = TestSubcomposition { content() }
\ No newline at end of file
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt
index 24a3119..d3d1a87 100644
--- a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt
+++ b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt
@@ -777,7 +777,6 @@
     }
 
     @Test
-    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
     fun validateRecomposeScopesDoNotGetLost() = compositionTest {
         var isHorizontal by mutableStateOf(false)
         val displayValue = mutableStateOf(0)
@@ -807,7 +806,7 @@
 
         isHorizontal = true
         Snapshot.sendApplyNotifications()
-        testCoroutineScheduler.advanceTimeBy(10)
+        advanceTimeBy(10)
 
         displayValue.value++
         expectChanges()
@@ -1470,6 +1469,78 @@
         assertEquals(2, hashList.size)
         assertEquals(hashList[0], hashList[1])
     }
+
+    @Test
+    fun parameterPassingThroughDeferredSubcompose() = compositionTest {
+        var state by mutableStateOf(false)
+        var lastSeen: Boolean? = null
+        val content = movableContentOf { parameter: Boolean ->
+            Container {
+                lastSeen = parameter
+            }
+        }
+
+        compose {
+            if (state) {
+                content(true)
+            } else {
+                DeferredSubcompose {
+                    content(state)
+                }
+            }
+        }
+
+        advanceTimeBy(5_000)
+
+        assertEquals(state, lastSeen)
+
+        repeat(5) {
+            state = !state
+
+            expectChanges()
+
+            assertEquals(state, lastSeen, "Failed in iteration $it")
+        }
+    }
+
+    @Test
+    fun stateChangesWhilePendingMove() = compositionTest {
+        var state = 0
+        var lastSeen: Int? = null
+        var deferred by mutableStateOf(false)
+        var scope: RecomposeScope? = null
+
+        val content = movableContentOf {
+            Container {
+                lastSeen = state
+                scope = currentRecomposeScope
+            }
+        }
+
+        compose {
+            if (deferred) {
+                DeferredSubcompose {
+                    content()
+                }
+                SideEffect {
+                    state++
+                    scope?.invalidate()
+                }
+            } else {
+                content()
+            }
+        }
+
+        advanceTimeBy(5_000)
+
+        assertEquals(state, lastSeen)
+
+        deferred = true
+
+        advance()
+
+        assertEquals(state, lastSeen)
+    }
 }
 
 @Composable
@@ -1593,7 +1664,6 @@
         composition.setContent(content)
     }
     DisposableEffect(Unit) {
-
         onDispose { composition.dispose() }
     }
 }
@@ -1622,14 +1692,14 @@
     }
 
     override fun onForgotten() {
-        check(count > 0) { "Abandoned or forgotten mor times than remembered" }
+        check(count > 0) { "Abandoned or forgotten more times than remembered" }
         forgottenCount++
         count--
         if (count == 0) died = true
     }
 
     override fun onAbandoned() {
-        check(count > 0) { "Abandoned or forgotten mor times than remembered" }
+        check(count > 0) { "Abandoned or forgotten more times than remembered" }
         abandonedCount++
         count--
         if (count == 0) died = true
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt
index cad7e12..617e518 100644
--- a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt
+++ b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/collection/IdentityArraySetTest.kt
@@ -182,6 +182,54 @@
         assertTrue(setOfT.containsAll(listOf(stuff[0], stuff[1], stuff[2])))
     }
 
+    @Test
+    fun addAll_Collection() {
+        set.addAll(list)
+
+        assertEquals(list.size, set.size)
+        for (value in list) {
+            assertTrue(value in set)
+        }
+    }
+
+    @Test
+    fun addAll_IdentityArraySet() {
+        val anotherSet = IdentityArraySet<Stuff>()
+        anotherSet.addAll(list)
+
+        set.addAll(anotherSet)
+
+        for (value in list) {
+            assertTrue(value in set)
+        }
+
+        set.addAll(anotherSet)
+
+        assertEquals(anotherSet.size, set.size)
+        for (value in list) {
+            assertTrue(value in set)
+        }
+
+        val stuff = Array(100) { Stuff(it) }
+        for (i in 0 until 100 step 2) {
+            anotherSet.add(stuff[i])
+        }
+        set.addAll(anotherSet)
+
+        for (i in stuff.indices) {
+            val value = stuff[i]
+            if (i % 2 == 0) {
+                assertTrue(value in set, "Expected to have element $i in $set")
+            } else {
+                assertFalse(value in set, "Didn't expect to have element $i in $set")
+            }
+        }
+
+        for (value in list) {
+            assertTrue(value in set)
+        }
+    }
+
     private fun testRemoveValueAtIndex(index: Int) {
         val value = set[index]
         val initialSize = set.size
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/CompositionTest.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/CompositionTest.kt
index 0cd8df6..15e98c8 100644
--- a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/CompositionTest.kt
+++ b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/CompositionTest.kt
@@ -62,7 +62,7 @@
                 val changeCount = recomposer.changeCount
                 Snapshot.sendApplyNotifications()
                 if (recomposer.hasPendingWork) {
-                    testScheduler.advanceTimeBy(5_000)
+                    advanceTimeBy(5_000)
                     check(ignorePendingWork || !recomposer.hasPendingWork) {
                         "Potentially infinite recomposition, still recomposing after advancing"
                     }
@@ -70,6 +70,8 @@
                 return recomposer.changeCount - changeCount
             }
 
+            override fun advanceTimeBy(amount: Long) = testScheduler.advanceTimeBy(amount)
+
             override fun advance(ignorePendingWork: Boolean) = advanceCount(ignorePendingWork) != 0L
 
             override fun verifyConsistent() {
@@ -113,6 +115,11 @@
     fun advanceCount(ignorePendingWork: Boolean = false): Long
 
     /**
+     * Advance the clock by [amount] ms
+     */
+    fun advanceTimeBy(amount: Long)
+
+    /**
      * Verify the composition is well-formed.
      */
     fun verifyConsistent()
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt
index 20ae087..cfa0614 100644
--- a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt
+++ b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt
@@ -47,6 +47,12 @@
             assertEquals(0, views.size, "Not expecting children but some found")
         }
     }
+
+    inline fun inlineValidate(block: MockViewListValidator.() -> Unit) {
+        this.block()
+        val hasNext = next()
+        assertEquals(false, hasNext, "Expected children but none found")
+    }
 }
 
 fun MockViewValidator.view(name: String, block: (MockViewValidator.() -> Unit)? = null) {
@@ -56,6 +62,13 @@
     MockViewListValidator(view.children).validate(block)
 }
 
+inline fun MockViewValidator.inlineView(name: String, block: MockViewValidator.() -> Unit) {
+    val hasNext = next()
+    assertTrue(hasNext, "Expected a $name, but none found")
+    assertEquals(name, view.name)
+    MockViewListValidator(view.children).inlineValidate(block)
+}
+
 fun <T> MockViewValidator.Repeated(of: Iterable<T>, block: MockViewValidator.(value: T) -> Unit) {
     for (value in of) {
         block(value)
@@ -64,6 +77,8 @@
 
 fun MockViewValidator.Linear() = view("linear", null)
 fun MockViewValidator.Linear(block: MockViewValidator.() -> Unit) = view("linear", block)
+inline fun MockViewValidator.InlineLinear(block: MockViewValidator.() -> Unit) =
+    inlineView("linear", block)
 fun MockViewValidator.box(block: MockViewValidator.() -> Unit) = view("box", block)
 fun MockViewValidator.Text(value: String) {
     view("text")
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Views.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Views.kt
index 1c332b0..a5617c9 100644
--- a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Views.kt
+++ b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/mock/Views.kt
@@ -48,6 +48,16 @@
 }
 
 @Composable
+inline fun InlineLinear(content: @Composable () -> Unit) {
+    ReusableComposeNode<View, ViewApplier>(
+        factory = { View().also { it.name = "linear" } },
+        update = { }
+    ) {
+        content()
+    }
+}
+
+@Composable
 fun Linear(
     onReuse: () -> Unit = {},
     onDeactivate: () -> Unit = {},
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
index cdff066..133b3ca 100644
--- a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
+++ b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
@@ -963,11 +963,11 @@
     @Test
     fun testNestedWithinTransparentMutableSnapshotDisposedCorrectly() {
         val outerSnapshot = TransparentObserverMutableSnapshot(
-            previousSnapshot = currentSnapshot() as? MutableSnapshot,
+            parentSnapshot = currentSnapshot() as? MutableSnapshot,
             specifiedReadObserver = null,
             specifiedWriteObserver = null,
             mergeParentObservers = false,
-            ownsPreviousSnapshot = false
+            ownsParentSnapshot = false
         )
 
         try {
@@ -1027,11 +1027,11 @@
         val state by mutableStateOf(0)
 
         val outerSnapshot = TransparentObserverMutableSnapshot(
-            previousSnapshot = currentSnapshot() as? MutableSnapshot,
+            parentSnapshot = currentSnapshot() as? MutableSnapshot,
             specifiedReadObserver = { outerChanges++ },
             specifiedWriteObserver = null,
             mergeParentObservers = false,
-            ownsPreviousSnapshot = false
+            ownsParentSnapshot = false
         )
 
         try {
diff --git a/compose/ui/ui-inspection/OWNERS b/compose/ui/ui-inspection/OWNERS
index 78dae4b..5548ceb 100644
--- a/compose/ui/ui-inspection/OWNERS
+++ b/compose/ui/ui-inspection/OWNERS
@@ -1,4 +1,4 @@
-davidherman@google.com
+# Bug component: 1333602
 jbakermalone@google.com
 jlauridsen@google.com
 sergeyv@google.com
diff --git a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt
index c26a84dc..a24934d 100644
--- a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt
+++ b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt
@@ -670,13 +670,18 @@
         builder.hideSystemNodes = false
         val nodes = builder.convert(composeView)
         dumpNodes(nodes, composeView, builder)
-        val androidView = nodes.flatMap { flatten(it) }.single { it.name == "AndroidView" }
+        val androidView = nodes.flatMap { flatten(it) }.first { it.name == "AndroidView" }
         assertThat(androidView.viewId).isEqualTo(0)
 
         validate(listOf(androidView), builder) {
             node(
                 name = "AndroidView",
                 fileName = "LayoutInspectorTreeTest.kt",
+                children = listOf("AndroidView")
+            )
+            node(
+                name = "AndroidView",
+                fileName = "AndroidView.android.kt",
                 children = listOf("ComposeNode")
             )
             node(
@@ -689,6 +694,51 @@
     }
 
     @Test
+    fun testAndroidViewWithOnResetOverload() {
+        val slotTableRecord = CompositionDataRecord.create()
+
+        show {
+            Inspectable(slotTableRecord) {
+                Column {
+                    Text("Compose Text")
+                    AndroidView(
+                        factory = { context ->
+                            TextView(context).apply {
+                                text = "AndroidView"
+                            }
+                        },
+                        onReset = {
+                            // Do nothing, just use the overload.
+                        }
+                    )
+                }
+            }
+        }
+        val composeView = findAndroidComposeView() as ViewGroup
+        composeView.setTag(R.id.inspection_slot_table_set, slotTableRecord.store)
+        val builder = LayoutInspectorTree()
+        builder.hideSystemNodes = false
+        val nodes = builder.convert(composeView)
+        dumpNodes(nodes, composeView, builder)
+        val androidView = nodes.flatMap { flatten(it) }.first { it.name == "AndroidView" }
+        assertThat(androidView.viewId).isEqualTo(0)
+
+        validate(listOf(androidView), builder) {
+            node(
+                name = "AndroidView",
+                fileName = "LayoutInspectorTreeTest.kt",
+                children = listOf("ReusableComposeNode")
+            )
+            node(
+                name = "ReusableComposeNode",
+                fileName = "AndroidView.android.kt",
+                hasViewIdUnder = composeView,
+                inlined = true,
+            )
+        }
+    }
+
+    @Test
     fun testDoubleAndroidView() {
         val slotTableRecord = CompositionDataRecord.create()
 
diff --git a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt
index 59dad61..8ba40fa 100644
--- a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt
+++ b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt
@@ -27,10 +27,10 @@
 import androidx.compose.ui.inspection.util.NO_ANCHOR_ID
 import androidx.compose.ui.layout.GraphicLayerInfo
 import androidx.compose.ui.layout.LayoutInfo
+import androidx.compose.ui.node.InteroperableComposeUiNode
 import androidx.compose.ui.node.Ref
 import androidx.compose.ui.node.RootForTest
 import androidx.compose.ui.platform.ViewRootForInspector
-import androidx.compose.ui.semantics.SemanticsModifier
 import androidx.compose.ui.semantics.getAllSemanticsNodes
 import androidx.compose.ui.tooling.data.ContextCache
 import androidx.compose.ui.tooling.data.ParameterInformation
@@ -44,7 +44,6 @@
 import androidx.compose.ui.unit.IntRect
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.toSize
-import androidx.compose.ui.node.InteroperableComposeUiNode
 import java.util.ArrayDeque
 import java.util.Collections
 import java.util.IdentityHashMap
@@ -98,6 +97,8 @@
     private val ownerMap = IdentityHashMap<InspectorNode, MutableList<MutableInspectorNode>>()
     /** Map from semantics id to a list of merged semantics information */
     private val semanticsMap = mutableMapOf<Int, List<RawParameter>>()
+    /* Map of seemantics id to a list of unmerged semantics information */
+    private val unmergedSemanticsMap = mutableMapOf<Int, List<RawParameter>>()
     /** Set of tree nodes that were stitched into another tree */
     private val stitched =
         Collections.newSetFromMap(IdentityHashMap<MutableInspectorNode, Boolean>())
@@ -154,9 +155,13 @@
     private fun collectSemantics(view: View) {
         val root = view as? RootForTest ?: return
         val nodes = root.semanticsOwner.getAllSemanticsNodes(mergingEnabled = true)
+        val unmergedNodes = root.semanticsOwner.getAllSemanticsNodes(mergingEnabled = false)
         nodes.forEach { node ->
             semanticsMap[node.id] = node.config.map { RawParameter(it.key.name, it.value) }
         }
+        unmergedNodes.forEach { node ->
+            unmergedSemanticsMap[node.id] = node.config.map { RawParameter(it.key.name, it.value) }
+        }
     }
 
     /**
@@ -236,6 +241,7 @@
         treeMap.clear()
         ownerMap.clear()
         semanticsMap.clear()
+        unmergedSemanticsMap.clear()
         stitched.clear()
         subCompositions.clear()
         foundNode = null
@@ -526,15 +532,13 @@
         node.bounds = bounds
         node.layoutNodes.add(layoutInfo)
         val modifierInfo = layoutInfo.getModifierInfo()
-        node.unmergedSemantics.addAll(
-            modifierInfo.asSequence()
-                .map { it.modifier }
-                .filterIsInstance<SemanticsModifier>()
-                .map { it.semanticsConfiguration }
-                .flatMap { config -> config.map { RawParameter(it.key.name, it.value) } }
-        )
 
-        val mergedSemantics = semanticsMap.get(layoutInfo.semanticsId)
+        val unmergedSemantics = unmergedSemanticsMap[layoutInfo.semanticsId]
+        if (unmergedSemantics != null) {
+            node.unmergedSemantics.addAll(unmergedSemantics)
+        }
+
+        val mergedSemantics = semanticsMap[layoutInfo.semanticsId]
         if (mergedSemantics != null) {
             node.mergedSemantics.addAll(mergedSemantics)
         }
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/ComposeUiTest.android.kt b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/ComposeUiTest.android.kt
index 10e97fa..09198ee 100644
--- a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/ComposeUiTest.android.kt
+++ b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/ComposeUiTest.android.kt
@@ -27,10 +27,7 @@
 import androidx.compose.ui.InternalComposeUiApi
 import androidx.compose.ui.node.RootForTest
 import androidx.compose.ui.platform.InfiniteAnimationPolicy
-import androidx.compose.ui.platform.ViewRootForTest
 import androidx.compose.ui.platform.WindowRecomposerPolicy
-import androidx.compose.ui.platform.textInputServiceFactory
-import androidx.compose.ui.semantics.SemanticsNode
 import androidx.compose.ui.test.junit4.ComposeIdlingResource
 import androidx.compose.ui.test.junit4.ComposeRootRegistry
 import androidx.compose.ui.test.junit4.EspressoLink
@@ -38,13 +35,10 @@
 import androidx.compose.ui.test.junit4.IdlingStrategy
 import androidx.compose.ui.test.junit4.MainTestClockImpl
 import androidx.compose.ui.test.junit4.RobolectricIdlingStrategy
-import androidx.compose.ui.test.junit4.TextInputServiceForTests
 import androidx.compose.ui.test.junit4.UncaughtExceptionHandler
 import androidx.compose.ui.test.junit4.awaitComposeRoots
 import androidx.compose.ui.test.junit4.isOnUiThread
 import androidx.compose.ui.test.junit4.waitForComposeRoots
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.text.input.TextInputForTests
 import androidx.compose.ui.unit.Density
 import androidx.test.core.app.ActivityScenario
 import androidx.test.core.app.ApplicationProvider
@@ -326,9 +320,7 @@
                     withTestCoroutines {
                         withWindowRecomposer {
                             withComposeIdlingResource {
-                                withTextInputService {
-                                    testReceiverScope.withDisposableContent(block)
-                                }
+                                testReceiverScope.withDisposableContent(block)
                             }
                         }
                     }
@@ -386,19 +378,6 @@
         }
     }
 
-    @OptIn(InternalComposeUiApi::class)
-    private fun <R> withTextInputService(block: () -> R): R {
-        val oldTextInputFactory = textInputServiceFactory
-        try {
-            textInputServiceFactory = {
-                TextInputServiceForTests(it)
-            }
-            return block()
-        } finally {
-            textInputServiceFactory = oldTextInputFactory
-        }
-    }
-
     internal inner class AndroidComposeUiTestImpl : AndroidComposeUiTest<A> {
         private var disposeContentHook: (() -> Unit)? = null
 
@@ -542,17 +521,6 @@
         override val mainClock: MainTestClock
             get() = mainClockImpl
 
-        @OptIn(ExperimentalTextApi::class)
-        override fun performTextInput(node: SemanticsNode, action: TextInputForTests.() -> Unit) {
-            val owner = node.root as ViewRootForTest
-
-            test.runOnIdle {
-                val textInput = owner.textInputForTests
-                    ?: error("No input session started. Missing a focus?")
-                action(textInput)
-            }
-        }
-
         override fun <T> runOnUiThread(action: () -> T): T {
             return androidx.compose.ui.test.junit4.runOnUiThread(action)
         }
diff --git a/compose/ui/ui-test-junit4/src/commonMain/kotlin/androidx/compose/ui/test/junit4/TextInputServiceForTests.kt b/compose/ui/ui-test-junit4/src/commonMain/kotlin/androidx/compose/ui/test/junit4/TextInputServiceForTests.kt
deleted file mode 100644
index 143dcda..0000000
--- a/compose/ui/ui-test-junit4/src/commonMain/kotlin/androidx/compose/ui/test/junit4/TextInputServiceForTests.kt
+++ /dev/null
@@ -1,84 +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.junit4
-
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.text.input.CommitTextCommand
-import androidx.compose.ui.text.input.EditCommand
-import androidx.compose.ui.text.input.ImeAction
-import androidx.compose.ui.text.input.ImeOptions
-import androidx.compose.ui.text.input.PlatformTextInputService
-import androidx.compose.ui.text.input.TextFieldValue
-import androidx.compose.ui.text.input.TextInputForTests
-import androidx.compose.ui.text.input.TextInputService
-import androidx.compose.ui.text.input.TextInputSession
-
-/**
- * Extra layer that serves as an observer between the text input service and text fields.
- *
- * When a text field gets a focus it calls to the text input service to provide its callback to
- * accept input from the IME. Here we grab that callback so we can fetch it commands the same
- * way IME would do.
- */
-@OptIn(ExperimentalTextApi::class)
-internal class TextInputServiceForTests(
-    platformTextInputService: PlatformTextInputService
-) : TextInputService(platformTextInputService), TextInputForTests {
-
-    private class Session(
-        var imeOptions: ImeOptions,
-        var onEditCommand: (List<EditCommand>) -> Unit,
-        var onImeActionPerformed: (ImeAction) -> Unit,
-    )
-
-    private var session: Session? = null
-
-    override fun startInput(
-        value: TextFieldValue,
-        imeOptions: ImeOptions,
-        onEditCommand: (List<EditCommand>) -> Unit,
-        onImeActionPerformed: (ImeAction) -> Unit
-    ): TextInputSession {
-        session = Session(
-            imeOptions = imeOptions,
-            onEditCommand = onEditCommand,
-            onImeActionPerformed = onImeActionPerformed
-        )
-        return super.startInput(
-            value,
-            imeOptions,
-            onEditCommand,
-            onImeActionPerformed
-        )
-    }
-
-    override fun stopInput(session: TextInputSession) {
-        this.session = null
-        super.stopInput(session)
-    }
-
-    override fun inputTextForTest(text: String) {
-        performEditCommand(listOf(CommitTextCommand(text, 1)))
-    }
-
-    private fun performEditCommand(commands: List<EditCommand>) {
-        requireSession().onEditCommand(commands)
-    }
-
-    private fun requireSession(): Session =
-        session ?: error("No input session started. Missing a focus?")
-}
\ No newline at end of file
diff --git a/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/ComposeUiTest.desktop.kt b/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/ComposeUiTest.desktop.kt
index 4592e71..8af350a 100644
--- a/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/ComposeUiTest.desktop.kt
+++ b/compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/ComposeUiTest.desktop.kt
@@ -22,12 +22,9 @@
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.node.RootForTest
 import androidx.compose.ui.platform.InfiniteAnimationPolicy
-import androidx.compose.ui.semantics.SemanticsNode
 import androidx.compose.ui.test.junit4.MainTestClockImpl
 import androidx.compose.ui.test.junit4.UncaughtExceptionHandler
 import androidx.compose.ui.test.junit4.isOnUiThread
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.text.input.TextInputForTests
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
 import kotlin.coroutines.CoroutineContext
@@ -200,11 +197,6 @@
     }
 
     private inner class DesktopTestOwner : TestOwner {
-        @OptIn(ExperimentalTextApi::class)
-        override fun performTextInput(node: SemanticsNode, action: TextInputForTests.() -> Unit) {
-            TODO()
-        }
-
         override fun <T> runOnUiThread(action: () -> T): T {
             return this@DesktopComposeUiTest.runOnUiThread(action)
         }
diff --git a/compose/ui/ui-test-junit4/src/test/kotlin/androidx/compose/ui/test/junit4/RobolectricComposeTest.kt b/compose/ui/ui-test-junit4/src/test/kotlin/androidx/compose/ui/test/junit4/RobolectricComposeTest.kt
index cafd3d3..db3efc8 100644
--- a/compose/ui/ui-test-junit4/src/test/kotlin/androidx/compose/ui/test/junit4/RobolectricComposeTest.kt
+++ b/compose/ui/ui-test-junit4/src/test/kotlin/androidx/compose/ui/test/junit4/RobolectricComposeTest.kt
@@ -60,8 +60,10 @@
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.performClick
 import androidx.compose.ui.test.performTextInput
+import androidx.compose.ui.test.performTextInputSelection
 import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.test.runComposeUiTest
+import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.window.Popup
 import androidx.test.espresso.AppNotIdleException
@@ -271,6 +273,8 @@
             TextField(value = text, onValueChange = { updatedText = it })
         }
         onNodeWithText(text).assertIsDisplayed()
+        // If selection isn't set on initial state, it will be 0.
+        onNodeWithText(text).performTextInputSelection(TextRange(1))
         onNodeWithText(text).performTextInput("b")
         runOnIdle {
             assertThat(updatedText).isEqualTo("ab")
diff --git a/compose/ui/ui-test/api/current.txt b/compose/ui/ui-test/api/current.txt
index dfe8a5e..eeb72ba 100644
--- a/compose/ui/ui-test/api/current.txt
+++ b/compose/ui/ui-test/api/current.txt
@@ -76,11 +76,13 @@
     method public static androidx.compose.ui.test.SemanticsMatcher hasContentDescription(String value, optional boolean substring, optional boolean ignoreCase);
     method public static androidx.compose.ui.test.SemanticsMatcher hasContentDescriptionExactly(java.lang.String... values);
     method public static androidx.compose.ui.test.SemanticsMatcher hasImeAction(int actionType);
+    method public static androidx.compose.ui.test.SemanticsMatcher hasInsertTextAtCursorAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasNoClickAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasNoScrollAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasParent(androidx.compose.ui.test.SemanticsMatcher matcher);
     method public static androidx.compose.ui.test.SemanticsMatcher hasPerformImeAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasProgressBarRangeInfo(androidx.compose.ui.semantics.ProgressBarRangeInfo rangeInfo);
+    method public static androidx.compose.ui.test.SemanticsMatcher hasRequestFocusAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasScrollAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasScrollToIndexAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasScrollToKeyAction();
diff --git a/compose/ui/ui-test/api/public_plus_experimental_current.txt b/compose/ui/ui-test/api/public_plus_experimental_current.txt
index f937e6e..445e9c0 100644
--- a/compose/ui/ui-test/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-test/api/public_plus_experimental_current.txt
@@ -82,11 +82,13 @@
     method public static androidx.compose.ui.test.SemanticsMatcher hasContentDescription(String value, optional boolean substring, optional boolean ignoreCase);
     method public static androidx.compose.ui.test.SemanticsMatcher hasContentDescriptionExactly(java.lang.String... values);
     method public static androidx.compose.ui.test.SemanticsMatcher hasImeAction(int actionType);
+    method public static androidx.compose.ui.test.SemanticsMatcher hasInsertTextAtCursorAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasNoClickAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasNoScrollAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasParent(androidx.compose.ui.test.SemanticsMatcher matcher);
     method public static androidx.compose.ui.test.SemanticsMatcher hasPerformImeAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasProgressBarRangeInfo(androidx.compose.ui.semantics.ProgressBarRangeInfo rangeInfo);
+    method public static androidx.compose.ui.test.SemanticsMatcher hasRequestFocusAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasScrollAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasScrollToIndexAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasScrollToKeyAction();
@@ -435,7 +437,6 @@
   @androidx.compose.ui.test.InternalTestApi public interface TestOwner {
     method public androidx.compose.ui.test.MainTestClock getMainClock();
     method public java.util.Set<androidx.compose.ui.node.RootForTest> getRoots(boolean atLeastOneRootExpected);
-    method public void performTextInput(androidx.compose.ui.semantics.SemanticsNode node, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextInputForTests,kotlin.Unit> action);
     method public <T> T! runOnUiThread(kotlin.jvm.functions.Function0<? extends T> action);
     property public abstract androidx.compose.ui.test.MainTestClock mainClock;
   }
diff --git a/compose/ui/ui-test/api/restricted_current.txt b/compose/ui/ui-test/api/restricted_current.txt
index 28d5fca..97c378c 100644
--- a/compose/ui/ui-test/api/restricted_current.txt
+++ b/compose/ui/ui-test/api/restricted_current.txt
@@ -76,11 +76,13 @@
     method public static androidx.compose.ui.test.SemanticsMatcher hasContentDescription(String value, optional boolean substring, optional boolean ignoreCase);
     method public static androidx.compose.ui.test.SemanticsMatcher hasContentDescriptionExactly(java.lang.String... values);
     method public static androidx.compose.ui.test.SemanticsMatcher hasImeAction(int actionType);
+    method public static androidx.compose.ui.test.SemanticsMatcher hasInsertTextAtCursorAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasNoClickAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasNoScrollAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasParent(androidx.compose.ui.test.SemanticsMatcher matcher);
     method public static androidx.compose.ui.test.SemanticsMatcher hasPerformImeAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasProgressBarRangeInfo(androidx.compose.ui.semantics.ProgressBarRangeInfo rangeInfo);
+    method public static androidx.compose.ui.test.SemanticsMatcher hasRequestFocusAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasScrollAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasScrollToIndexAction();
     method public static androidx.compose.ui.test.SemanticsMatcher hasScrollToKeyAction();
diff --git a/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/TextActionsTest.kt b/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/TextActionsTest.kt
index 0e295d8..54c4bc2 100644
--- a/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/TextActionsTest.kt
+++ b/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/TextActionsTest.kt
@@ -16,7 +16,7 @@
 
 package androidx.compose.ui.test
 
-import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.border
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.foundation.text.KeyboardActions
 import androidx.compose.foundation.text.KeyboardOptions
@@ -24,16 +24,19 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.insertTextAtCursor
 import androidx.compose.ui.semantics.performImeAction
+import androidx.compose.ui.semantics.requestFocus
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.semantics.setText
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.util.BoundaryNode
 import androidx.compose.ui.test.util.expectErrorMessageStartsWith
 import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
 import org.junit.Rule
@@ -50,18 +53,23 @@
     val rule = createComposeRule()
 
     @Composable
-    @OptIn(ExperimentalFoundationApi::class)
     fun TextFieldUi(
         imeAction: ImeAction = ImeAction.Default,
         keyboardActions: KeyboardActions = KeyboardActions.Default,
+        enabled: Boolean = true,
+        readOnly: Boolean = false,
         textCallback: (String) -> Unit = {}
     ) {
         val state = remember { mutableStateOf("") }
         BasicTextField(
-            modifier = Modifier.testTag(fieldTag),
+            modifier = Modifier
+                .testTag(fieldTag)
+                .border(0.dp, Color.Black),
             value = state.value,
             keyboardOptions = KeyboardOptions(imeAction = imeAction),
             keyboardActions = keyboardActions,
+            enabled = enabled,
+            readOnly = readOnly,
             onValueChange = {
                 state.value = it
                 textCallback(it)
@@ -70,6 +78,61 @@
     }
 
     @Test
+    fun sendText_requestFocusNotSupported_shouldFail() {
+        rule.setContent {
+            BoundaryNode(testTag = "node", Modifier.semantics {
+                setText { true }
+            })
+        }
+
+        expectErrorMessageStartsWith(
+            "Failed to perform text input.\n" +
+                "Failed to assert the following: (RequestFocus is defined)\n" +
+                "Semantics of the node:"
+        ) {
+            rule.onNodeWithTag("node")
+                .performTextInput("hello")
+        }
+    }
+
+    @Test
+    fun performTextInput_setTextNotSupported_shouldFail() {
+        rule.setContent {
+            BoundaryNode(fieldTag, Modifier.semantics {
+                insertTextAtCursor { true }
+            })
+        }
+
+        expectErrorMessageStartsWith(
+            "Failed to perform text input.\n" +
+                "Failed to assert the following: (SetText is defined)\n" +
+                "Semantics of the node:"
+        ) {
+            rule.onNodeWithTag(fieldTag)
+                .performTextInput("")
+        }
+    }
+
+    @Test
+    fun performTextInput_insertTextAtCursorNotSupported_shouldFail() {
+        rule.setContent {
+            BoundaryNode(fieldTag, Modifier.semantics {
+                setText { true }
+                requestFocus { true }
+            })
+        }
+
+        expectErrorMessageStartsWith(
+            "Failed to perform text input.\n" +
+                "Failed to assert the following: (InsertTextAtCursor is defined)\n" +
+                "Semantics of the node:"
+        ) {
+            rule.onNodeWithTag(fieldTag)
+                .performTextInput("")
+        }
+    }
+
+    @Test
     fun sendText_clearText() {
         var lastSeenText = ""
         rule.setContent {
@@ -93,29 +156,8 @@
         }
     }
 
-    @FlakyTest(bugId = 215584831)
     @Test
-    fun sendTextTwice_shouldAppend() {
-        var lastSeenText = ""
-        rule.setContent {
-            TextFieldUi {
-                lastSeenText = it
-            }
-        }
-
-        rule.onNodeWithTag(fieldTag)
-            .performTextInput("Hello ")
-
-        rule.onNodeWithTag(fieldTag)
-            .performTextInput("world!")
-
-        rule.runOnIdle {
-            assertThat(lastSeenText).isEqualTo("Hello world!")
-        }
-    }
-
-    // @Test - not always appends, seems to be flaky
-    fun sendTextTwice_shouldAppend_ver2() {
+    fun sendTextRepeatedly_shouldAppend() {
         var lastSeenText = ""
         rule.setContent {
             TextFieldUi {
@@ -126,11 +168,11 @@
         rule.onNodeWithTag(fieldTag)
             .performTextInput("Hello")
 
-        // This helps. So there must be some timing issue.
-        // Thread.sleep(3000)
-
-        rule.onNodeWithTag(fieldTag)
-            .performTextInput(" world!")
+        // "Type" one character at a time.
+        " world!".forEach {
+            rule.onNodeWithTag(fieldTag)
+                .performTextInput(it.toString())
+        }
 
         rule.runOnIdle {
             assertThat(lastSeenText).isEqualTo("Hello world!")
@@ -138,6 +180,36 @@
     }
 
     @Test
+    fun sendText_whenDisabled_shouldFail() {
+        rule.setContent {
+            TextFieldUi(enabled = false)
+        }
+
+        expectErrorMessageStartsWith(
+            "Failed to perform text input.\n" +
+                "Failed to assert the following: (is enabled)\n" +
+                "Semantics of the node:"
+        ) {
+            rule.onNodeWithTag(fieldTag).performTextInput("hi")
+        }
+    }
+
+    @Test
+    fun sendText_whenReadOnly_isAllowed() {
+        var lastSeenText = ""
+        rule.setContent {
+            TextFieldUi(readOnly = true) {
+                lastSeenText = it
+            }
+        }
+
+        rule.onNodeWithTag(fieldTag).performTextInput("hi")
+        rule.runOnIdle {
+            assertThat(lastSeenText).isEqualTo("hi")
+        }
+    }
+
+    @Test
     fun replaceText() {
         var lastSeenText = ""
         rule.setContent {
@@ -204,8 +276,10 @@
     @Test
     fun performImeAction_actionReturnsFalse_shouldFail() {
         rule.setContent {
-            BoundaryNode(testTag = "node", Modifier.semantics {
+            BoundaryNode(fieldTag, Modifier.semantics {
                 setText { true }
+                requestFocus { true }
+                insertTextAtCursor { true }
                 performImeAction { false }
             })
         }
@@ -214,7 +288,7 @@
             "Failed to perform IME action, handler returned false.\n" +
                 "Semantics of the node:"
         ) {
-            rule.onNodeWithTag("node")
+            rule.onNodeWithTag(fieldTag)
                 .performImeAction()
         }
     }
@@ -222,7 +296,7 @@
     @Test
     fun performImeAction_inputNotSupported_shouldFail() {
         rule.setContent {
-            BoundaryNode(testTag = "node")
+            BoundaryNode(fieldTag)
         }
 
         expectErrorMessageStartsWith(
@@ -230,8 +304,62 @@
                 "Failed to assert the following: (PerformImeAction is defined)\n" +
                 "Semantics of the node:"
         ) {
+            rule.onNodeWithTag(fieldTag)
+                .performImeAction()
+        }
+    }
+
+    @Test
+    fun performImeAction_focusNotSupported_shouldFail() {
+        rule.setContent {
+            BoundaryNode(testTag = "node", Modifier.semantics {
+                setText { true }
+                performImeAction { true }
+            })
+        }
+
+        expectErrorMessageStartsWith(
+            "Failed to perform IME action.\n" +
+                "Failed to assert the following: (RequestFocus is defined)\n" +
+                "Semantics of the node:"
+        ) {
             rule.onNodeWithTag("node")
                 .performImeAction()
         }
     }
+
+    @Test
+    fun performImeAction_whenDisabled_shouldFail() {
+        rule.setContent {
+            TextFieldUi(
+                imeAction = ImeAction.Done,
+                enabled = false
+            )
+        }
+
+        expectErrorMessageStartsWith(
+            "Failed to perform IME action.\n" +
+                "Failed to assert the following: (is enabled)\n" +
+                "Semantics of the node:"
+        ) {
+            rule.onNodeWithTag(fieldTag).performImeAction()
+        }
+    }
+
+    @Test
+    fun performImeAction_whenReadOnly_isAllowed() {
+        var actionPerformed = false
+        rule.setContent {
+            TextFieldUi(
+                imeAction = ImeAction.Done,
+                readOnly = true,
+                keyboardActions = KeyboardActions { actionPerformed = true }
+            )
+        }
+
+        rule.onNodeWithTag(fieldTag).performImeAction()
+        rule.runOnIdle {
+            assertThat(actionPerformed).isTrue()
+        }
+    }
 }
\ No newline at end of file
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Filters.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Filters.kt
index ca4d4ea..d6fdb5a 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Filters.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Filters.kt
@@ -32,7 +32,7 @@
  * @see SemanticsProperties.Disabled
  */
 fun isEnabled(): SemanticsMatcher =
-    !hasKey(SemanticsProperties.Disabled)
+    SemanticsMatcher("is enabled") { SemanticsProperties.Disabled !in it.config }
 
 /**
  * Returns whether the node is not enabled.
@@ -40,7 +40,7 @@
  * @see SemanticsProperties.Disabled
  */
 fun isNotEnabled(): SemanticsMatcher =
-    hasKey(SemanticsProperties.Disabled)
+    SemanticsMatcher("is not enabled") { SemanticsProperties.Disabled in it.config }
 
 /**
  * Return whether the node is checkable.
@@ -384,6 +384,16 @@
     hasKey(SemanticsActions.SetText)
 
 /**
+ * Returns whether the node defines a semantics action to insert text on it.
+ *
+ * This can be used to, for instance, filter out text fields.
+ *
+ * @see SemanticsActions.InsertTextAtCursor
+ */
+fun hasInsertTextAtCursorAction() =
+    hasKey(SemanticsActions.InsertTextAtCursor)
+
+/**
  * Returns whether the node defines a semantics action to perform the
  * [IME action][SemanticsProperties.ImeAction] on it.
  *
@@ -392,6 +402,13 @@
 fun hasPerformImeAction() = hasKey(SemanticsActions.PerformImeAction)
 
 /**
+ * Returns whether the node defines a semantics action to request focus.
+ *
+ * @see SemanticsActions.RequestFocus
+ */
+fun hasRequestFocusAction() = hasKey(SemanticsActions.RequestFocus)
+
+/**
  * Returns whether the node defines the ability to scroll to an item index.
  *
  * Note that not all scrollable containers have item indices. For example, a
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TestOwner.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TestOwner.kt
index da6b51a..49bc878 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TestOwner.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TestOwner.kt
@@ -19,8 +19,6 @@
 import androidx.compose.ui.node.RootForTest
 import androidx.compose.ui.semantics.SemanticsNode
 import androidx.compose.ui.semantics.getAllSemanticsNodes
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.text.input.TextInputForTests
 
 /**
  * Provides necessary services to facilitate testing.
@@ -35,13 +33,6 @@
     val mainClock: MainTestClock
 
     /**
-     * Runs [action] on the main thread to perform text input via the [TextInputForTests] for the
-     * active text input service for the given semantics node.
-     */
-    @OptIn(ExperimentalTextApi::class)
-    fun performTextInput(node: SemanticsNode, action: TextInputForTests.() -> Unit)
-
-    /**
      * Runs the given [action] on the ui thread.
      *
      * This is a blocking call.
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 79a1cd8..9cdb6fc 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
@@ -23,10 +23,8 @@
 import androidx.compose.ui.semantics.SemanticsPropertyReceiver
 import androidx.compose.ui.semantics.performImeAction
 import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.input.ImeAction
-import androidx.compose.ui.text.input.TextInputForTests
 
 /**
  * Clears the text in this node in similar way to IME.
@@ -40,9 +38,11 @@
  *
  * @param text Text to send.
  */
-@OptIn(ExperimentalTextApi::class)
 fun SemanticsNodeInteraction.performTextInput(text: String) {
-    performTextInput { inputTextForTest(text) }
+    getNodeAndFocus()
+    performSemanticsAction(SemanticsActions.InsertTextAtCursor) {
+        it(AnnotatedString(text))
+    }
 }
 
 /**
@@ -102,25 +102,18 @@
     }
 }
 
-@OptIn(ExperimentalTextApi::class)
-internal fun SemanticsNodeInteraction.performTextInput(action: TextInputForTests.() -> Unit) {
-    val node = getNodeAndFocus()
-
-    wrapAssertionErrorsWithNodeInfo(selector, node) {
-        @OptIn(InternalTestApi::class)
-        testContext.testOwner.performTextInput(node, action)
-    }
-}
-
 private fun SemanticsNodeInteraction.getNodeAndFocus(
     errorOnFail: String = "Failed to perform text input."
 ): SemanticsNode {
     val node = fetchSemanticsNode(errorOnFail)
+    assert(isEnabled()) { errorOnFail }
     assert(hasSetTextAction()) { errorOnFail }
+    assert(hasRequestFocusAction()) { errorOnFail }
+    assert(hasInsertTextAtCursorAction()) { errorOnFail }
 
     if (!isFocused().matches(node)) {
         // Get focus
-        performClick()
+        performSemanticsAction(SemanticsActions.RequestFocus)
     }
 
     return node
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 3cc0d5b..5c4739f 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
@@ -22,12 +22,10 @@
 import android.content.pm.Signature
 import android.content.res.Resources
 import androidx.annotation.WorkerThread
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.core.content.res.FontResourcesParserCompat
 import java.util.Arrays
 
 @SuppressLint("ListIterator") // this is not a hot code path, nor is it optimized
-@OptIn(ExperimentalTextApi::class)
 @WorkerThread
 internal fun GoogleFont.Provider.checkAvailable(
     packageManager: PackageManager,
@@ -73,7 +71,6 @@
     return b.joinToString(",", prefix = "byteArrayOf(", postfix = ")")
 }
 
-@OptIn(ExperimentalTextApi::class)
 private fun GoogleFont.Provider.loadCertsIfNeeded(resources: Resources): List<List<ByteArray?>?> {
     if (certificates != null) {
         return certificates
diff --git a/compose/ui/ui-text/api/current.txt b/compose/ui/ui-text/api/current.txt
index e218a96..7dcfe57 100644
--- a/compose/ui/ui-text/api/current.txt
+++ b/compose/ui/ui-text/api/current.txt
@@ -1019,9 +1019,7 @@
 
   public interface PlatformTextInputAdapter {
     method public android.view.inputmethod.InputConnection? createInputConnection(android.view.inputmethod.EditorInfo outAttrs);
-    method public androidx.compose.ui.text.input.TextInputForTests? getInputForTests();
     method public default void onDisposed();
-    property public abstract androidx.compose.ui.text.input.TextInputForTests? inputForTests;
   }
 
   @androidx.compose.runtime.Immutable public fun interface PlatformTextInputPlugin<T extends androidx.compose.ui.text.input.PlatformTextInputAdapter> {
@@ -1098,10 +1096,6 @@
     method public static androidx.compose.ui.text.AnnotatedString getTextBeforeSelection(androidx.compose.ui.text.input.TextFieldValue, int maxChars);
   }
 
-  public interface TextInputForTests {
-    method public void inputTextForTest(String text);
-  }
-
   public class TextInputService {
     ctor public TextInputService(androidx.compose.ui.text.input.PlatformTextInputService platformTextInputService);
     method @Deprecated public final void hideSoftwareKeyboard();
@@ -1295,6 +1289,7 @@
   }
 
   @kotlin.jvm.JvmInline public static final value class LineHeightStyle.Alignment {
+    ctor public LineHeightStyle.Alignment(float topRatio);
     field public static final androidx.compose.ui.text.style.LineHeightStyle.Alignment.Companion Companion;
   }
 
diff --git a/compose/ui/ui-text/api/public_plus_experimental_current.txt b/compose/ui/ui-text/api/public_plus_experimental_current.txt
index da4f33e..11478bf 100644
--- a/compose/ui/ui-text/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-text/api/public_plus_experimental_current.txt
@@ -632,9 +632,6 @@
   @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.ERROR, message="This is internal API that may change frequently and without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY}) public @interface InternalPlatformTextApi {
   }
 
-  public final class LayoutCompatKt {
-  }
-
 }
 
 package androidx.compose.ui.text.font {
@@ -1090,9 +1087,7 @@
 
   public interface PlatformTextInputAdapter {
     method public android.view.inputmethod.InputConnection? createInputConnection(android.view.inputmethod.EditorInfo outAttrs);
-    method public androidx.compose.ui.text.input.TextInputForTests? getInputForTests();
     method public default void onDisposed();
-    property public abstract androidx.compose.ui.text.input.TextInputForTests? inputForTests;
   }
 
   @androidx.compose.runtime.Immutable public fun interface PlatformTextInputPlugin<T extends androidx.compose.ui.text.input.PlatformTextInputAdapter> {
@@ -1184,10 +1179,6 @@
     method public static androidx.compose.ui.text.AnnotatedString getTextBeforeSelection(androidx.compose.ui.text.input.TextFieldValue, int maxChars);
   }
 
-  public interface TextInputForTests {
-    method public void inputTextForTest(String text);
-  }
-
   public class TextInputService {
     ctor public TextInputService(androidx.compose.ui.text.input.PlatformTextInputService platformTextInputService);
     method @Deprecated public final void hideSoftwareKeyboard();
@@ -1269,6 +1260,15 @@
 
 }
 
+package androidx.compose.ui.text.platform {
+
+  @androidx.compose.ui.text.InternalTextApi public final class URLSpanCache {
+    ctor public URLSpanCache();
+    method public android.text.style.URLSpan toURLSpan(androidx.compose.ui.text.UrlAnnotation urlAnnotation);
+  }
+
+}
+
 package androidx.compose.ui.text.platform.extensions {
 
   public final class TtsAnnotationExtensions_androidKt {
@@ -1276,10 +1276,6 @@
     method public static android.text.style.TtsSpan toSpan(androidx.compose.ui.text.VerbatimTtsAnnotation);
   }
 
-  public final class UrlAnnotationExtensions_androidKt {
-    method @androidx.compose.ui.text.ExperimentalTextApi public static android.text.style.URLSpan toSpan(androidx.compose.ui.text.UrlAnnotation);
-  }
-
 }
 
 package androidx.compose.ui.text.style {
@@ -1385,7 +1381,7 @@
   }
 
   @kotlin.jvm.JvmInline public static final value class LineHeightStyle.Alignment {
-    ctor @androidx.compose.ui.text.ExperimentalTextApi public LineHeightStyle.Alignment(float topRatio);
+    ctor public LineHeightStyle.Alignment(float topRatio);
     field public static final androidx.compose.ui.text.style.LineHeightStyle.Alignment.Companion Companion;
   }
 
diff --git a/compose/ui/ui-text/api/restricted_current.txt b/compose/ui/ui-text/api/restricted_current.txt
index e218a96..7dcfe57 100644
--- a/compose/ui/ui-text/api/restricted_current.txt
+++ b/compose/ui/ui-text/api/restricted_current.txt
@@ -1019,9 +1019,7 @@
 
   public interface PlatformTextInputAdapter {
     method public android.view.inputmethod.InputConnection? createInputConnection(android.view.inputmethod.EditorInfo outAttrs);
-    method public androidx.compose.ui.text.input.TextInputForTests? getInputForTests();
     method public default void onDisposed();
-    property public abstract androidx.compose.ui.text.input.TextInputForTests? inputForTests;
   }
 
   @androidx.compose.runtime.Immutable public fun interface PlatformTextInputPlugin<T extends androidx.compose.ui.text.input.PlatformTextInputAdapter> {
@@ -1098,10 +1096,6 @@
     method public static androidx.compose.ui.text.AnnotatedString getTextBeforeSelection(androidx.compose.ui.text.input.TextFieldValue, int maxChars);
   }
 
-  public interface TextInputForTests {
-    method public void inputTextForTest(String text);
-  }
-
   public class TextInputService {
     ctor public TextInputService(androidx.compose.ui.text.input.PlatformTextInputService platformTextInputService);
     method @Deprecated public final void hideSoftwareKeyboard();
@@ -1295,6 +1289,7 @@
   }
 
   @kotlin.jvm.JvmInline public static final value class LineHeightStyle.Alignment {
+    ctor public LineHeightStyle.Alignment(float topRatio);
     field public static final androidx.compose.ui.text.style.LineHeightStyle.Alignment.Companion Companion;
   }
 
diff --git a/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/HyphensLineBreakBenchmark.kt b/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/HyphensLineBreakBenchmark.kt
index d7d0b85..016ee62 100644
--- a/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/HyphensLineBreakBenchmark.kt
+++ b/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/HyphensLineBreakBenchmark.kt
@@ -18,14 +18,15 @@
 
 import android.graphics.Bitmap
 import android.graphics.Canvas
+import android.graphics.text.LineBreakConfig
+import android.graphics.text.LineBreaker
 import android.os.Build
+import android.text.Layout
 import android.text.TextPaint
 import androidx.benchmark.junit4.BenchmarkRule
 import androidx.benchmark.junit4.measureRepeated
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.android.InternalPlatformTextApi
-import androidx.compose.ui.text.android.LayoutCompat
-import androidx.compose.ui.text.android.TextLayout
+import androidx.compose.ui.text.android.StaticLayoutFactory
 import androidx.compose.ui.text.style.Hyphens
 import androidx.compose.ui.text.style.LineBreak
 import androidx.test.filters.LargeTest
@@ -36,7 +37,7 @@
 
 @LargeTest
 @RunWith(Parameterized::class)
-@OptIn(ExperimentalTextApi::class, InternalPlatformTextApi::class)
+@OptIn(InternalPlatformTextApi::class)
 class HyphensLineBreakBenchmark(
     private val textLength: Int,
     private val hyphensString: String,
@@ -82,8 +83,10 @@
             val textPaint = TextPaint()
             textPaint.textSize = textSize
             benchmarkRule.measureRepeated {
-                TextLayout.constructStaticLayout(text, width = width,
-                    textPaint = textPaint,
+                StaticLayoutFactory.create(
+                    text = text,
+                    width = width,
+                    paint = textPaint,
                     hyphenationFrequency = hyphenationFrequency,
                     lineBreakStyle = lineBreakStyle,
                     breakStrategy = breakStrategy,
@@ -101,8 +104,10 @@
             textPaint.textSize = textSize
             val canvas = Canvas(Bitmap.createBitmap(width, 1000, Bitmap.Config.ARGB_8888))
             benchmarkRule.measureRepeated {
-                val layout = TextLayout.constructStaticLayout(text, width = width,
-                    textPaint = textPaint,
+                val layout = StaticLayoutFactory.create(
+                    text = text,
+                    width = width,
+                    paint = textPaint,
                     hyphenationFrequency = hyphenationFrequency,
                     lineBreakStyle = lineBreakStyle,
                     breakStrategy = breakStrategy,
@@ -115,36 +120,36 @@
 
     private fun toLayoutHyphenationFrequency(hyphens: Hyphens?): Int = when (hyphens) {
         Hyphens.Auto -> if (Build.VERSION.SDK_INT <= 32) {
-            LayoutCompat.HYPHENATION_FREQUENCY_NORMAL
+            Layout.HYPHENATION_FREQUENCY_NORMAL
         } else {
-            LayoutCompat.HYPHENATION_FREQUENCY_NORMAL_FAST
+            Layout.HYPHENATION_FREQUENCY_NORMAL_FAST
         }
-        Hyphens.None -> LayoutCompat.HYPHENATION_FREQUENCY_NONE
-        else -> LayoutCompat.HYPHENATION_FREQUENCY_NONE
+        Hyphens.None -> Layout.HYPHENATION_FREQUENCY_NONE
+        else -> Layout.HYPHENATION_FREQUENCY_NONE
     }
 
     private fun toLayoutBreakStrategy(breakStrategy: LineBreak.Strategy?): Int =
         when (breakStrategy) {
-            LineBreak.Strategy.Simple -> LayoutCompat.BREAK_STRATEGY_SIMPLE
-            LineBreak.Strategy.HighQuality -> LayoutCompat.BREAK_STRATEGY_HIGH_QUALITY
-            LineBreak.Strategy.Balanced -> LayoutCompat.BREAK_STRATEGY_BALANCED
-            else -> LayoutCompat.BREAK_STRATEGY_SIMPLE
+            LineBreak.Strategy.Simple -> LineBreaker.BREAK_STRATEGY_SIMPLE
+            LineBreak.Strategy.HighQuality -> LineBreaker.BREAK_STRATEGY_HIGH_QUALITY
+            LineBreak.Strategy.Balanced -> LineBreaker.BREAK_STRATEGY_BALANCED
+            else -> LineBreaker.BREAK_STRATEGY_SIMPLE
         }
 
     private fun toLayoutLineBreakStyle(lineBreakStrictness: LineBreak.Strictness?): Int =
         when (lineBreakStrictness) {
-            LineBreak.Strictness.Default -> LayoutCompat.LINE_BREAK_STYLE_NONE
-            LineBreak.Strictness.Loose -> LayoutCompat.LINE_BREAK_STYLE_LOOSE
-            LineBreak.Strictness.Normal -> LayoutCompat.LINE_BREAK_STYLE_NORMAL
-            LineBreak.Strictness.Strict -> LayoutCompat.LINE_BREAK_STYLE_STRICT
-            else -> LayoutCompat.LINE_BREAK_STYLE_NONE
+            LineBreak.Strictness.Default -> LineBreakConfig.LINE_BREAK_STYLE_NONE
+            LineBreak.Strictness.Loose -> LineBreakConfig.LINE_BREAK_STYLE_LOOSE
+            LineBreak.Strictness.Normal -> LineBreakConfig.LINE_BREAK_STYLE_NORMAL
+            LineBreak.Strictness.Strict -> LineBreakConfig.LINE_BREAK_STYLE_STRICT
+            else -> LineBreakConfig.LINE_BREAK_STYLE_NONE
         }
 
     private fun toLayoutLineBreakWordStyle(lineBreakWordStyle: LineBreak.WordBreak?): Int =
         when (lineBreakWordStyle) {
-            LineBreak.WordBreak.Default -> LayoutCompat.LINE_BREAK_WORD_STYLE_NONE
-            LineBreak.WordBreak.Phrase -> LayoutCompat.LINE_BREAK_WORD_STYLE_PHRASE
-            else -> LayoutCompat.LINE_BREAK_WORD_STYLE_NONE
+            LineBreak.WordBreak.Default -> LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE
+            LineBreak.WordBreak.Phrase -> LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE
+            else -> LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE
         }
 }
 
diff --git a/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/ParagraphBenchmark.kt b/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/ParagraphBenchmark.kt
index c05380c..7c217b8 100644
--- a/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/ParagraphBenchmark.kt
+++ b/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/ParagraphBenchmark.kt
@@ -23,7 +23,6 @@
 import androidx.compose.ui.graphics.Canvas
 import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.Paragraph
 import androidx.compose.ui.text.ParagraphIntrinsics
 import androidx.compose.ui.text.SpanStyle
@@ -111,7 +110,6 @@
         )
     }
 
-    @OptIn(ExperimentalTextApi::class)
     private fun paragraphIntrinsics(
         text: String,
         spanStyles: List<AnnotatedString.Range<SpanStyle>>
diff --git a/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/ParagraphMethodBenchmark.kt b/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/ParagraphMethodBenchmark.kt
index 64e36a5..e5bbed1 100644
--- a/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/ParagraphMethodBenchmark.kt
+++ b/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/ParagraphMethodBenchmark.kt
@@ -20,7 +20,6 @@
 import androidx.benchmark.junit4.BenchmarkRule
 import androidx.benchmark.junit4.measureRepeated
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.Paragraph
 import androidx.compose.ui.text.ParagraphIntrinsics
 import androidx.compose.ui.text.TextStyle
@@ -60,7 +59,6 @@
     private val context: Context = InstrumentationRegistry.getInstrumentation().context
 
     // A fake resource loader required to construct Paragraph
-    @OptIn(ExperimentalTextApi::class)
     private val fontFamilyResolver = createFontFamilyResolver(context)
 
     private fun paragraphIntrinsics(
diff --git a/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/font/PlatformFontLookup.kt b/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/font/PlatformFontLookup.kt
index 5a1c3d3c..6bfae1e 100644
--- a/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/font/PlatformFontLookup.kt
+++ b/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/font/PlatformFontLookup.kt
@@ -19,7 +19,6 @@
 import android.content.Context
 import androidx.benchmark.junit4.BenchmarkRule
 import androidx.benchmark.junit4.measureRepeated
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.benchmark.cartesian
 import androidx.compose.ui.text.font.FontFamily
@@ -61,7 +60,7 @@
 
     private val context: Context = InstrumentationRegistry.getInstrumentation().context
 
-    @OptIn(ExperimentalTextApi::class, InternalTextApi::class)
+    @OptIn(InternalTextApi::class)
     @Test
     fun forceUncached() {
         benchmarkRule.measureRepeated {
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt
index afd1452..0a5a040 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/AndroidParagraphTest.kt
@@ -96,7 +96,6 @@
     private val defaultDensity = Density(density = 1f)
     private val context = InstrumentationRegistry.getInstrumentation().context
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun draw_with_newline_and_line_break_default_values() {
         with(defaultDensity) {
@@ -1012,7 +1011,6 @@
         assertThat(paragraph.textPaint.typeface).isNull()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     @MediumTest
     fun testEmptyFontFamily_withBoldFontWeightSelection() {
@@ -1030,7 +1028,6 @@
         assertThat(typeface.isItalic).isFalse()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     @MediumTest
     fun testEmptyFontFamily_withFontStyleSelection() {
@@ -1049,7 +1046,6 @@
         assertThat(typeface.isItalic).isTrue()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     @MediumTest
     fun testFontFamily_withGenericFamilyName() {
@@ -1069,7 +1065,6 @@
         assertThat(typeface.isItalic).isFalse()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     @MediumTest
     fun testFontFamily_withCustomFont() {
@@ -1931,7 +1926,6 @@
         assertThat(paragraph.textLocale.toLanguageTag()).isEqualTo("ja")
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun withIncludeFontPadding() {
         val text = "A"
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/FontTestData.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/FontTestData.kt
index 9e8e21e..5e4966f3 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/FontTestData.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/FontTestData.kt
@@ -22,7 +22,6 @@
 import androidx.testutils.fonts.R
 
 class FontTestData {
-    @OptIn(ExperimentalTextApi::class)
     companion object {
         // This sample font provides the following features:
         // 1. The width of most of visible characters equals to font size.
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt
index 8c5366f..efe1944 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt
@@ -1589,7 +1589,6 @@
             .isEqualToBitmap(multiParagraph2.bitmap(brush, 0.5f))
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun multiParagraph_appliesDrawStyle_toAllParagraphs() = with(defaultDensity) {
         val fontSize = 20.sp
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphFillBoundingBoxesTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphFillBoundingBoxesTest.kt
index ea4fad9..94f081a 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphFillBoundingBoxesTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphFillBoundingBoxesTest.kt
@@ -177,7 +177,6 @@
         ).isEqualToWithTolerance(ltrCharacterBoundariesForTestFont(text))
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun singleCharacterLineHeight_includeFontPaddingIsFalse() {
         val lineHeight = fontSize * 2
@@ -208,7 +207,6 @@
         )
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun multiLineCharacterLineHeight() {
         val lineHeight = fontSize * 2
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationIndentationFixTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationIndentationFixTest.kt
index 54aa03d..20fe4e3 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationIndentationFixTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationIndentationFixTest.kt
@@ -65,7 +65,7 @@
         }
     }
 
-    @SdkSuppress(minSdkVersion = 22) // b/269193223
+    @SdkSuppress(minSdkVersion = 23) // b/266743243
     @Test
     fun getLineLeftAndGetLineRight_Ltr_TextIndent() {
         val paragraph = paragraph(
@@ -149,7 +149,7 @@
         }
     }
 
-    @SdkSuppress(minSdkVersion = 22) // b/269193223
+    @SdkSuppress(minSdkVersion = 23) // b/266743243
     @Test
     fun getLineLeftAndGetLineRight_Ltr_TextIndent_sp_letterspacing() {
         val paragraph = paragraph(
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationLineHeightStyleTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationLineHeightStyleTest.kt
index ddef649..d68bd57 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationLineHeightStyleTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationLineHeightStyleTest.kt
@@ -36,7 +36,6 @@
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
-@OptIn(ExperimentalTextApi::class)
 class ParagraphIntegrationLineHeightStyleTest {
     private val fontFamilyMeasureFont = FontTestData.BASIC_MEASURE_FONT.toFontFamily()
     private val context = InstrumentationRegistry.getInstrumentation().context
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt
index 1220a5b..c2fe639 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt
@@ -2905,7 +2905,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun lineHeight_InEm_when_includeFontPadding_is_false() {
         val text = "abcdefgh"
@@ -2939,7 +2938,6 @@
     }
 
     @Suppress("DEPRECATION")
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun lineHeight_IsAppliedToFirstLine_when_includeFontPadding_is_true() {
         // values such as text or TextStyle attributes are from the b/227095468
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntrinsicsAsyncTypefaceTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntrinsicsAsyncTypefaceTest.kt
index d9571798..0aa49d2 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntrinsicsAsyncTypefaceTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntrinsicsAsyncTypefaceTest.kt
@@ -58,7 +58,7 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class, ExperimentalCoroutinesApi::class)
+    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
     fun hasStaleResolvedFonts_trueOnTypefaceUpdate_mainTypeface() {
         val loader = AsyncTestTypefaceLoader()
@@ -90,7 +90,7 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class, ExperimentalCoroutinesApi::class)
+    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
     fun hasStaleResolvedFonts_trueOnTypefaceUpdate_spanTypeface() {
         val loader = AsyncTestTypefaceLoader()
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextTestExtensions.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextTestExtensions.kt
index 0a6ca15..d629400 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextTestExtensions.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/TextTestExtensions.kt
@@ -152,7 +152,6 @@
     }
 }
 
-@OptIn(ExperimentalTextApi::class)
 internal fun UncachedFontFamilyResolver(
     context: Context
 ): FontFamily.Resolver = UncachedFontFamilyResolver(
@@ -160,7 +159,6 @@
     AndroidFontResolveInterceptor(context)
 )
 
-@OptIn(ExperimentalTextApi::class)
 internal fun UncachedFontFamilyResolver(
     platformFontLoader: PlatformFontLoader,
     platformResolveInterceptor: PlatformResolveInterceptor
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/AndroidFontTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/AndroidFontTest.kt
index d95adfb..21b13dd 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/AndroidFontTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/AndroidFontTest.kt
@@ -19,7 +19,6 @@
 import android.os.Build
 import android.os.ParcelFileDescriptor
 import androidx.annotation.RequiresApi
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.font.FontLoadingStrategy.Companion.Blocking
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -34,7 +33,6 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalTextApi::class)
 class AndroidFontTest {
     private var tempFile: File? = null
     private val context = InstrumentationRegistry.getInstrumentation().context
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/AndroidFontUtilsTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/AndroidFontUtilsTest.kt
index 8a55cb2..765f066 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/AndroidFontUtilsTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/AndroidFontUtilsTest.kt
@@ -17,7 +17,6 @@
 package androidx.compose.ui.text.font
 
 import android.graphics.Typeface
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
@@ -26,7 +25,6 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalTextApi::class)
 class AndroidFontUtilsTest {
 
     @Test
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/AndroidVariableFontTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/AndroidVariableFontTest.kt
index 5de107fd..a7abc96 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/AndroidVariableFontTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/AndroidVariableFontTest.kt
@@ -22,7 +22,6 @@
 import android.graphics.Typeface
 import android.os.ParcelFileDescriptor
 import android.text.TextPaint
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
@@ -34,7 +33,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@OptIn(ExperimentalTextApi::class)
 @MediumTest
 @RunWith(AndroidJUnit4::class)
 class AndroidVariableFontTest {
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsageTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsageTest.kt
index 2829a44..57dde35 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsageTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsageTest.kt
@@ -17,7 +17,6 @@
 package androidx.compose.ui.text.font
 
 import android.graphics.Typeface
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.font.testutils.AsyncFauxFont
 import androidx.compose.ui.text.font.testutils.AsyncTestTypefaceLoader
 import androidx.compose.ui.text.font.testutils.BlockingFauxFont
@@ -32,7 +31,6 @@
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
-@OptIn(ExperimentalTextApi::class)
 class DelegatingFontLoaderForDeprecatedUsageTest {
 
     private val context = InstrumentationRegistry.getInstrumentation().context
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/DeviceFontFamilyNameFontTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/DeviceFontFamilyNameFontTest.kt
index 716e856..260ad83 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/DeviceFontFamilyNameFontTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/DeviceFontFamilyNameFontTest.kt
@@ -17,7 +17,6 @@
 package androidx.compose.ui.text.font
 
 import android.graphics.Typeface
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.platform.app.InstrumentationRegistry
@@ -28,7 +27,6 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalTextApi::class)
 class DeviceFontFamilyNameFontTest {
 
     val context = InstrumentationRegistry.getInstrumentation().context
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverFileTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverFileTest.kt
index 7e49709..d0c931e 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverFileTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverFileTest.kt
@@ -19,7 +19,6 @@
 import android.content.Context
 import android.graphics.Typeface
 import android.os.ParcelFileDescriptor
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.UncachedFontFamilyResolver
 import androidx.compose.ui.text.matchers.assertThat
 import androidx.compose.ui.text.platform.bitmap
@@ -35,7 +34,6 @@
 
 @RunWith(AndroidJUnit4::class)
 @MediumTest
-@OptIn(ExperimentalTextApi::class)
 class FontFamilyResolverFileTest {
     private val context = InstrumentationRegistry.getInstrumentation().context
     private val assetFontPath = "subdirectory/asset_font.ttf"
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverImplCancellationTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverImplCancellationTest.kt
index ef79faf8..cbf8603 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverImplCancellationTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverImplCancellationTest.kt
@@ -19,7 +19,6 @@
 package androidx.compose.ui.text.font
 
 import android.graphics.Typeface
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.font.testutils.AsyncFauxFont
 import androidx.compose.ui.text.font.testutils.AsyncTestTypefaceLoader
 import androidx.compose.ui.text.matchers.assertThat
@@ -39,7 +38,6 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalTextApi::class)
 @ExperimentalCoroutinesApi
 class FontFamilyResolverImplCancellationTest {
     private lateinit var typefaceLoader: AsyncTestTypefaceLoader
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverImplPreloadTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverImplPreloadTest.kt
index ec16989..ad5dbea 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverImplPreloadTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverImplPreloadTest.kt
@@ -19,7 +19,6 @@
 package androidx.compose.ui.text.font
 
 import android.graphics.Typeface
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.FontTestData
 import androidx.compose.ui.text.font.testutils.AsyncFauxFont
 import androidx.compose.ui.text.font.testutils.AsyncTestTypefaceLoader
@@ -44,7 +43,6 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalTextApi::class)
 @ExperimentalCoroutinesApi
 class FontFamilyResolverImplPreloadTest {
     private lateinit var typefaceLoader: AsyncTestTypefaceLoader
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverImplTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverImplTest.kt
index 01d7dad..2dcdb87 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverImplTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontFamilyResolverImplTest.kt
@@ -19,7 +19,6 @@
 import android.content.Context
 import android.graphics.Typeface
 import android.os.Build
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.FontTestData
 import androidx.compose.ui.text.UncachedFontFamilyResolver
 import androidx.compose.ui.text.font.testutils.AsyncFauxFont
@@ -45,7 +44,6 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalTextApi::class)
 @ExperimentalCoroutinesApi
 class FontFamilyResolverImplTest {
     private lateinit var scope: TestScope
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapterPreloadTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapterPreloadTest.kt
index f477e95..44a252f 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapterPreloadTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapterPreloadTest.kt
@@ -19,7 +19,6 @@
 package androidx.compose.ui.text.font
 
 import android.graphics.Typeface
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.font.testutils.AsyncFauxFont
 import androidx.compose.ui.text.font.testutils.AsyncTestTypefaceLoader
 import androidx.compose.ui.text.font.testutils.BlockingFauxFont
@@ -43,7 +42,6 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalTextApi::class)
 class FontListFontFamilyTypefaceAdapterPreloadTest {
 
     private lateinit var typefaceLoader: AsyncTestTypefaceLoader
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapterTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapterTest.kt
index 26eda8c..26b1954 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapterTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapterTest.kt
@@ -20,7 +20,6 @@
 
 import android.content.Context
 import android.graphics.Typeface
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.font.testutils.AsyncFauxFont
 import androidx.compose.ui.text.font.testutils.AsyncTestTypefaceLoader
 import androidx.compose.ui.text.font.testutils.BlockingFauxFont
@@ -56,7 +55,6 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalTextApi::class)
 class FontListFontFamilyTypefaceAdapterTest {
 
     private lateinit var typefaceLoader: AsyncTestTypefaceLoader
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontSynthesisTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontSynthesisTest.kt
index eee7308..fcff7c3 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontSynthesisTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/FontSynthesisTest.kt
@@ -18,7 +18,6 @@
 
 import android.graphics.Typeface
 import android.os.Build
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.FontTestData
 import androidx.compose.ui.text.UncachedFontFamilyResolver
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -30,7 +29,6 @@
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
-@OptIn(ExperimentalTextApi::class)
 class FontSynthesisTest {
 
     private val context = InstrumentationRegistry.getInstrumentation().context
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/PlatformFontFamilyTypefaceAdapterTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/PlatformFontFamilyTypefaceAdapterTest.kt
index b5ff002..e5296c7 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/PlatformFontFamilyTypefaceAdapterTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/PlatformFontFamilyTypefaceAdapterTest.kt
@@ -17,7 +17,6 @@
 package androidx.compose.ui.text.font
 
 import android.graphics.Typeface
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.FontTestData
 import androidx.compose.ui.text.matchers.assertThat
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -31,7 +30,6 @@
 // see: (b/154330441)
 @RunWith(AndroidJUnit4::class)
 @MediumTest
-@OptIn(ExperimentalTextApi::class)
 class PlatformFontFamilyTypefaceAdapterTest {
 
     private val context = InstrumentationRegistry.getInstrumentation().context
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/PlatformTypefacesTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/PlatformTypefacesTest.kt
index c3d6828..6e3e298 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/PlatformTypefacesTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/PlatformTypefacesTest.kt
@@ -17,7 +17,6 @@
 package androidx.compose.ui.text.font
 
 import androidx.annotation.RequiresApi
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
@@ -129,7 +128,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun assertOptionalOnDeviceFontFamilyByName_returnsPlatformFallback_SansSerif_allWeights() {
         val subject = PlatformTypefaces()
@@ -150,7 +148,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun assertOptionalOnDeviceFontFamilyByName_returnsPlatformFallback_Serif_allWeights() {
         val subject = PlatformTypefaces()
@@ -171,7 +168,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun assertOptionalOnDeviceFontFamilyByName_returnsPlatformFallback_Monospace_allWeights() {
         val subject = PlatformTypefaces()
@@ -192,7 +188,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun assertOptionalOnDeviceFontFamilyByName_returnsPlatformFallback_Cursive_allWeights() {
         val subject = PlatformTypefaces()
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/testutils/AsyncTestFonts.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/testutils/AsyncTestFonts.kt
index e6b6a47..0c3455e 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/testutils/AsyncTestFonts.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/testutils/AsyncTestFonts.kt
@@ -133,7 +133,6 @@
     }
 }
 
-@OptIn(ExperimentalTextApi::class)
 class AsyncFauxFont(
     typefaceLoader: AsyncTestTypefaceLoader,
     override val weight: FontWeight = FontWeight.Normal,
@@ -145,7 +144,6 @@
     }
 }
 
-@OptIn(ExperimentalTextApi::class)
 class OptionalFauxFont(
     typefaceLoader: AsyncTestTypefaceLoader,
     internal val typeface: Typeface?,
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/testutils/FontFamilyResolverUtils.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/testutils/FontFamilyResolverUtils.kt
index fa18632..ce28acf 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/testutils/FontFamilyResolverUtils.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/font/testutils/FontFamilyResolverUtils.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.ui.text.font.testutils
 
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.font.AndroidFontLoader
 import androidx.compose.ui.text.font.FontFamily
 import androidx.compose.ui.text.font.FontStyle
@@ -35,7 +34,6 @@
  *
  * @return null if cache miss, otherwise result if [TypefaceResult.Immutable]
  */
-@OptIn(ExperimentalTextApi::class)
 internal fun TypefaceRequestCache.getImmutableResultFor(
     fontFamily: FontFamily,
     fontWeight: FontWeight = FontWeight.Normal,
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/AndroidAccessibilitySpannableStringTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/AndroidAccessibilitySpannableStringTest.kt
index 55fdaed..9c0efcb 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/AndroidAccessibilitySpannableStringTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/AndroidAccessibilitySpannableStringTest.kt
@@ -58,17 +58,18 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import androidx.test.platform.app.InstrumentationRegistry
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@OptIn(InternalTextApi::class)
+@OptIn(InternalTextApi::class, ExperimentalTextApi::class)
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 class AndroidAccessibilitySpannableStringTest {
     private val context = InstrumentationRegistry.getInstrumentation().context
     private val density = Density(context)
-    private val resourceLoader = UncachedFontFamilyResolver(context)
+    private val fontFamilyResolver = UncachedFontFamilyResolver(context)
+    private val urlSpanCache = URLSpanCache()
 
     @Test
     fun toAccessibilitySpannableString_with_locale() {
@@ -80,8 +81,11 @@
             }
         }
 
-        val spannableString =
-            annotatedString.toAccessibilitySpannableString(density, resourceLoader)
+        val spannableString = annotatedString.toAccessibilitySpannableString(
+            density,
+            fontFamilyResolver,
+            urlSpanCache
+        )
 
         assertThat(spannableString).isInstanceOf(SpannableString::class.java)
         assertThat(spannableString).hasSpan(
@@ -102,8 +106,11 @@
             }
         }
 
-        val spannableString =
-            annotatedString.toAccessibilitySpannableString(density, resourceLoader)
+        val spannableString = annotatedString.toAccessibilitySpannableString(
+            density,
+            fontFamilyResolver,
+            urlSpanCache
+        )
 
         assertThat(spannableString).isInstanceOf(SpannableString::class.java)
         assertThat(spannableString).hasSpan(
@@ -123,8 +130,11 @@
             }
         }
 
-        val spannableString =
-            annotatedString.toAccessibilitySpannableString(density, resourceLoader)
+        val spannableString = annotatedString.toAccessibilitySpannableString(
+            density,
+            fontFamilyResolver,
+            urlSpanCache
+        )
 
         assertThat(spannableString).isInstanceOf(SpannableString::class.java)
         assertThat(spannableString).hasSpan(
@@ -144,8 +154,11 @@
             }
         }
 
-        val spannableString =
-            annotatedString.toAccessibilitySpannableString(density, resourceLoader)
+        val spannableString = annotatedString.toAccessibilitySpannableString(
+            density,
+            fontFamilyResolver,
+            urlSpanCache
+        )
 
         assertThat(spannableString).isInstanceOf(SpannableString::class.java)
         assertThat(spannableString).hasSpan(
@@ -164,8 +177,11 @@
             }
         }
 
-        val spannableString =
-            annotatedString.toAccessibilitySpannableString(density, resourceLoader)
+        val spannableString = annotatedString.toAccessibilitySpannableString(
+            density,
+            fontFamilyResolver,
+            urlSpanCache
+        )
 
         assertThat(spannableString).isInstanceOf(SpannableString::class.java)
         assertThat(spannableString).hasSpan(
@@ -184,8 +200,11 @@
             }
         }
 
-        val spannableString =
-            annotatedString.toAccessibilitySpannableString(density, resourceLoader)
+        val spannableString = annotatedString.toAccessibilitySpannableString(
+            density,
+            fontFamilyResolver,
+            urlSpanCache
+        )
 
         assertThat(spannableString).isInstanceOf(SpannableString::class.java)
         assertThat(spannableString).hasSpan(
@@ -205,11 +224,14 @@
             }
         }
 
-        val spannableString =
-            annotatedString.toAccessibilitySpannableString(density, resourceLoader)
+        val spannableString = annotatedString.toAccessibilitySpannableString(
+            density,
+            fontFamilyResolver,
+            urlSpanCache
+        )
 
         assertThat(spannableString).isInstanceOf(SpannableString::class.java)
-        Truth.assertThat(
+        assertThat(
             spannableString.getSpans(
                 0,
                 spannableString.length,
@@ -227,8 +249,11 @@
             }
         }
 
-        val spannableString =
-            annotatedString.toAccessibilitySpannableString(density, resourceLoader)
+        val spannableString = annotatedString.toAccessibilitySpannableString(
+            density,
+            fontFamilyResolver,
+            urlSpanCache
+        )
 
         assertThat(spannableString).isInstanceOf(SpannableString::class.java)
         assertThat(spannableString).hasSpan(
@@ -245,8 +270,11 @@
             }
         }
 
-        val spannableString =
-            annotatedString.toAccessibilitySpannableString(density, resourceLoader)
+        val spannableString = annotatedString.toAccessibilitySpannableString(
+            density,
+            fontFamilyResolver,
+            urlSpanCache
+        )
 
         assertThat(spannableString).isInstanceOf(SpannableString::class.java)
         assertThat(spannableString).hasSpan(
@@ -266,8 +294,11 @@
             }
         }
 
-        val spannableString =
-            annotatedString.toAccessibilitySpannableString(density, resourceLoader)
+        val spannableString = annotatedString.toAccessibilitySpannableString(
+            density,
+            fontFamilyResolver,
+            urlSpanCache
+        )
 
         assertThat(spannableString).isInstanceOf(SpannableString::class.java)
         assertThat(spannableString).hasSpan(
@@ -287,8 +318,11 @@
             }
         }
 
-        val spannableString =
-            annotatedString.toAccessibilitySpannableString(density, resourceLoader)
+        val spannableString = annotatedString.toAccessibilitySpannableString(
+            density,
+            fontFamilyResolver,
+            urlSpanCache
+        )
 
         assertThat(spannableString).isInstanceOf(SpannableString::class.java)
         assertThat(spannableString).hasSpan(
@@ -298,7 +332,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun toAccessibilitySpannableString_with_verbatimTtsAnnotation() {
         val annotatedString = buildAnnotatedString {
@@ -308,8 +341,11 @@
             }
         }
 
-        val spannableString =
-            annotatedString.toAccessibilitySpannableString(density, resourceLoader)
+        val spannableString = annotatedString.toAccessibilitySpannableString(
+            density,
+            fontFamilyResolver,
+            urlSpanCache
+        )
 
         assertThat(spannableString).isInstanceOf(SpannableString::class.java)
         assertThat(spannableString).hasSpan(
@@ -320,7 +356,6 @@
         }
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun toAccessibilitySpannableString_with_urlAnnotation() {
         val annotatedString = buildAnnotatedString {
@@ -330,8 +365,11 @@
             }
         }
 
-        val spannableString =
-            annotatedString.toAccessibilitySpannableString(density, resourceLoader)
+        val spannableString = annotatedString.toAccessibilitySpannableString(
+            density,
+            fontFamilyResolver,
+            urlSpanCache
+        )
 
         assertThat(spannableString).isInstanceOf(SpannableString::class.java)
         assertThat(spannableString).hasSpan(
@@ -341,7 +379,6 @@
         }
     }
 
-    @OptIn(InternalTextApi::class)
     @Test
     fun fontsInSpanStyles_areIgnored() {
         // b/232238615
@@ -356,9 +393,32 @@
         val fontFamilyResolver = createFontFamilyResolver(context)
 
         // see if font span is added
-        string.toAccessibilitySpannableString(density, fontFamilyResolver)
+        string.toAccessibilitySpannableString(density, fontFamilyResolver, URLSpanCache())
 
         // toAccessibilitySpannableString should _not_ make any font requests
-        Truth.assertThat(loader.blockingRequests).isEmpty()
+        assertThat(loader.blockingRequests).isEmpty()
+    }
+
+    /**
+     * A11y services rely on ClickableSpans being the same instance for the same source spans across
+     * multiple calls of [toAccessibilitySpannableString] for the same source string. If this is not
+     * the case, their onClick handler will not be invoked. See b/253292081.
+     */
+    @Test
+    fun urlSpansAreSameInstanceForSameAnnotatedString() {
+        val string = buildAnnotatedString {
+            pushUrlAnnotation(UrlAnnotation("google.com"))
+            append("link")
+        }
+
+        val spannable1 =
+            string.toAccessibilitySpannableString(density, fontFamilyResolver, urlSpanCache)
+        val spannable2 =
+            string.toAccessibilitySpannableString(density, fontFamilyResolver, urlSpanCache)
+        val urlSpan1 = spannable1.getSpans(0, string.length, URLSpan::class.java).single()
+        val urlSpan2 = spannable2.getSpans(0, string.length, URLSpan::class.java).single()
+
+        assertThat(spannable1).isNotSameInstanceAs(spannable2)
+        assertThat(urlSpan1).isSameInstanceAs(urlSpan2)
     }
 }
\ No newline at end of file
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/AndroidTypefaceTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/AndroidTypefaceTest.kt
index 14b54fc..9df4326 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/AndroidTypefaceTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/AndroidTypefaceTest.kt
@@ -464,7 +464,6 @@
     }
 
     @Test(expected = IllegalStateException::class)
-    @OptIn(ExperimentalTextApi::class)
     fun throwsExceptionIfFontIsNotIncludedInTheApp() {
         val fontFamily = FontFamily(Font(-1))
         androidTypefaceFromFontFamily(context, fontFamily)
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/TextTestExtensions.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/TextTestExtensions.kt
index ab1625f..3717a8e 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/TextTestExtensions.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/platform/TextTestExtensions.kt
@@ -35,7 +35,7 @@
 }
 
 @OptIn(InternalPlatformTextApi::class)
-fun TextLayout.bitmap(): Bitmap {
+internal fun TextLayout.bitmap(): Bitmap {
     return layout.bitmap()
 }
 
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/style/HyphensTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/style/HyphensTest.kt
index 9daf874..aa850f8 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/style/HyphensTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/style/HyphensTest.kt
@@ -16,12 +16,10 @@
 
 package androidx.compose.ui.text.style
 
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.test.filters.SdkSuppress
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
-@OptIn(ExperimentalTextApi::class)
 @SdkSuppress(minSdkVersion = 28)
 class HyphensTest : TextLineBreaker() {
     private val text = "Transformation"
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/style/LineBreakTest.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/style/LineBreakTest.kt
index d657f6a..d86dfa9 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/style/LineBreakTest.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/style/LineBreakTest.kt
@@ -215,7 +215,7 @@
         assertThat(brokenLines).isEqualTo(expectedBrokenLines)
     }
 
-    @SdkSuppress(minSdkVersion = 22) // b/269193836
+    @SdkSuppress(minSdkVersion = 23) // b/269193836
     @Test
     fun correct_lineBreak_paragraph_result() {
         val expectedBrokenLines = listOf(
@@ -233,7 +233,7 @@
         assertThat(brokenLines).isEqualTo(expectedBrokenLines)
     }
 
-    @SdkSuppress(minSdkVersion = 22) // b/269193836
+    @SdkSuppress(minSdkVersion = 23) // b/269193836
     @Test
     fun correct_lineBreak_heading_result() {
         val expectedBrokenLines = listOf(
diff --git a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/style/TextLineBreaker.kt b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/style/TextLineBreaker.kt
index 6f3ff165..a0f3c2a 100644
--- a/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/style/TextLineBreaker.kt
+++ b/compose/ui/ui-text/src/androidAndroidTest/kotlin/androidx/compose/ui/text/style/TextLineBreaker.kt
@@ -16,66 +16,40 @@
 
 package androidx.compose.ui.text.style
 
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.text.MultiParagraph
-import androidx.compose.ui.text.TextLayoutInput
-import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.AndroidParagraph
+import androidx.compose.ui.text.Paragraph
 import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.ceilToInt
 import androidx.compose.ui.text.font.createFontFamilyResolver
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.constrain
 import androidx.test.platform.app.InstrumentationRegistry
-import kotlin.math.ceil
 
-@OptIn(ExperimentalTextApi::class)
 open class TextLineBreaker {
-    private val defaultDensity = Density(1f)
     private val context = InstrumentationRegistry.getInstrumentation().context
     private val fontFamilyResolver = createFontFamilyResolver(context)
     private val defaultHyphens = Hyphens.None
     private val defaultLineBreak = LineBreak.Simple
+    private val density = Density(density = 1f)
 
-    private fun constructTextLayoutResult(
+    private fun paragraph(
         text: String,
         textStyle: TextStyle,
         maxWidth: Int = Constraints.Infinity
-    ): TextLayoutResult {
-        val constraints = Constraints(maxWidth = maxWidth)
-
-        val input = TextLayoutInput(
-            text = AnnotatedString(text),
-            style = textStyle,
+    ): Paragraph {
+        return AndroidParagraph(
+            text = text,
+            spanStyles = listOf(),
             placeholders = listOf(),
+            style = textStyle,
             maxLines = Int.MAX_VALUE,
-            softWrap = true,
-            overflow = TextOverflow.Visible,
-            density = defaultDensity,
-            layoutDirection = LayoutDirection.Ltr,
-            fontFamilyResolver = fontFamilyResolver,
-            constraints = constraints
-        )
-
-        val paragraph = MultiParagraph(
-            annotatedString = input.text,
-            style = input.style,
-            constraints = input.constraints,
-            density = input.density,
-            fontFamilyResolver = input.fontFamilyResolver
-        )
-
-        return TextLayoutResult(
-            layoutInput = input,
-            multiParagraph = paragraph,
-            size = constraints.constrain(
-                IntSize(
-                    ceil(paragraph.width).toInt(),
-                    ceil(paragraph.height).toInt()
-                )
-            )
+            ellipsis = false,
+            constraints = Constraints(
+                maxWidth = maxWidth,
+                maxHeight = Float.POSITIVE_INFINITY.ceilToInt()
+            ),
+            density = density,
+            fontFamilyResolver = fontFamilyResolver
         )
     }
 
@@ -85,7 +59,7 @@
         lineBreak: LineBreak = defaultLineBreak,
         maxWidth: Int
     ): List<String> {
-        val layoutResult = constructTextLayoutResult(
+        val layoutResult = paragraph(
             text = text,
             textStyle = TextStyle(hyphens = hyphens, lineBreak = lineBreak),
             maxWidth = maxWidth
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidParagraph.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidParagraph.android.kt
index 49abe46..3aff906 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidParagraph.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/AndroidParagraph.android.kt
@@ -561,7 +561,7 @@
     else -> DEFAULT_ALIGNMENT
 }
 
-@OptIn(ExperimentalTextApi::class, InternalPlatformTextApi::class)
+@OptIn(InternalPlatformTextApi::class)
 private fun toLayoutHyphenationFrequency(hyphens: Hyphens?): Int = when (hyphens) {
     Hyphens.Auto -> if (Build.VERSION.SDK_INT <= 32) {
         HYPHENATION_FREQUENCY_NORMAL
@@ -572,7 +572,7 @@
     else -> DEFAULT_HYPHENATION_FREQUENCY
 }
 
-@OptIn(ExperimentalTextApi::class, InternalPlatformTextApi::class)
+@OptIn(InternalPlatformTextApi::class)
 private fun toLayoutBreakStrategy(breakStrategy: LineBreak.Strategy?): Int = when (breakStrategy) {
     LineBreak.Strategy.Simple -> BREAK_STRATEGY_SIMPLE
     LineBreak.Strategy.HighQuality -> BREAK_STRATEGY_HIGH_QUALITY
@@ -580,7 +580,7 @@
     else -> DEFAULT_BREAK_STRATEGY
 }
 
-@OptIn(ExperimentalTextApi::class, InternalPlatformTextApi::class)
+@OptIn(InternalPlatformTextApi::class)
 private fun toLayoutLineBreakStyle(lineBreakStrictness: LineBreak.Strictness?): Int =
     when (lineBreakStrictness) {
         LineBreak.Strictness.Default -> LINE_BREAK_STYLE_NONE
@@ -590,7 +590,7 @@
         else -> DEFAULT_LINE_BREAK_STYLE
     }
 
-@OptIn(ExperimentalTextApi::class, InternalPlatformTextApi::class)
+@OptIn(InternalPlatformTextApi::class)
 private fun toLayoutLineBreakWordStyle(lineBreakWordStyle: LineBreak.WordBreak?): Int =
     when (lineBreakWordStyle) {
         LineBreak.WordBreak.Default -> LINE_BREAK_WORD_STYLE_NONE
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/AndroidPreloadedFont.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/AndroidPreloadedFont.kt
index 5f0cad7..2dfc00b 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/AndroidPreloadedFont.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/AndroidPreloadedFont.kt
@@ -29,8 +29,7 @@
 import androidx.compose.ui.util.fastMap
 import java.io.File
 
-@OptIn(ExperimentalTextApi::class)
-internal sealed class AndroidPreloadedFont @OptIn(ExperimentalTextApi::class) constructor(
+internal sealed class AndroidPreloadedFont constructor(
     final override val weight: FontWeight,
     final override val style: FontStyle,
     variationSettings: FontVariation.Settings
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.android.kt
index d5927c4..24ceb0a 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.android.kt
@@ -17,7 +17,6 @@
 package androidx.compose.ui.text.font
 
 import android.content.Context
-import androidx.compose.ui.text.ExperimentalTextApi
 
 /**
  * Bridge between subclasses of Font.ResourceLoader and the new FontFamily.Resolver API.
@@ -38,7 +37,6 @@
  * A FontFamily.Resolver created this way will not share caches with other FontFamily.Resolvers.
  */
 @Suppress("DEPRECATION")
-@OptIn(ExperimentalTextApi::class)
 @Deprecated("This exists to bridge existing Font.ResourceLoader subclasses to be used as a" +
     "FontFamily.ResourceLoader during upgrade.",
     replaceWith = ReplaceWith("createFontFamilyResolver()"),
@@ -53,7 +51,6 @@
 }
 
 @Suppress("DEPRECATION")
-@OptIn(ExperimentalTextApi::class)
 @Deprecated("This exists to bridge existing Font.ResourceLoader APIs, and should be " +
     "removed with them",
     replaceWith = ReplaceWith("createFontFamilyResolver()"),
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.android.kt
index 88a701a..76cef40 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.android.kt
@@ -19,7 +19,6 @@
 import android.content.Context
 import android.graphics.Typeface
 import androidx.compose.runtime.State
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.InternalTextApi
 import kotlin.coroutines.CoroutineContext
 
@@ -89,7 +88,6 @@
  *
  * @suppress
  */
-@OptIn(ExperimentalTextApi::class)
 @InternalTextApi // exposed for benchmarking, not a stable API.
 fun emptyCacheFontFamilyResolver(context: Context): FontFamily.Resolver {
     return FontFamilyResolverImpl(
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/PlatformTypefaces.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/PlatformTypefaces.kt
index 7249b45..4e39b41 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/PlatformTypefaces.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/font/PlatformTypefaces.kt
@@ -58,7 +58,6 @@
      * @return typeface from system cache if available, or null if the system doesn't know this font
      * name
      */
-    @OptIn(ExperimentalTextApi::class)
     fun optionalOnDeviceFontFamilyByName(
         familyName: String,
         weight: FontWeight,
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/input/PlatformTextInputAdapter.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/input/PlatformTextInputAdapter.android.kt
index 395de4c..c7b92b1 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/input/PlatformTextInputAdapter.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/input/PlatformTextInputAdapter.android.kt
@@ -61,8 +61,6 @@
  *  [PlatformTextInputPlugin] when they are ready to begin processing text input. Platform APIs will
  *  not be delegated to an adapter unless it holds input focus.
  * - Implement [createInputConnection] to create an [InputConnection] that talks to the IME.
- * - Return a [TextInputForTests] instance from [inputForTests] that implements text operations
- *  defined by the Compose UI testing framework.
  * - Optionally implement [onDisposed] to clean up any resources when the adapter is no longer used
  *  in the composition and will be removed from the [PlatformTextInputPluginRegistry]'s cache.
  *
@@ -74,12 +72,6 @@
     // TODO(b/267235947) When fleshing out the desktop actual, we might want to pull some of these
     //  members up into the expect interface (e.g. maybe inputForTests).
 
-    /**
-     * The [TextInputForTests] used to inject text editing commands by the testing framework.
-     * This should only be called from tests, never in production.
-     */
-    val inputForTests: TextInputForTests?
-
     /** Delegate for [View.onCreateInputConnection]. */
     fun createInputConnection(outAttrs: EditorInfo): InputConnection?
 
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 cd3a8f2..f9f160e 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
@@ -32,13 +32,11 @@
 import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.SpanStyle
-import androidx.compose.ui.text.font.Font
 import androidx.compose.ui.text.font.FontFamily
 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.font.GenericFontFamily
-import androidx.compose.ui.text.font.createFontFamilyResolver
 import androidx.compose.ui.text.font.getAndroidTypefaceStyle
 import androidx.compose.ui.text.platform.extensions.setBackground
 import androidx.compose.ui.text.platform.extensions.setColor
@@ -49,27 +47,13 @@
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.util.fastForEach
 
-/**
- * Convert an AnnotatedString into SpannableString for Android text to speech support.
- *
- * @suppress
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-@InternalTextApi // used in ui:ui
-fun AnnotatedString.toAccessibilitySpannableString(
-    density: Density,
-    @Suppress("DEPRECATION") resourceLoader: Font.ResourceLoader
-): SpannableString {
-    @Suppress("DEPRECATION")
-    return toAccessibilitySpannableString(density, createFontFamilyResolver(resourceLoader))
-}
-
 @OptIn(ExperimentalTextApi::class)
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @InternalTextApi // used in ui:ui
 fun AnnotatedString.toAccessibilitySpannableString(
     density: Density,
-    fontFamilyResolver: FontFamily.Resolver
+    fontFamilyResolver: FontFamily.Resolver,
+    urlSpanCache: URLSpanCache
 ): SpannableString {
     val spannableString = SpannableString(text)
     spanStylesOrNull?.fastForEach { (style, start, end) ->
@@ -90,7 +74,7 @@
 
     getUrlAnnotations(0, length).fastForEach { (urlAnnotation, start, end) ->
         spannableString.setSpan(
-            urlAnnotation.toSpan(),
+            urlSpanCache.toURLSpan(urlAnnotation),
             start,
             end,
             Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
@@ -101,7 +85,6 @@
 }
 
 /** Apply the serializable styles to SpannableString. */
-@OptIn(ExperimentalTextApi::class)
 private fun SpannableString.setSpanStyle(
     spanStyle: SpanStyle,
     start: Int,
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 c7b1e40..5a79327 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
@@ -48,7 +48,6 @@
  * An implementation of [AndroidTypeface] for [FontListFontFamily]
  */
 // internal constructor for injecting FontMatcher for testing purpose
-@OptIn(ExperimentalTextApi::class)
 @Suppress("DEPRECATION")
 @Deprecated("This is not supported after downloadable fonts.")
 internal class AndroidFontListTypeface(
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/URLSpanCache.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/URLSpanCache.kt
new file mode 100644
index 0000000..a8444ba
--- /dev/null
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/URLSpanCache.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.text.platform
+
+import android.text.style.URLSpan
+import androidx.compose.ui.text.ExperimentalTextApi
+import androidx.compose.ui.text.InternalTextApi
+import androidx.compose.ui.text.UrlAnnotation
+import java.util.WeakHashMap
+
+/**
+ * This class converts [UrlAnnotation]s to [URLSpan]s, ensuring that the same instance of [URLSpan]
+ * will be returned for every instance of [UrlAnnotation]. This is required for [URLSpan]s (and
+ * any ClickableSpan) to be handled correctly by accessibility services, which require every
+ * ClickableSpan to have a stable ID across reads from the accessibility node. A11y services convert
+ * these spans to parcelable ones, then look them up later using their ID. Since the ID is a hidden
+ * property, the only way to satisfy this constraint is to actually use the same [URLSpan] instance
+ * every time.
+ *
+ * See b/253292081.
+ */
+// "URL" violates naming guidelines, but that is intentional to match the platform API.
+@Suppress("AcronymName")
+@OptIn(ExperimentalTextApi::class)
+@InternalTextApi
+class URLSpanCache {
+    private val spansByAnnotation = WeakHashMap<UrlAnnotation, URLSpan>()
+
+    @Suppress("AcronymName")
+    fun toURLSpan(urlAnnotation: UrlAnnotation): URLSpan =
+        spansByAnnotation.getOrPut(urlAnnotation) { URLSpan(urlAnnotation.url) }
+}
\ No newline at end of file
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/UrlAnnotationExtensions.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/UrlAnnotationExtensions.android.kt
deleted file mode 100644
index 12a9f5a..0000000
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/extensions/UrlAnnotationExtensions.android.kt
+++ /dev/null
@@ -1,24 +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.platform.extensions
-
-import android.text.style.URLSpan
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.text.UrlAnnotation
-
-@ExperimentalTextApi
-fun UrlAnnotation.toSpan(): URLSpan = URLSpan(url)
\ No newline at end of file
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.kt
index 3781437..59636a8 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.kt
@@ -16,10 +16,7 @@
 
 package androidx.compose.ui.text.font
 
-import androidx.compose.ui.text.ExperimentalTextApi
-
 @Suppress("DEPRECATION")
-@OptIn(ExperimentalTextApi::class)
 @Deprecated("This exists to bridge existing Font.ResourceLoader APIs, and should be " +
     "removed with them",
     replaceWith = ReplaceWith("createFontFamilyResolver()"),
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/Font.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/Font.kt
index 66e5205..185e012 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/Font.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/font/Font.kt
@@ -283,7 +283,6 @@
  *
  * @see FontFamily
  */
-@OptIn(ExperimentalTextApi::class)
 @Stable
 fun Font(
     resId: Int,
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextInputForTests.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextInputForTests.kt
deleted file mode 100644
index eccbfc7..0000000
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextInputForTests.kt
+++ /dev/null
@@ -1,44 +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.text.input
-
-/**
- * Defines additional operations that can be performed on text editors by UI tests that aren't
- * available as semantics actions. Tests call these methods indirectly, by the various `perform*`
- * extension functions on `SemanticsNodeInteraction`. [PlatformTextInputAdapter]s implement support
- * for these operations by returning instances of this interface from
- * [PlatformTextInputAdapter.inputForTests].
- *
- * Implementations of this interface should perform the requested operations at the lowest level
- * as possible (as close to the system calls as possible) to exercise as much of the production code
- * as possible. E.g. they should not operate directly on the text buffer, but emulate the calls
- * the platform would make if the user were performing these operations via the IME.
- */
-// If new methods need to be added to this interface to support additional testing APIs, they should
-// be given default implementations that throw UnsupportedOperationExceptions. This is not a concern
-// for backwards compatibility because it simply means that tests may not use new perform* methods
-// on older implementations that haven't linked against the newer version of Compose.
-interface TextInputForTests {
-
-    /**
-     * Sends the given text to this node in similar way to IME. The text should be inserted at the
-     * current cursor position.
-     *
-     * @param text Text to send.
-     */
-    fun inputTextForTest(text: String)
-}
\ No newline at end of file
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 afa4713..e6d44f7 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
@@ -16,7 +16,6 @@
 
 package androidx.compose.ui.text.style
 
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.PlatformParagraphStyle
 
 /**
@@ -195,7 +194,7 @@
      * between 0f (inclusive) and 1f (inclusive).
      */
     @kotlin.jvm.JvmInline
-    value class Alignment @ExperimentalTextApi constructor(internal val topRatio: Float) {
+    value class Alignment constructor(internal val topRatio: Float) {
 
         init {
             check(topRatio in 0f..1f || topRatio == -1f) {
@@ -235,7 +234,6 @@
              * +--------+
              * </pre>
              */
-            @OptIn(ExperimentalTextApi::class)
             val Top = Alignment(topRatio = 0f)
 
             /**
@@ -256,7 +254,6 @@
              * +--------+
              * </pre>
              */
-            @OptIn(ExperimentalTextApi::class)
             val Center = Alignment(topRatio = 0.5f)
 
             /**
@@ -265,7 +262,6 @@
              * will be distributed as 8 units to top, and 2 units to the bottom of the line. This is
              * the default behavior.
              */
-            @OptIn(ExperimentalTextApi::class)
             val Proportional = Alignment(topRatio = -1f)
 
             /**
@@ -289,7 +285,6 @@
              * +--------+
              * </pre>
              */
-            @OptIn(ExperimentalTextApi::class)
             val Bottom = Alignment(topRatio = 1f)
         }
     }
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/SpanStyleTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/SpanStyleTest.kt
index 3a3d0bf..fa5f81c 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/SpanStyleTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/SpanStyleTest.kt
@@ -42,9 +42,9 @@
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
-@OptIn(ExperimentalTextApi::class)
 @RunWith(JUnit4::class)
 class SpanStyleTest {
+    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `constructor with default values`() {
         val style = SpanStyle()
@@ -112,6 +112,7 @@
         assertThat(style.color).isEqualTo(color)
     }
 
+    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `constructor with half-transparent color`() {
         val color = Color.Red.copy(alpha = 0.5f)
@@ -203,6 +204,7 @@
         assertThat(style.fontFamily).isEqualTo(fontFamily)
     }
 
+    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `constructor with customized drawStyle`() {
         val stroke = Stroke(width = 4f)
@@ -449,7 +451,6 @@
         assertThat(newSpanStyle.localeList).isEqualTo(otherStyle.localeList)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `merge with null platformStyles has null platformStyle`() {
         val style = SpanStyle(platformStyle = null)
@@ -518,6 +519,7 @@
         assertThat(mergedStyle.alpha).isEqualTo(0.3f)
     }
 
+    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `merge with other's drawStyle is null should use this' drawStyle`() {
         val drawStyle1 = Stroke(cap = StrokeCap.Butt)
@@ -528,6 +530,7 @@
         assertThat(newSpanStyle.drawStyle).isEqualTo(drawStyle1)
     }
 
+    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `merge with other's drawStyle is set should use other's drawStyle`() {
         val drawStyle1 = Stroke(cap = StrokeCap.Butt)
@@ -877,7 +880,6 @@
         assertThat(newSpanStyle.textDecoration).isEqualTo(decoration2)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `lerp with null platformStyles have null platformStyle`() {
         val style = SpanStyle(platformStyle = null)
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleLayoutAttributesTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleLayoutAttributesTest.kt
index d595b1b..a9c8a9f 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleLayoutAttributesTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleLayoutAttributesTest.kt
@@ -287,7 +287,6 @@
     }
 
     @Suppress("DEPRECATION")
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun returns_false_for_platformStyle_change() {
         val style = TextStyle(platformStyle = PlatformTextStyle(includeFontPadding = false))
@@ -308,7 +307,6 @@
         ).isFalse()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun returns_false_for_lineHeightStyle_change() {
         val style = TextStyle(
@@ -329,7 +327,6 @@
         ).isFalse()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun returns_false_for_lineBreak_change() {
         val style = TextStyle(
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleResolveDefaultsTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleResolveDefaultsTest.kt
index e36f4b6..5bff8e8 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleResolveDefaultsTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleResolveDefaultsTest.kt
@@ -92,7 +92,6 @@
         ).isEqualTo(brush)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun test_use_provided_values_hyphens() {
         assertThat(
@@ -286,7 +285,6 @@
         ).isEqualTo(TextIndent(12.sp, 13.sp))
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun test_use_provided_values_lineBreak() {
         assertThat(
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleTest.kt
index 5a04d87..c6cff1d 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextStyleTest.kt
@@ -178,7 +178,6 @@
         assertThat(style.copy().hyphens).isEqualTo(Hyphens.Auto)
     }
 
-    @OptIn(ExperimentalTextApi::class)
     @Test
     fun `copy with hyphens returns new hyphens`() {
         val style = TextStyle(hyphens = Hyphens.Auto)
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/font/FontFamilyTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/font/FontFamilyTest.kt
index c37924e..e2298bf 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/font/FontFamilyTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/font/FontFamilyTest.kt
@@ -15,18 +15,15 @@
  */
 package androidx.compose.ui.text.font
 
-import androidx.compose.ui.text.ExperimentalTextApi
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
 @RunWith(JUnit4::class)
-@OptIn(ExperimentalTextApi::class)
 class FontFamilyTest {
 
     private val resourceId1 = 1
-    private val resourceId2 = 2
 
     @Test(expected = IllegalStateException::class)
     fun `cannot be instantiated with empty font list`() {
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/font/FontTestData.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/font/FontTestData.kt
index 0269266..2393d7a 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/font/FontTestData.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/font/FontTestData.kt
@@ -16,10 +16,7 @@
 
 package androidx.compose.ui.text.font
 
-import androidx.compose.ui.text.ExperimentalTextApi
-
 class FontTestData {
-    @OptIn(ExperimentalTextApi::class)
     companion object {
         private val resourceId = 1
 
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/font/FontVariationTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/font/FontVariationTest.kt
index 235c040..cac46e1 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/font/FontVariationTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/font/FontVariationTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.ui.text.font
 
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.TextUnit
 import androidx.compose.ui.unit.sp
@@ -25,7 +24,6 @@
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
-@OptIn(ExperimentalTextApi::class)
 @RunWith(JUnit4::class)
 class FontVariationTest {
     @Test
diff --git a/compose/ui/ui/api/current.ignore b/compose/ui/ui/api/current.ignore
new file mode 100644
index 0000000..2001639
--- /dev/null
+++ b/compose/ui/ui/api/current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+RemovedClass: androidx.compose.ui.platform.AndroidComposeView_androidKt:
+    Removed class androidx.compose.ui.platform.AndroidComposeView_androidKt
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 74cc50f..5a1455d 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -1764,6 +1764,11 @@
     method public static androidx.compose.ui.Modifier onRotaryScrollEvent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.rotary.RotaryScrollEvent,java.lang.Boolean> onRotaryScrollEvent);
   }
 
+  public interface RotaryInputModifierNode extends androidx.compose.ui.node.DelegatableNode {
+    method public boolean onPreRotaryScrollEvent(androidx.compose.ui.input.rotary.RotaryScrollEvent event);
+    method public boolean onRotaryScrollEvent(androidx.compose.ui.input.rotary.RotaryScrollEvent event);
+  }
+
   public final class RotaryScrollEvent {
     method public float getHorizontalScrollPixels();
     method public long getUptimeMillis();
@@ -2082,6 +2087,7 @@
 
   public abstract static class Placeable.PlacementScope {
     ctor public Placeable.PlacementScope();
+    method public androidx.compose.ui.layout.LayoutCoordinates? getCoordinates();
     method protected abstract androidx.compose.ui.unit.LayoutDirection getParentLayoutDirection();
     method protected abstract int getParentWidth();
     method public final void place(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex);
@@ -2092,6 +2098,7 @@
     method public final void placeRelativeWithLayer(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
     method public final void placeWithLayer(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
     method public final void placeWithLayer(androidx.compose.ui.layout.Placeable, long position, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
+    property public androidx.compose.ui.layout.LayoutCoordinates? coordinates;
     property protected abstract androidx.compose.ui.unit.LayoutDirection parentLayoutDirection;
     property protected abstract int parentWidth;
   }
@@ -2382,9 +2389,6 @@
     method public long calculateRecommendedTimeoutMillis(long originalTimeoutMillis, optional boolean containsIcons, optional boolean containsText, optional boolean containsControls);
   }
 
-  public final class AndroidComposeView_androidKt {
-  }
-
   public final class AndroidCompositionLocals_androidKt {
     method public static androidx.compose.runtime.ProvidableCompositionLocal<android.content.res.Configuration> getLocalConfiguration();
     method public static androidx.compose.runtime.ProvidableCompositionLocal<android.content.Context> getLocalContext();
@@ -2816,6 +2820,7 @@
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getDismiss();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getExpand();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<java.util.List<androidx.compose.ui.text.TextLayoutResult>,java.lang.Boolean>>> getGetTextLayoutResult();
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<androidx.compose.ui.text.AnnotatedString,java.lang.Boolean>>> getInsertTextAtCursor();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getOnClick();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getOnLongClick();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getPageDown();
@@ -2837,6 +2842,7 @@
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> Dismiss;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> Expand;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<java.util.List<androidx.compose.ui.text.TextLayoutResult>,java.lang.Boolean>>> GetTextLayoutResult;
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<androidx.compose.ui.text.AnnotatedString,java.lang.Boolean>>> InsertTextAtCursor;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> OnClick;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> OnLongClick;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> PageDown;
@@ -3021,6 +3027,7 @@
     method public static androidx.compose.ui.semantics.ScrollAxisRange getVerticalScrollAxisRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void heading(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void indexForKey(androidx.compose.ui.semantics.SemanticsPropertyReceiver, kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Integer> mapping);
+    method public static void insertTextAtCursor(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString,java.lang.Boolean>? action);
     method public static boolean isContainer(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void onClick(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
     method public static void onLongClick(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
@@ -3103,8 +3110,8 @@
 package androidx.compose.ui.viewinterop {
 
   public final class AndroidView_androidKt {
+    method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static <T extends android.view.View> void AndroidView(kotlin.jvm.functions.Function1<? super android.content.Context,? extends T> factory, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit> update);
     method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static <T extends android.view.View> void AndroidView(kotlin.jvm.functions.Function1<? super android.content.Context,? extends T> factory, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit>? onReset, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit> onRelease, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit> update);
-    method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static <T extends android.view.View> void AndroidView(kotlin.jvm.functions.Function1<? super android.content.Context,? extends T> factory, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super T,? extends kotlin.Unit> update);
     method public static kotlin.jvm.functions.Function1<android.view.View,kotlin.Unit> getNoOpUpdate();
     property public static final kotlin.jvm.functions.Function1<android.view.View,kotlin.Unit> NoOpUpdate;
   }
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index f9090fb..fa8d56d 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -1573,6 +1573,16 @@
     method public static int getNativeKeyCode(long);
   }
 
+  @androidx.compose.ui.ExperimentalComposeUiApi public interface SoftKeyboardInterceptionModifierNode extends androidx.compose.ui.node.DelegatableNode {
+    method public boolean onInterceptKeyBeforeSoftKeyboard(android.view.KeyEvent event);
+    method public boolean onPreInterceptKeyBeforeSoftKeyboard(android.view.KeyEvent event);
+  }
+
+  public final class SoftwareKeyboardInterceptionModifierKt {
+    method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier onInterceptKeyBeforeSoftKeyboard(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onInterceptKeyBeforeSoftKeyboard);
+    method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier onPreInterceptKeyBeforeSoftKeyboard(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.key.KeyEvent,java.lang.Boolean> onPreInterceptKeyBeforeSoftKeyboard);
+  }
+
 }
 
 package androidx.compose.ui.input.nestedscroll {
@@ -1910,7 +1920,7 @@
     method public static androidx.compose.ui.Modifier onRotaryScrollEvent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.rotary.RotaryScrollEvent,java.lang.Boolean> onRotaryScrollEvent);
   }
 
-  @androidx.compose.ui.ExperimentalComposeUiApi public interface RotaryInputModifierNode extends androidx.compose.ui.node.DelegatableNode {
+  public interface RotaryInputModifierNode extends androidx.compose.ui.node.DelegatableNode {
     method public boolean onPreRotaryScrollEvent(androidx.compose.ui.input.rotary.RotaryScrollEvent event);
     method public boolean onRotaryScrollEvent(androidx.compose.ui.input.rotary.RotaryScrollEvent event);
   }
@@ -2259,7 +2269,7 @@
 
   public abstract static class Placeable.PlacementScope {
     ctor public Placeable.PlacementScope();
-    method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.layout.LayoutCoordinates? getCoordinates();
+    method public androidx.compose.ui.layout.LayoutCoordinates? getCoordinates();
     method protected abstract androidx.compose.ui.unit.LayoutDirection getParentLayoutDirection();
     method protected abstract int getParentWidth();
     method public final void place(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex);
@@ -2270,7 +2280,7 @@
     method public final void placeRelativeWithLayer(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
     method public final void placeWithLayer(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
     method public final void placeWithLayer(androidx.compose.ui.layout.Placeable, long position, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
-    property @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.layout.LayoutCoordinates? coordinates;
+    property public androidx.compose.ui.layout.LayoutCoordinates? coordinates;
     property protected abstract androidx.compose.ui.unit.LayoutDirection parentLayoutDirection;
     property protected abstract int parentWidth;
   }
@@ -2549,13 +2559,11 @@
   public interface RootForTest {
     method public androidx.compose.ui.unit.Density getDensity();
     method public androidx.compose.ui.semantics.SemanticsOwner getSemanticsOwner();
-    method @androidx.compose.ui.text.ExperimentalTextApi public default androidx.compose.ui.text.input.TextInputForTests? getTextInputForTests();
     method public androidx.compose.ui.text.input.TextInputService getTextInputService();
     method @androidx.compose.ui.ExperimentalComposeUiApi public default void measureAndLayoutForTest();
     method public boolean sendKeyEvent(android.view.KeyEvent keyEvent);
     property public abstract androidx.compose.ui.unit.Density density;
     property public abstract androidx.compose.ui.semantics.SemanticsOwner semanticsOwner;
-    property @androidx.compose.ui.text.ExperimentalTextApi public default androidx.compose.ui.text.input.TextInputForTests? textInputForTests;
     property public abstract androidx.compose.ui.text.input.TextInputService textInputService;
   }
 
@@ -2597,9 +2605,6 @@
     method public long calculateRecommendedTimeoutMillis(long originalTimeoutMillis, optional boolean containsIcons, optional boolean containsText, optional boolean containsControls);
   }
 
-  public final class AndroidComposeView_androidKt {
-  }
-
   public final class AndroidCompositionLocals_androidKt {
     method public static androidx.compose.runtime.ProvidableCompositionLocal<android.content.res.Configuration> getLocalConfiguration();
     method public static androidx.compose.runtime.ProvidableCompositionLocal<android.content.Context> getLocalContext();
@@ -3068,6 +3073,7 @@
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getDismiss();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getExpand();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<java.util.List<androidx.compose.ui.text.TextLayoutResult>,java.lang.Boolean>>> getGetTextLayoutResult();
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<androidx.compose.ui.text.AnnotatedString,java.lang.Boolean>>> getInsertTextAtCursor();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getOnClick();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getOnLongClick();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getPageDown();
@@ -3089,6 +3095,7 @@
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> Dismiss;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> Expand;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<java.util.List<androidx.compose.ui.text.TextLayoutResult>,java.lang.Boolean>>> GetTextLayoutResult;
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<androidx.compose.ui.text.AnnotatedString,java.lang.Boolean>>> InsertTextAtCursor;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> OnClick;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> OnLongClick;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> PageDown;
@@ -3279,6 +3286,7 @@
     method public static androidx.compose.ui.semantics.ScrollAxisRange getVerticalScrollAxisRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void heading(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void indexForKey(androidx.compose.ui.semantics.SemanticsPropertyReceiver, kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Integer> mapping);
+    method public static void insertTextAtCursor(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString,java.lang.Boolean>? action);
     method @androidx.compose.ui.ExperimentalComposeUiApi public static void invisibleToUser(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static boolean isContainer(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void onClick(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
@@ -3367,8 +3375,8 @@
 package androidx.compose.ui.viewinterop {
 
   public final class AndroidView_androidKt {
+    method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static <T extends android.view.View> void AndroidView(kotlin.jvm.functions.Function1<? super android.content.Context,? extends T> factory, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit> update);
     method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static <T extends android.view.View> void AndroidView(kotlin.jvm.functions.Function1<? super android.content.Context,? extends T> factory, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit>? onReset, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit> onRelease, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit> update);
-    method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static <T extends android.view.View> void AndroidView(kotlin.jvm.functions.Function1<? super android.content.Context,? extends T> factory, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super T,? extends kotlin.Unit> update);
     method public static kotlin.jvm.functions.Function1<android.view.View,kotlin.Unit> getNoOpUpdate();
     property public static final kotlin.jvm.functions.Function1<android.view.View,kotlin.Unit> NoOpUpdate;
   }
diff --git a/compose/ui/ui/api/restricted_current.ignore b/compose/ui/ui/api/restricted_current.ignore
new file mode 100644
index 0000000..2001639
--- /dev/null
+++ b/compose/ui/ui/api/restricted_current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+RemovedClass: androidx.compose.ui.platform.AndroidComposeView_androidKt:
+    Removed class androidx.compose.ui.platform.AndroidComposeView_androidKt
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index d34024c..c8fa151 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -1764,6 +1764,11 @@
     method public static androidx.compose.ui.Modifier onRotaryScrollEvent(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.input.rotary.RotaryScrollEvent,java.lang.Boolean> onRotaryScrollEvent);
   }
 
+  public interface RotaryInputModifierNode extends androidx.compose.ui.node.DelegatableNode {
+    method public boolean onPreRotaryScrollEvent(androidx.compose.ui.input.rotary.RotaryScrollEvent event);
+    method public boolean onRotaryScrollEvent(androidx.compose.ui.input.rotary.RotaryScrollEvent event);
+  }
+
   public final class RotaryScrollEvent {
     method public float getHorizontalScrollPixels();
     method public long getUptimeMillis();
@@ -2088,6 +2093,7 @@
 
   public abstract static class Placeable.PlacementScope {
     ctor public Placeable.PlacementScope();
+    method public androidx.compose.ui.layout.LayoutCoordinates? getCoordinates();
     method protected abstract androidx.compose.ui.unit.LayoutDirection getParentLayoutDirection();
     method protected abstract int getParentWidth();
     method public final void place(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex);
@@ -2098,6 +2104,7 @@
     method public final void placeRelativeWithLayer(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
     method public final void placeWithLayer(androidx.compose.ui.layout.Placeable, int x, int y, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
     method public final void placeWithLayer(androidx.compose.ui.layout.Placeable, long position, optional float zIndex, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.GraphicsLayerScope,kotlin.Unit> layerBlock);
+    property public androidx.compose.ui.layout.LayoutCoordinates? coordinates;
     property protected abstract androidx.compose.ui.unit.LayoutDirection parentLayoutDirection;
     property protected abstract int parentWidth;
   }
@@ -2429,9 +2436,6 @@
     method public long calculateRecommendedTimeoutMillis(long originalTimeoutMillis, optional boolean containsIcons, optional boolean containsText, optional boolean containsControls);
   }
 
-  public final class AndroidComposeView_androidKt {
-  }
-
   public final class AndroidCompositionLocals_androidKt {
     method public static androidx.compose.runtime.ProvidableCompositionLocal<android.content.res.Configuration> getLocalConfiguration();
     method public static androidx.compose.runtime.ProvidableCompositionLocal<android.content.Context> getLocalContext();
@@ -2864,6 +2868,7 @@
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getDismiss();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getExpand();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<java.util.List<androidx.compose.ui.text.TextLayoutResult>,java.lang.Boolean>>> getGetTextLayoutResult();
+    method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<androidx.compose.ui.text.AnnotatedString,java.lang.Boolean>>> getInsertTextAtCursor();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getOnClick();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getOnLongClick();
     method public androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> getPageDown();
@@ -2885,6 +2890,7 @@
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> Dismiss;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> Expand;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<java.util.List<androidx.compose.ui.text.TextLayoutResult>,java.lang.Boolean>>> GetTextLayoutResult;
+    property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function1<androidx.compose.ui.text.AnnotatedString,java.lang.Boolean>>> InsertTextAtCursor;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> OnClick;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> OnLongClick;
     property public final androidx.compose.ui.semantics.SemanticsPropertyKey<androidx.compose.ui.semantics.AccessibilityAction<kotlin.jvm.functions.Function0<java.lang.Boolean>>> PageDown;
@@ -3069,6 +3075,7 @@
     method public static androidx.compose.ui.semantics.ScrollAxisRange getVerticalScrollAxisRange(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void heading(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void indexForKey(androidx.compose.ui.semantics.SemanticsPropertyReceiver, kotlin.jvm.functions.Function1<java.lang.Object,java.lang.Integer> mapping);
+    method public static void insertTextAtCursor(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString,java.lang.Boolean>? action);
     method public static boolean isContainer(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
     method public static void onClick(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
     method public static void onLongClick(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Boolean>? action);
@@ -3151,8 +3158,8 @@
 package androidx.compose.ui.viewinterop {
 
   public final class AndroidView_androidKt {
+    method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static <T extends android.view.View> void AndroidView(kotlin.jvm.functions.Function1<? super android.content.Context,? extends T> factory, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit> update);
     method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static <T extends android.view.View> void AndroidView(kotlin.jvm.functions.Function1<? super android.content.Context,? extends T> factory, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit>? onReset, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit> onRelease, optional kotlin.jvm.functions.Function1<? super T,kotlin.Unit> update);
-    method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static <T extends android.view.View> void AndroidView(kotlin.jvm.functions.Function1<? super android.content.Context,? extends T> factory, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super T,? extends kotlin.Unit> update);
     method public static kotlin.jvm.functions.Function1<android.view.View,kotlin.Unit> getNoOpUpdate();
     property public static final kotlin.jvm.functions.Function1<android.view.View,kotlin.Unit> NoOpUpdate;
   }
diff --git a/compose/ui/ui/build.gradle b/compose/ui/ui/build.gradle
index b76ffed..2b2f069 100644
--- a/compose/ui/ui/build.gradle
+++ b/compose/ui/ui/build.gradle
@@ -83,7 +83,7 @@
         implementation("androidx.savedstate:savedstate-ktx:1.2.1")
         implementation("androidx.lifecycle:lifecycle-runtime:2.6.1")
         implementation("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
-        implementation("androidx.profileinstaller:profileinstaller:1.2.1")
+        implementation("androidx.profileinstaller:profileinstaller:1.3.0")
         implementation("androidx.emoji2:emoji2:1.2.0")
 
         testImplementation(libs.testRules)
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/EventTypesDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/EventTypesDemo.kt
index f0abfb3..7d94bd5 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/EventTypesDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/EventTypesDemo.kt
@@ -42,7 +42,10 @@
 @Composable
 private fun TextItem(text: String, color: Color) {
     Row {
-        Box(Modifier.size(25.dp).background(color))
+        Box(
+            Modifier
+                .size(25.dp)
+                .background(color))
         Spacer(Modifier.width(5.dp))
         Text(text, fontSize = 20.sp)
     }
@@ -60,6 +63,7 @@
             PointerEventType.Enter -> Color.Green
             PointerEventType.Exit -> Color.Blue
             PointerEventType.Scroll -> Color(0xFF800080) // Purple
+            PointerEventType.Unknown -> Color.White
             else -> Color.Black
         }
         TextItem("$type $value", color)
diff --git a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/PlacementScopeCoordinatesSample.kt b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/PlacementScopeCoordinatesSample.kt
index a6f0137..fc5a7d4 100644
--- a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/PlacementScopeCoordinatesSample.kt
+++ b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/PlacementScopeCoordinatesSample.kt
@@ -17,7 +17,6 @@
 
 import androidx.annotation.Sampled
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.layout.Layout
@@ -27,7 +26,6 @@
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.round
 
-@OptIn(ExperimentalComposeUiApi::class)
 @Sampled
 @Composable
 fun PlacementScopeCoordinatesSample() {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AccessibilityIteratorsTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AccessibilityIteratorsTest.kt
index 7e48c7a..ffa6615 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AccessibilityIteratorsTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AccessibilityIteratorsTest.kt
@@ -31,7 +31,6 @@
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.font.Font
@@ -420,7 +419,6 @@
         Truth.assertThat(range).isNull()
     }
 
-    @OptIn(ExperimentalTextApi::class)
     private fun multiLineText(
         text: String,
         fontSize: TextUnit = 20.sp,
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/ClipDrawTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/ClipDrawTest.kt
index 7641c8a..cdb4b23 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/ClipDrawTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/ClipDrawTest.kt
@@ -20,8 +20,11 @@
 import android.os.Build
 import androidx.activity.compose.setContent
 import androidx.annotation.RequiresApi
+import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.AtLeastSize
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.Padding
@@ -41,24 +44,29 @@
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.drawscope.DrawScope
 import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.layout.layout
 import androidx.compose.ui.padding
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.runOnUiThreadIR
 import androidx.compose.ui.test.TestActivity
+import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.constrainHeight
+import androidx.compose.ui.unit.constrainWidth
 import androidx.compose.ui.waitAndScreenShot
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
 import org.junit.Assert
 import org.junit.Before
 import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
@@ -511,6 +519,48 @@
         }
     }
 
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun resizingIsReflectedInGraphicsLayer() {
+        var sizePx by mutableStateOf(20)
+        rule.runOnUiThread {
+            activity.setContent {
+                Box(
+                    modifier = Modifier
+                        .background(Color.White)
+                        .layout { measurable, constraints ->
+                            val placeable = measurable.measure(constraints)
+                            layout(30, 30) {
+                                placeable.place(IntOffset.Zero)
+                            }
+                        }
+                        .layout { measurable, constraints ->
+                            val placeable = measurable.measure(Constraints.fixed(sizePx, sizePx))
+                            layout(
+                                constraints.constrainWidth(sizePx),
+                                constraints.constrainHeight(sizePx)
+                            ) {
+                                placeable.place(IntOffset.Zero)
+                            }
+                        }
+                        .clipToBounds()
+                        .fillColor(Color.Red)
+                )
+            }
+        }
+
+        takeScreenShot(30).apply {
+            assertRect(Color.Red, size = 20, centerX = 10, centerY = 10)
+        }
+
+        drawLatch = CountDownLatch(1)
+        sizePx = 30
+
+        takeScreenShot(30).apply {
+            assertRect(Color.Red, size = 30)
+        }
+    }
+
     private fun Modifier.fillColor(color: Color): Modifier {
         return drawBehind {
             drawRect(
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/GraphicsLayerTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/GraphicsLayerTest.kt
index e7627aa..ba80c91 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/GraphicsLayerTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/GraphicsLayerTest.kt
@@ -31,6 +31,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.movableContentOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.testutils.assertPixelColor
@@ -90,6 +91,7 @@
 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.ceil
 import kotlin.math.roundToInt
 import org.junit.Assert.assertEquals
@@ -1412,4 +1414,162 @@
             assertEquals(Rect(0f, 0f, 10f, 10f), coordinates.boundsInRoot())
         }
     }
+
+    @Test
+    fun invalidationAfterMovingMovableContentWithLayer() {
+        var moveContent by mutableStateOf(false)
+        var counter by mutableStateOf(0)
+        var counterReadInDrawing = -1
+        val content = movableContentOf {
+            Box(
+                Modifier
+                    .size(5.dp)
+                    .graphicsLayer()
+                    .drawBehind {
+                        counterReadInDrawing = counter
+                    })
+        }
+
+        rule.setContent {
+            if (moveContent) {
+                Box(Modifier.size(5.dp)) {
+                    content()
+                }
+            } else {
+                Box(Modifier.size(10.dp)) {
+                    content()
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            moveContent = true
+        }
+
+        rule.runOnIdle {
+            assertThat(counterReadInDrawing).isEqualTo(counter)
+            counter++
+        }
+
+        rule.runOnIdle {
+            assertThat(counterReadInDrawing).isEqualTo(counter)
+        }
+    }
+
+    @Test
+    fun updatingLayerPropertiesAfterMovingMovableContent() {
+        var moveContent by mutableStateOf(false)
+        var counter by mutableStateOf(0)
+        var counterReadInLayerBlock = -1
+        val content = movableContentOf {
+            Box(
+                Modifier
+                    .size(5.dp)
+                    .graphicsLayer {
+                        counterReadInLayerBlock = counter
+                    }
+            )
+        }
+
+        rule.setContent {
+            if (moveContent) {
+                Box(Modifier.size(5.dp)) {
+                    content()
+                }
+            } else {
+                Box(Modifier.size(10.dp)) {
+                    content()
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            moveContent = true
+        }
+
+        rule.runOnIdle {
+            assertThat(counterReadInLayerBlock).isEqualTo(counter)
+            counter++
+        }
+
+        rule.runOnIdle {
+            assertThat(counterReadInLayerBlock).isEqualTo(counter)
+        }
+    }
+
+    @Test
+    fun updatingValueIsNotCausingRemeasureOrRelayout() {
+        var translationX by mutableStateOf(0f)
+        lateinit var coordinates: LayoutCoordinates
+        var remeasureCount = 0
+        var relayoutCount = 0
+        val layoutModifier = Modifier.layout { measurable, constraints ->
+            val placeable = measurable.measure(constraints)
+            remeasureCount++
+            layout(placeable.width, placeable.height) {
+                relayoutCount++
+                placeable.place(0, 0)
+            }
+        }
+        rule.setContent {
+            Box(Modifier.graphicsLayer(translationX = translationX).then(layoutModifier)) {
+                Layout(Modifier.onGloballyPositioned { coordinates = it }) { _, _ ->
+                    layout(10, 10) {}
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertEquals(0f, coordinates.boundsInRoot().left)
+            // reset counters
+            remeasureCount = 0
+            relayoutCount = 0
+            // update translation
+            translationX = 10f
+        }
+
+        rule.runOnIdle {
+            assertEquals(10f, coordinates.boundsInRoot().left)
+            assertEquals(0, remeasureCount)
+            assertEquals(0, relayoutCount)
+        }
+    }
+
+    @Test
+    fun updatingLambdaIsNotCausingRemeasureOrRelayout() {
+        var lambda by mutableStateOf<GraphicsLayerScope.() -> Unit>({})
+        lateinit var coordinates: LayoutCoordinates
+        var remeasureCount = 0
+        var relayoutCount = 0
+        val layoutModifier = Modifier.layout { measurable, constraints ->
+            val placeable = measurable.measure(constraints)
+            remeasureCount++
+            layout(placeable.width, placeable.height) {
+                relayoutCount++
+                placeable.place(0, 0)
+            }
+        }
+        rule.setContent {
+            Box(Modifier.graphicsLayer(lambda).then(layoutModifier)) {
+                Layout(Modifier.onGloballyPositioned { coordinates = it }) { _, _ ->
+                    layout(10, 10) {}
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertEquals(0f, coordinates.boundsInRoot().left)
+            // reset counters
+            remeasureCount = 0
+            relayoutCount = 0
+            // update lambda
+            lambda = { translationX = 10f }
+        }
+
+        rule.runOnIdle {
+            assertEquals(10f, coordinates.boundsInRoot().left)
+            assertEquals(0, remeasureCount)
+            assertEquals(0, relayoutCount)
+        }
+    }
 }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusEventCountTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusEventCountTest.kt
index 7c6cc56..7d49b02 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusEventCountTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusEventCountTest.kt
@@ -172,6 +172,34 @@
     }
 
     @Test
+    fun removingActiveComposable_onFocusEventIsCalledWithDefaultValue() {
+        // Arrange.
+        val focusStates = mutableListOf<FocusState>()
+        val focusRequester = FocusRequester()
+        var showBox by mutableStateOf(true)
+        rule.setFocusableContent {
+            if (showBox) {
+                Box(
+                    modifier = Modifier
+                        .onFocusEvent { focusStates.add(it) }
+                        .focusRequester(focusRequester)
+                        .focusTarget()
+                )
+            }
+        }
+        rule.runOnIdle {
+            focusRequester.requestFocus()
+            focusStates.clear()
+        }
+
+        // Act.
+        rule.runOnIdle { showBox = false }
+
+        // Assert.
+        rule.runOnIdle { assertThat(focusStates).isExactly(Inactive) }
+    }
+
+    @Test
     fun removingActiveFocusNode_onFocusEventIsCalledTwice() {
         // Arrange.
         val focusStates = mutableListOf<FocusState>()
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusTargetAttachDetachTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusTargetAttachDetachTest.kt
index 10b7e0e..525d640 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusTargetAttachDetachTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusTargetAttachDetachTest.kt
@@ -165,6 +165,67 @@
     }
 
     @Test
+    fun removedActiveFocusTargetAndFocusChanged_triggersOnFocusEvent() {
+        // Arrange.
+        lateinit var focusState: FocusState
+        val focusRequester = FocusRequester()
+        var optionalModifiers by mutableStateOf(true)
+        rule.setFocusableContent {
+            Box(
+                modifier = Modifier
+                    .focusRequester(focusRequester)
+                    .then(
+                        if (optionalModifiers) {
+                            Modifier
+                                .onFocusEvent { focusState = it }
+                                .focusTarget()
+                        } else {
+                            Modifier
+                        }
+                    )
+            )
+        }
+        rule.runOnIdle {
+            focusRequester.requestFocus()
+            assertThat(focusState.isFocused).isTrue()
+        }
+
+        // Act.
+        rule.runOnIdle { optionalModifiers = false }
+
+        // Assert.
+        rule.runOnIdle { assertThat(focusState.isFocused).isFalse() }
+    }
+
+    @Test
+    fun removedActiveComposable_doesNotTriggerOnFocusEvent() {
+        // Arrange.
+        lateinit var focusState: FocusState
+        val focusRequester = FocusRequester()
+        var optionalBox by mutableStateOf(true)
+        rule.setFocusableContent {
+            if (optionalBox) {
+                Box(
+                    modifier = Modifier
+                        .focusRequester(focusRequester)
+                        .onFocusEvent { focusState = it }
+                        .focusTarget()
+                )
+            }
+        }
+        rule.runOnIdle {
+            focusRequester.requestFocus()
+            assertThat(focusState.isFocused).isTrue()
+        }
+
+        // Act.
+        rule.runOnIdle { optionalBox = false }
+
+        // Assert.
+        rule.runOnIdle { assertThat(focusState.isFocused).isFalse() }
+    }
+
+    @Test
     fun removedCapturedFocusTarget_pointsToNextFocusTarget() {
         // Arrange.
         lateinit var focusState: FocusState
@@ -308,6 +369,45 @@
     }
 
     @Test
+    fun removedActiveComposable_clearsFocusFromAllParents() {
+        // Arrange.
+        lateinit var focusState: FocusState
+        lateinit var parentFocusState: FocusState
+        val focusRequester = FocusRequester()
+        var optionalBox by mutableStateOf(true)
+        rule.setFocusableContent {
+            Box(
+                modifier = Modifier
+                    .onFocusChanged { parentFocusState = it }
+                    .focusTarget()
+            ) {
+                if (optionalBox) {
+                    Box(
+                        modifier = Modifier
+                            .onFocusChanged { focusState = it }
+                            .focusRequester(focusRequester)
+                            .focusTarget()
+                    )
+                }
+            }
+        }
+        rule.runOnIdle {
+            focusRequester.requestFocus()
+            assertThat(focusState.hasFocus).isTrue()
+            assertThat(parentFocusState.hasFocus).isTrue()
+        }
+
+        // Act.
+        rule.runOnIdle { optionalBox = false }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(focusState.isFocused).isFalse()
+            assertThat(parentFocusState.isFocused).isFalse()
+        }
+    }
+
+    @Test
     fun removedDeactivatedParentFocusTarget_pointsToNextFocusTarget() {
         // Arrange.
         lateinit var focusState: FocusState
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/focus/FocusAwareEventPropagationTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/focus/FocusAwareEventPropagationTest.kt
index e4256b6..3baf4aa 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/focus/FocusAwareEventPropagationTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/focus/FocusAwareEventPropagationTest.kt
@@ -17,8 +17,9 @@
 package androidx.compose.ui.input.focus
 
 import android.view.KeyEvent as AndroidKeyEvent
+import android.view.KeyEvent.ACTION_DOWN
+import android.view.KeyEvent.KEYCODE_A
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.requiredSize
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
@@ -26,21 +27,24 @@
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.focus.focusTarget
 import androidx.compose.ui.focus.setFocusableContent
+import androidx.compose.ui.input.focus.FocusAwareEventPropagationTest.NodeType.InterruptedSoftKeyboardInput
 import androidx.compose.ui.input.focus.FocusAwareEventPropagationTest.NodeType.KeyInput
 import androidx.compose.ui.input.focus.FocusAwareEventPropagationTest.NodeType.RotaryInput
 import androidx.compose.ui.input.key.KeyEvent
-import androidx.compose.ui.input.key.KeyInputInputModifierNodeImpl
-import androidx.compose.ui.input.rotary.RotaryInputModifierNodeImpl
+import androidx.compose.ui.input.key.onKeyEvent
+import androidx.compose.ui.input.key.onInterceptKeyBeforeSoftKeyboard
+import androidx.compose.ui.input.key.onPreInterceptKeyBeforeSoftKeyboard
+import androidx.compose.ui.input.key.onPreviewKeyEvent
 import androidx.compose.ui.input.rotary.RotaryScrollEvent
-import androidx.compose.ui.node.ModifierNodeElement
-import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.input.rotary.onPreRotaryScrollEvent
+import androidx.compose.ui.input.rotary.onRotaryScrollEvent
 import androidx.compose.ui.test.ExperimentalTestApi
 import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.test.performKeyPress
 import androidx.compose.ui.test.performRotaryScrollInput
-import androidx.compose.ui.unit.dp
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -59,10 +63,8 @@
     val rule = createComposeRule()
 
     private val sentEvent: Any = when (nodeType) {
-        KeyInput ->
-            KeyEvent(AndroidKeyEvent(AndroidKeyEvent.ACTION_DOWN, AndroidKeyEvent.KEYCODE_A))
-        RotaryInput ->
-            RotaryScrollEvent(1f, 1f, 0L)
+        KeyInput, InterruptedSoftKeyboardInput -> KeyEvent(AndroidKeyEvent(ACTION_DOWN, KEYCODE_A))
+        RotaryInput -> RotaryScrollEvent(1f, 1f, 0L)
     }
     private var receivedEvent: Any? = null
     private val initialFocus = FocusRequester()
@@ -70,7 +72,7 @@
     companion object {
         @JvmStatic
         @Parameterized.Parameters(name = "node = {0}")
-        fun initParameters() = arrayOf(KeyInput, RotaryInput)
+        fun initParameters() = arrayOf(KeyInput, InterruptedSoftKeyboardInput, RotaryInput)
     }
 
     @Before
@@ -102,7 +104,8 @@
         // Assert.
         assertThat(receivedEvent).isNull()
         when (nodeType) {
-            KeyInput -> assertThat(error!!.message).contains("do not have an active focus target")
+            KeyInput, InterruptedSoftKeyboardInput ->
+                assertThat(error!!.message).contains("do not have an active focus target")
             RotaryInput -> assertThat(error).isNull()
         }
     }
@@ -133,14 +136,14 @@
         assertThat(receivedEvent).isNull()
         when (nodeType) {
             KeyInput -> assertThat(error!!.message).contains("do not have an active focus target")
-            RotaryInput -> assertThat(error).isNull()
+            InterruptedSoftKeyboardInput, RotaryInput -> assertThat(receivedEvent).isNull()
         }
     }
 
     @Test
     fun onFocusAwareEvent_afterFocusable_isNotTriggered() {
         // Arrange.
-        ContentWithInitialFocus {
+        rule.setContentWithInitialFocus {
             Box(
                 modifier = Modifier
                     .focusable(initiallyFocused = true)
@@ -158,14 +161,14 @@
         // Assert.
         when (nodeType) {
             KeyInput -> assertThat(receivedEvent).isEqualTo(sentEvent)
-            RotaryInput -> assertThat(receivedEvent).isNull()
+            InterruptedSoftKeyboardInput, RotaryInput -> assertThat(receivedEvent).isNull()
         }
     }
 
     @Test
     fun onPreFocusAwareEvent_afterFocusable_isNotTriggered() {
         // Arrange.
-        ContentWithInitialFocus {
+        rule.setContentWithInitialFocus {
             Box(
                 modifier = Modifier
                     .focusable(initiallyFocused = true)
@@ -182,14 +185,14 @@
         // Assert.
         when (nodeType) {
             KeyInput -> assertThat(receivedEvent).isEqualTo(sentEvent)
-            RotaryInput -> assertThat(receivedEvent).isNull()
+            InterruptedSoftKeyboardInput, RotaryInput -> assertThat(receivedEvent).isNull()
         }
     }
 
     @Test
     fun onFocusAwareEvent_isTriggered() {
         // Arrange.
-        ContentWithInitialFocus {
+        rule.setContentWithInitialFocus {
             Box(
                 modifier = Modifier
                     .onFocusAwareEvent {
@@ -210,7 +213,7 @@
     @Test
     fun onPreFocusAwareEvent_triggered() {
         // Arrange.
-        ContentWithInitialFocus {
+        rule.setContentWithInitialFocus {
             Box(
                 modifier = Modifier
                     .onPreFocusAwareEvent {
@@ -231,7 +234,7 @@
     @Test
     fun onFocusAwareEventNotTriggered_ifOnPreFocusAwareEventConsumesEvent_1() {
         // Arrange.
-        ContentWithInitialFocus {
+        rule.setContentWithInitialFocus {
             Box(
                 modifier = Modifier
                     .onFocusAwareEvent {
@@ -257,7 +260,7 @@
     @Test
     fun onFocusAwareEventNotTriggered_ifOnPreFocusAwareEventConsumesEvent_2() {
         // Arrange.
-        ContentWithInitialFocus {
+        rule.setContentWithInitialFocus {
             Box(
                 modifier = Modifier
                     .onPreFocusAwareEvent {
@@ -286,7 +289,7 @@
         var triggerIndex = 1
         var onFocusAwareEventTrigger = 0
         var onPreFocusAwareEventTrigger = 0
-        ContentWithInitialFocus {
+        rule.setContentWithInitialFocus {
             Box(
                 modifier = Modifier
                     .onFocusAwareEvent {
@@ -317,7 +320,7 @@
         var triggerIndex = 1
         var onFocusAwareEventTrigger = 0
         var onPreFocusAwareEventTrigger = 0
-        ContentWithInitialFocus {
+        rule.setContentWithInitialFocus {
             Box(
                 modifier = Modifier
                     .onPreFocusAwareEvent {
@@ -350,7 +353,7 @@
         var parentOnPreFocusAwareEventTrigger = 0
         var childOnFocusAwareEventTrigger = 0
         var childOnPreFocusAwareEventTrigger = 0
-        ContentWithInitialFocus {
+        rule.setContentWithInitialFocus {
             Box(
                 modifier = Modifier
                     .onFocusAwareEvent {
@@ -398,7 +401,7 @@
         var parentOnPreFocusAwareEventTrigger = 0
         var childOnFocusAwareEventTrigger = 0
         var childOnPreFocusAwareEventTrigger = 0
-        ContentWithInitialFocus {
+        rule.setContentWithInitialFocus {
             Box(
                 modifier = Modifier
                     .onFocusAwareEvent {
@@ -447,7 +450,7 @@
         var parentOnPreFocusAwareEventTrigger = 0
         var childOnFocusAwareEventTrigger = 0
         var childOnPreFocusAwareEventTrigger = 0
-        ContentWithInitialFocus {
+        rule.setContentWithInitialFocus {
             Box(
                 modifier = Modifier
                     .onFocusAwareEvent {
@@ -508,7 +511,7 @@
 
     private fun SemanticsNodeInteraction.performFocusAwareInput(sentEvent: Any) {
         when (nodeType) {
-            KeyInput -> {
+            KeyInput, InterruptedSoftKeyboardInput -> {
                 check(sentEvent is KeyEvent)
                 performKeyPress(sentEvent)
             }
@@ -522,55 +525,24 @@
         }
     }
 
-    private fun ContentWithInitialFocus(content: @Composable () -> Unit) {
-        rule.setContent {
-            Box(modifier = Modifier.requiredSize(10.dp, 10.dp)) { content() }
-        }
-        rule.runOnIdle { initialFocus.requestFocus() }
+    private fun ComposeContentTestRule.setContentWithInitialFocus(content: @Composable () -> Unit) {
+        setFocusableContent(content)
+        runOnIdle { initialFocus.requestFocus() }
     }
 
-    private fun Modifier.onFocusAwareEvent(onEvent: (Any) -> Boolean): Modifier = this.then(
-        FocusAwareEventElement(onEvent, nodeType, EventType.OnEvent)
-    )
-
-    private fun Modifier.onPreFocusAwareEvent(onEvent: (Any) -> Boolean): Modifier = this.then(
-        FocusAwareEventElement(onEvent, nodeType, EventType.PreEvent)
-    )
-
     @OptIn(ExperimentalComposeUiApi::class)
-    private data class FocusAwareEventElement(
-        private val callback: (Any) -> Boolean,
-        private val nodeType: NodeType,
-        private val eventType: EventType
-    ) : ModifierNodeElement<Modifier.Node>() {
-        override fun create() = when (nodeType) {
-            KeyInput -> KeyInputInputModifierNodeImpl(
-                onEvent = callback.takeIf { eventType == EventType.OnEvent },
-                onPreEvent = callback.takeIf { eventType == EventType.PreEvent }
-            )
-            RotaryInput -> RotaryInputModifierNodeImpl(
-                onEvent = callback.takeIf { eventType == EventType.OnEvent },
-                onPreEvent = callback.takeIf { eventType == EventType.PreEvent }
-            )
-        }
-
-        override fun update(node: Modifier.Node) = when (nodeType) {
-            KeyInput -> (node as KeyInputInputModifierNodeImpl).apply {
-                onEvent = callback.takeIf { eventType == EventType.OnEvent }
-                onPreEvent = callback.takeIf { eventType == EventType.PreEvent }
-            }
-            RotaryInput -> (node as RotaryInputModifierNodeImpl).apply {
-                onEvent = callback.takeIf { eventType == EventType.OnEvent }
-                onPreEvent = callback.takeIf { eventType == EventType.PreEvent }
-            }
-        }
-
-        override fun InspectorInfo.inspectableProperties() {
-            name = "onEvent"
-            properties["onEvent"] = callback
-        }
+    private fun Modifier.onFocusAwareEvent(onEvent: (Any) -> Boolean): Modifier = when (nodeType) {
+        KeyInput -> onKeyEvent(onEvent)
+        InterruptedSoftKeyboardInput -> onInterceptKeyBeforeSoftKeyboard(onEvent)
+        RotaryInput -> onRotaryScrollEvent(onEvent)
     }
 
-    enum class NodeType { KeyInput, RotaryInput }
-    enum class EventType { PreEvent, OnEvent }
+    @OptIn(ExperimentalComposeUiApi::class)
+    private fun Modifier.onPreFocusAwareEvent(onPreEvent: (Any) -> Boolean) = when (nodeType) {
+        KeyInput -> onPreviewKeyEvent(onPreEvent)
+        InterruptedSoftKeyboardInput -> onPreInterceptKeyBeforeSoftKeyboard(onPreEvent)
+        RotaryInput -> onPreRotaryScrollEvent(onPreEvent)
+    }
+
+    enum class NodeType { KeyInput, InterruptedSoftKeyboardInput, RotaryInput }
 }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/HardwareKeyInputTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/HardwareKeyInputTest.kt
new file mode 100644
index 0000000..5b4c1ba
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/key/HardwareKeyInputTest.kt
@@ -0,0 +1,316 @@
+/*
+ * 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.input.key
+
+import android.view.KeyEvent as AndroidKeyEvent
+import android.view.KeyEvent.KEYCODE_A as KeyCodeA
+import android.view.KeyEvent.ACTION_DOWN
+import android.view.KeyEvent.ACTION_UP
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.focus.focusTarget
+import androidx.compose.ui.focus.setFocusableContent
+import androidx.compose.ui.input.key.KeyEventType.Companion.KeyDown
+import androidx.compose.ui.input.key.KeyEventType.Companion.KeyUp
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performKeyPress
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.junit.Rule
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalComposeUiApi::class)
+class HardwareKeyInputTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    val initialFocus = FocusRequester()
+
+    @Test
+    fun onKeyToSoftKeyboardInterceptedEventTriggered() {
+        // Arrange.
+        var receivedKeyEvent: KeyEvent? = null
+        rule.setContentWithInitialFocus {
+            Box(
+                Modifier
+                    .onInterceptKeyBeforeSoftKeyboard {
+                        receivedKeyEvent = it
+                        true
+                    }
+                    .focusRequester(initialFocus)
+                    .focusTarget()
+            )
+        }
+
+        // Act.
+        val keyConsumed = rule.onRoot().performKeyPress(keyEvent(KeyCodeA, KeyUp))
+
+        // Assert.
+        rule.runOnIdle {
+            val keyEvent = checkNotNull(receivedKeyEvent)
+            assertThat(keyEvent.key).isEqualTo(Key.A)
+            assertThat(keyEvent.type).isEqualTo(KeyUp)
+            assertThat(keyConsumed).isTrue()
+        }
+    }
+
+    @Test
+    fun onPreKeyToSoftKeyboardInterceptedEventTriggered() {
+        // Arrange.
+        var receivedKeyEvent: KeyEvent? = null
+        rule.setContentWithInitialFocus {
+            Box(
+                Modifier
+                    .onPreInterceptKeyBeforeSoftKeyboard {
+                        receivedKeyEvent = it
+                        true
+                    }
+                    .focusRequester(initialFocus)
+                    .focusTarget()
+            )
+        }
+
+        // Act.
+        val keyConsumed = rule.onRoot().performKeyPress(keyEvent(KeyCodeA, KeyUp))
+
+        // Assert.
+        rule.runOnIdle {
+            val keyEvent = checkNotNull(receivedKeyEvent)
+            assertThat(keyEvent.key).isEqualTo(Key.A)
+            assertThat(keyEvent.type).isEqualTo(KeyUp)
+            assertThat(keyConsumed).isTrue()
+        }
+    }
+
+    @Test
+    fun onKeyEventNotTriggered_ifOnPreKeyEventConsumesEvent() {
+        // Arrange.
+        var receivedPreKeyEvent: KeyEvent? = null
+        var receivedKeyEvent: KeyEvent? = null
+        rule.setContentWithInitialFocus {
+            Box(
+                Modifier
+                    .onInterceptKeyBeforeSoftKeyboard {
+                        receivedKeyEvent = it
+                        true
+                    }
+                    .onPreInterceptKeyBeforeSoftKeyboard {
+                        receivedPreKeyEvent = it
+                        true
+                    }
+                    .focusRequester(initialFocus)
+                    .focusTarget()
+            )
+        }
+
+        // Act.
+        val keyConsumed = rule.onRoot().performKeyPress(keyEvent(KeyCodeA, KeyUp))
+
+        // Assert.
+        rule.runOnIdle {
+            val keyEvent = checkNotNull(receivedPreKeyEvent)
+            assertThat(keyEvent.key).isEqualTo(Key.A)
+            assertThat(keyEvent.type).isEqualTo(KeyUp)
+            assertThat(keyConsumed).isTrue()
+
+            assertThat(receivedKeyEvent).isNull()
+        }
+    }
+
+    @Test
+    fun onKeyEvent_triggeredAfter_onPreviewKeyEvent() {
+        // Arrange.
+        var triggerIndex = 1
+        var onInterceptedKeyEventTrigger = 0
+        var onInterceptedPreKeyEventTrigger = 0
+        rule.setContentWithInitialFocus {
+            Box(
+                Modifier
+                    .onInterceptKeyBeforeSoftKeyboard {
+                        onInterceptedKeyEventTrigger = triggerIndex++
+                        true
+                    }
+                    .onPreInterceptKeyBeforeSoftKeyboard {
+                        onInterceptedPreKeyEventTrigger = triggerIndex++
+                        false
+                    }
+                    .focusRequester(initialFocus)
+                    .focusTarget()
+            )
+        }
+
+        // Act.
+        rule.onRoot().performKeyPress(keyEvent(KeyCodeA, KeyUp))
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(onInterceptedPreKeyEventTrigger).isEqualTo(1)
+            assertThat(onInterceptedKeyEventTrigger).isEqualTo(2)
+        }
+    }
+
+    @Test
+    fun onKeyEvent_onKeyToSoftKeyboardInterceptedEvent_interaction() {
+        // Arrange.
+        var triggerIndex = 1
+        var onInterceptedKeyEventTrigger = 0
+        var onInterceptedPreKeyEventTrigger = 0
+        var onKeyEventTrigger = 0
+        var onPreviewKeyEventTrigger = 0
+        rule.setContentWithInitialFocus {
+            Box(
+                Modifier
+                    .onKeyEvent {
+                        onKeyEventTrigger = triggerIndex++
+                        false
+                    }
+                    .onPreviewKeyEvent {
+                        onPreviewKeyEventTrigger = triggerIndex++
+                        false
+                    }
+                    .onInterceptKeyBeforeSoftKeyboard {
+                        onInterceptedKeyEventTrigger = triggerIndex++
+                        false
+                    }
+                    .onPreInterceptKeyBeforeSoftKeyboard {
+                        onInterceptedPreKeyEventTrigger = triggerIndex++
+                        false
+                    }
+                    .focusRequester(initialFocus)
+                    .focusTarget()
+            )
+        }
+
+        // Act.
+        rule.onRoot().performKeyPress(keyEvent(KeyCodeA, KeyUp))
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(onInterceptedPreKeyEventTrigger).isEqualTo(1)
+            assertThat(onInterceptedKeyEventTrigger).isEqualTo(2)
+            assertThat(onPreviewKeyEventTrigger).isEqualTo(3)
+            assertThat(onKeyEventTrigger).isEqualTo(4)
+        }
+    }
+
+    @Test
+    fun onKeyEvent_onKeyToSoftKeyboardInterceptedEvent_parentChildInteraction() {
+        // Arrange.
+        var triggerIndex = 1
+        var onInterceptedKeyEventChildTrigger = 0
+        var onInterceptedKeyEventParentTrigger = 0
+        var onPreInterceptedKeyEventChildTrigger = 0
+        var onPreInterceptedKeyEvenParentTrigger = 0
+        var onKeyEventChildTrigger = 0
+        var onKeyEventParentTrigger = 0
+        var onPreKeyEventChildTrigger = 0
+        var onPreKeyEventParentTrigger = 0
+        rule.setContentWithInitialFocus {
+            Box(
+                Modifier
+                    .onKeyEvent {
+                        onKeyEventParentTrigger = triggerIndex++
+                        false
+                    }
+                    .onPreviewKeyEvent {
+                        onPreKeyEventParentTrigger = triggerIndex++
+                        false
+                    }
+                    .onInterceptKeyBeforeSoftKeyboard {
+                        onInterceptedKeyEventParentTrigger = triggerIndex++
+                        false
+                    }
+                    .onPreInterceptKeyBeforeSoftKeyboard {
+                        onPreInterceptedKeyEvenParentTrigger = triggerIndex++
+                        false
+                    }
+            ) {
+                Box(
+                    Modifier
+                        .onKeyEvent {
+                            onKeyEventChildTrigger = triggerIndex++
+                            false
+                        }
+                        .onPreviewKeyEvent {
+                            onPreKeyEventChildTrigger = triggerIndex++
+                            false
+                        }
+                        .onInterceptKeyBeforeSoftKeyboard {
+                            onInterceptedKeyEventChildTrigger = triggerIndex++
+                            false
+                        }
+                        .onPreInterceptKeyBeforeSoftKeyboard {
+                            onPreInterceptedKeyEventChildTrigger = triggerIndex++
+                            false
+                        }
+                        .focusRequester(initialFocus)
+                        .focusable()
+                )
+            }
+        }
+
+        // Act.
+        rule.onRoot().performKeyPress(keyEvent(KeyCodeA, KeyUp))
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(onPreInterceptedKeyEvenParentTrigger).isEqualTo(1)
+            assertThat(onPreInterceptedKeyEventChildTrigger).isEqualTo(2)
+            assertThat(onInterceptedKeyEventChildTrigger).isEqualTo(3)
+            assertThat(onInterceptedKeyEventParentTrigger).isEqualTo(4)
+            assertThat(onPreKeyEventParentTrigger).isEqualTo(5)
+            assertThat(onPreKeyEventChildTrigger).isEqualTo(6)
+            assertThat(onKeyEventChildTrigger).isEqualTo(7)
+            assertThat(onKeyEventParentTrigger).isEqualTo(8)
+        }
+    }
+
+    private fun ComposeContentTestRule.setContentWithInitialFocus(content: @Composable () -> Unit) {
+        setFocusableContent {
+            Box(modifier = Modifier.requiredSize(100.dp, 100.dp)) { content() }
+        }
+        runOnIdle {
+            initialFocus.requestFocus()
+        }
+    }
+
+    /**
+     * The [KeyEvent] is usually created by the system. This function creates an instance of
+     * [KeyEvent] that can be used in tests.
+     */
+    private fun keyEvent(keycode: Int, keyEventType: KeyEventType): KeyEvent {
+        val action = when (keyEventType) {
+            KeyDown -> ACTION_DOWN
+            KeyUp -> ACTION_UP
+            else -> error("Unknown key event type")
+        }
+        return KeyEvent(AndroidKeyEvent(0L, 0L, action, keycode, 0, 0))
+    }
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputDensityTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputDensityTest.kt
index 417ace5..4f5a9d6 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputDensityTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputDensityTest.kt
@@ -32,6 +32,8 @@
 import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.unit.Density
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -47,7 +49,7 @@
     @get:Rule
     val rule = createComposeRule()
 
-    val tag = "Tagged Layout"
+    private val tag = "Tagged Layout"
 
     @Test
     fun sendNotANumberDensityInPointerEvents() {
@@ -211,16 +213,36 @@
                             awaitPointerEvent()
                         }
                     }
-                })
+                }.testTag(tag)
+                )
             }
         }
 
+        // Because the pointer input coroutine scope is created lazily, that is, it won't be
+        // created/triggered until there is a event(tap), we must trigger a tap to instantiate the
+        // pointer input block of code.
+        rule.waitForIdle()
+        rule.onNodeWithTag(tag)
+            .performTouchInput {
+                down(Offset.Zero)
+                moveBy(Offset(1f, 1f))
+                up()
+            }
+
         rule.runOnIdle {
             assertThat(pointerInputDensities.size).isEqualTo(1)
             assertThat(pointerInputDensities.last()).isEqualTo(5f)
             density = 9f
         }
 
+        rule.waitForIdle()
+        rule.onNodeWithTag(tag)
+            .performTouchInput {
+                down(Offset.Zero)
+                moveBy(Offset(1f, 1f))
+                up()
+            }
+
         rule.runOnIdle {
             assertThat(pointerInputDensities.size).isEqualTo(2)
             assertThat(pointerInputDensities.last()).isEqualTo(9f)
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputViewConfigurationTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputViewConfigurationTest.kt
new file mode 100644
index 0000000..887b17a
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputViewConfigurationTest.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.input.pointer
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.testutils.TestViewConfiguration
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
+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
+
+/**
+ * The block of code for a pointer input should be reset if the view configuration changes. This
+ * class tests all those key possibilities.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class PointerInputViewConfigurationTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    private val tag = "Tagged Layout"
+
+    @Test
+    fun compositionLocalViewConfigurationChangeRestartsPointerInputOverload1() {
+        compositionLocalViewConfigurationChangeRestartsPointerInput {
+            Modifier.pointerInput(Unit, block = it)
+        }
+    }
+
+    @Test
+    fun compositionLocalViewConfigurationChangeRestartsPointerInputOverload2() {
+        compositionLocalViewConfigurationChangeRestartsPointerInput {
+            Modifier.pointerInput(Unit, Unit, block = it)
+        }
+    }
+
+    @Test
+    fun compositionLocalViewConfigurationChangeRestartsPointerInputOverload3() {
+        compositionLocalViewConfigurationChangeRestartsPointerInput {
+            Modifier.pointerInput(Unit, Unit, Unit, block = it)
+        }
+    }
+
+    private fun compositionLocalViewConfigurationChangeRestartsPointerInput(
+        pointerInput: (block: suspend PointerInputScope.() -> Unit) -> Modifier
+    ) {
+        var viewConfigurationTouchSlop by mutableStateOf(18f)
+
+        val pointerInputViewConfigurations = mutableListOf<Float>()
+        rule.setContent {
+            CompositionLocalProvider(
+                LocalViewConfiguration provides TestViewConfiguration(
+                    touchSlop = viewConfigurationTouchSlop
+                ),
+            ) {
+                Box(pointerInput {
+                    pointerInputViewConfigurations.add(viewConfigurationTouchSlop)
+                    awaitPointerEventScope {
+                        while (true) {
+                            awaitPointerEvent()
+                        }
+                    }
+                }.testTag(tag)
+                )
+            }
+        }
+
+        // Because the pointer input coroutine scope is created lazily, that is, it won't be
+        // created/triggered until there is a event(tap), we must trigger a tap to instantiate the
+        // pointer input block of code.
+        rule.waitForIdle()
+        rule.onNodeWithTag(tag)
+            .performTouchInput {
+                down(Offset.Zero)
+                moveBy(Offset(1f, 1f))
+                up()
+            }
+
+        rule.runOnIdle {
+            assertThat(pointerInputViewConfigurations.size).isEqualTo(1)
+            assertThat(pointerInputViewConfigurations.last()).isEqualTo(18f)
+            viewConfigurationTouchSlop = 20f
+        }
+
+        rule.waitForIdle()
+        rule.onNodeWithTag(tag)
+            .performTouchInput {
+                down(Offset.Zero)
+                moveBy(Offset(1f, 1f))
+                up()
+            }
+
+        rule.runOnIdle {
+            assertThat(pointerInputViewConfigurations.size).isEqualTo(2)
+            assertThat(pointerInputViewConfigurations.last()).isEqualTo(20f)
+        }
+    }
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilterTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilterTest.kt
index d5b715f..bfda21fb 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilterTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/SuspendingPointerInputFilterTest.kt
@@ -16,37 +16,34 @@
 
 package androidx.compose.ui.input.pointer
 
-import androidx.activity.compose.setContent
 import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
-import androidx.compose.runtime.snapshots.Snapshot
-import androidx.compose.testutils.TestViewConfiguration
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.platform.InspectableValue
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.PointerInputModifierNode
+import androidx.compose.ui.platform.InspectorInfo
 import androidx.compose.ui.platform.ValueElement
+import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
-import androidx.compose.ui.test.TestActivity
+import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.unit.IntSize
-import androidx.lifecycle.Lifecycle
-import androidx.test.core.app.ActivityScenario
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
+import androidx.test.filters.MediumTest
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CompletableDeferred
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.receiveAsFlow
 import kotlinx.coroutines.flow.toList
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.suspendCancellableCoroutine
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runTest
 import kotlinx.coroutines.withTimeout
 import kotlinx.coroutines.yield
@@ -56,66 +53,86 @@
 import org.junit.Assert.fail
 import org.junit.Test
 import org.junit.runner.RunWith
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
+import org.junit.Rule
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalCoroutinesApi::class)
 class SuspendingPointerInputFilterTest {
+    @get:Rule
+    val rule = createComposeRule()
+
     @After
     fun after() {
         // some tests may set this
         isDebugInspectorInfoEnabled = false
     }
 
-    private fun runTestUnconfined(test: suspend TestScope.() -> Unit) =
-        runTest(UnconfinedTestDispatcher()) {
-            test()
-        }
-
     @Test
-    fun testAwaitSingleEvent(): Unit = runTestUnconfined {
-        val filter = SuspendingPointerInputFilter(TestViewConfiguration())
-
-        val result = CompletableDeferred<PointerEvent>()
-        launch {
-            with(filter) {
-                awaitPointerEventScope {
-                    result.complete(awaitPointerEvent())
-                }
-            }
-        }
-
+    @MediumTest
+    fun testAwaitSingleEvent() {
+        val latch = CountDownLatch(1)
         val emitter = PointerInputChangeEmitter()
         val expectedChange = emitter.nextChange(Offset(5f, 5f))
 
-        filter.onPointerEvent(
-            expectedChange.toPointerEvent(),
-            PointerEventPass.Main,
-            IntSize(10, 10)
-        )
+        // Used to manually trigger a PointerEvent created from our PointerInputChange.
+        var testSuspendPointerInputModifierNodeElement:
+            TestSuspendPointerInputModifierNodeElement? = null
+        var returnedChange: PointerEvent? = null
 
-        val receivedEvent = withTimeout(200) {
-            result.await()
+        rule.setContent {
+            testSuspendPointerInputModifierNodeElement = Modifier.customTestingPointerInput(Unit) {
+                awaitPointerEventScope {
+                    returnedChange = awaitPointerEvent()
+                    latch.countDown()
+                }
+            } as TestSuspendPointerInputModifierNodeElement
+
+            Box(
+                modifier = testSuspendPointerInputModifierNodeElement!!
+            )
         }
 
-        assertEquals(expectedChange, receivedEvent.firstChange)
+        rule.runOnIdle {
+            testSuspendPointerInputModifierNodeElement?.let {
+                it.pointerInputModifierNode?.onPointerEvent(
+                    expectedChange.toPointerEvent(),
+                    PointerEventPass.Main,
+                    IntSize(10, 10)
+                )
+            }
+        }
+
+        rule.runOnIdle {
+            assertTrue("Waiting for relaunch timed out", latch.await(200, TimeUnit.MILLISECONDS))
+            assertEquals(expectedChange, returnedChange?.firstChange)
+        }
     }
 
     @Test
-    fun testAwaitSeveralEvents(): Unit = runTestUnconfined {
-        val filter = SuspendingPointerInputFilter(TestViewConfiguration())
+    @MediumTest
+    fun testAwaitSeveralEvents() {
+        val latch = CountDownLatch(3)
         val results = Channel<PointerEvent>(Channel.UNLIMITED)
-        launch {
-            with(filter) {
+
+        // Used to manually trigger a PointerEvent(s) created from our PointerInputChange(s).
+        var testSuspendPointerInputModifierNodeElement:
+            TestSuspendPointerInputModifierNodeElement? = null
+
+        rule.setContent {
+            testSuspendPointerInputModifierNodeElement = Modifier.customTestingPointerInput(Unit) {
                 awaitPointerEventScope {
                     repeat(3) {
                         results.trySend(awaitPointerEvent())
+                        latch.countDown()
                     }
                     results.close()
                 }
-            }
+            } as TestSuspendPointerInputModifierNodeElement
+
+            Box(
+                modifier = testSuspendPointerInputModifierNodeElement!!
+            )
         }
 
         val emitter = PointerInputChangeEmitter()
@@ -126,36 +143,62 @@
         )
 
         val bounds = IntSize(20, 20)
-        expected.forEach {
-            filter.onPointerEvent(it.toPointerEvent(), PointerEventPass.Main, bounds)
-        }
-        val received = withTimeout(200) {
-            results.receiveAsFlow()
-                .map { it.firstChange }
-                .toList()
+
+        rule.runOnIdle {
+            expected.forEach { pointerInputChange ->
+                testSuspendPointerInputModifierNodeElement?.let { testerNodeElement ->
+                    testerNodeElement.pointerInputModifierNode?.onPointerEvent(
+                        pointerInputChange.toPointerEvent(),
+                        PointerEventPass.Main,
+                        bounds
+                    )
+                }
+            }
         }
 
-        assertEquals(expected, received)
+        rule.runOnIdle {
+            assertTrue("Waiting for relaunch timed out", latch.await(200, TimeUnit.MILLISECONDS))
+
+            runTest {
+                val received = withTimeout(200) {
+                    results.receiveAsFlow()
+                        .map { it.firstChange }
+                        .toList()
+                }
+                assertEquals(expected, received)
+            }
+        }
     }
 
     @Test
-    fun testSyntheticCancelEvent(): Unit = runTestUnconfined {
+    @MediumTest
+    fun testSyntheticCancelEvent() {
         var currentEventAtEnd: PointerEvent? = null
-        val filter = SuspendingPointerInputFilter(TestViewConfiguration())
+        val latch = CountDownLatch(3)
         val results = Channel<PointerEvent>(Channel.UNLIMITED)
-        launch {
-            with(filter) {
+
+        // Used to manually trigger a PointerEvent(s) created from our PointerInputChange(s).
+        var testSuspendPointerInputModifierNodeElement:
+            TestSuspendPointerInputModifierNodeElement? = null
+
+        rule.setContent {
+            testSuspendPointerInputModifierNodeElement = Modifier.customTestingPointerInput(Unit) {
                 awaitPointerEventScope {
                     try {
                         repeat(3) {
                             results.trySend(awaitPointerEvent())
+                            latch.countDown()
                         }
                         results.close()
                     } finally {
                         currentEventAtEnd = currentEvent
                     }
                 }
-            }
+            } as TestSuspendPointerInputModifierNodeElement
+
+            Box(
+                modifier = testSuspendPointerInputModifierNodeElement!!
+            )
         }
 
         val bounds = IntSize(50, 50)
@@ -174,7 +217,9 @@
                     emitter2.nextChange(Offset(10f, 10f), down = false)
                 )
             ),
-            // Synthetic cancel should look like this;
+            // Synthetic cancel should look like this (Note: this specific event isn't ever
+            // triggered directly, it's just for reference so you know what onCancelPointerInput()
+            // triggers).
             // Both pointers are there, but only the with the pressed = true is changed to false,
             // and the down change is consumed.
             PointerEvent(
@@ -203,37 +248,79 @@
             )
         )
 
-        expectedEvents.take(expectedEvents.size - 1).forEach {
-            filter.onPointerEvent(it, PointerEventPass.Initial, bounds)
-            filter.onPointerEvent(it, PointerEventPass.Main, bounds)
-            filter.onPointerEvent(it, PointerEventPass.Final, bounds)
-        }
-        filter.onCancel()
+        rule.runOnIdle {
+            expectedEvents.take(expectedEvents.size - 1).forEach { pointerEvent ->
+                testSuspendPointerInputModifierNodeElement?.let { testerNodeElement ->
+                    // Initial
+                    testerNodeElement.pointerInputModifierNode?.onPointerEvent(
+                        pointerEvent,
+                        PointerEventPass.Initial,
+                        bounds
+                    )
 
-        val received = withTimeout(200) {
-            results.receiveAsFlow().toList()
+                    // Main
+                    testerNodeElement.pointerInputModifierNode?.onPointerEvent(
+                        pointerEvent,
+                        PointerEventPass.Main,
+                        bounds
+                    )
+
+                    // Final
+                    testerNodeElement.pointerInputModifierNode?.onPointerEvent(
+                        pointerEvent,
+                        PointerEventPass.Final,
+                        bounds
+                    )
+                }
+            }
+
+            // Triggers cancel event
+            testSuspendPointerInputModifierNodeElement?.let { testerNodeElement ->
+                testerNodeElement.pointerInputModifierNode?.onCancelPointerInput()
+            }
         }
 
-        assertThat(expectedEvents).hasSize(received.size)
+        // Checks events triggered are the correct ones
+        rule.runOnIdle {
+            assertTrue("Waiting for relaunch timed out", latch.await(200, TimeUnit.MILLISECONDS))
 
-        expectedEvents.forEachIndexed { index, expectedEvent ->
-            val actualEvent = received[index]
-            PointerEventSubject.assertThat(actualEvent).isStructurallyEqualTo(expectedEvent)
+            runTest {
+                val received = withTimeout(200) {
+                    results.receiveAsFlow().toList()
+                }
+
+                assertThat(expectedEvents).hasSize(received.size)
+
+                expectedEvents.forEachIndexed { index, expectedEvent ->
+                    val actualEvent = received[index]
+                    PointerEventSubject.assertThat(actualEvent).isStructurallyEqualTo(expectedEvent)
+                }
+                assertThat(currentEventAtEnd).isNotNull()
+                PointerEventSubject.assertThat(currentEventAtEnd!!)
+                    .isStructurallyEqualTo(expectedEvents.last())
+            }
         }
-        assertThat(currentEventAtEnd).isNotNull()
-        PointerEventSubject.assertThat(currentEventAtEnd!!)
-            .isStructurallyEqualTo(expectedEvents.last())
     }
 
     @Test
-    fun testNoSyntheticCancelEventWhenPressIsFalse(): Unit = runTestUnconfined {
+    @LargeTest
+    fun testNoSyntheticCancelEventWhenPressIsFalse() {
         var currentEventAtEnd: PointerEvent? = null
-        val filter = SuspendingPointerInputFilter(TestViewConfiguration())
         val results = Channel<PointerEvent>(Channel.UNLIMITED)
-        launch {
-            with(filter) {
+
+        // Used to manually trigger a PointerEvent(s) created from our PointerInputChange(s).
+        var testSuspendPointerInputModifierNodeElement:
+            TestSuspendPointerInputModifierNodeElement? = null
+
+        rule.setContent {
+            testSuspendPointerInputModifierNodeElement = Modifier.customTestingPointerInput(Unit) {
                 awaitPointerEventScope {
                     try {
+                        // NOTE: This will never trigger 3 times. There are only two events
+                        // triggered followed by a onCancelPointerInput() call which doesn't trigger
+                        // an event because the previous event has down (press) set to false, so we
+                        // will always get an exception thrown with the last repeat's timeout
+                        // (we expect this).
                         repeat(3) {
                             withTimeout(200) {
                                 results.trySend(awaitPointerEvent())
@@ -244,154 +331,383 @@
                         results.close()
                     }
                 }
-            }
+            } as TestSuspendPointerInputModifierNodeElement
+
+            Box(
+                modifier = testSuspendPointerInputModifierNodeElement!!
+            )
         }
 
         val bounds = IntSize(50, 50)
         val emitter1 = PointerInputChangeEmitter(0)
         val emitter2 = PointerInputChangeEmitter(1)
-        val expectedEvents = listOf(
+        val twoExpectedEvents = listOf(
             PointerEvent(
                 listOf(
                     emitter1.nextChange(Offset(5f, 5f)),
                     emitter2.nextChange(Offset(10f, 10f))
                 )
             ),
+            // Pointer event changes don't have any pressed pointers!
             PointerEvent(
                 listOf(
                     emitter1.nextChange(Offset(6f, 6f), down = false),
                     emitter2.nextChange(Offset(10f, 10f), down = false)
                 )
             )
-            // Unlike when a pointer is down, there is no cancel event sent
-            // when there aren't any pressed pointers. There's no event stream to cancel.
         )
 
-        expectedEvents.forEach {
-            filter.onPointerEvent(it, PointerEventPass.Initial, bounds)
-            filter.onPointerEvent(it, PointerEventPass.Main, bounds)
-            filter.onPointerEvent(it, PointerEventPass.Final, bounds)
-        }
-        filter.onCancel()
+        rule.runOnIdle {
+            twoExpectedEvents.forEach { pointerEvent ->
+                testSuspendPointerInputModifierNodeElement?.let { testerNodeElement ->
+                    // Initial
+                    testerNodeElement.pointerInputModifierNode?.onPointerEvent(
+                        pointerEvent,
+                        PointerEventPass.Initial,
+                        bounds
+                    )
 
-        withTimeout(400) {
-            while (!results.isClosedForSend) {
-                yield()
-            }
-        }
+                    // Main
+                    testerNodeElement.pointerInputModifierNode?.onPointerEvent(
+                        pointerEvent,
+                        PointerEventPass.Main,
+                        bounds
+                    )
 
-        val received = results.receiveAsFlow().toList()
-
-        assertThat(received).hasSize(expectedEvents.size)
-
-        expectedEvents.forEachIndexed { index, expectedEvent ->
-            val actualEvent = received[index]
-            PointerEventSubject.assertThat(actualEvent).isStructurallyEqualTo(expectedEvent)
-        }
-        assertThat(currentEventAtEnd).isNotNull()
-        PointerEventSubject.assertThat(currentEventAtEnd!!)
-            .isStructurallyEqualTo(expectedEvents.last())
-    }
-
-    @Test
-    fun testCancelledHandlerBlock() = runTestUnconfined {
-        val filter = SuspendingPointerInputFilter(TestViewConfiguration())
-        val counter = TestCounter()
-        val handler = launch {
-            with(filter) {
-                try {
-                    awaitPointerEventScope {
-                        try {
-                            counter.expect(1, "about to call awaitPointerEvent")
-                            awaitPointerEvent()
-                            fail("awaitPointerEvent returned; should have thrown for cancel")
-                        } finally {
-                            counter.expect(3, "inner finally block running")
-                        }
-                    }
-                } finally {
-                    counter.expect(4, "outer finally block running; inner finally should have run")
-                }
-            }
-        }
-
-        counter.expect(2, "before cancelling handler; awaitPointerEvent should be suspended")
-        handler.cancel()
-        counter.expect(5, "after cancelling; finally blocks should have run")
-    }
-
-    @Test
-    fun testInspectorValue() = runBlocking<Unit> {
-        isDebugInspectorInfoEnabled = true
-        val block: suspend PointerInputScope.() -> Unit = {}
-        val modifier = Modifier.pointerInput(Unit, block) as InspectableValue
-
-        assertThat(modifier.nameFallback).isEqualTo("pointerInput")
-        assertThat(modifier.valueOverride).isNull()
-        assertThat(modifier.inspectableElements.asIterable()).containsExactly(
-            ValueElement("key1", Unit),
-            ValueElement("block", block)
-        )
-    }
-
-    @Test
-    @LargeTest
-    fun testRestartPointerInput() = runBlocking {
-        var toAdd by mutableStateOf("initial")
-        val result = mutableListOf<String>()
-        val latch = CountDownLatch(2)
-        ActivityScenario.launch(TestActivity::class.java).use { scenario ->
-            scenario.moveToState(Lifecycle.State.CREATED)
-            scenario.onActivity {
-                it.setContent {
-                    // Read the value in composition to change the lambda capture below
-                    val toCapture = toAdd
-                    Box(
-                        Modifier.pointerInput(toCapture) {
-                            result += toCapture
-                            latch.countDown()
-                            suspendCancellableCoroutine<Unit> {}
-                        }
+                    // Final
+                    testerNodeElement.pointerInputModifierNode?.onPointerEvent(
+                        pointerEvent,
+                        PointerEventPass.Final,
+                        bounds
                     )
                 }
             }
-            scenario.moveToState(Lifecycle.State.STARTED)
-            Snapshot.withMutableSnapshot {
-                toAdd = "secondary"
+
+            // Manually triggers cancel event.
+            // Note: This will not trigger an event in the customPointerInput block because the
+            // previous events don't have any pressed pointers.
+            testSuspendPointerInputModifierNodeElement?.let { testerNodeElement ->
+                testerNodeElement.pointerInputModifierNode?.onCancelPointerInput()
             }
-            assertTrue("waiting for relaunch timed out", latch.await(3, TimeUnit.SECONDS))
-            assertEquals(
-                listOf("initial", "secondary"),
-                result
+        }
+
+        rule.mainClock.advanceTimeBy(1000)
+
+        rule.runOnIdle {
+            runTest {
+                withTimeout(400) {
+                    while (!results.isClosedForSend) {
+                        yield()
+                    }
+                }
+
+                val received = results.receiveAsFlow().toList()
+
+                assertThat(received).hasSize(twoExpectedEvents.size)
+
+                twoExpectedEvents.forEachIndexed { index, expectedEvent ->
+                    val actualEvent = received[index]
+                    PointerEventSubject.assertThat(actualEvent).isStructurallyEqualTo(expectedEvent)
+                }
+                assertThat(currentEventAtEnd).isNotNull()
+                PointerEventSubject.assertThat(currentEventAtEnd!!)
+                    .isStructurallyEqualTo(twoExpectedEvents.last())
+            }
+        }
+    }
+
+    @Test
+    @MediumTest
+    fun testCancelledHandlerBlock() {
+        val counter = TestCounter()
+
+        // Used to manually trigger a PointerEvent(s) created from our PointerInputChange(s).
+        var testSuspendPointerInputModifierNodeElement:
+            TestSuspendPointerInputModifierNodeElement? = null
+
+        rule.setContent {
+            testSuspendPointerInputModifierNodeElement = Modifier.customTestingPointerInput(Unit) {
+                try {
+                    awaitPointerEventScope {
+                        try {
+                            counter.expect(3, "about to call awaitPointerEvent")
+
+                            // With only one event triggered, this will stay stuck in the repeat
+                            // block until the Job is cancelled via
+                            // SuspendPointerInputModifierNode.resetHandling()
+                            repeat(2) {
+                                awaitPointerEvent()
+                                counter.expect(
+                                    4,
+                                    "One and only pointer event triggered to create Job."
+                                )
+                            }
+
+                            fail("awaitPointerEvent returned; should have thrown for cancel")
+                        } finally {
+                            counter.expect(6, "inner finally block running")
+                        }
+                    }
+                } finally {
+                    counter.expect(7, "outer finally block running; inner " +
+                        "finally should have run")
+                }
+            } as TestSuspendPointerInputModifierNodeElement
+
+            Box(
+                modifier = testSuspendPointerInputModifierNodeElement!!
+            )
+        }
+
+        val emitter = PointerInputChangeEmitter()
+        val singleEvent = emitter.nextChange(Offset(5f, 5f))
+        val singleEventBounds = IntSize(20, 20)
+
+        rule.runOnIdle {
+            counter.expect(
+                1,
+                "Job to handle pointer input not created yet; awaitPointerEvent should " +
+                    "be suspended"
+            )
+
+            testSuspendPointerInputModifierNodeElement?.let { testerNodeElement ->
+                counter.expect(
+                    2,
+                    "Trigger pointer input event to create Job for handing handle pointer" +
+                        " input (done lazily in SuspendPointerInputModifierNode)."
+                )
+
+                testerNodeElement.pointerInputModifierNode?.onPointerEvent(
+                    singleEvent.toPointerEvent(),
+                    PointerEventPass.Main,
+                    singleEventBounds
+                )
+            }
+
+            counter.expect(5, "before cancelling handler; awaitPointerEvent " +
+                "should be suspended")
+
+            // Cancels Job that manages pointer input events in SuspendPointerInputModifierNode.
+            testSuspendPointerInputModifierNodeElement?.resetsPointerInputBlockHandler()
+            counter.expect(8, "after cancelling; finally blocks should have run")
+        }
+    }
+
+    @Test
+    @MediumTest
+    fun testInspectorValue() {
+        isDebugInspectorInfoEnabled = true
+
+        rule.setContent {
+            val block: suspend PointerInputScope.() -> Unit = {}
+            val modifier =
+                Modifier.pointerInput(Unit, block) as SuspendPointerInputModifierNodeElement
+
+            assertThat(modifier.nameFallback).isEqualTo("pointerInput")
+            assertThat(modifier.valueOverride).isNull()
+            assertThat(modifier.inspectableElements.asIterable()).containsExactly(
+                ValueElement("key1", Unit),
+                ValueElement("key2", null),
+                ValueElement("keys", null),
+                ValueElement("block", block)
             )
         }
     }
 
-    @Test(expected = PointerEventTimeoutCancellationException::class)
-    fun testWithTimeout() = runTestUnconfined {
-        val filter = SuspendingPointerInputFilter(TestViewConfiguration())
-        filter.coroutineScope = this
-        with(filter) {
-            awaitPointerEventScope {
-                withTimeout(10) {
-                    awaitPointerEvent()
-                }
+    @Test
+    @MediumTest
+    fun testRestartPointerInputWithTouchEvent() {
+        val emitter = PointerInputChangeEmitter()
+        val expectedChange = emitter.nextChange(Offset(5f, 5f))
+
+        // Used to manually trigger a PointerEvent created from our PointerInputChange.
+        var testSuspendPointerInputModifierNodeElement:
+            TestSuspendPointerInputModifierNodeElement? = null
+
+        var forceRecompositionCount by mutableStateOf(0)
+        var compositionCount = 0
+        var pointerInputBlockExecutionCount = 0
+
+        rule.setContent {
+            // Read the value in composition to change the lambda capture below
+            val toCapture = forceRecompositionCount
+            compositionCount++
+
+            testSuspendPointerInputModifierNodeElement =
+                Modifier.customTestingPointerInput(toCapture) {
+                    // pointerInput now lazily executes this block of code meaning it won't be
+                    // executed until an actual event happens.
+                    pointerInputBlockExecutionCount++
+                    suspendCancellableCoroutine<Unit> {}
+                } as TestSuspendPointerInputModifierNodeElement
+            Box(modifier = testSuspendPointerInputModifierNodeElement!!)
+        }
+
+        forceRecompositionCount = 1
+
+        rule.runOnIdle {
+            // Triggers first and only event (and launches coroutine).
+            // Note: SuspendPointerInputModifierNode actually launches its coroutine lazily, so it
+            // will not be launched until the first event is triggered which is what we do here.
+            testSuspendPointerInputModifierNodeElement?.let {
+                it.pointerInputModifierNode?.onPointerEvent(
+                    expectedChange.toPointerEvent(),
+                    PointerEventPass.Main,
+                    IntSize(5, 5)
+                )
             }
         }
+
+        rule.runOnIdle {
+            assertEquals(compositionCount, 2)
+            // One pointer input event, should have triggered one execution.
+            assertEquals(pointerInputBlockExecutionCount, 1)
+        }
     }
 
     @Test
-    fun testWithTimeoutOrNull() = runTestUnconfined {
-        val filter = SuspendingPointerInputFilter(TestViewConfiguration())
-        filter.coroutineScope = this
-        val result: PointerEvent? = with(filter) {
-            awaitPointerEventScope {
-                withTimeoutOrNull(10) {
-                    awaitPointerEvent()
+    @MediumTest
+    fun testRestartPointerInputWithNoTouchEvents() {
+        var forceRecompositionCount by mutableStateOf(0)
+        var compositionCount = 0
+        var pointerInputBlockExecutionCount = 0
+
+        rule.setContent {
+            // Read the value in composition to change the lambda capture below
+            val toCapture = forceRecompositionCount
+            compositionCount++
+            Box(
+                Modifier.pointerInput(toCapture) {
+                    // pointerInput now lazily executes this block of code meaning it won't be
+                    // executed until an actual event happens.
+                    pointerInputBlockExecutionCount++
+                    suspendCancellableCoroutine<Unit> {}
                 }
+            )
+        }
+
+        forceRecompositionCount = 1
+
+        rule.runOnIdle {
+            assertEquals(compositionCount, 2)
+            // No pointer input events, no block executions.
+            assertEquals(pointerInputBlockExecutionCount, 0)
+        }
+    }
+
+    @Test
+    @LargeTest
+    fun testWithTimeout() {
+        val latch = CountDownLatch(1)
+        val emitter = PointerInputChangeEmitter()
+        val expectedChange = emitter.nextChange(Offset(5f, 5f))
+
+        // Used to manually trigger a PointerEvent created from our PointerInputChange.
+        var testSuspendPointerInputModifierNodeElement:
+            TestSuspendPointerInputModifierNodeElement? = null
+
+        rule.setContent {
+            testSuspendPointerInputModifierNodeElement = Modifier.customTestingPointerInput(Unit) {
+                awaitPointerEventScope {
+                    try {
+                        // Handles first event (needed to trigger the creation of the coroutine
+                        // since it is lazily created).
+                        awaitPointerEvent()
+
+                        // Times out waiting for second event (no second event is triggered in this
+                        // test).
+                        withTimeout(10) {
+                            awaitPointerEvent()
+                        }
+                    } catch (exception: Exception) {
+                        assertThat(exception)
+                            .isInstanceOf(PointerEventTimeoutCancellationException::class.java)
+                        latch.countDown()
+                    }
+                }
+            } as TestSuspendPointerInputModifierNodeElement
+
+            Box(modifier = testSuspendPointerInputModifierNodeElement!!)
+        }
+
+        rule.runOnIdle {
+            // Triggers first event (and launches coroutine).
+            // Note: SuspendPointerInputModifierNode actually launches its coroutine lazily, so it
+            // will not be launched until the first event is triggered which is what we do here.
+            testSuspendPointerInputModifierNodeElement?.let {
+                it.pointerInputModifierNode?.onPointerEvent(
+                    expectedChange.toPointerEvent(),
+                    PointerEventPass.Main,
+                    IntSize(5, 5)
+                )
             }
         }
-        assertThat(result).isNull()
+
+        rule.mainClock.advanceTimeBy(1000)
+
+        rule.runOnIdle {
+            assertTrue(latch.await(2, TimeUnit.SECONDS))
+        }
+    }
+
+    @Test
+    @LargeTest
+    fun testWithTimeoutOrNull() {
+        val emitter = PointerInputChangeEmitter()
+        val expectedChange = emitter.nextChange(Offset(5f, 5f))
+
+        // Sets an empty default (if not updated to null after call (expected), it will fail).
+        var resultOfTimeoutOrNull: PointerEvent? = PointerEvent(listOf())
+
+        // Used to manually trigger a PointerEvent created from our PointerInputChange.
+        var testSuspendPointerInputModifierNodeElement:
+            TestSuspendPointerInputModifierNodeElement? = null
+
+        rule.setContent {
+            testSuspendPointerInputModifierNodeElement = Modifier.customTestingPointerInput(Unit) {
+                awaitPointerEventScope {
+                    try {
+                        // Handles first event (needed to trigger the creation of the coroutine
+                        // since it is lazily created).
+                        awaitPointerEvent()
+
+                        // Times out waiting for second event (no second event is triggered in this
+                        // test).
+                        resultOfTimeoutOrNull = withTimeoutOrNull(10) {
+                            awaitPointerEvent()
+                        }
+                    } catch (exception: Exception) {
+                        // An exception should not be raised in this test, but, just in case one is,
+                        // we want to verify it isn't the one withTimeout will usually raise.
+                        assertThat(exception)
+                            .isNotInstanceOf(PointerEventTimeoutCancellationException::class.java)
+                    }
+                }
+            } as TestSuspendPointerInputModifierNodeElement
+
+            Box(
+                modifier = testSuspendPointerInputModifierNodeElement!!
+            )
+        }
+
+        rule.runOnIdle {
+            // Triggers first event (and launches coroutine).
+            // Note: SuspendPointerInputModifierNode actually launches its coroutine lazily, so it
+            // will not be launched until the first event is triggered which is what we do here.
+            testSuspendPointerInputModifierNodeElement?.let {
+                it.pointerInputModifierNode?.onPointerEvent(
+                    expectedChange.toPointerEvent(),
+                    PointerEventPass.Main,
+                    IntSize(5, 5)
+                )
+            }
+        }
+
+        rule.mainClock.advanceTimeBy(1000)
+
+        rule.runOnIdle {
+            assertThat(resultOfTimeoutOrNull).isNull()
+        }
     }
 }
 
@@ -438,3 +754,76 @@
         count = expected
     }
 }
+
+// Customized version of [Modifier.pointerInput] that uses the customized version of the
+// [SuspendPointerInputModifierNodeElement] class below (it allows us to manually trigger
+// [PointerEvent] events.
+internal fun Modifier.customTestingPointerInput(
+    key1: Any?,
+    block: suspend PointerInputScope.() -> Unit
+): Modifier = this then TestSuspendPointerInputModifierNodeElement(
+    key1 = key1,
+    block = block
+)
+
+// Matches [SuspendPointerInputModifierNodeElement] implementation but maintains a reference to a
+// [SuspendPointerInputModifierNode], so we can manually trigger [PointerEvent] events.
+@OptIn(ExperimentalComposeUiApi::class)
+internal class TestSuspendPointerInputModifierNodeElement(
+    val key1: Any? = null,
+    val key2: Any? = null,
+    val keys: Array<out Any?>? = null,
+    val block: suspend PointerInputScope.() -> Unit
+) : ModifierNodeElement<SuspendPointerInputModifierNode>() {
+    private var suspendPointerInputModifierNode: SuspendPointerInputModifierNode? = null
+    var pointerInputModifierNode: PointerInputModifierNode? = null
+
+    override fun InspectorInfo.inspectableProperties() {
+        debugInspectorInfo {
+            name = "pointerInput"
+            properties["key1"] = key1
+            properties["key2"] = key2
+            properties["keys"] = keys
+            properties["block"] = block
+        }
+    }
+
+    override fun create(): SuspendPointerInputModifierNode {
+        suspendPointerInputModifierNode = SuspendPointerInputModifierNode(block)
+        pointerInputModifierNode = suspendPointerInputModifierNode
+        return suspendPointerInputModifierNode as SuspendPointerInputModifierNode
+    }
+
+    override fun update(node: SuspendPointerInputModifierNode): SuspendPointerInputModifierNode {
+        node.block = block
+        suspendPointerInputModifierNode = node
+        pointerInputModifierNode = suspendPointerInputModifierNode
+        return suspendPointerInputModifierNode as SuspendPointerInputModifierNode
+    }
+
+    // Cancels Job that manages pointer input events in SuspendPointerInputModifierNode.
+    fun resetsPointerInputBlockHandler() {
+        suspendPointerInputModifierNode?.resetBlock()
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is SuspendPointerInputModifierNodeElement) return false
+        if (key1 != other.key1) return false
+        if (key2 != other.key2) return false
+        if (keys != null) {
+            if (other.keys == null) return false
+            if (!keys.contentEquals(other.keys)) return false
+        } else if (other.keys != null) return false
+        if (block != other.block) return false
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = key1?.hashCode() ?: 0
+        result = 31 * result + (key2?.hashCode() ?: 0)
+        result = 31 * result + (keys?.contentHashCode() ?: 0)
+        result = 31 * result + block.hashCode()
+        return result
+    }
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/Helpers.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/Helpers.kt
index cf021ed..7a5a9b6 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/Helpers.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/Helpers.kt
@@ -47,7 +47,6 @@
 import androidx.compose.ui.platform.TextToolbar
 import androidx.compose.ui.platform.ViewConfiguration
 import androidx.compose.ui.platform.WindowInfo
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.font.Font
 import androidx.compose.ui.text.font.FontFamily
 import androidx.compose.ui.text.input.PlatformTextInputPluginRegistry
@@ -158,7 +157,6 @@
         get() = TODO("Not yet implemented")
     override val textInputService: TextInputService
         get() = TODO("Not yet implemented")
-    @OptIn(ExperimentalTextApi::class)
     override val platformTextInputPluginRegistry: PlatformTextInputPluginRegistry
         get() = TODO("Not yet implemented")
     override val pointerIconService: PointerIconService
@@ -294,7 +292,7 @@
     }
 }
 
-internal fun LayoutNode.add(child: LayoutNode) = insertAt(children.count(), child)
+internal fun LayoutNode.add(child: LayoutNode) = insertAt(foldedChildren.count(), child)
 
 internal fun LayoutNode.measureInLayoutBlock() {
     measurePolicy = MeasureInLayoutBlock()
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/LookaheadLayoutTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/LookaheadLayoutTest.kt
index ba5d463..d22cae1 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/LookaheadLayoutTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/LookaheadLayoutTest.kt
@@ -74,12 +74,16 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.round
 import androidx.compose.ui.unit.sp
+import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastForEachIndexed
+import androidx.compose.ui.util.fastMap
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import java.lang.Integer.max
 import junit.framework.TestCase.assertEquals
 import junit.framework.TestCase.assertTrue
 import kotlin.math.roundToInt
+import kotlin.random.Random
 import kotlinx.coroutines.launch
 import org.junit.Rule
 import org.junit.Test
@@ -97,6 +101,58 @@
     val excessiveAssertions = AndroidOwnerExtraAssertionsRule()
 
     @Test
+    fun randomLookaheadPlacementOrder() {
+        val nodeList = List(10) { node() }
+        val placementOrder = (0..9).toMutableList()
+        fun generateRandomPlaceOrder() {
+            repeat(9) {
+                val swapId = Random.nextInt(it, 10)
+                val tmp = placementOrder[it]
+                placementOrder[it] = placementOrder[swapId]
+                placementOrder[swapId] = tmp
+            }
+        }
+
+        val root = node {
+            generateRandomPlaceOrder()
+            add(LayoutNode(isVirtual = true).apply {
+                isVirtualLookaheadRoot = true
+                add(nodeList[0])
+                add(LayoutNode(isVirtual = true).apply {
+                    repeat(4) {
+                        add(nodeList[it + 1])
+                    }
+                })
+                add(LayoutNode(isVirtual = true).apply {
+                    repeat(5) {
+                        add(nodeList[5 + it])
+                    }
+                })
+            })
+            measurePolicy = MeasurePolicy { measurables, constraints ->
+                assertEquals(10, measurables.size)
+                val placeables = measurables.fastMap { it.measure(constraints) }
+                assertEquals(10, placeables.size)
+                layout(100, 100) {
+                    placementOrder.fastForEach { id ->
+                        placeables[id].place(0, 0)
+                    }
+                }
+            }
+        }
+        val delegate = createDelegate(root)
+        repeat(5) {
+            placementOrder.fastForEachIndexed { placeOrder, nodeId ->
+                assertEquals(placeOrder, nodeList[nodeId].lookaheadPassDelegate!!.placeOrder)
+                assertEquals(placeOrder, nodeList[nodeId].measurePassDelegate.placeOrder)
+            }
+            generateRandomPlaceOrder()
+            root.requestLookaheadRemeasure()
+            delegate.measureAndLayout()
+        }
+    }
+
+    @Test
     fun lookaheadLayoutAnimation() {
         var isLarge by mutableStateOf(true)
         var size1 = IntSize.Zero
@@ -1014,8 +1070,8 @@
                         Text(
                             text =
                             "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec" +
-                            " non felis euismod nunc commodo pharetra a nec eros. Sed varius," +
-                            " metus sed facilisis condimentum, orci orci aliquet arcu",
+                                " non felis euismod nunc commodo pharetra a nec eros. Sed varius," +
+                                " metus sed facilisis condimentum, orci orci aliquet arcu",
                             Modifier.fillMaxWidth(),
                         )
                     }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/PlacementLayoutCoordinatesTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/PlacementLayoutCoordinatesTest.kt
index b048f0e..f655381 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/PlacementLayoutCoordinatesTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/PlacementLayoutCoordinatesTest.kt
@@ -58,7 +58,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@OptIn(ExperimentalComposeUiApi::class)
 @MediumTest
 @RunWith(AndroidJUnit4::class)
 class PlacementLayoutCoordinatesTest {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/CompositionLocalModifierNodeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/CompositionLocalModifierNodeTest.kt
index c11ac21..897ab66 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/CompositionLocalModifierNodeTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/CompositionLocalModifierNodeTest.kt
@@ -17,6 +17,7 @@
 package androidx.compose.ui.node
 
 import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.ReusableContent
 import androidx.compose.runtime.compositionLocalOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -186,6 +187,80 @@
         }
     }
 
+    // Regression test for b/271875799
+    @Test
+    fun compositionLocalsUpdateWhenContentMoves() {
+        var readValue = -1
+        var providedValue by mutableStateOf(42)
+
+        var contentKey by mutableStateOf(1)
+        val modifier = modifierNodeElementOf {
+            object : Modifier.Node(), DrawModifierNode,
+                CompositionLocalConsumerModifierNode {
+                override fun ContentDrawScope.draw() {
+                    readValue = currentValueOf(localInt)
+                    drawContent()
+                }
+            }
+        }
+
+        rule.setContent {
+            CompositionLocalProvider(localInt provides providedValue) {
+                ReusableContent(contentKey) {
+                    Layout(modifier, EmptyBoxMeasurePolicy)
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(readValue).isEqualTo(42)
+        }
+
+        contentKey++
+        providedValue = 86
+
+        rule.runOnIdle {
+            assertThat(readValue).isEqualTo(86)
+        }
+    }
+
+    // Regression test for b/271875799
+    @Test
+    fun staticCompositionLocalsUpdateWhenContentMoves() {
+        var readValue = -1
+        var providedValue by mutableStateOf(32)
+
+        var contentKey by mutableStateOf(1)
+        val modifier = modifierNodeElementOf {
+            object : Modifier.Node(), DrawModifierNode,
+                CompositionLocalConsumerModifierNode {
+                override fun ContentDrawScope.draw() {
+                    readValue = currentValueOf(staticLocalInt)
+                    drawContent()
+                }
+            }
+        }
+
+        rule.setContent {
+            CompositionLocalProvider(staticLocalInt provides providedValue) {
+                ReusableContent(contentKey) {
+                    Layout(modifier, EmptyBoxMeasurePolicy)
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            assertThat(readValue).isEqualTo(32)
+        }
+
+        contentKey++
+        providedValue = 64
+
+        rule.runOnIdle {
+            assertThat(readValue).isEqualTo(64)
+        }
+    }
+
     private inline fun <reified T : Modifier.Node> modifierNodeElementOf(
         crossinline create: () -> T
     ): ModifierNodeElement<T> = object : ModifierNodeElement<T>() {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/AndroidClipboardManagerTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/AndroidClipboardManagerTest.kt
index 1411e537..23c7652 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/AndroidClipboardManagerTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/AndroidClipboardManagerTest.kt
@@ -22,7 +22,6 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shadow
 import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.buildAnnotatedString
 import androidx.compose.ui.text.font.FontStyle
@@ -45,7 +44,6 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalTextApi::class)
 class AndroidClipboardManagerTest {
 
     private val context = InstrumentationRegistry.getInstrumentation().context
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/AndroidFontResourceLoaderTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/AndroidFontResourceLoaderTest.kt
index 1bbb481..c2cc74f 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/AndroidFontResourceLoaderTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/AndroidFontResourceLoaderTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.ui.platform
 
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.font.Font
 import androidx.compose.ui.text.font.FontStyle
 import androidx.compose.ui.text.font.FontWeight
@@ -30,7 +29,6 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalTextApi::class)
 class AndroidFontResourceLoaderTest {
     private val context = InstrumentationRegistry.getInstrumentation().context
 
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/text/input/PlatformTextInputAdapterRegistryTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/text/input/PlatformTextInputAdapterRegistryTest.kt
index 483afd2..959c593 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/text/input/PlatformTextInputAdapterRegistryTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/text/input/PlatformTextInputAdapterRegistryTest.kt
@@ -317,8 +317,6 @@
         var isDisposed: Boolean = false
             private set
 
-        override val inputForTests: TextInputForTests = NoopInputForTests
-
         override fun createInputConnection(outAttrs: EditorInfo): InputConnection? {
             // Not testing android stuff in this test.
             TODO("Not implemented for test")
@@ -329,8 +327,4 @@
             isDisposed = true
         }
     }
-
-    private object NoopInputForTests : TextInputForTests {
-        override fun inputTextForTest(text: String) = TODO("Not implemented for test")
-    }
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/text/input/PlatformTextInputEditTextIntegrationTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/text/input/PlatformTextInputEditTextIntegrationTest.kt
index 1d69672..0e1c259 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/text/input/PlatformTextInputEditTextIntegrationTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/text/input/PlatformTextInputEditTextIntegrationTest.kt
@@ -27,7 +27,9 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalPlatformTextInputPluginRegistry
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.insertTextAtCursor
 import androidx.compose.ui.semantics.performImeAction
+import androidx.compose.ui.semantics.requestFocus
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.semantics.setSelection
 import androidx.compose.ui.semantics.setText
@@ -40,7 +42,6 @@
 import androidx.compose.ui.test.performTextInput
 import androidx.compose.ui.test.performTextInputSelection
 import androidx.compose.ui.test.performTextReplacement
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -58,7 +59,7 @@
  * This test exercises the use case of an [EditText] embedded in a composition using the text input
  * plugin system to wire into Compose's testing framework.
  */
-@OptIn(ExperimentalTextApi::class, ExperimentalTestApi::class)
+@OptIn(ExperimentalTestApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class PlatformTextInputEditTextIntegrationTest {
@@ -167,6 +168,10 @@
         AndroidView(
             modifier = modifier.semantics {
                 // Required for the semantics actions to recognize this node as a text editor.
+                requestFocus {
+                    adapter.editText?.requestFocus()
+                    return@requestFocus false
+                }
                 setText { text ->
                     adapter.editText?.also {
                         it.setText(text.text)
@@ -174,6 +179,14 @@
                     }
                     return@setText false
                 }
+                insertTextAtCursor { text ->
+                    adapter.editText?.also {
+                        // TODO(aosp/2485435) Actually insert at cursor when focus doesn't happen
+                        //  via click.
+                        it.text.append(text)
+                    }
+                    return@insertTextAtCursor false
+                }
                 setSelection { start, end, _ ->
                     adapter.editText?.also {
                         it.setSelection(start, end)
@@ -196,7 +209,7 @@
     private class EditTextWrapper(
         context: Context,
         private val adapter: TestAdapter
-    ) : EditText(context), TextInputForTests {
+    ) : EditText(context) {
 
         override fun onFocusChanged(
             focused: Boolean,
@@ -215,10 +228,6 @@
                 adapter.editText = null
             }
         }
-
-        override fun inputTextForTest(text: String) {
-            this.text.append(text)
-        }
     }
 
     private object TestPlugin : PlatformTextInputPlugin<TestAdapter> {
@@ -232,7 +241,6 @@
         val context: PlatformTextInput,
     ) : PlatformTextInputAdapter {
         var editText: EditTextWrapper? = null
-        override val inputForTests: TextInputForTests? get() = editText
         override fun createInputConnection(outAttrs: EditorInfo): InputConnection? = null
     }
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/text/input/PlatformTextInputTestIntegrationTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/text/input/PlatformTextInputTestIntegrationTest.kt
deleted file mode 100644
index 9d158a5..0000000
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/text/input/PlatformTextInputTestIntegrationTest.kt
+++ /dev/null
@@ -1,182 +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.text.input
-
-import android.view.View
-import android.view.inputmethod.EditorInfo
-import android.view.inputmethod.InputConnection
-import androidx.compose.foundation.focusable
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.size
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.onFocusChanged
-import androidx.compose.ui.platform.LocalPlatformTextInputPluginRegistry
-import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.semantics.SemanticsActions
-import androidx.compose.ui.semantics.performImeAction
-import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.semantics.setText
-import androidx.compose.ui.test.SemanticsNodeInteraction
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithTag
-import androidx.compose.ui.test.performImeAction
-import androidx.compose.ui.test.performSemanticsAction
-import androidx.compose.ui.test.performTextInput
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.unit.dp
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.google.common.truth.Truth.assertThat
-import kotlin.test.assertFailsWith
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * This test only tests that the [SemanticsNodeInteraction] extension functions related to text
- * input get sent to the [PlatformTextInputAdapter]'s [TextInputForTests].
- *
- * It does *not* test integration with Android's text input system or platform-specific code.
- */
-@OptIn(ExperimentalTextApi::class)
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class PlatformTextInputTestIntegrationTest {
-
-    @get:Rule
-    val rule = createComposeRule()
-
-    @Test
-    fun whenNoFieldFocused() {
-        val testCommands = mutableListOf<String>()
-        rule.setContent {
-            TestTextField(testCommands, Modifier.testTag("field"))
-        }
-
-        val error = assertFailsWith<IllegalStateException> {
-            rule.onNodeWithTag("field").performTextInput("hello")
-        }
-        assertThat(error).hasMessageThat().isEqualTo("No input session started. Missing a focus?")
-    }
-
-    @Test
-    fun sendsAllTestCommandsToFocusedAdapter() {
-        val testCommands = mutableListOf<String>()
-        rule.setContent {
-            TestTextField(testCommands, Modifier.testTag("field"))
-        }
-
-        with(rule.onNodeWithTag("field")) {
-            performSemanticsAction(SemanticsActions.RequestFocus)
-
-            performTextInput("hello")
-            performImeAction()
-        }
-
-        rule.runOnIdle {
-            assertThat(testCommands).containsExactly(
-                "input(hello)",
-                "performImeAction",
-            ).inOrder()
-        }
-    }
-
-    @Test
-    fun handlesFocusChange() {
-        val testCommands1 = mutableListOf<String>()
-        val testCommands2 = mutableListOf<String>()
-        rule.setContent {
-            TestTextField(testCommands1, Modifier.testTag("field1"))
-            TestTextField(testCommands2, Modifier.testTag("field2"))
-        }
-
-        with(rule.onNodeWithTag("field1")) {
-            performSemanticsAction(SemanticsActions.RequestFocus)
-            performTextInput("hello")
-        }
-        with(rule.onNodeWithTag("field2")) {
-            performSemanticsAction(SemanticsActions.RequestFocus)
-            performTextInput("world")
-        }
-
-        rule.runOnIdle {
-            assertThat(testCommands1).containsExactly("input(hello)")
-            assertThat(testCommands2).containsExactly("input(world)")
-        }
-    }
-
-    @Composable
-    private fun TestTextField(
-        testCommands: MutableList<String>,
-        modifier: Modifier = Modifier
-    ) {
-        val adapter = LocalPlatformTextInputPluginRegistry.current
-            .rememberAdapter(TestPlugin)
-
-        Box(
-            modifier
-                .size(1.dp)
-                .onFocusChanged {
-                    if (it.isFocused) {
-                        adapter.startInput(testCommands)
-                    } else {
-                        adapter.endInput()
-                    }
-                }
-                .focusable()
-                .semantics {
-                    setText { true }
-                    performImeAction {
-                        testCommands += "performImeAction"
-                        true
-                    }
-                }
-        )
-    }
-
-    private object TestPlugin : PlatformTextInputPlugin<TestAdapter> {
-        override fun createAdapter(
-            platformTextInput: PlatformTextInput,
-            view: View
-        ): TestAdapter = TestAdapter(platformTextInput)
-    }
-
-    private class TestAdapter(
-        private val context: PlatformTextInput,
-    ) : PlatformTextInputAdapter, TextInputForTests {
-        private var testCommands: MutableList<String>? = null
-
-        fun startInput(testCommands: MutableList<String>) {
-            this.testCommands = testCommands
-            context.requestInputFocus()
-        }
-
-        fun endInput() {
-            context.releaseInputFocus()
-            this.testCommands = null
-        }
-
-        override val inputForTests get() = this
-
-        override fun createInputConnection(outAttrs: EditorInfo): InputConnection? = null
-
-        override fun inputTextForTest(text: String) {
-            testCommands!! += "input($text)"
-        }
-    }
-}
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/text/input/PlatformTextInputViewIntegrationTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/text/input/PlatformTextInputViewIntegrationTest.kt
index 77d84d7..f6744de 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/text/input/PlatformTextInputViewIntegrationTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/text/input/PlatformTextInputViewIntegrationTest.kt
@@ -24,7 +24,6 @@
 import androidx.compose.ui.platform.LocalPlatformTextInputPluginRegistry
 import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
@@ -32,7 +31,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@OptIn(ExperimentalTextApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class PlatformTextInputViewIntegrationTest {
@@ -134,8 +132,6 @@
         val actionLabel = "test connection!"
         val inputConnection = TestInputConnection(view)
 
-        override val inputForTests: TextInputForTests = NoopInputForTests
-
         override fun createInputConnection(outAttrs: EditorInfo): InputConnection {
             outAttrs.actionLabel = actionLabel
             return inputConnection
@@ -148,8 +144,4 @@
     }
 
     private class TestInputConnection(view: View) : BaseInputConnection(view, false)
-
-    private object NoopInputForTests : TextInputForTests {
-        override fun inputTextForTest(text: String) = TODO("Not implemented for test")
-    }
 }
\ No newline at end of file
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 14b1cc3..60fe14e 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
@@ -49,7 +49,6 @@
 import android.view.inputmethod.InputConnection
 import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresApi
-import androidx.annotation.RestrictTo
 import androidx.annotation.VisibleForTesting
 import androidx.compose.runtime.collection.mutableVectorOf
 import androidx.compose.runtime.derivedStateOf
@@ -58,7 +57,6 @@
 import androidx.compose.runtime.referentialEqualityPolicy
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.InternalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.autofill.AndroidAutofill
 import androidx.compose.ui.autofill.Autofill
@@ -137,9 +135,6 @@
 import androidx.compose.ui.text.font.createFontFamilyResolver
 import androidx.compose.ui.text.input.AndroidTextInputServicePlugin
 import androidx.compose.ui.text.input.PlatformTextInputPluginRegistryImpl
-import androidx.compose.ui.text.input.PlatformTextInputService
-import androidx.compose.ui.text.input.TextInputForTests
-import androidx.compose.ui.text.input.TextInputService
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntOffset
@@ -393,9 +388,6 @@
         AndroidTextInputServicePlugin
     ).adapter.service
 
-    override val textInputForTests: TextInputForTests?
-        get() = platformTextInputPluginRegistry.focusedAdapter?.inputForTests
-
     @Deprecated(
         "fontLoader is deprecated, use fontFamilyResolver",
         replaceWith = ReplaceWith("fontFamilyResolver")
@@ -632,22 +624,42 @@
         }
     }
 
-    override fun sendKeyEvent(keyEvent: KeyEvent): Boolean {
-        return focusOwner.dispatchKeyEvent(keyEvent)
-    }
+    /**
+     * This function is used by the testing framework to send key events.
+     */
+    override fun sendKeyEvent(keyEvent: KeyEvent): Boolean =
+        // First dispatch the key event to mimic the event being intercepted before it is sent to
+        // the soft keyboard.
+        focusOwner.dispatchInterceptedSoftKeyboardEvent(keyEvent) ||
+            // Next, send the key event to the Soft Keyboard.
+            // TODO(b/272600716): Send the key event to the IME.
 
-    override fun dispatchKeyEvent(event: AndroidKeyEvent) =
+            // Finally, dispatch the key event to onPreKeyEvent/onKeyEvent listeners.
+            focusOwner.dispatchKeyEvent(keyEvent)
+
+    override fun dispatchKeyEvent(event: AndroidKeyEvent): Boolean {
         if (isFocused) {
             // Focus lies within the Compose hierarchy, so we dispatch the key event to the
             // appropriate place.
             _windowInfo.keyboardModifiers = PointerKeyboardModifiers(event.metaState)
-            sendKeyEvent(KeyEvent(event))
+            // If the event is not consumed, use the default implementation.
+            return focusOwner.dispatchKeyEvent(KeyEvent(event)) || super.dispatchKeyEvent(event)
         } else {
-            // This Owner has a focused child view, which is a view interop use case,
+            // This Owner has a focused child view, which is a view interoperability use case,
             // so we use the default ViewGroup behavior which will route tke key event to the
             // focused view.
-            super.dispatchKeyEvent(event)
+            return super.dispatchKeyEvent(event)
         }
+    }
+
+    override fun dispatchKeyEventPreIme(event: AndroidKeyEvent): Boolean {
+        return (isFocused && focusOwner.dispatchInterceptedSoftKeyboardEvent(KeyEvent(event))) ||
+            // If this view is not focused, and it received a key event, it means this is a view
+            // interoperability use case and we need to route the event to the embedded child view.
+            // Also, if this event wasn't consumed by the compose hierarchy, we need to send it back
+            // to the parent view. Both these cases are handles by the default view implementation.
+            super.dispatchKeyEventPreIme(event)
+    }
 
     override fun onAttach(node: LayoutNode) {
     }
@@ -1386,9 +1398,8 @@
         eventTime: Long,
         forceHover: Boolean = true
     ) {
-        val oldAction = motionEvent.actionMasked
         // don't send any events for pointers that are "up" unless they support hover
-        val upIndex = when (oldAction) {
+        val upIndex = when (motionEvent.actionMasked) {
             ACTION_UP -> if (action == ACTION_HOVER_ENTER || action == ACTION_HOVER_EXIT) -1 else 0
             ACTION_POINTER_UP -> motionEvent.actionIndex
             else -> -1
@@ -1782,12 +1793,6 @@
     else -> LayoutDirection.Ltr
 }
 
-/** @suppress */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-@InternalComposeUiApi // used by testing infra
-var textInputServiceFactory: (PlatformTextInputService) -> TextInputService =
-    { TextInputService(it) }
-
 /**
  * These classes are here to ensure that the classes that use this API will get verified and can be
  * AOT compiled. It is expected that this class will soft-fail verification, but the classes
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 4bcf007..ee20288 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
@@ -52,7 +52,6 @@
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.toComposeRect
 import androidx.compose.ui.layout.boundsInParent
-import androidx.compose.ui.layout.boundsInWindow
 import androidx.compose.ui.layout.positionInRoot
 import androidx.compose.ui.node.HitTestResult
 import androidx.compose.ui.node.LayoutNode
@@ -83,6 +82,7 @@
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.platform.URLSpanCache
 import androidx.compose.ui.text.platform.toAccessibilitySpannableString
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.toSize
@@ -190,6 +190,7 @@
     return null
 }
 
+@OptIn(InternalTextApi::class)
 internal class AndroidComposeViewAccessibilityDelegateCompat(val view: AndroidComposeView) :
     AccessibilityDelegateCompat() {
     companion object {
@@ -393,6 +394,8 @@
     private val EXTRA_DATA_TEST_TRAVERSALAFTER_VAL =
         "android.view.accessibility.extra.EXTRA_DATA_TEST_TRAVERSALAFTER_VAL"
 
+    private val urlSpanCache = URLSpanCache()
+
     /**
      * A snapshot of the semantics node. The children here is fixed and are taken from the time
      * this node is constructed. While a SemanticsNode always contains the up-to-date children.
@@ -556,17 +559,17 @@
     ): Comparator<SemanticsNode> {
         // First compare the coordinates LTR
         var comparator = compareBy<SemanticsNode>(
-            { it.layoutNode.coordinates.boundsInWindow().left },
-            { it.layoutNode.coordinates.boundsInWindow().top },
-            { it.layoutNode.coordinates.boundsInWindow().bottom },
-            { it.layoutNode.coordinates.boundsInWindow().right })
+            { it.boundsInWindow.left },
+            { it.boundsInWindow.top },
+            { it.boundsInWindow.bottom },
+            { it.boundsInWindow.right })
         // Modify comparison if we're not using LTR comparison strategy to use RTL instead
         if (layoutIsRtl) {
             comparator = compareBy(
-                { it.layoutNode.coordinates.boundsInWindow().right },
-                { it.layoutNode.coordinates.boundsInWindow().top },
-                { it.layoutNode.coordinates.boundsInWindow().bottom },
-                { it.layoutNode.coordinates.boundsInWindow().left })
+                { it.boundsInWindow.right },
+                { it.boundsInWindow.top },
+                { it.boundsInWindow.bottom },
+                { it.boundsInWindow.left })
         }
         return comparator
             // then compare by layoutNode's zIndex and placement order
@@ -600,8 +603,8 @@
             node: SemanticsNode
         ): Boolean {
             // Conversion to long is needed in order to utilize `until`, which has no float ver
-            val entryTopCoord = node.layoutNode.coordinates.boundsInWindow().top
-            val entryBottomCoord = node.layoutNode.coordinates.boundsInWindow().bottom
+            val entryTopCoord = node.boundsInWindow.top
+            val entryBottomCoord = node.boundsInWindow.bottom
             val entryRange = entryTopCoord.rangeUntil(entryBottomCoord)
 
             for (currIndex in 0..rowGroupings.lastIndex) {
@@ -635,7 +638,7 @@
             val currEntry = parentListToSort[entryIndex]
             // If this is the first entry, or vertical groups don't overlap
             if (entryIndex == 0 || !placedEntryRowOverlaps(currEntry)) {
-                val newRect = currEntry.layoutNode.coordinates.boundsInWindow()
+                val newRect = currEntry.boundsInWindow
                 rowGroupings.add(Pair(newRect, mutableListOf(currEntry)))
             } // otherwise, we've already iterated through, found and placed it in a matching group
         }
@@ -1290,7 +1293,6 @@
         }
     }
 
-    @OptIn(InternalTextApi::class)
     private fun setText(
         node: SemanticsNode,
         info: AccessibilityNodeInfoCompat,
@@ -1298,13 +1300,21 @@
         val fontFamilyResolver: FontFamily.Resolver = view.fontFamilyResolver
         val editableTextToAssign = trimToSize(
             node.unmergedConfig.getTextForTextField()
-                ?.toAccessibilitySpannableString(density = view.density, fontFamilyResolver),
+                ?.toAccessibilitySpannableString(
+                    density = view.density,
+                    fontFamilyResolver,
+                    urlSpanCache
+                ),
             ParcelSafeTextLength
         )
 
         val textToAssign = trimToSize(
             node.unmergedConfig.getOrNull(SemanticsProperties.Text)?.firstOrNull()
-                ?.toAccessibilitySpannableString(density = view.density, fontFamilyResolver),
+                ?.toAccessibilitySpannableString(
+                    density = view.density,
+                    fontFamilyResolver,
+                    urlSpanCache
+                ),
             ParcelSafeTextLength
         )
 
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/AndroidTextInputServicePlugin.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/AndroidTextInputServicePlugin.kt
index 1d2be4b0..befbdba 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/AndroidTextInputServicePlugin.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/AndroidTextInputServicePlugin.kt
@@ -21,8 +21,6 @@
 import android.view.View
 import android.view.inputmethod.EditorInfo
 import android.view.inputmethod.InputConnection
-import androidx.compose.ui.InternalComposeUiApi
-import androidx.compose.ui.platform.textInputServiceFactory
 import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.input.AndroidTextInputServicePlugin.Adapter
 
@@ -42,14 +40,9 @@
  */
 internal object AndroidTextInputServicePlugin : PlatformTextInputPlugin<Adapter> {
 
-    @OptIn(InternalComposeUiApi::class)
     override fun createAdapter(platformTextInput: PlatformTextInput, view: View): Adapter {
         val platformService = TextInputServiceAndroid(view, platformTextInput)
-        // This indirection is used for tests (see testInput above). This could be cleaned up now
-        // that both halves live in the same class, but not worth the refactoring given the text
-        // field api rewrite.
-        val service = textInputServiceFactory(platformService)
-        return Adapter(service, platformService)
+        return Adapter(TextInputService(platformService), platformService)
     }
 
     class Adapter(
@@ -57,10 +50,6 @@
         private val androidService: TextInputServiceAndroid
     ) : PlatformTextInputAdapter {
 
-        override val inputForTests: TextInputForTests
-            get() = service as? TextInputForTests
-                ?: error("Text input service wrapper not set up! Did you use ComposeTestRule?")
-
         override fun createInputConnection(outAttrs: EditorInfo): InputConnection =
             androidService.createInputConnection(outAttrs)
     }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidView.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidView.android.kt
index 1c9c17a..ef4a0f4 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidView.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidView.android.kt
@@ -49,11 +49,48 @@
 import androidx.lifecycle.findViewTreeLifecycleOwner
 import androidx.savedstate.SavedStateRegistryOwner
 
-@Deprecated(
-    message = "AndroidView now has arguments for onReset and onRelease callbacks. This original " +
-        "overload is retained for binary compatibility only.",
-    level = DeprecationLevel.HIDDEN
-)
+/**
+ * Composes an Android [View] obtained from [factory]. The [factory] block will be called exactly
+ * once to obtain the [View] being composed, and it is also guaranteed to be invoked on the UI
+ * thread. Therefore, in addition to creating the [View], the [factory] block can also be used to
+ * perform one-off initializations and [View] constant properties' setting. The [update] block can
+ * run multiple times (on the UI thread as well) due to recomposition, and it is the right place to
+ * set the new properties. Note that the block will also run once right after the [factory] block
+ * completes.
+ *
+ * [AndroidView] is commonly needed for using Views that are infeasible to be reimplemented in
+ * Compose and there is no corresponding Compose API. Common examples for the moment are
+ * WebView, SurfaceView, AdView, etc.
+ *
+ * This overload of [AndroidView] does not automatically pool or reuse Views. If placed inside of a
+ * reusable container (including inside a [LazyRow][androidx.compose.foundation.lazy.LazyRow] or
+ * [LazyColumn][androidx.compose.foundation.lazy.LazyColumn]), the View instances will always be
+ * discarded and recreated if the composition hierarchy containing the AndroidView changes, even
+ * if its group structure did not change and the View could have conceivably been reused.
+ *
+ * To opt-in for View reuse, call the overload of [AndroidView] that accepts an `onReset` callback,
+ * and provide a non-null implementation for this callback. Since it is expensive to discard and
+ * recreate View instances, reusing Views can lead to noticeable performance improvements —
+ * especially when building a scrolling list of [AndroidViews][AndroidView]. It is highly
+ * recommended to opt-in to View reuse when possible.
+ *
+ * [AndroidView] will not clip its content to the layout bounds. Use [View.setClipToOutline] on
+ * the child View to clip the contents, if desired. Developers will likely want to do this with
+ * all subclasses of SurfaceView to keep its contents contained.
+ *
+ * [AndroidView] has nested scroll interop capabilities if the containing view has nested scroll
+ * enabled. This means this Composable can dispatch scroll deltas if it is placed inside a
+ * container that participates in nested scroll. For more information on how to enable
+ * nested scroll interop:
+ * @sample androidx.compose.ui.samples.ViewInComposeNestedScrollInteropSample
+ *
+ * @sample androidx.compose.ui.samples.AndroidViewSample
+ *
+ * @param factory The block creating the [View] to be composed.
+ * @param modifier The modifier to be applied to the layout.
+ * @param update A callback to be invoked after the layout is inflated and upon recomposition to
+ * update the information and state of the view.
+ */
 @Composable
 @UiComposable
 fun <T : View> AndroidView(
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 3e001e7..b7b614a 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
@@ -169,7 +169,6 @@
             private set
 
         private var scope: CoroutineScope? = null
-        // CoroutineScope(baseContext + Job(parent = baseContext[Job]))
         val coroutineScope: CoroutineScope
             get() = scope ?: CoroutineScope(
                 requireOwner().coroutineContext +
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusInvalidationManager.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusInvalidationManager.kt
index 8421e1b..6217e96 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusInvalidationManager.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusInvalidationManager.kt
@@ -73,7 +73,13 @@
         focusEventNodes.forEach { focusEventNode ->
             // When focus nodes are removed, the corresponding focus events are scheduled for
             // invalidation. If the focus event was also removed, we don't need to invalidate it.
-            if (!focusEventNode.node.isAttached) return@forEach
+            // We call onFocusEvent with the default value, just to make it easier for the user,
+            // so that they don't have to keep track of whether they caused a focused item to be
+            // removed (Which would cause it to lose focus).
+            if (!focusEventNode.node.isAttached) {
+                focusEventNode.onFocusEvent(Inactive)
+                return@forEach
+            }
 
             var requiresUpdate = true
             var aggregatedNode = false
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwner.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwner.kt
index 9566714..04671af 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwner.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwner.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.ui.focus
 
-import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.input.key.KeyEvent
@@ -80,6 +79,11 @@
     fun dispatchKeyEvent(keyEvent: KeyEvent): Boolean
 
     /**
+     * Dispatches an intercepted soft keyboard key event through the compose hierarchy.
+     */
+    fun dispatchInterceptedSoftKeyboardEvent(keyEvent: KeyEvent): Boolean
+
+    /**
      * Dispatches a rotary scroll event through the compose hierarchy.
      */
     fun dispatchRotaryEvent(event: RotaryScrollEvent): Boolean
@@ -87,18 +91,15 @@
     /**
      * Schedule a FocusTarget node to be invalidated after onApplyChanges.
      */
-    @OptIn(ExperimentalComposeUiApi::class)
     fun scheduleInvalidation(node: FocusTargetModifierNode)
 
     /**
      * Schedule a FocusEvent node to be invalidated after onApplyChanges.
      */
-    @OptIn(ExperimentalComposeUiApi::class)
     fun scheduleInvalidation(node: FocusEventModifierNode)
 
     /**
      * Schedule a FocusProperties node to be invalidated after onApplyChanges.
      */
-    @OptIn(ExperimentalComposeUiApi::class)
     fun scheduleInvalidation(node: FocusPropertiesModifierNode)
 }
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 7a08381..638594b 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
@@ -192,14 +192,25 @@
             onPreVisit = { if (it.onPreKeyEvent(keyEvent)) return true },
             onVisit = { if (it.onKeyEvent(keyEvent)) return true }
         )
+        return false
+    }
 
+    @OptIn(ExperimentalComposeUiApi::class)
+    override fun dispatchInterceptedSoftKeyboardEvent(keyEvent: KeyEvent): Boolean {
+        val focusedSoftKeyboardInterceptionNode = rootFocusNode.findActiveFocusNode()
+            ?.nearestAncestor(Nodes.SoftKeyboardKeyInput)
+
+        focusedSoftKeyboardInterceptionNode?.traverseAncestors(
+            type = Nodes.SoftKeyboardKeyInput,
+            onPreVisit = { if (it.onPreInterceptKeyBeforeSoftKeyboard(keyEvent)) return true },
+            onVisit = { if (it.onInterceptKeyBeforeSoftKeyboard(keyEvent)) return true }
+        )
         return false
     }
 
     /**
      * Dispatches a rotary scroll event through the compose hierarchy.
      */
-    @OptIn(ExperimentalComposeUiApi::class)
     override fun dispatchRotaryEvent(event: RotaryScrollEvent): Boolean {
         val focusedRotaryInputNode = rootFocusNode.findActiveFocusNode()
             ?.nearestAncestor(Nodes.RotaryInput)
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerModifier.kt
index cc534a5..ba495f2 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerModifier.kt
@@ -399,6 +399,12 @@
     val spotShadowColor: Color,
     val compositingStrategy: CompositingStrategy
 ) : ModifierNodeElement<SimpleGraphicsLayerModifier>() {
+
+    /**
+     * [SimpleGraphicsLayerModifier.invalidateLayerBlock] is doing the manual invalidation.
+     */
+    override val autoInvalidate = false
+
     override fun create(): SimpleGraphicsLayerModifier {
         return SimpleGraphicsLayerModifier(
             scaleX = scaleX,
@@ -543,10 +549,18 @@
 private data class BlockGraphicsLayerElement(
     val block: GraphicsLayerScope.() -> Unit
 ) : ModifierNodeElement<BlockGraphicsLayerModifier>() {
+
+    /**
+     * We can skip remeasuring as we only need to rerun the placement block. we request it
+     * manually in the [update] block.
+     */
+    override val autoInvalidate = false
+
     override fun create() = BlockGraphicsLayerModifier(block)
 
     override fun update(node: BlockGraphicsLayerModifier) = node.apply {
         layerBlock = block
+        invalidateLayerBlock()
     }
 
     override fun InspectorInfo.inspectableProperties() {
@@ -559,6 +573,13 @@
     var layerBlock: GraphicsLayerScope.() -> Unit,
 ) : LayoutModifierNode, Modifier.Node() {
 
+    fun invalidateLayerBlock() {
+        requireCoordinator(Nodes.Layout).wrapped?.updateLayerBlock(
+            layerBlock,
+            forceUpdateLayerParameters = true
+        )
+    }
+
     override fun MeasureScope.measure(
         measurable: Measurable,
         constraints: Constraints
@@ -617,7 +638,7 @@
     fun invalidateLayerBlock() {
         requireCoordinator(Nodes.Layout).wrapped?.updateLayerBlock(
             this.layerBlock,
-            forceLayerInvalidated = true
+            forceUpdateLayerParameters = true
         )
     }
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt
index 4d620d4..b823385 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifier.kt
@@ -17,6 +17,8 @@
 package androidx.compose.ui.input.key
 
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.InspectorInfo
 
 /**
  * Adding this [modifier][Modifier] to the [modifier][Modifier] parameter of a component will
@@ -30,7 +32,7 @@
  */
 fun Modifier.onKeyEvent(
     onKeyEvent: (KeyEvent) -> Boolean
-): Modifier = this then OnKeyEventElement(onKeyEvent)
+): Modifier = this then KeyInputElement(onKeyEvent = onKeyEvent, onPreKeyEvent = null)
 
 /**
  * Adding this [modifier][Modifier] to the [modifier][Modifier] parameter of a component will
@@ -46,4 +48,35 @@
  */
 fun Modifier.onPreviewKeyEvent(
     onPreviewKeyEvent: (KeyEvent) -> Boolean
-): Modifier = this then OnPreviewKeyEvent(onPreviewKeyEvent)
\ No newline at end of file
+): Modifier = this then KeyInputElement(onKeyEvent = null, onPreKeyEvent = onPreviewKeyEvent)
+
+private data class KeyInputElement(
+    val onKeyEvent: ((KeyEvent) -> Boolean)?,
+    val onPreKeyEvent: ((KeyEvent) -> Boolean)?
+) : ModifierNodeElement<KeyInputModifierNodeImpl>() {
+    override fun create() = KeyInputModifierNodeImpl(onKeyEvent, onPreKeyEvent)
+
+    override fun update(node: KeyInputModifierNodeImpl) = node.apply {
+        onEvent = onKeyEvent
+        onPreEvent = onPreKeyEvent
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        onKeyEvent?.let {
+            name = "onKeyEvent"
+            properties["onKeyEvent"] = it
+        }
+        onPreKeyEvent?.let {
+            name = "onPreviewKeyEvent"
+            properties["onPreviewKeyEvent"] = it
+        }
+    }
+}
+
+private class KeyInputModifierNodeImpl(
+    var onEvent: ((KeyEvent) -> Boolean)?,
+    var onPreEvent: ((KeyEvent) -> Boolean)?
+) : KeyInputModifierNode, Modifier.Node() {
+    override fun onKeyEvent(event: KeyEvent): Boolean = this.onEvent?.invoke(event) ?: false
+    override fun onPreKeyEvent(event: KeyEvent): Boolean = this.onPreEvent?.invoke(event) ?: false
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifierNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifierNode.kt
index 8521dec..550c5c4 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifierNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/KeyInputModifierNode.kt
@@ -18,8 +18,6 @@
 
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.node.DelegatableNode
-import androidx.compose.ui.node.ModifierNodeElement
-import androidx.compose.ui.platform.InspectorInfo
 
 /**
  * Implement this interface to create a [Modifier.Node] that can intercept hardware Key events.
@@ -47,48 +45,3 @@
      */
     fun onPreKeyEvent(event: KeyEvent): Boolean
 }
-
-internal data class OnKeyEventElement(
-    val onKeyEvent: (KeyEvent) -> Boolean
-) : ModifierNodeElement<KeyInputInputModifierNodeImpl>() {
-    override fun create() = KeyInputInputModifierNodeImpl(
-        onEvent = onKeyEvent,
-        onPreEvent = null
-    )
-
-    override fun update(node: KeyInputInputModifierNodeImpl) = node.apply {
-        onEvent = onKeyEvent
-        onPreEvent = null
-    }
-
-    override fun InspectorInfo.inspectableProperties() {
-        name = "onKeyEvent"
-        properties["onKeyEvent"] = onKeyEvent
-    }
-}
-
-internal data class OnPreviewKeyEvent(
-    val onPreviewKeyEvent: (KeyEvent) -> Boolean
-) : ModifierNodeElement<KeyInputInputModifierNodeImpl>() {
-    override fun create(): KeyInputInputModifierNodeImpl {
-        return KeyInputInputModifierNodeImpl(onEvent = null, onPreEvent = onPreviewKeyEvent)
-    }
-
-    override fun update(node: KeyInputInputModifierNodeImpl) = node.apply {
-        onPreEvent = onPreviewKeyEvent
-        onEvent = null
-    }
-
-    override fun InspectorInfo.inspectableProperties() {
-        name = "onPreviewKeyEvent"
-        properties["onPreviewKeyEvent"] = onPreviewKeyEvent
-    }
-}
-
-internal class KeyInputInputModifierNodeImpl(
-    var onEvent: ((KeyEvent) -> Boolean)?,
-    var onPreEvent: ((KeyEvent) -> Boolean)?
-) : KeyInputModifierNode, Modifier.Node() {
-    override fun onKeyEvent(event: KeyEvent): Boolean = this.onEvent?.invoke(event) ?: false
-    override fun onPreKeyEvent(event: KeyEvent): Boolean = this.onPreEvent?.invoke(event) ?: false
-}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/SoftKeyboardInterceptionModifierNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/SoftKeyboardInterceptionModifierNode.kt
new file mode 100644
index 0000000..31eb488
--- /dev/null
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/SoftKeyboardInterceptionModifierNode.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.input.key
+
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.node.DelegatableNode
+
+/**
+ * Implement this interface to create a [Modifier.Node] that can intercept hardware Key events
+ * before they are sent to the software keyboard.
+ *
+ * The event is routed to the focused item. Before reaching the focused item,
+ * [onPreInterceptKeyBeforeSoftKeyboard] is called for parents of the focused item.
+ * If the parents don't consume the event, [onPreSoftwareKeyboardKeyEvent]() is
+ * called for the focused item. If the event is still not consumed,
+ * [onInterceptKeyBeforeSoftKeyboard] is called on the focused item's parents.
+ */
+@ExperimentalComposeUiApi
+interface SoftKeyboardInterceptionModifierNode : DelegatableNode {
+    /**
+     * This function is called when a [KeyEvent] is received by this node during the upward
+     * pass. While implementing this callback, return true to stop propagation of this event.
+     * If you return false, the key event will be sent to this
+     * [SoftKeyboardInterceptionModifierNode]'s parent.
+     */
+    fun onInterceptKeyBeforeSoftKeyboard(event: KeyEvent): Boolean
+
+    /**
+     * This function is called when a [KeyEvent] is received by this node during the
+     * downward pass. It gives ancestors of a focused component the chance to intercept an event.
+     * Return true to stop propagation of this event. If you return false, the event will be sent
+     * to this [SoftKeyboardInterceptionModifierNode]'s child. If none of the children consume
+     * the event, it will be sent back up to the root using the [onPreInterceptKeyBeforeSoftKeyboard]
+     * function.
+     */
+    fun onPreInterceptKeyBeforeSoftKeyboard(event: KeyEvent): Boolean
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/SoftwareKeyboardInterceptionModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/SoftwareKeyboardInterceptionModifier.kt
new file mode 100644
index 0000000..88fc93a
--- /dev/null
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/key/SoftwareKeyboardInterceptionModifier.kt
@@ -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.compose.ui.input.key
+
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.InspectorInfo
+
+/**
+ * Adding this [modifier][Modifier] to the [modifier][Modifier] parameter of a component will
+ * allow it to intercept hardware key events before they are sent to the software keyboard.
+ *
+ * @param onInterceptKeyBeforeSoftKeyboard This callback is invoked when the user interacts with
+ * the hardware keyboard. While implementing this callback, return true to stop propagation of this
+ * event. If you return false, the key event will be sent to this
+ * [SoftKeyboardInterceptionModifierNode]'s parent, and ultimately to the software keyboard.
+ *
+ * @sample androidx.compose.ui.samples.KeyEventSample
+ */
+@ExperimentalComposeUiApi
+fun Modifier.onInterceptKeyBeforeSoftKeyboard(
+    onInterceptKeyBeforeSoftKeyboard: (KeyEvent) -> Boolean
+): Modifier = this then SoftKeyboardInterceptionElement(
+    onKeyEvent = onInterceptKeyBeforeSoftKeyboard,
+    onPreKeyEvent = null
+)
+
+/**
+ * Adding this [modifier][Modifier] to the [modifier][Modifier] parameter of a component will
+ * allow it to intercept hardware key events before they are sent to the software keyboard. This
+ * modifier is similar to [onInterceptKeyBeforeSoftKeyboard], but allows a parent composable to
+ * intercept the hardware key event before any child.
+ *
+ * @param onPreInterceptKeyBeforeSoftKeyboard This callback is invoked when the user interacts
+ * with the hardware keyboard. It gives ancestors of a focused component the chance to intercept a
+ * [KeyEvent]. Return true to stop propagation of this event. If you return false, the key event
+ * will be sent to this [SoftKeyboardInterceptionModifierNode]'s child. If none of the children
+ * consume the event, it will be sent back up to the root [KeyInputModifierNode] using the
+ * onKeyEvent callback, and ultimately to the software keyboard.
+ *
+ * @sample androidx.compose.ui.samples.KeyEventSample
+ */
+@ExperimentalComposeUiApi
+fun Modifier.onPreInterceptKeyBeforeSoftKeyboard(
+    onPreInterceptKeyBeforeSoftKeyboard: (KeyEvent) -> Boolean,
+): Modifier = this then SoftKeyboardInterceptionElement(
+    onKeyEvent = null,
+    onPreKeyEvent = onPreInterceptKeyBeforeSoftKeyboard
+)
+
+private data class SoftKeyboardInterceptionElement(
+    val onKeyEvent: ((KeyEvent) -> Boolean)?,
+    val onPreKeyEvent: ((KeyEvent) -> Boolean)?
+) : ModifierNodeElement<InterceptedKeyInputModifierNodeImpl>() {
+    override fun create() = InterceptedKeyInputModifierNodeImpl(
+        onEvent = onKeyEvent,
+        onPreEvent = onPreKeyEvent
+    )
+
+    override fun update(node: InterceptedKeyInputModifierNodeImpl) = node.apply {
+        onEvent = onKeyEvent
+        onPreEvent = onPreKeyEvent
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        onKeyEvent?.let {
+            name = "onKeyToSoftKeyboardInterceptedEvent"
+            properties["onKeyToSoftKeyboardInterceptedEvent"] = it
+        }
+        onPreKeyEvent?.let {
+            name = "onPreKeyToSoftKeyboardInterceptedEvent"
+            properties["onPreKeyToSoftKeyboardInterceptedEvent"] = it
+        }
+    }
+}
+
+@OptIn(ExperimentalComposeUiApi::class)
+private class InterceptedKeyInputModifierNodeImpl(
+    var onEvent: ((KeyEvent) -> Boolean)?,
+    var onPreEvent: ((KeyEvent) -> Boolean)?
+) : SoftKeyboardInterceptionModifierNode, Modifier.Node() {
+    override fun onInterceptKeyBeforeSoftKeyboard(event: KeyEvent): Boolean =
+        onEvent?.invoke(event) ?: false
+    override fun onPreInterceptKeyBeforeSoftKeyboard(event: KeyEvent): Boolean =
+        onPreEvent?.invoke(event) ?: false
+}
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 e238fc0..3c7f48b 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
@@ -16,18 +16,13 @@
 
 package androidx.compose.ui.input.pointer
 
-import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.collection.mutableVectorOf
-import androidx.compose.runtime.remember
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
 import androidx.compose.ui.fastMapNotNull
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.platform.ViewConfiguration
-import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.platform.synchronized
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntSize
@@ -43,13 +38,16 @@
 import kotlin.math.max
 import kotlinx.coroutines.CancellableContinuation
 import kotlinx.coroutines.CancellationException
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.DelicateCoroutinesApi
-import kotlinx.coroutines.GlobalScope
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.suspendCancellableCoroutine
 import androidx.compose.ui.internal.JvmDefaultWithCompatibility
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.PointerInputModifierNode
+import androidx.compose.ui.node.requireLayoutNode
+import androidx.compose.ui.platform.InspectorInfo
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Job
 
 /**
  * Receiver scope for awaiting pointer events in a call to
@@ -229,22 +227,10 @@
 fun Modifier.pointerInput(
     key1: Any?,
     block: suspend PointerInputScope.() -> Unit
-): Modifier = composed(
-    inspectorInfo = debugInspectorInfo {
-        name = "pointerInput"
-        properties["key1"] = key1
-        properties["block"] = block
-    }
-) {
-    val density = LocalDensity.current
-    val viewConfiguration = LocalViewConfiguration.current
-    remember(density) { SuspendingPointerInputFilter(viewConfiguration, density) }.also { filter ->
-        LaunchedEffect(filter, key1) {
-            filter.coroutineScope = this
-            filter.block()
-        }
-    }
-}
+): Modifier = this then SuspendPointerInputModifierNodeElement(
+    key1 = key1,
+    block = block
+)
 
 /**
  * Create a modifier for processing pointer input within the region of the modified element.
@@ -276,23 +262,11 @@
     key1: Any?,
     key2: Any?,
     block: suspend PointerInputScope.() -> Unit
-): Modifier = composed(
-    inspectorInfo = debugInspectorInfo {
-        name = "pointerInput"
-        properties["key1"] = key1
-        properties["key2"] = key2
-        properties["block"] = block
-    }
-) {
-    val density = LocalDensity.current
-    val viewConfiguration = LocalViewConfiguration.current
-    remember(density) { SuspendingPointerInputFilter(viewConfiguration, density) }.also { filter ->
-        LaunchedEffect(filter, key1, key2) {
-            filter.coroutineScope = this
-            filter.block()
-        }
-    }
-}
+): Modifier = this then SuspendPointerInputModifierNodeElement(
+    key1 = key1,
+    key2 = key2,
+    block = block
+)
 
 /**
  * Create a modifier for processing pointer input within the region of the modified element.
@@ -322,20 +296,54 @@
 fun Modifier.pointerInput(
     vararg keys: Any?,
     block: suspend PointerInputScope.() -> Unit
-): Modifier = composed(
-    inspectorInfo = debugInspectorInfo {
+): Modifier = this then SuspendPointerInputModifierNodeElement(
+    keys = keys,
+    block = block
+)
+
+@OptIn(ExperimentalComposeUiApi::class)
+internal class SuspendPointerInputModifierNodeElement(
+    val key1: Any? = null,
+    val key2: Any? = null,
+    val keys: Array<out Any?>? = null,
+    val block: suspend PointerInputScope.() -> Unit
+) : ModifierNodeElement<SuspendPointerInputModifierNode>() {
+    override fun InspectorInfo.inspectableProperties() {
         name = "pointerInput"
+        properties["key1"] = key1
+        properties["key2"] = key2
         properties["keys"] = keys
         properties["block"] = block
     }
-) {
-    val density = LocalDensity.current
-    val viewConfiguration = LocalViewConfiguration.current
-    remember(density) { SuspendingPointerInputFilter(viewConfiguration, density) }.also { filter ->
-        LaunchedEffect(filter, *keys) {
-            filter.coroutineScope = this
-            filter.block()
-        }
+
+    override fun create(): SuspendPointerInputModifierNode {
+        return SuspendPointerInputModifierNode(block)
+    }
+
+    override fun update(node: SuspendPointerInputModifierNode): SuspendPointerInputModifierNode {
+        node.block = block
+        return node
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is SuspendPointerInputModifierNodeElement) return false
+
+        if (key1 != other.key1) return false
+        if (key2 != other.key2) return false
+        if (keys != null) {
+            if (other.keys == null) return false
+            if (!keys.contentEquals(other.keys)) return false
+        } else if (other.keys != null) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = key1?.hashCode() ?: 0
+        result = 31 * result + (key2?.hashCode() ?: 0)
+        result = 31 * result + (keys?.contentHashCode() ?: 0)
+        return result
     }
 }
 
@@ -343,29 +351,44 @@
 
 /**
  * Implementation notes:
- * This class does a lot of lifting. It is both a [PointerInputModifier] and that modifier's
- * own [pointerInputFilter]. It is returned by way of a [Modifier.composed] from
- * the [Modifier.pointerInput] builder and is always 1-1 with an instance of application to
- * a LayoutNode.
+ * This class does a lot of lifting. [PointerInputModifierNode] receives, interprets, and, consumes
+ * [PointerInputChange]s while the state (and the coroutineScope used to execute [block]) is
+ * retained in [Modifier.Node].
  *
- * [SuspendingPointerInputFilter] implements the [PointerInputScope] used to offer the
- * [Modifier.pointerInput] DSL and carries the [Density] from [LocalDensity] at the point of
- * the modifier's materialization. Even if this value were returned to the [PointerInputFilter]
- * callbacks, we would still need the value at composition time in order for [Modifier.pointerInput]
- * to begin its internal [LaunchedEffect] for the provided code block.
+ * [SuspendPointerInputModifierNode] implements the [PointerInputScope] used to offer the
+ * [Modifier.pointerInput] DSL and provides the [Density] from [LocalDensity] lazily from the
+ * layout node when it is needed.
+ *
+ * Note: The coroutine that executes the passed block for listening to events is launched lazily
+ * when the first event is fired (making it more efficient) and is cancelled via resetHandling()
+ * when
  */
-// TODO: Suppressing deprecation for synchronized; need to move to atomicfu wrapper
-@Suppress("DEPRECATION_ERROR")
-internal class SuspendingPointerInputFilter(
-    override val viewConfiguration: ViewConfiguration,
-    density: Density = Density(1f)
-) : PointerInputFilter(),
-    PointerInputModifier,
-    PointerInputScope,
-    Density by density {
+@OptIn(ExperimentalComposeUiApi::class)
+internal class SuspendPointerInputModifierNode(
+    block: suspend PointerInputScope.() -> Unit
+) : Modifier.Node(), PointerInputModifierNode, PointerInputScope, Density {
 
-    override val pointerInputFilter: PointerInputFilter
-        get() = this
+    var block = block
+        set(value) {
+            resetBlock()
+            field = value
+        }
+
+    override val density: Float
+        get() = requireLayoutNode().density.density
+
+    override val fontScale: Float
+        get() = requireLayoutNode().density.fontScale
+
+    override val viewConfiguration
+        get() = requireLayoutNode().viewConfiguration
+
+    override val size: IntSize
+        get() = boundsSize
+
+    // The code block passed in as a parameter to handle pointer input events is now executed lazily
+    // when the first event fires. This job indicates that pointer input handler job is running.
+    private var pointerInputJob: Job? = null
 
     private var currentEvent: PointerEvent = EmptyPointerEvent
 
@@ -373,7 +396,8 @@
      * Actively registered input handlers from currently ongoing calls to [awaitPointerEventScope].
      * Must use `synchronized(pointerHandlers)` to access.
      */
-    private val pointerHandlers = mutableVectorOf<PointerEventHandlerCoroutine<*>>()
+    private val pointerHandlers =
+        mutableVectorOf<SuspendPointerInputModifierNode.PointerEventHandlerCoroutine<*>>()
 
     /**
      * Scratch list for dispatching to handlers for a particular phase.
@@ -381,7 +405,8 @@
      * resumed continuations may add/remove handlers without affecting the current dispatch pass.
      * Must only access on the UI thread.
      */
-    private val dispatchingPointerHandlers = mutableVectorOf<PointerEventHandlerCoroutine<*>>()
+    private val dispatchingPointerHandlers =
+        mutableVectorOf<SuspendPointerInputModifierNode.PointerEventHandlerCoroutine<*>>()
 
     /**
      * The last pointer event we saw where at least one pointer was currently down; null otherwise.
@@ -398,12 +423,6 @@
      */
     private var boundsSize: IntSize = IntSize.Zero
 
-    /**
-     * This will be changed immediately on launching, but I always want it to be non-null.
-     */
-    @OptIn(DelicateCoroutinesApi::class)
-    var coroutineScope: CoroutineScope = GlobalScope
-
     override val extendedTouchPadding: Size
         get() {
             val minimumTouchTargetSize = viewConfiguration.minimumTouchTargetSize.toSize()
@@ -415,6 +434,27 @@
 
     override var interceptOutOfBoundsChildEvents: Boolean = false
 
+    override fun onDetach() {
+        resetBlock()
+        super.onDetach()
+    }
+
+    /**
+     * This cancels the existing coroutine and essentially resets the block's execution. Note, the
+     * block still executes lazily, meaning nothing will be done until a new event comes in.
+     * More details: This is triggered from a LayoutNode if the Density or ViewConfiguration change
+     * (in an older implementation using composed, these values were used as keys so it would reset
+     * everything when either change, we do that manually now through this function). It is also
+     * used for testing.
+     */
+    fun resetBlock() {
+        val localJob = pointerInputJob
+        if (localJob != null) {
+            localJob.cancel(CancellationException())
+            pointerInputJob = null
+        }
+    }
+
     /**
      * Snapshot the current [pointerHandlers] and run [block] on each one.
      * May not be called reentrant or concurrent with itself.
@@ -426,7 +466,7 @@
      */
     private inline fun forEachCurrentPointerHandler(
         pass: PointerEventPass,
-        block: (PointerEventHandlerCoroutine<*>) -> Unit
+        block: (SuspendPointerInputModifierNode.PointerEventHandlerCoroutine<*>) -> Unit
     ) {
         // Copy handlers to avoid mutating the collection during dispatch
         synchronized(pointerHandlers) {
@@ -436,6 +476,7 @@
             when (pass) {
                 PointerEventPass.Initial, PointerEventPass.Final ->
                     dispatchingPointerHandlers.forEach(block)
+
                 PointerEventPass.Main ->
                     dispatchingPointerHandlers.forEachReversed(block)
             }
@@ -466,6 +507,13 @@
         if (pass == PointerEventPass.Initial) {
             currentEvent = pointerEvent
         }
+
+        // Coroutine lazily launches when first event comes in.
+        if (pointerInputJob == null) {
+            // 'start = CoroutineStart.UNDISPATCHED' required so handler doesn't miss first event.
+            pointerInputJob = coroutineScope.launch(start = CoroutineStart.UNDISPATCHED) { block() }
+        }
+
         dispatchPointerEvent(pointerEvent, pass)
 
         lastPointerEvent = pointerEvent.takeIf { event ->
@@ -473,8 +521,7 @@
         }
     }
 
-    @OptIn(ExperimentalComposeUiApi::class)
-    override fun onCancel() {
+    override fun onCancelPointerInput() {
         // Synthesize a cancel event for whatever state we previously saw, if one is applicable.
         // A cancel event is one where all previously down pointers are now up, the change in
         // down-ness is consumed. Any pointers that were previously hovering are left unchanged.
@@ -506,6 +553,9 @@
         dispatchPointerEvent(cancelEvent, PointerEventPass.Final)
 
         lastPointerEvent = null
+
+        // Cancels existing coroutine (Job) handling events.
+        resetBlock()
     }
 
     override suspend fun <R> awaitPointerEventScope(
@@ -546,18 +596,18 @@
      */
     private inner class PointerEventHandlerCoroutine<R>(
         private val completion: Continuation<R>,
-    ) : AwaitPointerEventScope, Density by this@SuspendingPointerInputFilter, Continuation<R> {
+    ) : AwaitPointerEventScope, Density by this@SuspendPointerInputModifierNode, Continuation<R> {
         private var pointerAwaiter: CancellableContinuation<PointerEvent>? = null
         private var awaitPass: PointerEventPass = PointerEventPass.Main
 
         override val currentEvent: PointerEvent
-            get() = this@SuspendingPointerInputFilter.currentEvent
+            get() = this@SuspendPointerInputModifierNode.currentEvent
         override val size: IntSize
-            get() = this@SuspendingPointerInputFilter.boundsSize
+            get() = this@SuspendPointerInputModifierNode.boundsSize
         override val viewConfiguration: ViewConfiguration
-            get() = this@SuspendingPointerInputFilter.viewConfiguration
+            get() = this@SuspendPointerInputModifierNode.viewConfiguration
         override val extendedTouchPadding: Size
-            get() = this@SuspendingPointerInputFilter.extendedTouchPadding
+            get() = this@SuspendPointerInputModifierNode.extendedTouchPadding
 
         fun offerPointerEvent(event: PointerEvent, pass: PointerEventPass) {
             if (pass == awaitPass) {
@@ -612,6 +662,7 @@
                     PointerEventTimeoutCancellationException(timeMillis)
                 )
             }
+
             val job = coroutineScope.launch {
                 // Delay twice because the timeout continuation needs to be lower-priority than
                 // input events, not treated fairly in FIFO order. The second
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/rotary/RotaryInputModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/rotary/RotaryInputModifier.kt
index 076a965..6b2351c 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/rotary/RotaryInputModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/rotary/RotaryInputModifier.kt
@@ -17,6 +17,8 @@
 package androidx.compose.ui.input.rotary
 
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.InspectorInfo
 
 /**
  * Adding this [modifier][Modifier] to the [modifier][Modifier] parameter of a component will
@@ -39,7 +41,10 @@
  */
 fun Modifier.onRotaryScrollEvent(
     onRotaryScrollEvent: (RotaryScrollEvent) -> Boolean
-): Modifier = this then OnRotaryScrollEventElement(onRotaryScrollEvent)
+): Modifier = this then OnRotaryScrollEventElement(
+    onRotaryScrollEvent = onRotaryScrollEvent,
+    onPreRotaryScrollEvent = null
+)
 
 /**
  * Adding this [modifier][Modifier] to the [modifier][Modifier] parameter of a component will
@@ -64,4 +69,43 @@
  */
 fun Modifier.onPreRotaryScrollEvent(
     onPreRotaryScrollEvent: (RotaryScrollEvent) -> Boolean
-): Modifier = this then OnPreRotaryScrollEventElement(onPreRotaryScrollEvent)
+): Modifier = this then OnRotaryScrollEventElement(
+    onRotaryScrollEvent = null,
+    onPreRotaryScrollEvent = onPreRotaryScrollEvent
+)
+
+private data class OnRotaryScrollEventElement(
+    val onRotaryScrollEvent: ((RotaryScrollEvent) -> Boolean)?,
+    val onPreRotaryScrollEvent: ((RotaryScrollEvent) -> Boolean)?
+) : ModifierNodeElement<RotaryInputModifierNodeImpl>() {
+    override fun create() = RotaryInputModifierNodeImpl(
+        onEvent = onRotaryScrollEvent,
+        onPreEvent = onPreRotaryScrollEvent
+    )
+
+    override fun update(node: RotaryInputModifierNodeImpl) = node.apply {
+        onEvent = onRotaryScrollEvent
+        onPreEvent = onPreRotaryScrollEvent
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        onRotaryScrollEvent?.let {
+            name = "onRotaryScrollEvent"
+            properties["onRotaryScrollEvent"] = it
+        }
+        onPreRotaryScrollEvent?.let {
+            name = "onPreRotaryScrollEvent"
+            properties["onPreRotaryScrollEvent"] = it
+        }
+    }
+}
+
+private class RotaryInputModifierNodeImpl(
+    var onEvent: ((RotaryScrollEvent) -> Boolean)?,
+    var onPreEvent: ((RotaryScrollEvent) -> Boolean)?
+) : RotaryInputModifierNode, Modifier.Node() {
+    override fun onRotaryScrollEvent(event: RotaryScrollEvent) =
+        onEvent?.invoke(event) ?: false
+    override fun onPreRotaryScrollEvent(event: RotaryScrollEvent) =
+        onPreEvent?.invoke(event) ?: false
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/rotary/RotaryInputModifierNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/rotary/RotaryInputModifierNode.kt
index 05f8d64..d59566c 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/rotary/RotaryInputModifierNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/rotary/RotaryInputModifierNode.kt
@@ -16,11 +16,8 @@
 
 package androidx.compose.ui.input.rotary
 
-import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.node.DelegatableNode
-import androidx.compose.ui.node.ModifierNodeElement
-import androidx.compose.ui.platform.InspectorInfo
 
 /**
  * Implement this interface to create a [Modifier.Node] that can intercept rotary scroll events.
@@ -30,7 +27,6 @@
  * consume the event, [onPreRotaryScrollEvent]() is called for the focused item. If the event is
  * still not consumed, [onRotaryScrollEvent]() is called on the focused item's parents.
  */
-@ExperimentalComposeUiApi
 interface RotaryInputModifierNode : DelegatableNode {
     /**
      * This function is called when a [RotaryScrollEvent] is received by this node during the upward
@@ -48,56 +44,3 @@
      */
     fun onPreRotaryScrollEvent(event: RotaryScrollEvent): Boolean
 }
-
-@OptIn(ExperimentalComposeUiApi::class)
-internal data class OnRotaryScrollEventElement(
-    val onRotaryScrollEvent: (RotaryScrollEvent) -> Boolean
-) : ModifierNodeElement<RotaryInputModifierNodeImpl>() {
-    override fun create() = RotaryInputModifierNodeImpl(
-        onEvent = onRotaryScrollEvent,
-        onPreEvent = null
-    )
-
-    override fun update(node: RotaryInputModifierNodeImpl) = node.apply {
-        onEvent = onRotaryScrollEvent
-        onPreEvent = null
-    }
-
-    override fun InspectorInfo.inspectableProperties() {
-        name = "onRotaryScrollEvent"
-        properties["onRotaryScrollEvent"] = onRotaryScrollEvent
-    }
-}
-
-@OptIn(ExperimentalComposeUiApi::class)
-internal data class OnPreRotaryScrollEventElement(
-    val onPreRotaryScrollEvent: (RotaryScrollEvent) -> Boolean
-) : ModifierNodeElement<RotaryInputModifierNodeImpl>() {
-    override fun create() = RotaryInputModifierNodeImpl(
-        onEvent = null,
-        onPreEvent = onPreRotaryScrollEvent
-    )
-
-    override fun update(node: RotaryInputModifierNodeImpl) = node.apply {
-        onPreEvent = onPreRotaryScrollEvent
-        onEvent = null
-    }
-
-    override fun InspectorInfo.inspectableProperties() {
-        name = "onPreRotaryScrollEvent"
-        properties["onPreRotaryScrollEvent"] = onPreRotaryScrollEvent
-    }
-}
-
-@OptIn(ExperimentalComposeUiApi::class)
-internal class RotaryInputModifierNodeImpl(
-    var onEvent: ((RotaryScrollEvent) -> Boolean)?,
-    var onPreEvent: ((RotaryScrollEvent) -> Boolean)?
-) : RotaryInputModifierNode, Modifier.Node() {
-    override fun onRotaryScrollEvent(event: RotaryScrollEvent): Boolean {
-        return onEvent?.invoke(event) ?: false
-    }
-    override fun onPreRotaryScrollEvent(event: RotaryScrollEvent): Boolean {
-        return onPreEvent?.invoke(event) ?: false
-    }
-}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Placeable.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Placeable.kt
index f2f70d3..99301bd 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Placeable.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Placeable.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.ui.layout
 
-import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.graphics.GraphicsLayerScope
 import androidx.compose.ui.node.LayoutNodeLayoutDelegate
 import androidx.compose.ui.node.NodeCoordinator
@@ -161,9 +160,6 @@
          *
          * @sample androidx.compose.ui.samples.PlacementScopeCoordinatesSample
          */
-        @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
-        @ExperimentalComposeUiApi
-        @get:ExperimentalComposeUiApi
         open val coordinates: LayoutCoordinates?
             get() = null
 
@@ -342,7 +338,6 @@
                 private set
             private var _coordinates: LayoutCoordinates? = null
 
-            @ExperimentalComposeUiApi
             override val coordinates: LayoutCoordinates?
                 get() {
                     layoutDelegate?.coordinatesAccessedDuringPlacement = true
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
index 032a313..f241519 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/SubcomposeLayout.kt
@@ -485,7 +485,10 @@
                     val nodeState = nodeToNodeState[node]!!
                     val slotId = nodeState.slotId
                     if (reusableSlotIdsSet.contains(slotId)) {
-                        node.measuredByParent = UsageByParent.NotUsed
+                        node.measurePassDelegate.measuredByParent = UsageByParent.NotUsed
+                        node.lookaheadPassDelegate?.let {
+                            it.measuredByParent = UsageByParent.NotUsed
+                        }
                         reusableCount++
                         if (nodeState.active) {
                             nodeState.active = false
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerNodeCoordinator.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerNodeCoordinator.kt
index b2e69f4..07917f3 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerNodeCoordinator.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerNodeCoordinator.kt
@@ -54,7 +54,8 @@
             performingMeasure(constraints) {
                 // before rerunning the user's measure block reset previous measuredByParent for children
                 layoutNode.forEachChild {
-                    it.measuredByParentInLookahead = LayoutNode.UsageByParent.NotUsed
+                    it.lookaheadPassDelegate!!.measuredByParent =
+                        LayoutNode.UsageByParent.NotUsed
                 }
                 val measureResult = with(layoutNode.measurePolicy) {
                     measure(
@@ -73,8 +74,7 @@
         }
 
         override fun placeChildren() {
-            layoutNode.layoutDelegate.lookaheadPassDelegate!!.onPlaced()
-            alignmentLinesOwner.layoutChildren()
+            layoutNode.lookaheadPassDelegate!!.onNodePlaced()
         }
 
         override fun minIntrinsicWidth(height: Int) =
@@ -99,14 +99,14 @@
     override fun measure(constraints: Constraints): Placeable = performingMeasure(constraints) {
         // before rerunning the user's measure block reset previous measuredByParent for children
         layoutNode.forEachChild {
-            it.measuredByParent = LayoutNode.UsageByParent.NotUsed
+            it.measurePassDelegate.measuredByParent = LayoutNode.UsageByParent.NotUsed
         }
 
         measureResult = with(layoutNode.measurePolicy) {
             measure(layoutNode.childMeasurables, constraints)
         }
         onMeasured()
-        return this
+        this
     }
 
     override fun minIntrinsicWidth(height: Int) =
@@ -137,7 +137,7 @@
 
         onPlaced()
 
-        layoutNode.onNodePlaced()
+        layoutNode.measurePassDelegate.onNodePlaced()
     }
 
     override fun calculateAlignmentLine(alignmentLine: AlignmentLine): Int {
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 6ca357e..33e422f 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
@@ -47,6 +47,7 @@
 import androidx.compose.ui.node.Nodes.FocusEvent
 import androidx.compose.ui.node.Nodes.FocusProperties
 import androidx.compose.ui.node.Nodes.FocusTarget
+import androidx.compose.ui.node.Nodes.SuspendPointerInput
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.platform.LocalViewConfiguration
@@ -284,14 +285,14 @@
      * and layout related impl during *lookahead*. For the actual measure & layout, use
      * [measurePassDelegate].
      */
-    private val lookaheadPassDelegate
+    internal val lookaheadPassDelegate
         get() = layoutDelegate.lookaheadPassDelegate
 
     /**
      * The measure pass delegate for the [LayoutNode]. This delegate is responsible for the actual
      * measure & layout, after lookahead if any.
      */
-    private val measurePassDelegate
+    internal val measurePassDelegate
         get() = layoutDelegate.measurePassDelegate
 
     /**
@@ -443,7 +444,8 @@
         if (parent == null) {
             // it is a root node and attached root nodes are always placed (as there is no parent
             // to place them explicitly)
-            isPlaced = true
+            measurePassDelegate.isPlaced = true
+            lookaheadPassDelegate?.let { it.isPlaced = true }
         }
 
         // Use the inner coordinator of first non-virtual parent
@@ -463,7 +465,7 @@
         // Update lookahead root when attached. For nested cases, we'll always use the
         // closest lookahead root
         updateLookaheadRoot()
-        nodes.attach(performInvalidations = false)
+        nodes.attach()
         _foldedChildren.forEach { child ->
             child.attach(owner)
         }
@@ -492,7 +494,8 @@
         if (parent != null) {
             parent.invalidateLayer()
             parent.invalidateMeasurements()
-            measuredByParent = UsageByParent.NotUsed
+            measurePassDelegate.measuredByParent = UsageByParent.NotUsed
+            lookaheadPassDelegate?.let { it.measuredByParent = UsageByParent.NotUsed }
         }
         layoutDelegate.resetAlignmentLines()
         onDetach?.invoke(owner)
@@ -514,9 +517,8 @@
         _foldedChildren.forEach { child ->
             child.detach()
         }
-        placeOrder = NotPlacedPlaceOrder
-        previousPlaceOrder = NotPlacedPlaceOrder
-        isPlaced = false
+        measurePassDelegate.onNodeDetached()
+        lookaheadPassDelegate?.onNodeDetached()
     }
 
     private fun addVirtualLookaheadChild(layoutNode: LayoutNode) {
@@ -661,6 +663,8 @@
             if (field != value) {
                 field = value
                 onDensityOrLayoutDirectionChanged()
+
+                invalidatePointerInputModifiers()
             }
         }
 
@@ -676,6 +680,13 @@
         }
 
     override var viewConfiguration: ViewConfiguration = DummyViewConfiguration
+        set(value) {
+            if (field != value) {
+                field = value
+                invalidatePointerInputModifiers()
+            }
+        }
+
     override var compositionLocalMap = CompositionLocalMap.Empty
         set(value) {
             field = value
@@ -684,7 +695,12 @@
             viewConfiguration = value[LocalViewConfiguration]
             @OptIn(ExperimentalComposeUiApi::class)
             nodes.headToTail(Nodes.CompositionLocalConsumer) { modifierNode ->
-                autoInvalidateUpdatedNode(modifierNode as Modifier.Node)
+                val delegatedNode = modifierNode.node
+                if (delegatedNode.isAttached) {
+                    autoInvalidateUpdatedNode(delegatedNode)
+                } else {
+                    delegatedNode.updatedNodeAwaitingAttachForInvalidation = true
+                }
             }
         }
 
@@ -698,6 +714,14 @@
         invalidateLayers()
     }
 
+    // The pointer input's code block (for handling incoming events) needs to be reset if either
+    // the density or view configuration changes (see implementation for more details).
+    private fun invalidatePointerInputModifiers() {
+        nodes.headToTail(type = SuspendPointerInput) {
+            it.resetBlock()
+        }
+    }
+
     /**
      * The measured width of this layout and all of its [modifier]s. Shortcut for `size.width`.
      */
@@ -722,41 +746,30 @@
     /**
      * Whether or not this [LayoutNode] and all of its parents have been placed in the hierarchy.
      */
-    override var isPlaced: Boolean = false
-        private set
+    override val isPlaced: Boolean
+        get() = measurePassDelegate.isPlaced
 
     /**
      * The order in which this node was placed by its parent during the previous `layoutChildren`.
      * Before the placement the order is set to [NotPlacedPlaceOrder] to all the children. Then
-     * every placed node assigns this variable to [parent]s [nextChildPlaceOrder] and increments
-     * this counter. Not placed items will still have [NotPlacedPlaceOrder] set.
+     * every placed node assigns this variable to [parent]s MeasurePassDelegate's
+     * nextChildPlaceOrder and increments this counter. Not placed items will still
+     * have [NotPlacedPlaceOrder] set.
      */
-    internal var placeOrder: Int = NotPlacedPlaceOrder
-        private set
-
-    /**
-     * The value [placeOrder] had during the previous parent `layoutChildren`. Helps us to
-     * understand if the order did change.
-     */
-    internal var previousPlaceOrder: Int = NotPlacedPlaceOrder
-        private set
-
-    /**
-     * The counter on a parent node which is used by its children to understand the order in which
-     * they were placed.
-     * @see placeOrder
-     */
-    private var nextChildPlaceOrder: Int = 0
+    internal val placeOrder: Int
+        get() = measurePassDelegate.placeOrder
 
     /**
      * Remembers how the node was measured by the parent.
      */
-    internal var measuredByParent: UsageByParent = UsageByParent.NotUsed
+    internal val measuredByParent: UsageByParent
+        get() = measurePassDelegate.measuredByParent
 
     /**
      * Remembers how the node was measured by the parent in lookahead.
      */
-    internal var measuredByParentInLookahead: UsageByParent = UsageByParent.NotUsed
+    internal val measuredByParentInLookahead: UsageByParent
+        get() = lookaheadPassDelegate?.measuredByParent ?: UsageByParent.NotUsed
 
     /**
      * Remembers how the node was measured using intrinsics by an ancestor.
@@ -785,7 +798,8 @@
      * Default zIndex is 0. We use sum of the values passed as zIndex to place() by the
      * parent layout and all the applied modifiers.
      */
-    private var zIndex: Float = 0f
+    private val zIndex: Float
+        get() = measurePassDelegate.zIndex
 
     /**
      * The inner state associated with [androidx.compose.ui.layout.SubcomposeLayout].
@@ -907,12 +921,7 @@
             // clear the intrinsics usage for everything that was requested previously.
             clearSubtreePlacementIntrinsicsUsage()
         }
-        try {
-            relayoutWithoutParentInProgress = true
-            measurePassDelegate.replace()
-        } finally {
-            relayoutWithoutParentInProgress = false
-        }
+        measurePassDelegate.replace()
     }
 
     internal fun lookaheadReplace() {
@@ -924,13 +933,6 @@
         lookaheadPassDelegate!!.replace()
     }
 
-    /**
-     * Is true during [replace] invocation. Helps to differentiate between the cases when our
-     * parent is measuring us during the measure block, and when we are remeasured individually
-     * because of some change. This could be useful to know if we need to record the placing order.
-     */
-    private var relayoutWithoutParentInProgress = false
-
     internal fun draw(canvas: Canvas) = outerCoordinator.draw(canvas)
 
     /**
@@ -981,106 +983,6 @@
         )
     }
 
-    /**
-     * Invoked when the parent placed the node. It will trigger the layout.
-     */
-    internal fun onNodePlaced() {
-        val parent = parent
-
-        var newZIndex = innerCoordinator.zIndex
-        forEachCoordinator {
-            newZIndex += it.zIndex
-        }
-        if (newZIndex != zIndex) {
-            zIndex = newZIndex
-            parent?.onZSortedChildrenInvalidated()
-            parent?.invalidateLayer()
-        }
-
-        if (!isPlaced) {
-            // when the visibility of a child has been changed we need to invalidate
-            // parents inner layer - the layer in which this child will be drawn
-            parent?.invalidateLayer()
-            markNodeAndSubtreeAsPlaced()
-        }
-
-        if (parent != null) {
-            if (!relayoutWithoutParentInProgress && parent.layoutState == LayingOut) {
-                // the parent is currently placing its children
-                check(placeOrder == NotPlacedPlaceOrder) {
-                    "Place was called on a node which was placed already"
-                }
-                placeOrder = parent.nextChildPlaceOrder
-                parent.nextChildPlaceOrder++
-            }
-            // if relayoutWithoutParentInProgress is true we were asked to be relaid out without
-            // affecting the parent. this means our placeOrder didn't change since the last time
-            // parent placed us.
-        } else {
-            // parent is null for the root node
-            placeOrder = 0
-        }
-
-        layoutDelegate.alignmentLinesOwner.layoutChildren()
-    }
-
-    internal fun clearPlaceOrder() {
-        // reset the place order counter which will be used by the children
-        nextChildPlaceOrder = 0
-        forEachChild { child ->
-            // and reset the place order for all the children before placing them
-            child.previousPlaceOrder = child.placeOrder
-            child.placeOrder = LayoutNode.NotPlacedPlaceOrder
-            // before rerunning the user's layout block reset previous measuredByParent
-            // for children which we measured in the layout block during the last run.
-            if (child.measuredByParent == LayoutNode.UsageByParent.InLayoutBlock) {
-                child.measuredByParent = LayoutNode.UsageByParent.NotUsed
-            }
-        }
-    }
-
-    internal fun checkChildrenPlaceOrderForUpdates() {
-        forEachChild { child ->
-            // we set `placeOrder` to NotPlacedPlaceOrder for all the children, then
-            // during the placeChildren() invocation the real order will be assigned for
-            // all the placed children.
-            if (child.previousPlaceOrder != child.placeOrder) {
-                onZSortedChildrenInvalidated()
-                invalidateLayer()
-                if (child.placeOrder == LayoutNode.NotPlacedPlaceOrder) {
-                    child.markSubtreeAsNotPlaced()
-                }
-            }
-        }
-    }
-
-    private fun markNodeAndSubtreeAsPlaced() {
-        val wasPlaced = isPlaced
-        isPlaced = true
-        if (!wasPlaced) {
-            // if the node was not placed previous remeasure request could have been ignored
-            if (measurePending) {
-                requestRemeasure(forceRequest = true)
-            } else if (lookaheadMeasurePending) {
-                requestLookaheadRemeasure(forceRequest = true)
-            }
-        }
-        // invalidate all the nodes layers that were invalidated while the node was not placed
-        forEachCoordinatorIncludingInner {
-            if (it.lastLayerDrawingWasSkipped) {
-                it.invalidateLayer()
-            }
-        }
-        forEachChild {
-            // this child was placed during the previous parent's layoutChildren(). this means that
-            // before the parent became not placed this child was placed. we need to restore that
-            if (it.placeOrder != NotPlacedPlaceOrder) {
-                it.markNodeAndSubtreeAsPlaced()
-                rescheduleRemeasureOrRelayout(it)
-            }
-        }
-    }
-
     internal fun rescheduleRemeasureOrRelayout(it: LayoutNode) {
         when (it.layoutState) {
             Idle -> {
@@ -1104,15 +1006,6 @@
         }
     }
 
-    private fun markSubtreeAsNotPlaced() {
-        if (isPlaced) {
-            isPlaced = false
-            forEachChild {
-                it.markSubtreeAsNotPlaced()
-            }
-        }
-    }
-
     /**
      * Used to request a new measurement + layout pass from the owner.
      */
@@ -1323,7 +1216,11 @@
         layoutDelegate.markLookaheadMeasurePending()
 
     override fun forceRemeasure() {
-        requestRemeasure()
+        if (lookaheadRoot != null) {
+            requestLookaheadRemeasure()
+        } else {
+            requestRemeasure()
+        }
         val lastConstraints = layoutDelegate.lastConstraints
         if (lastConstraints != null) {
             owner?.measureAndLayout(this, lastConstraints)
@@ -1342,7 +1239,7 @@
     /**
      * Calls [block] on all [LayoutModifierNodeCoordinator]s in the NodeCoordinator chain.
      */
-    private inline fun forEachCoordinator(block: (LayoutModifierNodeCoordinator) -> Unit) {
+    internal inline fun forEachCoordinator(block: (LayoutModifierNodeCoordinator) -> Unit) {
         var coordinator: NodeCoordinator? = outerCoordinator
         val inner = innerCoordinator
         while (coordinator !== inner) {
@@ -1354,7 +1251,7 @@
     /**
      * Calls [block] on all [NodeCoordinator]s in the NodeCoordinator chain.
      */
-    private inline fun forEachCoordinatorIncludingInner(block: (NodeCoordinator) -> Unit) {
+    internal inline fun forEachCoordinatorIncludingInner(block: (NodeCoordinator) -> Unit) {
         var delegate: NodeCoordinator? = outerCoordinator
         val final = innerCoordinator.wrapped
         while (delegate != final && delegate != null) {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt
index 64a3e9b..bc0e2ae 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt
@@ -21,6 +21,7 @@
 import androidx.compose.ui.layout.AlignmentLine
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.node.LayoutNode.Companion.NotPlacedPlaceOrder
 import androidx.compose.ui.node.LayoutNode.LayoutState
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.IntOffset
@@ -110,6 +111,18 @@
     private var lookaheadLayoutPendingForAlignment = false
 
     /**
+     * The counter on a parent node which is used by its children to understand the order in
+     * which they were placed in the lookahead pass.
+     */
+    private var nextChildLookaheadPlaceOrder: Int = 0
+
+    /**
+     * The counter on a parent node which is used by its children to understand the order in
+     * which they were placed in the main pass.
+     */
+    private var nextChildPlaceOrder: Int = 0
+
+    /**
      * Marks the layoutNode dirty for another layout pass.
      */
     internal fun markLayoutPending() {
@@ -208,6 +221,32 @@
      * actual measure/layout pass.
      */
     inner class MeasurePassDelegate : Measurable, Placeable(), AlignmentLinesOwner {
+
+        /**
+         * Is true during [replace] invocation. Helps to differentiate between the cases when our
+         * parent is measuring us during the measure block, and when we are remeasured individually
+         * because of some change. This could be useful to know if we need to record the placing
+         * order.
+         */
+        private var relayoutWithoutParentInProgress: Boolean = false
+
+        /**
+         * The value [placeOrder] had during the previous parent `layoutChildren`. Helps us to
+         * understand if the order did change.
+         */
+        internal var previousPlaceOrder: Int = NotPlacedPlaceOrder
+            private set
+
+        /**
+         * The order in which this node was placed by its parent during the previous `layoutChildren`.
+         * Before the placement the order is set to [NotPlacedPlaceOrder] to all the children. Then
+         * every placed node assigns this variable to parent's LayoutNodeLayoutDelegate's
+         * nextChildPlaceOrder and increments this counter. Not placed items will still have
+         * [NotPlacedPlaceOrder] set.
+         */
+        internal var placeOrder: Int = NotPlacedPlaceOrder
+            private set
+
         private var measuredOnce = false
         private var placedOnce = false
         val lastConstraints: Constraints?
@@ -216,6 +255,7 @@
             } else {
                 null
             }
+        internal var measuredByParent: LayoutNode.UsageByParent = LayoutNode.UsageByParent.NotUsed
         internal var duringAlignmentLinesQuery = false
 
         private var lastPosition: IntOffset = IntOffset.Zero
@@ -225,8 +265,12 @@
         private var parentDataDirty: Boolean = true
         override var parentData: Any? = null
             private set
-        override val isPlaced: Boolean
-            get() = layoutNode.isPlaced
+
+        /**
+         * Whether or not this [LayoutNode] and all of its parents have been placed in the hierarchy.
+         */
+        override var isPlaced: Boolean = false
+            internal set
         override val innerCoordinator: NodeCoordinator
             get() = layoutNode.innerCoordinator
         override val alignmentLines: AlignmentLines = LayoutNodeAlignmentLines(this)
@@ -258,7 +302,8 @@
             // and we need to be remeasured, not relaid out
             if (layoutPendingForAlignment ||
                 (!duringAlignmentLinesQuery && !innerCoordinator.isPlacingForAlignment &&
-                    layoutPending)) {
+                    layoutPending)
+            ) {
                 layoutPending = false
                 val oldLayoutState = layoutState
                 layoutState = LayoutState.LayingOut
@@ -268,13 +313,13 @@
                         this,
                         affectsLookahead = false
                     ) {
-                        layoutNode.clearPlaceOrder()
+                        clearPlaceOrder()
                         forEachChildAlignmentLinesOwner {
-                            it.alignmentLines.usedDuringParentLayout
+                            it.alignmentLines.usedDuringParentLayout = false
                         }
                         innerCoordinator.measureResult.placeChildren()
 
-                        layoutNode.checkChildrenPlaceOrderForUpdates()
+                        checkChildrenPlaceOrderForUpdates()
                         forEachChildAlignmentLinesOwner {
                             it.alignmentLines.previousUsedDuringParentLayout =
                                 it.alignmentLines.usedDuringParentLayout
@@ -297,6 +342,131 @@
             if (alignmentLines.dirty && alignmentLines.required) alignmentLines.recalculate()
         }
 
+        private fun checkChildrenPlaceOrderForUpdates() {
+            with(layoutNode) {
+                forEachChild { child ->
+                    // we set `placeOrder` to NotPlacedPlaceOrder for all the children, then
+                    // during the placeChildren() invocation the real order will be assigned for
+                    // all the placed children.
+                    if (child.measurePassDelegate.previousPlaceOrder != child.placeOrder) {
+                        onZSortedChildrenInvalidated()
+                        invalidateLayer()
+                        if (child.placeOrder == NotPlacedPlaceOrder) {
+                            child.measurePassDelegate.markSubtreeAsNotPlaced()
+                        }
+                    }
+                }
+            }
+        }
+
+        private fun markSubtreeAsNotPlaced() {
+            if (isPlaced) {
+                isPlaced = false
+                forEachChildDelegate {
+                    it.markSubtreeAsNotPlaced()
+                }
+            }
+        }
+
+        private fun markNodeAndSubtreeAsPlaced() {
+            val wasPlaced = isPlaced
+            isPlaced = true
+            with(layoutNode) {
+                if (!wasPlaced) {
+                    // if the node was not placed previous remeasure request could have been ignored
+                    if (measurePending) {
+                        requestRemeasure(forceRequest = true)
+                    } else if (lookaheadMeasurePending) {
+                        requestLookaheadRemeasure(forceRequest = true)
+                    }
+                }
+                // invalidate all the nodes layers that were invalidated while the node was not placed
+                forEachCoordinatorIncludingInner {
+                    if (it.lastLayerDrawingWasSkipped) {
+                        it.invalidateLayer()
+                    }
+                }
+                forEachChild {
+                    // this child was placed during the previous parent's layoutChildren(). this
+                    // means that before the parent became not placed this child was placed. we need
+                    // to restore that
+                    if (it.placeOrder != NotPlacedPlaceOrder) {
+                        it.measurePassDelegate.markNodeAndSubtreeAsPlaced()
+                        rescheduleRemeasureOrRelayout(it)
+                    }
+                }
+            }
+        }
+
+        internal var zIndex: Float = 0f
+            private set
+
+        /**
+         * Invoked when the parent placed the node. It will trigger the layout.
+         */
+        internal fun onNodePlaced() {
+            val parent = layoutNode.parent
+
+            var newZIndex = innerCoordinator.zIndex
+            layoutNode.forEachCoordinator {
+                newZIndex += it.zIndex
+            }
+            if (newZIndex != zIndex) {
+                zIndex = newZIndex
+                parent?.onZSortedChildrenInvalidated()
+                parent?.invalidateLayer()
+            }
+
+            if (!isPlaced) {
+                // when the visibility of a child has been changed we need to invalidate
+                // parents inner layer - the layer in which this child will be drawn
+                parent?.invalidateLayer()
+                markNodeAndSubtreeAsPlaced()
+            }
+
+            if (parent != null) {
+                if (!relayoutWithoutParentInProgress &&
+                    parent.layoutState == LayoutState.LayingOut
+                ) {
+                    // the parent is currently placing its children
+                    check(placeOrder == NotPlacedPlaceOrder) {
+                        "Place was called on a node which was placed already"
+                    }
+                    placeOrder = parent.layoutDelegate.nextChildPlaceOrder
+                    parent.layoutDelegate.nextChildPlaceOrder++
+                }
+                // if relayoutWithoutParentInProgress is true we were asked to be relaid out without
+                // affecting the parent. this means our placeOrder didn't change since the last time
+                // parent placed us.
+            } else {
+                // parent is null for the root node
+                placeOrder = 0
+            }
+
+            layoutChildren()
+        }
+
+        private fun clearPlaceOrder() {
+            // reset the place order counter which will be used by the children
+            nextChildPlaceOrder = 0
+            forEachChildDelegate { child ->
+                // and reset the place order for all the children before placing them
+                child.previousPlaceOrder = child.placeOrder
+                child.placeOrder = NotPlacedPlaceOrder
+                // before rerunning the user's layout block reset previous measuredByParent
+                // for children which we measured in the layout block during the last run.
+                if (child.measuredByParent == LayoutNode.UsageByParent.InLayoutBlock) {
+                    child.measuredByParent = LayoutNode.UsageByParent.NotUsed
+                }
+            }
+        }
+
+        private inline fun forEachChildDelegate(block: (MeasurePassDelegate) -> Unit) {
+            layoutNode.children.fastForEach {
+                block(it.measurePassDelegate)
+            }
+        }
+
         /**
          * The function to be executed when the parent layout measures its children.
          */
@@ -313,10 +483,12 @@
             if (layoutNode.isOutMostLookaheadRoot()) {
                 measuredOnce = true
                 measurementConstraints = constraints
-                layoutNode.measuredByParentInLookahead = LayoutNode.UsageByParent.NotUsed
-                lookaheadPassDelegate!!.measure(constraints)
+                lookaheadPassDelegate!!.run {
+                    measuredByParent = LayoutNode.UsageByParent.NotUsed
+                    measure(constraints)
+                }
             }
-            layoutNode.trackMeasurementByParent()
+            trackMeasurementByParent(layoutNode)
             remeasure(constraints)
             return this
         }
@@ -358,21 +530,20 @@
             return false
         }
 
-        private fun LayoutNode.trackMeasurementByParent() {
-            val parent = parent
+        private fun trackMeasurementByParent(node: LayoutNode) {
+            val parent = node.parent
             if (parent != null) {
                 check(
                     measuredByParent == LayoutNode.UsageByParent.NotUsed ||
-                        @Suppress("DEPRECATION") canMultiMeasure
-                ) {
-                    "measure() may not be called multiple times on the same Measurable. Current " +
-                        "state $measuredByParent. Parent state ${parent.layoutState}."
-                }
+                        @Suppress("DEPRECATION") node.canMultiMeasure
+                ) { MeasuredTwiceErrorMessage }
                 measuredByParent = when (parent.layoutState) {
                     LayoutState.Measuring ->
                         LayoutNode.UsageByParent.InMeasureBlock
+
                     LayoutState.LayingOut ->
                         LayoutNode.UsageByParent.InLayoutBlock
+
                     else -> throw IllegalStateException(
                         "Measurable could be only measured from the parent's measure or layout" +
                             " block. Parents state is ${parent.layoutState}"
@@ -418,7 +589,15 @@
             if (layoutNode.isOutMostLookaheadRoot()) {
                 // Lookahead placement first
                 with(PlacementScope) {
-                    lookaheadPassDelegate!!.place(position.x, position.y)
+                    lookaheadPassDelegate!!.let {
+                        // Since this is the root of the lookahead delegate tree, no parent will
+                        // reset the place order, therefore we have to do it manually.
+                        layoutNode.parent?.run {
+                            layoutDelegate.nextChildLookaheadPlaceOrder = 0
+                        }
+                        it.placeOrder = NotPlacedPlaceOrder
+                        it.place(position.x, position.y)
+                    }
                 }
             }
 
@@ -461,8 +640,13 @@
          * post-lookahead pass.
          */
         fun replace() {
-            check(placedOnce)
-            placeOuterCoordinator(lastPosition, lastZIndex, lastLayerBlock)
+            try {
+                relayoutWithoutParentInProgress = true
+                check(placedOnce)
+                placeOuterCoordinator(lastPosition, lastZIndex, lastLayerBlock)
+            } finally {
+                relayoutWithoutParentInProgress = false
+            }
         }
 
         override fun minIntrinsicWidth(height: Int): Int {
@@ -513,6 +697,7 @@
         fun invalidateParentData() {
             parentDataDirty = true
         }
+
         fun updateParentData(): Boolean {
             if (!parentDataDirty) return false
             parentDataDirty = false
@@ -573,7 +758,8 @@
                 layoutNode.children.fastForEach { child ->
                     val childLayoutDelegate = child.layoutDelegate
                     if (childLayoutDelegate.coordinatesAccessedDuringPlacement &&
-                        !childLayoutDelegate.layoutPending) {
+                        !childLayoutDelegate.layoutPending
+                    ) {
                         child.requestRelayout()
                     }
                     childLayoutDelegate.measurePassDelegate
@@ -632,12 +818,20 @@
                 when (intrinsicsUsageByParent) {
                     LayoutNode.UsageByParent.InMeasureBlock ->
                         intrinsicsUsingParent.requestRemeasure(forceRequest)
+
                     LayoutNode.UsageByParent.InLayoutBlock ->
                         intrinsicsUsingParent.requestRelayout(forceRequest)
+
                     else -> error("Intrinsics isn't used by the parent")
                 }
             }
         }
+
+        fun onNodeDetached() {
+            placeOrder = NotPlacedPlaceOrder
+            previousPlaceOrder = NotPlacedPlaceOrder
+            isPlaced = false
+        }
     }
 
     /**
@@ -646,6 +840,33 @@
      */
     inner class LookaheadPassDelegate : Placeable(), Measurable, AlignmentLinesOwner {
 
+        /**
+         * Is true during [replace] invocation. Helps to differentiate between the cases when our
+         * parent is measuring us during the measure block, and when we are remeasured individually
+         * because of some change. This could be useful to know if we need to record the placing
+         * order.
+         */
+        private var relayoutWithoutParentInProgress: Boolean = false
+
+        /**
+         * The value [placeOrder] had during the previous parent `layoutChildren`. Helps us to
+         * understand if the order did change.
+         */
+        private var previousPlaceOrder: Int = NotPlacedPlaceOrder
+            private set
+
+        /**
+         * The order in which this node was placed by its parent during the previous
+         * `layoutChildren`. Before the placement the order is set to [NotPlacedPlaceOrder] to all
+         * the children. Then every placed node assigns this variable to parent's
+         * LayoutNodeLayoutDelegate's [nextChildLookaheadPlaceOrder] and increments this counter.
+         * Not placed items will still have [NotPlacedPlaceOrder] set.
+         */
+        internal var placeOrder: Int = NotPlacedPlaceOrder
+
+        internal var measuredByParent = LayoutNode.UsageByParent.NotUsed
+        internal val measurePassDelegate: MeasurePassDelegate
+            get() = this@LayoutNodeLayoutDelegate.measurePassDelegate
         internal var duringAlignmentLinesQuery: Boolean = false
         private var placedOnce: Boolean = false
         private var measuredOnce: Boolean = false
@@ -653,9 +874,13 @@
             get() = lookaheadConstraints
         private var lookaheadConstraints: Constraints? = null
         private var lastPosition: IntOffset = IntOffset.Zero
+        internal var lastZIndex: Float = 0f
+            private set
+
+        internal var lastLayerBlock: (GraphicsLayerScope.() -> Unit)? = null
+            private set
 
         override var isPlaced: Boolean = false
-        private var isPreviouslyPlaced: Boolean = false
         override val innerCoordinator: NodeCoordinator
             get() = layoutNode.innerCoordinator
         override val alignmentLines: AlignmentLines = LookaheadAlignmentLines(this)
@@ -692,44 +917,28 @@
             // and we need to be remeasured, not relaid out
             if (lookaheadLayoutPendingForAlignment ||
                 (!duringAlignmentLinesQuery && !lookaheadDelegate.isPlacingForAlignment &&
-                lookaheadLayoutPending)
+                    lookaheadLayoutPending)
             ) {
                 lookaheadLayoutPending = false
                 val oldLayoutState = layoutState
                 layoutState = LayoutState.LookaheadLayingOut
                 val owner = layoutNode.requireOwner()
                 owner.snapshotObserver.observeLayoutSnapshotReads(layoutNode) {
-                    forEachChildDelegate {
-                        it.isPreviouslyPlaced = it.isPlaced
-                        it.isPlaced = false
-                    }
-                    layoutNode.forEachChild {
-                        // Before rerunning the user's layout block reset previous
-                        // lookaheadlyMeasuredByParent for children which we measured in the
-                        // layout block during the last run.
-                        if (it.measuredByParentInLookahead ==
-                            LayoutNode.UsageByParent.InLayoutBlock
-                        ) {
-                            it.measuredByParentInLookahead = LayoutNode.UsageByParent.NotUsed
-                        }
-                    }
+                    clearPlaceOrder()
                     forEachChildAlignmentLinesOwner { child ->
                         child.alignmentLines.usedDuringParentLayout = false
                     }
                     lookaheadDelegate.measureResult.placeChildren()
+                    checkChildrenPlaceOrderForUpdates()
                     forEachChildAlignmentLinesOwner { child ->
                         child.alignmentLines.previousUsedDuringParentLayout =
                             child.alignmentLines.usedDuringParentLayout
                     }
-                    forEachChildDelegate {
-                        if (!it.isPlaced) {
-                            it.markSubtreeNotPlaced()
-                        }
-                    }
                 }
                 layoutState = oldLayoutState
                 if (coordinatesAccessedDuringPlacement &&
-                    lookaheadDelegate.isPlacingForAlignment) {
+                    lookaheadDelegate.isPlacingForAlignment
+                ) {
                     requestLayout()
                 }
                 lookaheadLayoutPendingForAlignment = false
@@ -740,9 +949,24 @@
             if (alignmentLines.dirty && alignmentLines.required) alignmentLines.recalculate()
         }
 
-        private fun markSubtreeNotPlaced() {
-            isPlaced = false
-            forEachChildDelegate { it.markSubtreeNotPlaced() }
+        private fun checkChildrenPlaceOrderForUpdates() {
+            forEachChildDelegate { child ->
+                // we set `placeOrder` to NotPlacedPlaceOrder for all the children, then
+                // during the placeChildren() invocation the real order will be assigned for
+                // all the placed children.
+                if (child.previousPlaceOrder != child.placeOrder) {
+                    if (child.placeOrder == NotPlacedPlaceOrder) {
+                        child.markSubtreeAsNotPlaced()
+                    }
+                }
+            }
+        }
+
+        private fun markSubtreeAsNotPlaced() {
+            if (isPlaced) {
+                isPlaced = false
+                forEachChildDelegate { it.markSubtreeAsNotPlaced() }
+            }
         }
 
         override fun calculateAlignmentLines(): Map<AlignmentLine, Int> {
@@ -797,7 +1021,8 @@
                 layoutNode.children.fastForEach { child ->
                     val childLayoutDelegate = child.layoutDelegate
                     if (childLayoutDelegate.coordinatesAccessedDuringPlacement &&
-                        !childLayoutDelegate.layoutPending) {
+                        !childLayoutDelegate.layoutPending
+                    ) {
                         child.requestLookaheadRelayout()
                     }
                     childLayoutDelegate.lookaheadPassDelegate
@@ -807,7 +1032,7 @@
         }
 
         override fun measure(constraints: Constraints): Placeable {
-            layoutNode.trackLookaheadMeasurementByParent()
+            trackLookaheadMeasurementByParent(layoutNode)
             if (layoutNode.intrinsicsUsageByParent == LayoutNode.UsageByParent.NotUsed) {
                 // This LayoutNode may have asked children for intrinsics. If so, we should
                 // clear the intrinsics usage for everything that was requested previously.
@@ -822,29 +1047,28 @@
         }
 
         // Track lookahead measurement
-        private fun LayoutNode.trackLookaheadMeasurementByParent() {
+        private fun trackLookaheadMeasurementByParent(node: LayoutNode) {
             // when we measure the root it is like the virtual parent is currently laying out
-            val parent = parent
+            val parent = node.parent
             if (parent != null) {
                 check(
-                    measuredByParentInLookahead == LayoutNode.UsageByParent.NotUsed ||
-                        @Suppress("DEPRECATION") canMultiMeasure
-                ) {
-                    "measure() may not be called multiple times on the same Measurable. Current " +
-                        "state $measuredByParentInLookahead. Parent state ${parent.layoutState}."
-                }
-                measuredByParentInLookahead = when (parent.layoutState) {
+                    measuredByParent == LayoutNode.UsageByParent.NotUsed ||
+                        @Suppress("DEPRECATION") node.canMultiMeasure
+                ) { MeasuredTwiceErrorMessage }
+                measuredByParent = when (parent.layoutState) {
                     LayoutState.LookaheadMeasuring, LayoutState.Measuring ->
                         LayoutNode.UsageByParent.InMeasureBlock
+
                     LayoutState.LayingOut, LayoutState.LookaheadLayingOut ->
                         LayoutNode.UsageByParent.InLayoutBlock
+
                     else -> throw IllegalStateException(
                         "Measurable could be only measured from the parent's measure or layout" +
                             " block. Parents state is ${parent.layoutState}"
                     )
                 }
             } else {
-                measuredByParentInLookahead = LayoutNode.UsageByParent.NotUsed
+                measuredByParent = LayoutNode.UsageByParent.NotUsed
             }
         }
 
@@ -900,6 +1124,8 @@
                 }
             }
             lastPosition = position
+            lastZIndex = zIndex
+            lastLayerBlock = layerBlock
             layoutState = LayoutState.Idle
         }
 
@@ -987,12 +1213,14 @@
                         } else {
                             intrinsicsUsingParent.requestRemeasure(forceRequest)
                         }
+
                     LayoutNode.UsageByParent.InLayoutBlock ->
                         if (intrinsicsUsingParent.lookaheadRoot != null) {
                             intrinsicsUsingParent.requestLookaheadRelayout(forceRequest)
                         } else {
                             intrinsicsUsingParent.requestRelayout(forceRequest)
                         }
+
                     else -> error("Intrinsics isn't used by the parent")
                 }
             }
@@ -1010,19 +1238,65 @@
             return changed
         }
 
-        fun onPlaced() {
+        internal fun onNodePlaced() {
+            val parent = layoutNode.parent
             if (!isPlaced) {
-                isPlaced = true
-                if (!isPreviouslyPlaced) {
-                    requestSubtreeForLookahead()
+                markNodeAndSubtreeAsPlaced()
+            }
+            if (parent != null) {
+                if (!relayoutWithoutParentInProgress &&
+                    parent.layoutState == LayoutState.LayingOut ||
+                    parent.layoutState == LayoutState.LookaheadLayingOut
+                ) {
+                    // the parent is currently placing its children
+                    check(placeOrder == NotPlacedPlaceOrder) {
+                        "Place was called on a node which was placed already"
+                    }
+                    placeOrder = parent.layoutDelegate.nextChildLookaheadPlaceOrder
+                    parent.layoutDelegate.nextChildLookaheadPlaceOrder++
+                }
+                // if relayoutWithoutParentInProgress is true we were asked to be relaid out without
+                // affecting the parent. this means our placeOrder didn't change since the last time
+                // parent placed us.
+            } else {
+                // parent is null for the root node
+                placeOrder = 0
+            }
+            layoutChildren()
+        }
+
+        private fun clearPlaceOrder() {
+            // reset the place order counter which will be used by the children
+            this@LayoutNodeLayoutDelegate.nextChildLookaheadPlaceOrder = 0
+            forEachChildDelegate { child ->
+                // and reset the place order for all the children before placing them
+                child.previousPlaceOrder = child.placeOrder
+                child.placeOrder = NotPlacedPlaceOrder
+                // before rerunning the user's layout block reset previous measuredByParent
+                // for children which we measured in the layout block during the last run.
+                if (child.measuredByParent == LayoutNode.UsageByParent.InLayoutBlock) {
+                    child.measuredByParent = LayoutNode.UsageByParent.NotUsed
                 }
             }
         }
 
-        private fun requestSubtreeForLookahead() {
+        private fun markNodeAndSubtreeAsPlaced() {
+            val wasPlaced = isPlaced
+            isPlaced = true
+            if (!wasPlaced) {
+                if (lookaheadMeasurePending) {
+                    // if the node was not placed previous remeasure request could have been ignored
+                    layoutNode.requestLookaheadRemeasure(forceRequest = true)
+                }
+            }
+
             layoutNode.forEachChild {
-                it.rescheduleRemeasureOrRelayout(it)
-                it.layoutDelegate.lookaheadPassDelegate!!.requestSubtreeForLookahead()
+                // this child was placed during the previous parent's layoutChildren(). this means that
+                // before the parent became not placed this child was placed. we need to restore that
+                if (it.placeOrder != NotPlacedPlaceOrder) {
+                    it.lookaheadPassDelegate!!.markNodeAndSubtreeAsPlaced()
+                    it.rescheduleRemeasureOrRelayout(it)
+                }
             }
         }
 
@@ -1064,8 +1338,19 @@
         }
 
         fun replace() {
-            check(placedOnce)
-            placeAt(lastPosition, 0f, null)
+            try {
+                relayoutWithoutParentInProgress = true
+                check(placedOnce)
+                placeAt(lastPosition, 0f, null)
+            } finally {
+                relayoutWithoutParentInProgress = false
+            }
+        }
+
+        fun onNodeDetached() {
+            placeOrder = NotPlacedPlaceOrder
+            previousPlaceOrder = NotPlacedPlaceOrder
+            isPlaced = false
         }
     }
 
@@ -1174,6 +1459,12 @@
     )
 }
 
+private const val MeasuredTwiceErrorMessage: String =
+    "measure() may not be called multiple times on the same Measurable. If you want to " +
+        "get the content size of the Measurable before calculating the final constraints, " +
+        "please use methods like minIntrinsicWidth()/maxIntrinsicWidth() and " +
+        "minIntrinsicHeight()/maxIntrinsicHeight()"
+
 /**
  * AlignmentLinesOwner defines APIs that are needed to respond to alignment line changes, and to
  * query alignment line related info.
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt
index e6eeda9..5684882 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt
@@ -213,7 +213,7 @@
             syncCoordinators()
         }
         if (attachNeeded && layoutNode.isAttached) {
-            attach(performInvalidations = true)
+            attach()
         }
     }
 
@@ -264,17 +264,15 @@
         outerCoordinator = coordinator
     }
 
-    fun attach(performInvalidations: Boolean) {
+    fun attach() {
         headToTail {
             if (!it.isAttached) {
                 it.attach()
-                if (performInvalidations) {
-                    if (it.insertedNodeAwaitingAttachForInvalidation) {
-                        autoInvalidateInsertedNode(it)
-                    }
-                    if (it.updatedNodeAwaitingAttachForInvalidation) {
-                        autoInvalidateUpdatedNode(it)
-                    }
+                if (it.insertedNodeAwaitingAttachForInvalidation) {
+                    autoInvalidateInsertedNode(it)
+                }
+                if (it.updatedNodeAwaitingAttachForInvalidation) {
+                    autoInvalidateUpdatedNode(it)
                 }
                 // when we attach with performInvalidations == false no separate
                 // invalidations needed as the whole LayoutNode is attached to the tree.
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 9bdb4f2..89da2e1 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
@@ -221,12 +221,12 @@
         } else {
             wrappedBy?.invalidateLayer()
         }
-        layoutNode.owner?.onLayoutChange(layoutNode)
         measuredSize = IntSize(width, height)
-        graphicsLayerScope.size = measuredSize.toSize()
+        updateLayerParameters(invokeOnLayoutChange = false)
         visitNodes(Nodes.Draw) {
             it.onMeasureResultChanged()
         }
+        layoutNode.owner?.onLayoutChange(layoutNode)
     }
 
     override var position: IntOffset = IntOffset.Zero
@@ -281,12 +281,10 @@
 
     protected inline fun performingMeasure(
         constraints: Constraints,
-        block: () -> Placeable
+        crossinline block: () -> Placeable
     ): Placeable {
         measurementConstraints = constraints
-        val result = block()
-        layer?.resize(measuredSize)
-        return result
+        return block()
     }
 
     fun onMeasured() {
@@ -308,7 +306,7 @@
         zIndex: Float,
         layerBlock: (GraphicsLayerScope.() -> Unit)?
     ) {
-        onLayerBlockUpdated(layerBlock)
+        updateLayerBlock(layerBlock)
         if (this.position != position) {
             this.position = position
             layoutNode.layoutDelegate.measurePassDelegate
@@ -380,20 +378,11 @@
 
     fun updateLayerBlock(
         layerBlock: (GraphicsLayerScope.() -> Unit)?,
-        forceLayerInvalidated: Boolean = false
+        forceUpdateLayerParameters: Boolean = false
     ) {
-        val layerInvalidated = this.layerBlock !== layerBlock || forceLayerInvalidated
-        this.layerBlock = layerBlock
-        onLayerBlockUpdated(layerBlock, forceLayerInvalidated = layerInvalidated)
-    }
-
-    private fun onLayerBlockUpdated(
-        layerBlock: (GraphicsLayerScope.() -> Unit)?,
-        forceLayerInvalidated: Boolean = false
-    ) {
-        val layerInvalidated = this.layerBlock !== layerBlock || layerDensity != layoutNode
+        val updateParameters = this.layerBlock !== layerBlock || layerDensity != layoutNode
             .density || layerLayoutDirection != layoutNode.layoutDirection ||
-            forceLayerInvalidated
+            forceUpdateLayerParameters
         this.layerBlock = layerBlock
         this.layerDensity = layoutNode.density
         this.layerLayoutDirection = layoutNode.layoutDirection
@@ -410,7 +399,7 @@
                 updateLayerParameters()
                 layoutNode.innerLayerCoordinatorIsDirty = true
                 invalidateParentLayer()
-            } else if (layerInvalidated) {
+            } else if (updateParameters) {
                 updateLayerParameters()
             }
         } else {
@@ -427,7 +416,7 @@
         }
     }
 
-    private fun updateLayerParameters() {
+    private fun updateLayerParameters(invokeOnLayoutChange: Boolean = true) {
         val layer = layer
         if (layer != null) {
             val layerBlock = requireNotNull(layerBlock)
@@ -462,11 +451,13 @@
                 density = layoutNode.density
             )
             isClipping = graphicsLayerScope.clip
+            lastLayerAlpha = graphicsLayerScope.alpha
+            if (invokeOnLayoutChange) {
+                layoutNode.owner?.onLayoutChange(layoutNode)
+            }
         } else {
             require(layerBlock == null)
         }
-        lastLayerAlpha = graphicsLayerScope.alpha
-        layoutNode.owner?.onLayoutChange(layoutNode)
     }
 
     private val invalidateParentLayer: () -> Unit = {
@@ -906,7 +897,10 @@
      * attached to the [Owner].
      */
     fun onLayoutNodeAttach() {
-        onLayerBlockUpdated(layerBlock)
+        // this call will update the parameters of the layer (alpha, scale, etc)
+        updateLayerBlock(layerBlock, forceUpdateLayerParameters = true)
+        // this call will invalidate the content of the layer
+        layer?.invalidate()
     }
 
     /**
@@ -916,7 +910,7 @@
     fun onRelease() {
         released = true
         if (layer != null) {
-            onLayerBlockUpdated(null)
+            updateLayerBlock(null)
         }
     }
 
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 04a697e..2d857bd 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
@@ -28,7 +28,9 @@
 import androidx.compose.ui.focus.FocusPropertiesModifierNode
 import androidx.compose.ui.focus.FocusTargetModifierNode
 import androidx.compose.ui.input.key.KeyInputModifierNode
+import androidx.compose.ui.input.key.SoftKeyboardInterceptionModifierNode
 import androidx.compose.ui.input.pointer.PointerInputModifier
+import androidx.compose.ui.input.pointer.SuspendPointerInputModifierNode
 import androidx.compose.ui.input.rotary.RotaryInputModifierNode
 import androidx.compose.ui.layout.IntermediateLayoutModifierNode
 import androidx.compose.ui.layout.LayoutModifier
@@ -39,6 +41,7 @@
 import androidx.compose.ui.modifier.ModifierLocalConsumer
 import androidx.compose.ui.modifier.ModifierLocalNode
 import androidx.compose.ui.modifier.ModifierLocalProvider
+import androidx.compose.ui.node.Nodes.SuspendPointerInput
 import androidx.compose.ui.semantics.SemanticsModifier
 
 @JvmInline
@@ -53,7 +56,6 @@
 // its own measureNode if the measureNode happens to implement LayoutAware. If the measureNode
 // implements any other node interfaces, such as draw, those should be visited by the coordinator
 // below them.
-@OptIn(ExperimentalComposeUiApi::class)
 internal val NodeKind<*>.includeSelfInTraversal: Boolean
     get() = mask and Nodes.LayoutAware.mask != 0
 
@@ -95,10 +97,14 @@
     @JvmStatic
     inline val CompositionLocalConsumer
         get() = NodeKind<CompositionLocalConsumerModifierNode>(0b1 shl 15)
+    @JvmStatic
+    inline val SuspendPointerInput get() = NodeKind<SuspendPointerInputModifierNode>(0b1 shl 16)
+    @JvmStatic
+    inline val SoftKeyboardKeyInput
+        get() = NodeKind<SoftKeyboardInterceptionModifierNode>(0b1 shl 17)
     // ...
 }
 
-@OptIn(ExperimentalComposeUiApi::class)
 internal fun calculateNodeKindSetFrom(element: Modifier.Element): Int {
     var mask = Nodes.Any.mask
     if (element is LayoutModifier) {
@@ -188,6 +194,12 @@
     if (node is CompositionLocalConsumerModifierNode) {
         mask = mask or Nodes.CompositionLocalConsumer
     }
+    if (node is SuspendPointerInputModifierNode) {
+        mask = mask or SuspendPointerInput
+    }
+    if (node is SoftKeyboardInterceptionModifierNode) {
+        mask = mask or Nodes.SoftKeyboardKeyInput
+    }
     return mask
 }
 
@@ -195,16 +207,12 @@
 private const val Inserted = 1
 private const val Removed = 2
 
-@OptIn(ExperimentalComposeUiApi::class)
 internal fun autoInvalidateRemovedNode(node: Modifier.Node) = autoInvalidateNode(node, Removed)
 
-@OptIn(ExperimentalComposeUiApi::class)
 internal fun autoInvalidateInsertedNode(node: Modifier.Node) = autoInvalidateNode(node, Inserted)
 
-@OptIn(ExperimentalComposeUiApi::class)
 internal fun autoInvalidateUpdatedNode(node: Modifier.Node) = autoInvalidateNode(node, Updated)
 
-@OptIn(ExperimentalComposeUiApi::class)
 private fun autoInvalidateNode(node: Modifier.Node, phase: Int) {
     check(node.isAttached)
     if (node.isKind(Nodes.Layout) && node is LayoutModifierNode) {
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 cd21be2..df5870a 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
@@ -33,7 +33,6 @@
 import androidx.compose.ui.platform.TextToolbar
 import androidx.compose.ui.platform.ViewConfiguration
 import androidx.compose.ui.platform.WindowInfo
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.font.Font
 import androidx.compose.ui.text.font.FontFamily
 import androidx.compose.ui.text.input.PlatformTextInputPluginRegistry
@@ -112,7 +111,6 @@
 
     val textInputService: TextInputService
 
-    @OptIn(ExperimentalTextApi::class)
     val platformTextInputPluginRegistry: PlatformTextInputPluginRegistry
 
     val pointerIconService: PointerIconService
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/PointerInputModifierNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/PointerInputModifierNode.kt
index deb97c5..c6a86b8 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/PointerInputModifierNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/PointerInputModifierNode.kt
@@ -19,6 +19,7 @@
 import androidx.compose.ui.input.pointer.PointerEvent
 import androidx.compose.ui.input.pointer.PointerEventPass
 import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.PointerInputFilter
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.unit.IntSize
 
@@ -28,7 +29,7 @@
  * [PointerInputModifierNode]s don't also react to them.
  *
  * This is the [androidx.compose.ui.Modifier.Node] equivalent of
- * [androidx.compose.ui.input.pointer.PointerInputModifier]
+ * [androidx.compose.ui.input.pointer.PointerInputFilter].
  *
  * @sample androidx.compose.ui.samples.PointerInputModifierNodeSample
  */
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/RootForTest.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/RootForTest.kt
index 4d8b129..e009d51 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/RootForTest.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/RootForTest.kt
@@ -19,8 +19,6 @@
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.input.key.KeyEvent
 import androidx.compose.ui.semantics.SemanticsOwner
-import androidx.compose.ui.text.ExperimentalTextApi
-import androidx.compose.ui.text.input.TextInputForTests
 import androidx.compose.ui.text.input.TextInputService
 import androidx.compose.ui.unit.Density
 
@@ -45,15 +43,6 @@
     val textInputService: TextInputService
 
     /**
-     * The [TextInputForTests] for the active text service in this root, or null if no text service
-     * is actively handling an input session.
-     */
-    @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
-    @ExperimentalTextApi
-    @get:ExperimentalTextApi
-    val textInputForTests: TextInputForTests? get() = null
-
-    /**
      * Send this [KeyEvent] to the focused component in this [Owner].
      *
      * @return true if the event was consumed. False otherwise.
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/CompositionLocals.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/CompositionLocals.kt
index f654184..d1d8cd0 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/CompositionLocals.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/CompositionLocals.kt
@@ -29,7 +29,6 @@
 import androidx.compose.ui.input.pointer.PointerIconService
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.node.Owner
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.font.Font
 import androidx.compose.ui.text.font.FontFamily
 import androidx.compose.ui.text.input.PlatformTextInputPluginRegistry
@@ -144,7 +143,6 @@
  * Higher-level text input APIs in the Foundation library are more appropriate for most cases.
  */
 // Experimental in desktop.
-@OptIn(ExperimentalTextApi::class)
 val LocalPlatformTextInputPluginRegistry =
     staticCompositionLocalOf<PlatformTextInputPluginRegistry> {
         error("No PlatformTextInputPluginRegistry provided")
@@ -182,7 +180,6 @@
     null
 }
 
-@OptIn(ExperimentalTextApi::class)
 @ExperimentalComposeUiApi
 @Composable
 internal fun ProvideCommonCompositionLocals(
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 ac6025d..c1513da 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
@@ -273,6 +273,11 @@
     val SetText = ActionPropertyKey<(AnnotatedString) -> Boolean>("SetText")
 
     /**
+     * @see SemanticsPropertyReceiver.insertTextAtCursor
+     */
+    val InsertTextAtCursor = ActionPropertyKey<(AnnotatedString) -> Boolean>("InsertTextAtCursor")
+
+    /**
      * @see SemanticsPropertyReceiver.performImeAction
      */
     val PerformImeAction = ActionPropertyKey<() -> Boolean>("PerformImeAction")
@@ -1039,6 +1044,22 @@
 }
 
 /**
+ * Action to insert text into this node at the current cursor position, or replacing the selection
+ * if text is selected.
+ *
+ * Expected to be used on editable text fields.
+ *
+ * @param label Optional label for this action.
+ * @param action Action to be performed when [SemanticsActions.InsertTextAtCursor] is called.
+ */
+fun SemanticsPropertyReceiver.insertTextAtCursor(
+    label: String? = null,
+    action: ((AnnotatedString) -> Boolean)?
+) {
+    this[SemanticsActions.InsertTextAtCursor] = AccessibilityAction(label, action)
+}
+
+/**
  * Action to invoke the IME action handler configured on the node.
  *
  * Expected to be used on editable text fields.
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
index 95e5dc6..7ab1907 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
@@ -61,7 +61,6 @@
 import androidx.compose.ui.platform.invertTo
 import androidx.compose.ui.semantics.SemanticsConfiguration
 import androidx.compose.ui.semantics.SemanticsModifier
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.font.Font
 import androidx.compose.ui.text.font.FontFamily
 import androidx.compose.ui.text.input.PlatformTextInputPluginRegistry
@@ -2532,7 +2531,6 @@
         get() = Density(1f)
     override val textInputService: TextInputService
         get() = TODO("Not yet implemented")
-    @OptIn(ExperimentalTextApi::class)
     override val platformTextInputPluginRegistry: PlatformTextInputPluginRegistry
         get() = TODO("Not yet implemented")
     override val pointerIconService: PointerIconService
@@ -2797,4 +2795,6 @@
     val node = node as? BackwardsCompatNode
         ?: error("Incorrectly assumed Modifier.Node was a BackwardsCompatNode")
     return node.element
-}
\ No newline at end of file
+}
+
+private fun LayoutNode.onNodePlaced() = measurePassDelegate.onNodePlaced()
\ No newline at end of file
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/ModifierLocalConsumerEntityTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/ModifierLocalConsumerEntityTest.kt
index 639263a..e4550eb 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/ModifierLocalConsumerEntityTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/ModifierLocalConsumerEntityTest.kt
@@ -41,7 +41,6 @@
 import androidx.compose.ui.platform.TextToolbar
 import androidx.compose.ui.platform.ViewConfiguration
 import androidx.compose.ui.platform.WindowInfo
-import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.font.Font
 import androidx.compose.ui.text.font.FontFamily
 import androidx.compose.ui.text.input.PlatformTextInputPluginRegistry
@@ -347,7 +346,6 @@
             get() = TODO("Not yet implemented")
         override val textInputService: TextInputService
             get() = TODO("Not yet implemented")
-        @OptIn(ExperimentalTextApi::class)
         override val platformTextInputPluginRegistry: PlatformTextInputPluginRegistry
             get() = TODO("Not yet implemented")
         override val pointerIconService: PointerIconService
diff --git a/constraintlayout/constraintlayout-compose/api/current.txt b/constraintlayout/constraintlayout-compose/api/current.txt
index 473ecba..47442ef 100644
--- a/constraintlayout/constraintlayout-compose/api/current.txt
+++ b/constraintlayout/constraintlayout-compose/api/current.txt
@@ -138,15 +138,15 @@
     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 String spans, optional String skips, optional int[] rowWeights, optional float verticalGap, optional float padding);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createColumn(androidx.constraintlayout.compose.LayoutReference![] elements, optional String spans, optional String skips, optional float verticalGap, optional int[] rowWeights, optional float paddingHorizontal, optional float paddingVertical);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createColumn(androidx.constraintlayout.compose.LayoutReference![] elements, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional int[] rowWeights, optional float verticalGap, optional float padding);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createColumn(androidx.constraintlayout.compose.LayoutReference![] elements, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional float verticalGap, optional int[] rowWeights, optional float paddingHorizontal, optional float paddingVertical);
     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, optional int orientation, optional int rows, optional int columns, optional float verticalGap, optional float horizontalGap, optional int[] rowWeights, optional int[] columnWeights, optional String skips, optional String spans, optional float padding, optional androidx.constraintlayout.compose.GridFlag![] flags);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createGrid(androidx.constraintlayout.compose.LayoutReference![] elements, optional int orientation, optional int rows, optional int columns, optional float verticalGap, optional float horizontalGap, optional int[] rowWeights, optional int[] columnWeights, optional String skips, optional String spans, optional float paddingHorizontal, optional float paddingVertical, optional androidx.constraintlayout.compose.GridFlag![] flags);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createGrid(androidx.constraintlayout.compose.LayoutReference![] elements, optional int orientation, optional int rows, optional int columns, optional float verticalGap, optional float horizontalGap, optional int[] rowWeights, optional int[] columnWeights, optional String skips, optional String spans, optional float paddingLeft, optional float paddingTop, optional float paddingRight, optional float paddingBottom, optional androidx.constraintlayout.compose.GridFlag![] flags);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createGrid(androidx.constraintlayout.compose.LayoutReference![] elements, optional int orientation, optional int rows, optional int columns, optional float verticalGap, optional float horizontalGap, optional int[] rowWeights, optional int[] columnWeights, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional float padding, optional androidx.constraintlayout.compose.GridFlag![] flags);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createGrid(androidx.constraintlayout.compose.LayoutReference![] elements, optional int orientation, optional int rows, optional int columns, optional float verticalGap, optional float horizontalGap, optional int[] rowWeights, optional int[] columnWeights, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional float paddingHorizontal, optional float paddingVertical, optional androidx.constraintlayout.compose.GridFlag![] flags);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createGrid(androidx.constraintlayout.compose.LayoutReference![] elements, optional int orientation, optional int rows, optional int columns, optional float verticalGap, optional float horizontalGap, optional int[] rowWeights, optional int[] columnWeights, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional float paddingStart, optional float paddingTop, optional float paddingEnd, optional float paddingBottom, optional androidx.constraintlayout.compose.GridFlag![] 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);
@@ -160,8 +160,8 @@
     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 String spans, optional String skips, optional float horizontalGap, optional int[] columnWeights, optional float padding);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createRow(androidx.constraintlayout.compose.LayoutReference![] elements, optional String spans, optional String skips, optional float horizontalGap, optional int[] columnWeights, optional float paddingHorizontal, optional float paddingVertical);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createRow(androidx.constraintlayout.compose.LayoutReference![] elements, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional float horizontalGap, optional int[] columnWeights, optional float padding);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createRow(androidx.constraintlayout.compose.LayoutReference![] elements, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional float horizontalGap, optional int[] columnWeights, optional float paddingHorizontal, optional float paddingVertical);
     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);
@@ -448,6 +448,22 @@
     enum_constant public static final androidx.constraintlayout.compose.MotionLayoutFlag FullMeasure;
   }
 
+  @kotlin.jvm.JvmInline public final value class Skip {
+    ctor public Skip(String description);
+    ctor public Skip(int position, int rows, int columns);
+    ctor public Skip(int position, int size);
+    method public String getDescription();
+    property public final String description;
+  }
+
+  @kotlin.jvm.JvmInline public final value class Span {
+    ctor public Span(String description);
+    ctor public Span(int position, int rows, int columns);
+    ctor public Span(int position, int size);
+    method public String getDescription();
+    property public final String description;
+  }
+
   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();
diff --git a/constraintlayout/constraintlayout-compose/api/public_plus_experimental_current.txt b/constraintlayout/constraintlayout-compose/api/public_plus_experimental_current.txt
index f2b3795..a72f32c 100644
--- a/constraintlayout/constraintlayout-compose/api/public_plus_experimental_current.txt
+++ b/constraintlayout/constraintlayout-compose/api/public_plus_experimental_current.txt
@@ -173,15 +173,15 @@
     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 String spans, optional String skips, optional int[] rowWeights, optional float verticalGap, optional float padding);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createColumn(androidx.constraintlayout.compose.LayoutReference![] elements, optional String spans, optional String skips, optional float verticalGap, optional int[] rowWeights, optional float paddingHorizontal, optional float paddingVertical);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createColumn(androidx.constraintlayout.compose.LayoutReference![] elements, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional int[] rowWeights, optional float verticalGap, optional float padding);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createColumn(androidx.constraintlayout.compose.LayoutReference![] elements, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional float verticalGap, optional int[] rowWeights, optional float paddingHorizontal, optional float paddingVertical);
     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, optional int orientation, optional int rows, optional int columns, optional float verticalGap, optional float horizontalGap, optional int[] rowWeights, optional int[] columnWeights, optional String skips, optional String spans, optional float padding, optional androidx.constraintlayout.compose.GridFlag![] flags);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createGrid(androidx.constraintlayout.compose.LayoutReference![] elements, optional int orientation, optional int rows, optional int columns, optional float verticalGap, optional float horizontalGap, optional int[] rowWeights, optional int[] columnWeights, optional String skips, optional String spans, optional float paddingHorizontal, optional float paddingVertical, optional androidx.constraintlayout.compose.GridFlag![] flags);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createGrid(androidx.constraintlayout.compose.LayoutReference![] elements, optional int orientation, optional int rows, optional int columns, optional float verticalGap, optional float horizontalGap, optional int[] rowWeights, optional int[] columnWeights, optional String skips, optional String spans, optional float paddingLeft, optional float paddingTop, optional float paddingRight, optional float paddingBottom, optional androidx.constraintlayout.compose.GridFlag![] flags);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createGrid(androidx.constraintlayout.compose.LayoutReference![] elements, optional int orientation, optional int rows, optional int columns, optional float verticalGap, optional float horizontalGap, optional int[] rowWeights, optional int[] columnWeights, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional float padding, optional androidx.constraintlayout.compose.GridFlag![] flags);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createGrid(androidx.constraintlayout.compose.LayoutReference![] elements, optional int orientation, optional int rows, optional int columns, optional float verticalGap, optional float horizontalGap, optional int[] rowWeights, optional int[] columnWeights, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional float paddingHorizontal, optional float paddingVertical, optional androidx.constraintlayout.compose.GridFlag![] flags);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createGrid(androidx.constraintlayout.compose.LayoutReference![] elements, optional int orientation, optional int rows, optional int columns, optional float verticalGap, optional float horizontalGap, optional int[] rowWeights, optional int[] columnWeights, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional float paddingStart, optional float paddingTop, optional float paddingEnd, optional float paddingBottom, optional androidx.constraintlayout.compose.GridFlag![] 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);
@@ -195,8 +195,8 @@
     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 String spans, optional String skips, optional float horizontalGap, optional int[] columnWeights, optional float padding);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createRow(androidx.constraintlayout.compose.LayoutReference![] elements, optional String spans, optional String skips, optional float horizontalGap, optional int[] columnWeights, optional float paddingHorizontal, optional float paddingVertical);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createRow(androidx.constraintlayout.compose.LayoutReference![] elements, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional float horizontalGap, optional int[] columnWeights, optional float padding);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createRow(androidx.constraintlayout.compose.LayoutReference![] elements, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional float horizontalGap, optional int[] columnWeights, optional float paddingHorizontal, optional float paddingVertical);
     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);
@@ -788,6 +788,22 @@
     property public final androidx.constraintlayout.compose.RelativePosition Path;
   }
 
+  @kotlin.jvm.JvmInline public final value class Skip {
+    ctor public Skip(String description);
+    ctor public Skip(int position, int rows, int columns);
+    ctor public Skip(int position, int size);
+    method public String getDescription();
+    property public final String description;
+  }
+
+  @kotlin.jvm.JvmInline public final value class Span {
+    ctor public Span(String description);
+    ctor public Span(int position, int rows, int columns);
+    ctor public Span(int position, int size);
+    method public String getDescription();
+    property public final String description;
+  }
+
   @androidx.constraintlayout.compose.ExperimentalMotionApi public final class SpringBoundary {
     method public String getName();
     property public final String name;
diff --git a/constraintlayout/constraintlayout-compose/api/restricted_current.txt b/constraintlayout/constraintlayout-compose/api/restricted_current.txt
index 49e7f3a..35fb12d 100644
--- a/constraintlayout/constraintlayout-compose/api/restricted_current.txt
+++ b/constraintlayout/constraintlayout-compose/api/restricted_current.txt
@@ -145,15 +145,15 @@
     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 String spans, optional String skips, optional int[] rowWeights, optional float verticalGap, optional float padding);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createColumn(androidx.constraintlayout.compose.LayoutReference![] elements, optional String spans, optional String skips, optional float verticalGap, optional int[] rowWeights, optional float paddingHorizontal, optional float paddingVertical);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createColumn(androidx.constraintlayout.compose.LayoutReference![] elements, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional int[] rowWeights, optional float verticalGap, optional float padding);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createColumn(androidx.constraintlayout.compose.LayoutReference![] elements, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional float verticalGap, optional int[] rowWeights, optional float paddingHorizontal, optional float paddingVertical);
     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, optional int orientation, optional int rows, optional int columns, optional float verticalGap, optional float horizontalGap, optional int[] rowWeights, optional int[] columnWeights, optional String skips, optional String spans, optional float padding, optional androidx.constraintlayout.compose.GridFlag![] flags);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createGrid(androidx.constraintlayout.compose.LayoutReference![] elements, optional int orientation, optional int rows, optional int columns, optional float verticalGap, optional float horizontalGap, optional int[] rowWeights, optional int[] columnWeights, optional String skips, optional String spans, optional float paddingHorizontal, optional float paddingVertical, optional androidx.constraintlayout.compose.GridFlag![] flags);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createGrid(androidx.constraintlayout.compose.LayoutReference![] elements, optional int orientation, optional int rows, optional int columns, optional float verticalGap, optional float horizontalGap, optional int[] rowWeights, optional int[] columnWeights, optional String skips, optional String spans, optional float paddingLeft, optional float paddingTop, optional float paddingRight, optional float paddingBottom, optional androidx.constraintlayout.compose.GridFlag![] flags);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createGrid(androidx.constraintlayout.compose.LayoutReference![] elements, optional int orientation, optional int rows, optional int columns, optional float verticalGap, optional float horizontalGap, optional int[] rowWeights, optional int[] columnWeights, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional float padding, optional androidx.constraintlayout.compose.GridFlag![] flags);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createGrid(androidx.constraintlayout.compose.LayoutReference![] elements, optional int orientation, optional int rows, optional int columns, optional float verticalGap, optional float horizontalGap, optional int[] rowWeights, optional int[] columnWeights, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional float paddingHorizontal, optional float paddingVertical, optional androidx.constraintlayout.compose.GridFlag![] flags);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createGrid(androidx.constraintlayout.compose.LayoutReference![] elements, optional int orientation, optional int rows, optional int columns, optional float verticalGap, optional float horizontalGap, optional int[] rowWeights, optional int[] columnWeights, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional float paddingStart, optional float paddingTop, optional float paddingEnd, optional float paddingBottom, optional androidx.constraintlayout.compose.GridFlag![] 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);
@@ -167,8 +167,8 @@
     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 String spans, optional String skips, optional float horizontalGap, optional int[] columnWeights, optional float padding);
-    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createRow(androidx.constraintlayout.compose.LayoutReference![] elements, optional String spans, optional String skips, optional float horizontalGap, optional int[] columnWeights, optional float paddingHorizontal, optional float paddingVertical);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createRow(androidx.constraintlayout.compose.LayoutReference![] elements, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional float horizontalGap, optional int[] columnWeights, optional float padding);
+    method public final androidx.constraintlayout.compose.ConstrainedLayoutReference createRow(androidx.constraintlayout.compose.LayoutReference![] elements, optional androidx.constraintlayout.compose.Skip![] skips, optional androidx.constraintlayout.compose.Span![] spans, optional float horizontalGap, optional int[] columnWeights, optional float paddingHorizontal, optional float paddingVertical);
     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);
@@ -613,6 +613,22 @@
     method public androidx.constraintlayout.compose.MotionProgress fromState(androidx.compose.runtime.State<java.lang.Float> progressState, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onUpdate);
   }
 
+  @kotlin.jvm.JvmInline public final value class Skip {
+    ctor public Skip(String description);
+    ctor public Skip(int position, int rows, int columns);
+    ctor public Skip(int position, int size);
+    method public String getDescription();
+    property public final String description;
+  }
+
+  @kotlin.jvm.JvmInline public final value class Span {
+    ctor public Span(String description);
+    ctor public Span(int position, int rows, int columns);
+    ctor public Span(int position, int size);
+    method public String getDescription();
+    property public final String description;
+  }
+
   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();
diff --git a/constraintlayout/constraintlayout-compose/build.gradle b/constraintlayout/constraintlayout-compose/build.gradle
index 96395d1..639aa60 100644
--- a/constraintlayout/constraintlayout-compose/build.gradle
+++ b/constraintlayout/constraintlayout-compose/build.gradle
@@ -29,17 +29,17 @@
 dependencies {
     if(!AndroidXComposePlugin.isMultiplatformEnabled(project)) {
         implementation(project(":compose:ui:ui"))
-        implementation("androidx.compose.ui:ui-unit:1.4.0-beta02")
-        implementation("androidx.compose.ui:ui-util:1.4.0-beta02")
-        implementation("androidx.compose.foundation:foundation:1.4.0-beta02")
-        implementation("androidx.compose.foundation:foundation-layout:1.4.0-beta02")
+        implementation(project(":compose:ui:ui-unit"))
+        implementation(project(":compose:ui:ui-util"))
+        implementation(project(":compose:foundation:foundation"))
+        implementation(project(":compose:foundation:foundation-layout"))
 
         implementation(project(":constraintlayout:constraintlayout-core"))
 
-        androidTestImplementation("androidx.compose.material:material:1.4.0-beta02")
-        androidTestImplementation("androidx.compose.ui:ui-test:1.4.0-beta02")
-        androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.4.0-beta02")
-        androidTestImplementation("androidx.compose.ui:ui-test-manifest:1.4.0-beta02")
+        androidTestImplementation(project(":compose:material:material"))
+        androidTestImplementation(project(":compose:ui:ui-test"))
+        androidTestImplementation(project(":compose:ui:ui-test-junit4"))
+        androidTestImplementation(project(":compose:ui:ui-test-manifest"))
         androidTestImplementation(project(":activity:activity"))
 
         androidTestImplementation(libs.kotlinTest)
@@ -67,10 +67,10 @@
 //                implementation(libs.kotlinStdlibCommon)
 
                 implementation(project(":compose:ui:ui"))
-                implementation(project(":compose:ui:ui-unit"))
-                implementation(project(":compose:ui:ui-util"))
-                implementation(project(":compose:foundation:foundation"))
-                implementation(project(":compose:foundation:foundation-layout"))
+                implementation("androidx.compose.ui:ui-unit:1.4.0-beta02")
+                implementation("androidx.compose.ui:ui-util:1.4.0-beta02")
+                implementation("androidx.compose.foundation:foundation:1.4.0-beta02")
+                implementation("androidx.compose.foundation:foundation-layout:1.4.0-beta02")
                 implementation(project(":constraintlayout:constraintlayout-core"))
 
             }
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/AllDemos.kt b/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/AllDemos.kt
index 935adbe..f03ec69 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/AllDemos.kt
+++ b/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/AllDemos.kt
@@ -53,7 +53,8 @@
 val AllComposeConstraintLayoutDemos: List<ComposeDemo> =
     listOf(
         ComposeDemo("CustomColorInKeyAttributes") { CustomColorInKeyAttributesDemo() },
-        ComposeDemo("SimpleOnSwipe") { SimpleOnSwipe() },
+        ComposeDemo("Simple OnSwipe") { SimpleOnSwipe() },
+        ComposeDemo("Multiple OnSwipe") { MultiSwipeDsl() },
         ComposeDemo("AnimatedChainOrientation") { ChainsAnimatedOrientationDemo() },
         ComposeDemo("CollapsibleToolbar w/ Column") { ToolBarDslDemo() },
         ComposeDemo("CollapsibleToolbar w/ LazyColumn") { ToolBarLazyDslDemo() },
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/OnSwipeDemos.kt b/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/OnSwipeDemos.kt
index 8fdbe10..d3f65d8 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/OnSwipeDemos.kt
+++ b/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/OnSwipeDemos.kt
@@ -25,20 +25,33 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.Button
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.Stable
 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.text.style.TextAlign
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
+import androidx.constraintlayout.compose.Dimension
 import androidx.constraintlayout.compose.ExperimentalMotionApi
 import androidx.constraintlayout.compose.MotionLayout
 import androidx.constraintlayout.compose.MotionLayoutDebugFlags
 import androidx.constraintlayout.compose.MotionScene
+import androidx.constraintlayout.compose.OnSwipe
+import androidx.constraintlayout.compose.SwipeDirection
+import androidx.constraintlayout.compose.SwipeMode
+import androidx.constraintlayout.compose.SwipeSide
+import androidx.constraintlayout.compose.SwipeTouchUp
 import androidx.constraintlayout.compose.layoutId
 import androidx.constraintlayout.compose.rememberMotionLayoutState
 
@@ -155,4 +168,137 @@
         }
         Text(text = "Current progress: ${motionLayoutState.currentProgress}")
     }
+}
+
+@Preview
+@Composable
+fun MultiSwipeDsl() {
+    val modes = remember { arrayOf(SwipeMode.Velocity, SwipeMode.Spring) }
+    val touchUps = remember {
+        arrayOf(
+            SwipeTouchUp.AutoComplete,
+            SwipeTouchUp.ToStart,
+            SwipeTouchUp.ToEnd,
+            SwipeTouchUp.Stop,
+            SwipeTouchUp.Decelerate,
+            SwipeTouchUp.NeverCompleteStart,
+            SwipeTouchUp.NeverCompleteEnd,
+        )
+    }
+    val endWidth = arrayOf(50, 200)
+    val simpleSwipeConfigs = remember {
+        val configCombinations = mutableListOf<SimpleSwipeConfig>()
+        touchUps.forEach { touchUp ->
+            endWidth.forEach { width ->
+                modes.forEach { mode ->
+                    configCombinations.add(
+                        SimpleSwipeConfig(
+                            mode,
+                            width,
+                            touchUp
+                        )
+                    )
+                }
+            }
+        }
+        return@remember configCombinations
+    }
+    Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
+        simpleSwipeConfigs.forEach { config ->
+            Box(
+                modifier = Modifier
+                    .height(20.dp)
+                    .fillMaxWidth()
+                    .background(Color.LightGray)
+            )
+            SimpleSwipeDsl(config)
+        }
+    }
+}
+
+@Stable
+@Immutable
+private data class SimpleSwipeConfig(
+    val mode: SwipeMode,
+    val endWidth: Int,
+    val touchUp: SwipeTouchUp
+)
+
+@Composable
+private fun SimpleSwipeDsl(config: SimpleSwipeConfig) {
+    val mode = config.mode
+    val endWidth = config.endWidth
+    val touchUp = config.touchUp
+    val titleText = "(${mode.name} $endWidth ${touchUp.name})"
+
+    MotionLayout(
+        modifier = Modifier
+            .height(70.dp)
+            .fillMaxWidth()
+            .background(Color.White),
+        motionScene = MotionScene {
+            val title = createRefFor("title")
+            val box = createRefFor("box")
+
+            val from = constraintSet {
+                constrain(title) {
+                    width = Dimension.wrapContent
+                    height = Dimension.value(50.dp)
+                    centerTo(parent)
+                    customFloat("mValue", 0.0f)
+                    customColor("back", Color(0xffffffff))
+                }
+                constrain(box) {
+                    width = Dimension.value(50.dp)
+                    height = Dimension.value(50.dp)
+                    bottom.linkTo(parent.bottom)
+                    top.linkTo(parent.top)
+                    start.linkTo(parent.start, 70.dp)
+                    rotationZ = 0f
+                    customColor("boxColor", Color(0xff00ffff))
+                }
+            }
+            val to = constraintSet(extendConstraintSet = from) {
+                constrain(title) {
+                    customFloat("mValue", 100.0f)
+                    customColor("back", Color(0xffFF88FF))
+                }
+                constrain(box) {
+                    width = Dimension.value(endWidth.dp)
+                    clearHorizontal()
+                    end.linkTo(parent.end, 70.dp)
+                    rotationZ = 360f
+                    customColor("boxColor", Color(0xFF00FF00))
+                }
+            }
+            defaultTransition(
+                from = from,
+                to = to
+            ) {
+                onSwipe = OnSwipe(
+                    anchor = box,
+                    direction = SwipeDirection.Right,
+                    side = SwipeSide.Left,
+                    mode = mode,
+                    onTouchUp = touchUp
+                )
+            }
+        }
+    ) {
+        val progress = customFloat("title", "mValue")
+        val textBackColor = customColor("title", "back")
+
+        Text(
+            text = "$titleText  $progress",
+            modifier = Modifier
+                .layoutId("title")
+                .background(textBackColor),
+            textAlign = TextAlign.Center
+        )
+        Box(
+            modifier = Modifier
+                .background(customProperties("box").color("boxColor"))
+                .layoutId("box")
+        )
+    }
 }
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/PuzzleDemo.kt b/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/PuzzleDemo.kt
index 42be16c..f5d5cc4 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/PuzzleDemo.kt
+++ b/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/PuzzleDemo.kt
@@ -34,7 +34,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.drawscope.clipRect
-import androidx.compose.ui.graphics.drawscope.withTransform
+import androidx.compose.ui.graphics.drawscope.translate
 import androidx.compose.ui.graphics.painter.Painter
 import androidx.compose.ui.graphics.vector.rememberVectorPainter
 import androidx.compose.ui.layout.layoutId
@@ -173,15 +173,12 @@
 ) {
     Canvas(modifier.fillMaxSize()) {
         clipRect {
-            withTransform({
-                scale(scaleY = gridSize.toFloat(), scaleX = gridSize.toFloat())
-                translate(
-                    left = -(x - gridSize / 2) * size.width / gridSize,
-                    top = -(y - gridSize / 2) * size.height / gridSize
-                )
-            }) {
+            translate(
+                left = -x * size.width,
+                top = -y * size.height
+            ) {
                 with(painter) {
-                    draw(size)
+                    draw(size.times(gridSize.toFloat()))
                 }
             }
         }
diff --git a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/GridDslTest.kt b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/GridDslTest.kt
index 6450111..e8a854d 100644
--- a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/GridDslTest.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/GridDslTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -72,8 +72,8 @@
                 numColumns = columns,
                 hGap = 0,
                 vGap = 0,
-                gridSpans = "",
-                gridSkips = "",
+                gridSpans = arrayOf(),
+                gridSkips = arrayOf(),
                 gridRowWeights = intArrayOf(),
                 gridColumnWeights = intArrayOf(),
                 gridFlags = arrayOf(),
@@ -112,8 +112,8 @@
                 numColumns = columns,
                 hGap = 0,
                 vGap = 0,
-                gridSpans = "",
-                gridSkips = "",
+                gridSpans = arrayOf(),
+                gridSkips = arrayOf(),
                 gridRowWeights = intArrayOf(),
                 gridColumnWeights = intArrayOf(),
                 gridFlags = arrayOf(),
@@ -152,8 +152,8 @@
                 numColumns = columns,
                 hGap = 0,
                 vGap = 0,
-                gridSpans = "",
-                gridSkips = "",
+                gridSpans = arrayOf(),
+                gridSkips = arrayOf(),
                 gridRowWeights = intArrayOf(),
                 gridColumnWeights = intArrayOf(),
                 gridFlags = arrayOf(),
@@ -192,8 +192,8 @@
                 numColumns = columns,
                 hGap = 0,
                 vGap = 0,
-                gridSpans = "",
-                gridSkips = "",
+                gridSpans = arrayOf(),
+                gridSkips = arrayOf(),
                 gridRowWeights = intArrayOf(),
                 gridColumnWeights = intArrayOf(),
                 gridFlags = arrayOf(),
@@ -232,8 +232,8 @@
                 numColumns = columns,
                 hGap = 0,
                 vGap = 0,
-                gridSpans = "",
-                gridSkips = "0:1x1",
+                gridSpans = arrayOf(),
+                gridSkips = arrayOf(Skip(0, 1, 1)),
                 gridRowWeights = intArrayOf(),
                 gridColumnWeights = intArrayOf(),
                 gridFlags = arrayOf(),
@@ -271,8 +271,8 @@
                 numColumns = columns,
                 hGap = 0,
                 vGap = 0,
-                gridSpans = "",
-                gridSkips = "0:2x1",
+                gridSpans = arrayOf(),
+                gridSkips = arrayOf(Skip(0, 2, 1)),
                 gridRowWeights = intArrayOf(),
                 gridColumnWeights = intArrayOf(),
                 gridFlags = arrayOf(GridFlag.SpansRespectWidgetOrder, GridFlag.SubGridByColRow)
@@ -309,8 +309,8 @@
                 numColumns = columns,
                 hGap = 0,
                 vGap = 0,
-                gridSpans = "0:1x2",
-                gridSkips = "",
+                gridSpans = arrayOf(Span(0, 1, 2)),
+                gridSkips = arrayOf(),
                 gridRowWeights = intArrayOf(),
                 gridColumnWeights = intArrayOf(),
                 gridFlags = arrayOf(),
@@ -349,8 +349,8 @@
                 numColumns = columns,
                 hGap = 0,
                 vGap = 0,
-                gridSpans = "1:2x1",
-                gridSkips = "",
+                gridSpans = arrayOf(Span(1, 2, 1)),
+                gridSkips = arrayOf(),
                 gridRowWeights = intArrayOf(),
                 gridColumnWeights = intArrayOf(),
                 gridFlags = arrayOf(GridFlag.SpansRespectWidgetOrder),
@@ -389,8 +389,8 @@
                 numColumns = columns,
                 hGap = 0,
                 vGap = 0,
-                gridSpans = "0:2x1",
-                gridSkips = "",
+                gridSpans = arrayOf(Span(0, 2, 1)),
+                gridSkips = arrayOf(),
                 gridRowWeights = intArrayOf(),
                 gridColumnWeights = intArrayOf(),
                 gridFlags = arrayOf(GridFlag.SubGridByColRow),
@@ -430,8 +430,8 @@
                 numColumns = columns,
                 hGap = 0,
                 vGap = 0,
-                gridSpans = "",
-                gridSkips = "",
+                gridSpans = arrayOf(),
+                gridSkips = arrayOf(),
                 gridRowWeights = weights,
                 gridColumnWeights = intArrayOf(),
                 gridFlags = arrayOf(),
@@ -468,8 +468,8 @@
                 numColumns = columns,
                 hGap = 0,
                 vGap = 0,
-                gridSpans = "",
-                gridSkips = "",
+                gridSpans = arrayOf(),
+                gridSkips = arrayOf(),
                 gridRowWeights = intArrayOf(),
                 gridColumnWeights = weights,
                 gridFlags = arrayOf(),
@@ -523,8 +523,8 @@
         modifier: Modifier = Modifier,
         numRows: Int,
         numColumns: Int,
-        gridSpans: String,
-        gridSkips: String,
+        gridSpans: Array<Span>,
+        gridSkips: Array<Skip>,
         gridRowWeights: IntArray,
         gridColumnWeights: IntArray,
         boxesCount: Int,
diff --git a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/RowColumnDslTest.kt b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/RowColumnDslTest.kt
index bf64550..9a75d12 100644
--- a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/RowColumnDslTest.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/RowColumnDslTest.kt
@@ -63,8 +63,8 @@
         rule.setContent {
             ColumnComposableTest(
                 modifier = Modifier.size(rootSize),
-                gridSkips = "",
-                gridSpans = "",
+                gridSkips = arrayOf(),
+                gridSpans = arrayOf(),
                 boxesCount = boxesCount,
                 vGap = 0,
                 gridRowWeights = intArrayOf(),
@@ -95,8 +95,8 @@
         rule.setContent {
             ColumnComposableTest(
                 modifier = Modifier.size(rootSize),
-                gridSkips = "1:2",
-                gridSpans = "",
+                gridSkips = arrayOf(Skip(1, 2)),
+                gridSpans = arrayOf(),
                 boxesCount = boxesCount,
                 vGap = 0,
                 gridRowWeights = intArrayOf(),
@@ -129,8 +129,8 @@
         rule.setContent {
             ColumnComposableTest(
                 modifier = Modifier.size(rootSize),
-                gridSkips = "",
-                gridSpans = "0:2",
+                gridSkips = arrayOf(),
+                gridSpans = arrayOf(Span(0, 2)),
                 boxesCount = boxesCount,
                 vGap = 0,
                 gridRowWeights = intArrayOf(),
@@ -161,8 +161,8 @@
         rule.setContent {
             RowComposableTest(
                 modifier = Modifier.size(rootSize),
-                gridSkips = "",
-                gridSpans = "",
+                gridSkips = arrayOf(),
+                gridSpans = arrayOf(),
                 boxesCount = boxesCount,
                 hGap = 0,
                 gridColumnWeights = intArrayOf()
@@ -193,8 +193,8 @@
         rule.setContent {
             RowComposableTest(
                 modifier = Modifier.size(rootSize),
-                gridSkips = "1:2",
-                gridSpans = "",
+                gridSkips = arrayOf(Skip(1, 2)),
+                gridSpans = arrayOf(),
                 boxesCount = boxesCount,
                 hGap = 0,
                 gridColumnWeights = intArrayOf()
@@ -227,8 +227,8 @@
         rule.setContent {
             RowComposableTest(
                 modifier = Modifier.size(rootSize),
-                gridSkips = "",
-                gridSpans = "0:2",
+                gridSkips = arrayOf(),
+                gridSpans = arrayOf(Span(0, 2)),
                 boxesCount = boxesCount,
                 hGap = 0,
                 gridColumnWeights = intArrayOf()
@@ -256,8 +256,8 @@
     @Composable
     private fun ColumnComposableTest(
         modifier: Modifier = Modifier,
-        gridSkips: String,
-        gridSpans: String,
+        gridSkips: Array<Skip>,
+        gridSpans: Array<Span>,
         gridRowWeights: IntArray,
         boxesCount: Int,
         vGap: Int,
@@ -300,8 +300,8 @@
     @Composable
     private fun RowComposableTest(
         modifier: Modifier = Modifier,
-        gridSkips: String,
-        gridSpans: String,
+        gridSkips: Array<Skip>,
+        gridSpans: Array<Span>,
         gridColumnWeights: IntArray,
         boxesCount: Int,
         hGap: Int,
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 351bad1..dc5a978 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
@@ -703,8 +703,8 @@
      *      val weights = intArrayOf(3, 3, 2, 2, 1)
      *      val g1 = createRow(
      *          a, b, c, d, e,
-     *          spans = "1:2"
-     *          skips = "1:1,3:2",
+     *          skips = arrayOf(Skip(1, 1), Skip(3, 2)),
+     *          spans = arrayOf(Span(1, 2)),
      *          horizontalGap = 10.dp,
      *          columnWeights = weights,
      *          padding = 10.dp,
@@ -727,16 +727,16 @@
      *    }
      *
      * @param elements [LayoutReference]s to be laid out by the Grid helper
-     * @param spans specify area(s) in a Row to be spanned - format: positionxsize
-     * @param skips specify area(s) in a Row to be skipped - format: positionxsize
+     * @param skips specify area(s) in a Row to be skipped - format: Skip(index, size)
+     * @param spans specify area(s) in a Row to be spanned - format: Span(index, size)
      * @param horizontalGap defines the gap between views in the x axis
      * @param columnWeights defines the weight of each column
      * @param padding sets padding around the content
      */
     fun createRow(
         vararg elements: LayoutReference,
-        spans: String = "",
-        skips: String = "",
+        skips: Array<Skip> = arrayOf(),
+        spans: Array<Span> = arrayOf(),
         horizontalGap: Dp = 0.dp,
         columnWeights: IntArray = intArrayOf(),
         padding: Dp = 0.dp,
@@ -744,13 +744,13 @@
         return createGrid(
             elements = elements,
             rows = 1,
-            spans = spans,
             skips = skips,
+            spans = spans,
             horizontalGap = horizontalGap,
             columnWeights = columnWeights,
-            paddingLeft = padding,
+            paddingStart = padding,
             paddingTop = padding,
-            paddingRight = padding,
+            paddingEnd = padding,
             paddingBottom = padding,
         )
     }
@@ -768,8 +768,8 @@
      *      val weights = intArrayOf(3, 3, 2, 2, 1)
      *      val g1 = createRow(
      *          a, b, c, d, e,
-     *          spans = "1:2"
-     *          skips = "1:1,3:2",
+     *          skips = arrayOf(Skip(1, 1), Skip(3, 2)),
+     *          spans = arrayOf(Span(1, 2)),
      *          horizontalGap = 10.dp,
      *          columnWeights = weights,
      *          paddingHorizontal = 10.dp,
@@ -793,17 +793,17 @@
      *   }
      *
      * @param elements [LayoutReference]s to be laid out by the Grid helper
-     * @param spans specify area(s) in a Row to be spanned - format: positionxsize
-     * @param skips specify area(s) in a Row to be skipped - format: positionxsize
+     * @param skips specify area(s) in a Row to be skipped - format: Skip(index, size)
+     * @param spans specify area(s) in a Row to be spanned - format: Span(index, size)
      * @param horizontalGap defines the gap between views in the y axis
      * @param columnWeights defines the weight of each column
-     * @param paddingHorizontal sets paddingLeft and paddingRight of the content
+     * @param paddingHorizontal sets paddingStart and paddingEnd of the content
      * @param paddingVertical sets paddingTop and paddingBottom of the content
      */
     fun createRow(
         vararg elements: LayoutReference,
-        spans: String = "",
-        skips: String = "",
+        skips: Array<Skip> = arrayOf(),
+        spans: Array<Span> = arrayOf(),
         horizontalGap: Dp = 0.dp,
         columnWeights: IntArray = intArrayOf(),
         paddingHorizontal: Dp = 0.dp,
@@ -812,13 +812,13 @@
         return createGrid(
             elements = elements,
             rows = 1,
-            spans = spans,
             skips = skips,
+            spans = spans,
             horizontalGap = horizontalGap,
             columnWeights = columnWeights,
-            paddingLeft = paddingHorizontal,
+            paddingStart = paddingHorizontal,
             paddingTop = paddingVertical,
-            paddingRight = paddingHorizontal,
+            paddingEnd = paddingHorizontal,
             paddingBottom = paddingVertical,
         )
     }
@@ -836,8 +836,8 @@
      *      val weights = intArrayOf(3, 3, 2, 2, 1)
      *      val g1 = createColumn(
      *          a, b, c, d, e,
-     *          spans = "1:2"
-     *          skips = "1:1,3:2",
+     *          skips = arrayOf(Skip(1, 1), Skip(3, 2)),
+     *          spans = arrayOf(Span(1, 2)),
      *          verticalGap = 10.dp,
      *          rowWeights = weights,
      *          padding = 10.dp,
@@ -860,16 +860,16 @@
      *    }
      *
      * @param elements [LayoutReference]s to be laid out by the Grid helper
-     * @param spans specify area(s) in a Column to be spanned - format: positionxsize
-     * @param skips specify area(s) in a Column to be skipped - format: positionxsize
+     * @param spans specify area(s) in a Column to be spanned - format: Span(index, size)
+     * @param skips specify area(s) in a Column to be skipped - format: Skip(index, size)
      * @param verticalGap defines the gap between views in the y axis
      * @param rowWeights defines the weight of each row
      * @param padding sets padding around the content
      */
     fun createColumn(
         vararg elements: LayoutReference,
-        spans: String = "",
-        skips: String = "",
+        skips: Array<Skip> = arrayOf(),
+        spans: Array<Span> = arrayOf(),
         rowWeights: IntArray = intArrayOf(),
         verticalGap: Dp = 0.dp,
         padding: Dp = 0.dp,
@@ -877,13 +877,13 @@
         return createGrid(
             elements = elements,
             columns = 1,
-            spans = spans,
             skips = skips,
+            spans = spans,
             verticalGap = verticalGap,
             rowWeights = rowWeights,
-            paddingLeft = padding,
+            paddingStart = padding,
             paddingTop = padding,
-            paddingRight = padding,
+            paddingEnd = padding,
             paddingBottom = padding,
         )
     }
@@ -901,8 +901,8 @@
      *      val weights = intArrayOf(3, 3, 2, 2, 1)
      *      val g1 = createColumn(
      *          a, b, c, d, e,
-     *          spans = "1:2"
-     *          skips = "1:1,3:2",
+     *          skips = arrayOf(Skip(1, 1), Skip(3, 2)),
+     *          spans = arrayOf(Span(1, 2)),
      *          verticalGap = 10.dp,
      *          rowWeights = weights,
      *          padding = 10.dp,
@@ -925,17 +925,17 @@
      *    }
      *
      * @param elements [LayoutReference]s to be laid out by the Grid helper
-     * @param spans specify area(s) in a Column to be spanned - format: positionxsize
-     * @param skips specify area(s) in a Column to be skipped - format: positionxsize
+     * @param skips specify area(s) in a Column to be skipped - format: Skip(index, size)
+     * @param spans specify area(s) in a Column to be spanned - format: Span(index, size)
      * @param verticalGap defines the gap between views in the y axis
      * @param rowWeights defines the weight of each row
-     * @param paddingHorizontal sets paddingLeft and paddingRight of the content
+     * @param paddingHorizontal sets paddingStart and paddingEnd of the content
      * @param paddingVertical sets paddingTop and paddingBottom of the content
      */
     fun createColumn(
         vararg elements: LayoutReference,
-        spans: String = "",
-        skips: String = "",
+        skips: Array<Skip> = arrayOf(),
+        spans: Array<Span> = arrayOf(),
         verticalGap: Dp = 0.dp,
         rowWeights: IntArray = intArrayOf(),
         paddingHorizontal: Dp = 0.dp,
@@ -944,13 +944,13 @@
         return createGrid(
             elements = elements,
             columns = 1,
-            spans = spans,
             skips = skips,
+            spans = spans,
             verticalGap = verticalGap,
             rowWeights = rowWeights,
-            paddingLeft = paddingHorizontal,
+            paddingStart = paddingHorizontal,
             paddingTop = paddingVertical,
-            paddingRight = paddingHorizontal,
+            paddingEnd = paddingHorizontal,
             paddingBottom = paddingVertical,
         )
     }
@@ -979,8 +979,8 @@
      *          columns = 3,
      *          verticalGap = 25.dp,
      *          horizontalGap = 25.dp,
-     *          spans = "0:1x3",
-     *          skips = "12:1x1",
+     *          skips = arrayOf(Skip(12, 1, 1)),
+     *          spans = arrayOf(Span(0, 1, 3)),
      *          rowWeights = weights,
      *          paddingHorizontal = 10.dp,
      *          paddingVertical = 10.dp,
@@ -1018,15 +1018,15 @@
      * @param rowWeights defines the weight of each row
      * @param columnWeights defines the weight of each column
      * @param skips defines the positions in a Grid to be skipped
-     *        the format of the input string is "index:rowxcol"
-     *        index - the index of the starting position
-     *        row - the number of rows to skip
-     *        col- the number of columns to skip
+     *        the format: Skip(position, rows, columns)
+     *        position - the index of the starting position
+     *        rows - the number of rows to skip
+     *        coloumns - the number of columns to skip
      * @param spans defines the spanned area(s) in Grid
-     *        the format of the input string is "index:rowxcol"
-     *        index - the index of the starting position
-     *        row - the number of rows to span
-     *        col- the number of columns to span
+     *        the format: Span(position, rows, columns)
+     *        position - the index of the starting position
+     *        rows - the number of rows to span
+     *        coloumns - the number of columns to span
      * @param padding sets padding around the content
      * @param flags set different flags to be enabled (not case-sensitive), including
      *          SubGridByColRow: reverse the width and height specification for spans/skips.
@@ -1049,8 +1049,8 @@
         horizontalGap: Dp = 0.dp,
         rowWeights: IntArray = intArrayOf(),
         columnWeights: IntArray = intArrayOf(),
-        skips: String = "",
-        spans: String = "",
+        skips: Array<Skip> = arrayOf(),
+        spans: Array<Span> = arrayOf(),
         padding: Dp = 0.dp,
         flags: Array<GridFlag> = arrayOf(),
     ): ConstrainedLayoutReference {
@@ -1065,9 +1065,9 @@
             columnWeights = columnWeights,
             skips = skips,
             spans = spans,
-            paddingLeft = padding,
+            paddingStart = padding,
             paddingTop = padding,
-            paddingRight = padding,
+            paddingEnd = padding,
             paddingBottom = padding,
             flags = flags,
         )
@@ -1097,8 +1097,8 @@
      *          columns = 3,
      *          verticalGap = 25.dp,
      *          horizontalGap = 25.dp,
-     *          spans = "0:1x3",
-     *          skips = "12:1x1",
+     *          skips = arrayOf(Skip(12, 1, 1)),
+     *          spans = arrayOf(Span(0, 1, 3)),
      *          rowWeights = weights,
      *          paddingHorizontal = 10.dp,
      *          paddingVertical = 10.dp,
@@ -1136,16 +1136,16 @@
      * @param columnWeights defines the weight of each column
      * @param orientation 0 if horizontal and 1 if vertical
      * @param skips defines the positions in a Grid to be skipped
-     *        the format of the input string is "index:rowxcol"
-     *        index - the index of the starting position
-     *        row - the number of rows to skip
-     *        col- the number of columns to skip
+     *        the format: Skip(position, rows, columns)
+     *        position - the index of the starting position
+     *        rows - the number of rows to skip
+     *        coloumns - the number of columns to skip
      * @param spans defines the spanned area(s) in Grid
-     *        the format of the input string is "index:rowxcol"
-     *        index - the index of the starting position
-     *        row - the number of rows to span
-     *        col- the number of columns to span
-     * @param paddingHorizontal sets paddingLeft and paddingRight of the content
+     *        the format: Span(position, rows, columns)
+     *        position - the index of the starting position
+     *        rows - the number of rows to span
+     *        coloumns - the number of columns to span
+     * @param paddingHorizontal sets paddingStart and paddingEnd of the content
      * @param paddingVertical sets paddingTop and paddingBottom of the content
      * @param flags set different flags to be enabled (not case-sensitive), including
      *          SubGridByColRow: reverse the width and height specification for spans/skips.
@@ -1168,8 +1168,8 @@
         horizontalGap: Dp = 0.dp,
         rowWeights: IntArray = intArrayOf(),
         columnWeights: IntArray = intArrayOf(),
-        skips: String = "",
-        spans: String = "",
+        skips: Array<Skip> = arrayOf(),
+        spans: Array<Span> = arrayOf(),
         paddingHorizontal: Dp = 0.dp,
         paddingVertical: Dp = 0.dp,
         flags: Array<GridFlag> = arrayOf(),
@@ -1185,9 +1185,9 @@
             verticalGap = verticalGap,
             skips = skips,
             spans = spans,
-            paddingLeft = paddingHorizontal,
+            paddingStart = paddingHorizontal,
             paddingTop = paddingVertical,
-            paddingRight = paddingHorizontal,
+            paddingEnd = paddingHorizontal,
             paddingBottom = paddingVertical,
             flags = flags
         )
@@ -1217,12 +1217,12 @@
      *          columns = 3,
      *          verticalGap = 25.dp,
      *          horizontalGap = 25.dp,
-     *          spans = "0:1x3",
-     *          skips = "12:1x1",
+     *          skips = arrayOf(Skip(12, 1, 1)),
+     *          spans = arrayOf(Span(0, 1, 3)),
      *          rowWeights = weights,
-     *          paddingLeft = 10.dp,
+     *          paddingStart = 10.dp,
      *          paddingTop = 10.dp,
-     *          paddingRight = 10.dp,
+     *          paddingEnd = 10.dp,
      *          paddingBottom = 10.dp,
      *          flags = flags,
      *      )
@@ -1258,18 +1258,18 @@
      * @param rowWeights defines the weight of each row
      * @param columnWeights defines the weight of each column
      * @param skips defines the positions in a Grid to be skipped
-     *        the format of the input string is "index:rowxcol"
-     *        index - the index of the starting position
-     *        row - the number of rows to skip
-     *        col- the number of columns to skip
+     *        the format: Skip(position, rows, columns)
+     *        position - the index of the starting position
+     *        rows - the number of rows to skip
+     *        coloumns - the number of columns to skip
      * @param spans defines the spanned area(s) in Grid
-     *        the format of the input string is "index:rowxcol"
-     *        index - the index of the starting position
-     *        row - the number of rows to span
-     *        col- the number of columns to span
-     * @param paddingLeft sets paddingLeft of the content
+     *        the format: Span(position, rows, columns)
+     *        position - the index of the starting position
+     *        rows - the number of rows to span
+     *        coloumns - the number of columns to span
+     * @param paddingStart sets paddingStart of the content
      * @param paddingTop sets paddingTop of the content
-     * @param paddingRight sets paddingRight of the content
+     * @param paddingEnd sets paddingEnd of the content
      * @param paddingBottom sets paddingBottom of the content
      * @param flags set different flags to be enabled (not case-sensitive), including
      *          SubGridByColRow: reverse the width and height specification for spans/skips.
@@ -1292,11 +1292,11 @@
         horizontalGap: Dp = 0.dp,
         rowWeights: IntArray = intArrayOf(),
         columnWeights: IntArray = intArrayOf(),
-        skips: String = "",
-        spans: String = "",
-        paddingLeft: Dp = 0.dp,
+        skips: Array<Skip> = arrayOf(),
+        spans: Array<Span> = arrayOf(),
+        paddingStart: Dp = 0.dp,
         paddingTop: Dp = 0.dp,
-        paddingRight: Dp = 0.dp,
+        paddingEnd: Dp = 0.dp,
         paddingBottom: Dp = 0.dp,
         flags: Array<GridFlag> = arrayOf(),
     ): ConstrainedLayoutReference {
@@ -1307,9 +1307,9 @@
             elementArray.add(CLString.from(it.id.toString()))
         }
         val paddingArray = CLArray(charArrayOf()).apply {
-            add(CLNumber(paddingLeft.value))
+            add(CLNumber(paddingStart.value))
             add(CLNumber(paddingTop.value))
-            add(CLNumber(paddingRight.value))
+            add(CLNumber(paddingEnd.value))
             add(CLNumber(paddingBottom.value))
         }
         flags.forEach {
@@ -1324,6 +1324,15 @@
             strColumnWeights = columnWeights.joinToString(",")
         }
 
+        var strSkips = ""
+        var strSpans = ""
+        if (skips.isNotEmpty()) {
+            strSkips = skips.joinToString(",") { it.description }
+        }
+        if (spans.isNotEmpty()) {
+            strSpans = spans.joinToString(",") { it.description }
+        }
+
         ref.asCLContainer().apply {
             put("contains", elementArray)
             putString("type", "grid")
@@ -1335,8 +1344,8 @@
             put("padding", paddingArray)
             putString("rowWeights", strRowWeights)
             putString("columnWeights", strColumnWeights)
-            putString("skips", skips)
-            putString("spans", spans)
+            putString("skips", strSkips)
+            putString("spans", strSpans)
             put("flags", flagArray)
         }
 
@@ -1938,4 +1947,16 @@
         val SpreadInside = FlowStyle("spread_inside")
         val Packed = FlowStyle("packed")
     }
+}
+
+@JvmInline
+value class Skip(val description: String) {
+    constructor(position: Int, rows: Int, columns: Int) : this("$position:${rows}x$columns")
+    constructor(position: Int, size: Int) : this("$position:$size")
+}
+
+@JvmInline
+value class Span(val description: String) {
+    constructor(position: Int, rows: Int, columns: Int) : this("$position:${rows}x$columns")
+    constructor(position: Int, size: Int) : this("$position:$size")
 }
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-core/api/current.txt b/constraintlayout/constraintlayout-core/api/current.txt
index 9fcc11f..fffd4db 100644
--- a/constraintlayout/constraintlayout-core/api/current.txt
+++ b/constraintlayout/constraintlayout-core/api/current.txt
@@ -2601,8 +2601,8 @@
     method public float getHorizontalGaps();
     method public int getOrientation();
     method public int getPaddingBottom();
-    method public int getPaddingLeft();
-    method public int getPaddingRight();
+    method public int getPaddingEnd();
+    method public int getPaddingStart();
     method public int getPaddingTop();
     method public String? getRowWeights();
     method public int getRowsSet();
@@ -2616,8 +2616,8 @@
     method public void setHorizontalGaps(float);
     method public void setOrientation(int);
     method public void setPaddingBottom(int);
-    method public void setPaddingLeft(int);
-    method public void setPaddingRight(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);
diff --git a/constraintlayout/constraintlayout-core/api/public_plus_experimental_current.txt b/constraintlayout/constraintlayout-core/api/public_plus_experimental_current.txt
index 9fcc11f..fffd4db 100644
--- a/constraintlayout/constraintlayout-core/api/public_plus_experimental_current.txt
+++ b/constraintlayout/constraintlayout-core/api/public_plus_experimental_current.txt
@@ -2601,8 +2601,8 @@
     method public float getHorizontalGaps();
     method public int getOrientation();
     method public int getPaddingBottom();
-    method public int getPaddingLeft();
-    method public int getPaddingRight();
+    method public int getPaddingEnd();
+    method public int getPaddingStart();
     method public int getPaddingTop();
     method public String? getRowWeights();
     method public int getRowsSet();
@@ -2616,8 +2616,8 @@
     method public void setHorizontalGaps(float);
     method public void setOrientation(int);
     method public void setPaddingBottom(int);
-    method public void setPaddingLeft(int);
-    method public void setPaddingRight(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);
diff --git a/constraintlayout/constraintlayout-core/api/restricted_current.txt b/constraintlayout/constraintlayout-core/api/restricted_current.txt
index c60c866..8d0e51f 100644
--- a/constraintlayout/constraintlayout-core/api/restricted_current.txt
+++ b/constraintlayout/constraintlayout-core/api/restricted_current.txt
@@ -2604,8 +2604,8 @@
     method public float getHorizontalGaps();
     method public int getOrientation();
     method public int getPaddingBottom();
-    method public int getPaddingLeft();
-    method public int getPaddingRight();
+    method public int getPaddingEnd();
+    method public int getPaddingStart();
     method public int getPaddingTop();
     method public String? getRowWeights();
     method public int getRowsSet();
@@ -2619,8 +2619,8 @@
     method public void setHorizontalGaps(float);
     method public void setOrientation(int);
     method public void setPaddingBottom(int);
-    method public void setPaddingLeft(int);
-    method public void setPaddingRight(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);
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/motion/utils/StopLogicEngine.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/motion/utils/StopLogicEngine.java
index 621fce0..0982b43 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/motion/utils/StopLogicEngine.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/motion/utils/StopLogicEngine.java
@@ -179,10 +179,10 @@
     private void setup(float velocity, float distance, float maxAcceleration, float maxVelocity,
             float maxTime) {
         mDone = false;
+        mStage3EndPosition = distance;
         if (velocity == 0) {
             velocity = 0.0001f;
         }
-        this.mStage1Velocity = velocity;
         float min_time_to_stop = velocity / maxAcceleration;
         float stopDistance = min_time_to_stop * velocity / 2;
 
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java
index 582bb9b..fcc3c51 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java
@@ -1002,17 +1002,17 @@
                     break;
                 case "padding":
                     CLElement paddingObject = element.get(param);
-                    int paddingLeft = 0;
+                    int paddingStart = 0;
                     int paddingTop = 0;
-                    int paddingRight = 0;
+                    int paddingEnd = 0;
                     int paddingBottom = 0;
                     if (paddingObject instanceof CLArray && ((CLArray) paddingObject).size() > 1) {
-                        paddingLeft = ((CLArray) paddingObject).getInt(0);
-                        paddingRight = paddingLeft;
+                        paddingStart = ((CLArray) paddingObject).getInt(0);
+                        paddingEnd = paddingStart;
                         paddingTop = ((CLArray) paddingObject).getInt(1);
                         paddingBottom = paddingTop;
                         if (((CLArray) paddingObject).size() > 2) {
-                            paddingRight = ((CLArray) paddingObject).getInt(2);
+                            paddingEnd = ((CLArray) paddingObject).getInt(2);
                             try {
                                 paddingBottom = ((CLArray) paddingObject).getInt(3);
                             } catch (ArrayIndexOutOfBoundsException e) {
@@ -1021,14 +1021,14 @@
 
                         }
                     } else {
-                        paddingLeft = paddingObject.getInt();
-                        paddingTop = paddingLeft;
-                        paddingRight = paddingLeft;
-                        paddingBottom = paddingLeft;
+                        paddingStart = paddingObject.getInt();
+                        paddingTop = paddingStart;
+                        paddingEnd = paddingStart;
+                        paddingBottom = paddingStart;
                     }
-                    grid.setPaddingLeft(paddingLeft);
+                    grid.setPaddingStart(paddingStart);
                     grid.setPaddingTop(paddingTop);
-                    grid.setPaddingRight(paddingRight);
+                    grid.setPaddingEnd(paddingEnd);
                     grid.setPaddingBottom(paddingBottom);
                     break;
                 case "flags":
@@ -2088,4 +2088,4 @@
         }
         return null;
     }
-}
+}
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/Transition.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/Transition.java
index 877cafd..3bbe216 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/Transition.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/Transition.java
@@ -280,9 +280,17 @@
             float rest = currentPosition + 0.5f * Math.abs(velocity) * velocity / mMaxAcceleration;
             switch (mOnTouchUp) {
                 case ON_UP_AUTOCOMPLETE_TO_START:
+                    if (currentPosition >= 1f) {
+                        return 1;
+                    }
+                    return 0;
                 case ON_UP_NEVER_COMPLETE_TO_END:
                     return 0;
                 case ON_UP_AUTOCOMPLETE_TO_END:
+                    if (currentPosition <= 0f) {
+                        return 0;
+                    }
+                    return 1;
                 case ON_UP_NEVER_COMPLETE_TO_START:
                     return 1;
                 case ON_UP_STOP:
@@ -309,7 +317,14 @@
 
         void config(float position, float velocity, long start, float duration) {
             mStart = start;
+            if (Math.abs(velocity) > mMaxVelocity) {
+                velocity = mMaxVelocity * Math.signum(velocity);
+            }
             mDestination = getDestinationPosition(position, velocity, duration);
+            if (mDestination == position) {
+                mEngine = null;
+                return;
+            }
             if ((mOnTouchUp == ON_UP_DECELERATE)
                     && (mAutoCompleteMode == MODE_CONTINUOUS_VELOCITY)) {
                 StopLogicEngine.Decelerate sld;
@@ -381,7 +396,7 @@
             if (mOnTouchUp == ON_UP_STOP) {
                 return false;
             }
-            return !mEngine.isStopped();
+            return mEngine != null && !mEngine.isStopped();
         }
     }
 
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/GridReference.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/GridReference.java
index 792634e..8942bfc 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/GridReference.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/GridReference.java
@@ -48,14 +48,14 @@
     private GridCore mGrid;
 
     /**
-     * padding left
+     * padding start
      */
-    private int mPaddingLeft = 0;
+    private int mPaddingStart = 0;
 
     /**
-     * padding right
+     * padding end
      */
-    private int mPaddingRight = 0;
+    private int mPaddingEnd = 0;
 
     /**
      * padding top
@@ -121,32 +121,32 @@
      * get padding left
      * @return padding left
      */
-    public int getPaddingLeft() {
-        return mPaddingLeft;
+    public int getPaddingStart() {
+        return mPaddingStart;
     }
 
     /**
      * set padding left
-     * @param paddingLeft padding left to be set
+     * @param paddingStart padding left to be set
      */
-    public void setPaddingLeft(int paddingLeft) {
-        mPaddingLeft = paddingLeft;
+    public void setPaddingStart(int paddingStart) {
+        mPaddingStart = paddingStart;
     }
 
     /**
      * get padding right
      * @return padding right
      */
-    public int getPaddingRight() {
-        return mPaddingRight;
+    public int getPaddingEnd() {
+        return mPaddingEnd;
     }
 
     /**
      * set padding right
-     * @param paddingRight padding right to be set
+     * @param paddingEnd padding right to be set
      */
-    public void setPaddingRight(int paddingRight) {
-        mPaddingRight = paddingRight;
+    public void setPaddingEnd(int paddingEnd) {
+        mPaddingEnd = paddingEnd;
     }
 
     /**
@@ -457,4 +457,4 @@
         // General attributes of a widget
         applyBase();
     }
-}
+}
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout/build.gradle b/constraintlayout/constraintlayout/build.gradle
index 894dfc7..060d756 100644
--- a/constraintlayout/constraintlayout/build.gradle
+++ b/constraintlayout/constraintlayout/build.gradle
@@ -26,7 +26,7 @@
     implementation("androidx.appcompat:appcompat:1.2.0")
     implementation("androidx.core:core:1.3.2")
     implementation(project(":constraintlayout:constraintlayout-core"))
-    implementation("androidx.profileinstaller:profileinstaller:1.2.1")
+    implementation("androidx.profileinstaller:profileinstaller:1.3.0")
 
     testImplementation(libs.junit)
 
diff --git a/coordinatorlayout/coordinatorlayout/src/main/java/androidx/coordinatorlayout/widget/CoordinatorLayout.java b/coordinatorlayout/coordinatorlayout/src/main/java/androidx/coordinatorlayout/widget/CoordinatorLayout.java
index 05a23875..70782d1 100644
--- a/coordinatorlayout/coordinatorlayout/src/main/java/androidx/coordinatorlayout/widget/CoordinatorLayout.java
+++ b/coordinatorlayout/coordinatorlayout/src/main/java/androidx/coordinatorlayout/widget/CoordinatorLayout.java
@@ -115,6 +115,8 @@
         NestedScrollingParent3 {
     static final String TAG = "CoordinatorLayout";
     static final String WIDGET_PACKAGE_NAME;
+    // For the UP/DOWN keys, we scroll 1/10th of the screen.
+    private static final float KEY_SCROLL_FRACTION_AMOUNT = 0.1f;
 
     static {
         final Package pkg = CoordinatorLayout.class.getPackage();
@@ -181,6 +183,13 @@
     // This only exist to prevent GC and object instantiation costs that are present before API 21.
     private final int[] mNestedScrollingV2ConsumedCompat = new int[2];
 
+    // Array to be mutated by calls to nested scrolling related methods triggered by key events.
+    // Because these scrolling events rely on lower level methods using mBehaviorConsumed, we need
+    // a separate variable to save memory. As with the above, this only exist to prevent GC and
+    // object instantiation costs that are
+    // present before API 21.
+    private final int[] mKeyTriggeredScrollConsumed = new int[2];
+
     private boolean mDisallowInterceptReset;
 
     private boolean mIsAttachedToWindow;
@@ -1945,47 +1954,46 @@
             if (event.getAction() == KeyEvent.ACTION_DOWN) {
                 switch (event.getKeyCode()) {
                     case KeyEvent.KEYCODE_DPAD_UP:
-                    case KeyEvent.KEYCODE_DPAD_DOWN:
-                    case KeyEvent.KEYCODE_SPACE:
-
-                        int yScrollDelta;
-
-                        if (event.getKeyCode() == KeyEvent.KEYCODE_SPACE) {
-                            if (event.isShiftPressed()) {
-                                // Places the CoordinatorLayout at the top of the available
-                                // content.
-                                // Note: The delta may represent a value that would overshoot the
-                                // top of the screen, but the children only use as much of the
-                                // delta as they can support, so it will always go exactly to the
-                                // top.
-                                yScrollDelta = -getFullContentHeight();
-                            } else {
-                                // Places the CoordinatorLayout at the bottom of the available
-                                // content.
-                                yScrollDelta = getFullContentHeight() - getHeight();
-                            }
-
-                        } else if (event.isAltPressed()) { // For UP and DOWN KeyEvents
-                            // Full page scroll
-                            yScrollDelta = getHeight();
-
+                        if (event.isAltPressed()) {
+                            // Inverse to move up the screen
+                            handled = moveVertically(-pageDelta());
                         } else {
-                            // Regular arrow scroll
-                            yScrollDelta = (int) (getHeight() * 0.1f);
+                            // Inverse to move up the screen
+                            handled = moveVertically(-lineDelta());
                         }
+                        break;
 
-                        View focusedView = findDeepestFocusedChild(this);
-
-                        // Convert delta to negative if the key event is UP.
-                        if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP) {
-                            yScrollDelta = -yScrollDelta;
+                    case KeyEvent.KEYCODE_DPAD_DOWN:
+                        if (event.isAltPressed()) {
+                            handled = moveVertically(pageDelta());
+                        } else {
+                            handled = moveVertically(lineDelta());
                         }
+                        break;
 
-                        handled = manuallyTriggersNestedScrollFromKeyEvent(
-                                focusedView,
-                                yScrollDelta
-                        );
+                    case KeyEvent.KEYCODE_PAGE_UP:
+                        // Inverse to move up the screen
+                        handled = moveVertically(-pageDelta());
+                        break;
 
+                    case KeyEvent.KEYCODE_PAGE_DOWN:
+                        handled = moveVertically(pageDelta());
+                        break;
+
+                    case KeyEvent.KEYCODE_SPACE:
+                        if (event.isShiftPressed()) {
+                            handled = moveVertically(distanceToTop());
+                        } else {
+                            handled = moveVertically(distanceToBottom());
+                        }
+                        break;
+
+                    case KeyEvent.KEYCODE_MOVE_HOME:
+                        handled = moveVertically(distanceToTop());
+                        break;
+
+                    case KeyEvent.KEYCODE_MOVE_END:
+                        handled = moveVertically(distanceToBottom());
                         break;
                 }
             }
@@ -1994,6 +2002,36 @@
         return handled;
     }
 
+    // Distance for moving one arrow key tap.
+    private int lineDelta() {
+        return (int) (getHeight() * KEY_SCROLL_FRACTION_AMOUNT);
+    }
+
+    private int pageDelta() {
+        return getHeight();
+    }
+
+    private int distanceToTop() {
+        // Note: The delta may represent a value that would overshoot the
+        // top of the screen, but the children only use as much of the
+        // delta as they can support, so it will always go exactly to the
+        // top.
+        return -getFullContentHeight();
+    }
+
+    private int distanceToBottom() {
+        return getFullContentHeight() - getHeight();
+    }
+
+    private boolean moveVertically(int yScrollDelta) {
+        View focusedView = findDeepestFocusedChild(this);
+
+        return manuallyTriggersNestedScrollFromKeyEvent(
+                focusedView,
+                yScrollDelta
+        );
+    }
+
     private View findDeepestFocusedChild(View startingParentView) {
         View focusedView = startingParentView;
         while (focusedView != null) {
@@ -2050,6 +2088,10 @@
                 ViewCompat.TYPE_NON_TOUCH
         );
 
+        // Reset consumed values to zero.
+        mKeyTriggeredScrollConsumed[0] = 0;
+        mKeyTriggeredScrollConsumed[1] = 0;
+
         onNestedScroll(
                 focusedView,
                 0,
@@ -2057,12 +2099,12 @@
                 0,
                 yScrollDelta,
                 ViewCompat.TYPE_NON_TOUCH,
-                mBehaviorConsumed
+                mKeyTriggeredScrollConsumed
         );
 
         onStopNestedScroll(focusedView, ViewCompat.TYPE_NON_TOUCH);
 
-        if (mBehaviorConsumed[1] > 0) {
+        if (mKeyTriggeredScrollConsumed[1] > 0) {
             handled = true;
         }
 
diff --git a/core/core-ktx/build.gradle b/core/core-ktx/build.gradle
index 21bb1d2..b8c9576 100644
--- a/core/core-ktx/build.gradle
+++ b/core/core-ktx/build.gradle
@@ -7,7 +7,7 @@
 }
 
 dependencies {
-    // Atomic group
+    // Atomically versioned.
     constraints {
         implementation(project(":core:core"))
     }
diff --git a/core/core-testing/api/current.txt b/core/core-testing/api/current.txt
new file mode 100644
index 0000000..bca6bb8
--- /dev/null
+++ b/core/core-testing/api/current.txt
@@ -0,0 +1,11 @@
+// Signature format: 4.0
+package androidx.core.testing.util {
+
+  public final class TestConsumer<T> implements androidx.core.util.Consumer<T> {
+    ctor public TestConsumer();
+    method public void accept(T? t);
+    method public void assertValues(java.util.List<? extends T> values);
+  }
+
+}
+
diff --git a/core/core-testing/api/public_plus_experimental_current.txt b/core/core-testing/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..bca6bb8
--- /dev/null
+++ b/core/core-testing/api/public_plus_experimental_current.txt
@@ -0,0 +1,11 @@
+// Signature format: 4.0
+package androidx.core.testing.util {
+
+  public final class TestConsumer<T> implements androidx.core.util.Consumer<T> {
+    ctor public TestConsumer();
+    method public void accept(T? t);
+    method public void assertValues(java.util.List<? extends T> values);
+  }
+
+}
+
diff --git a/ads/ads-identifier/api/res-current.txt b/core/core-testing/api/res-current.txt
similarity index 100%
rename from ads/ads-identifier/api/res-current.txt
rename to core/core-testing/api/res-current.txt
diff --git a/core/core-testing/api/restricted_current.txt b/core/core-testing/api/restricted_current.txt
new file mode 100644
index 0000000..bca6bb8
--- /dev/null
+++ b/core/core-testing/api/restricted_current.txt
@@ -0,0 +1,11 @@
+// Signature format: 4.0
+package androidx.core.testing.util {
+
+  public final class TestConsumer<T> implements androidx.core.util.Consumer<T> {
+    ctor public TestConsumer();
+    method public void accept(T? t);
+    method public void assertValues(java.util.List<? extends T> values);
+  }
+
+}
+
diff --git a/core/core-testing/build.gradle b/core/core-testing/build.gradle
new file mode 100644
index 0000000..fdc6f76
--- /dev/null
+++ b/core/core-testing/build.gradle
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryType
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    // Atomically versioned.
+    constraints {
+        implementation(project(":core:core"))
+    }
+    api(libs.kotlinStdlib)
+    implementation("androidx.annotation:annotation-jvm:1.6.0")
+    api(project(":core:core"))
+
+    testImplementation(libs.testCore)
+    testImplementation(libs.testRunner)
+    testImplementation(libs.junit)
+}
+
+android {
+    namespace "androidx.core.testing"
+}
+
+androidx {
+    name = "androidx.core:core-testing"
+    type = LibraryType.PUBLISHED_LIBRARY
+    mavenVersion = LibraryVersions.CORE
+    inceptionYear = "2023"
+    description = "Write tests using core APIs."
+}
diff --git a/core/core-testing/src/main/java/androidx/core/testing/util/TestConsumer.kt b/core/core-testing/src/main/java/androidx/core/testing/util/TestConsumer.kt
new file mode 100644
index 0000000..14623df
--- /dev/null
+++ b/core/core-testing/src/main/java/androidx/core/testing/util/TestConsumer.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.testing.util
+
+import androidx.annotation.GuardedBy
+import androidx.core.util.Consumer
+import java.util.concurrent.locks.ReentrantLock
+import kotlin.concurrent.withLock
+
+/**
+ * An implementation of [Consumer] to capture values during a test and allows developers to perform
+ * assertions on the values.
+ * @param T the type of the input to the operation
+ */
+class TestConsumer<T> : Consumer<T> {
+
+    private val lock = ReentrantLock()
+
+    @GuardedBy("lock")
+    private val values = mutableListOf<T>()
+
+    /**
+     * Records the value in the order it was received.
+     * @param t the input argument.
+     */
+    override fun accept(t: T) {
+        lock.withLock {
+            values.add(t)
+        }
+    }
+
+    /**
+     * Asserts that the [values] match the received values. This method checks the order and the
+     * elements.
+     * @param values expected to be in the [TestConsumer]
+     * @throws AssertionError if the values do not match the current values.
+     */
+    fun assertValues(values: List<T>) {
+        lock.withLock {
+            if (this.values != values) {
+                throw AssertionError("Expected $values but received ${this.values}")
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/core-testing/src/test/java/androidx/core/testing/util/TestConsumerTest.kt b/core/core-testing/src/test/java/androidx/core/testing/util/TestConsumerTest.kt
new file mode 100644
index 0000000..48e0f9d
--- /dev/null
+++ b/core/core-testing/src/test/java/androidx/core/testing/util/TestConsumerTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.testing.util
+
+import org.junit.Test
+
+class TestConsumerTest {
+
+    private val testConsumer = TestConsumer<Any>()
+
+    @Test
+    fun test_checkingValues_recordsValue() {
+        val values = listOf(Object(), Object(), Object())
+
+        values.forEach(testConsumer::accept)
+
+        testConsumer.assertValues(values)
+    }
+
+    @Test(expected = AssertionError::class)
+    fun test_checkingValues_checksSize() {
+        val values = listOf(Object(), Object(), Object())
+
+        values.forEach(testConsumer::accept)
+
+        testConsumer.assertValues(values.take(2))
+    }
+
+    @Test(expected = AssertionError::class)
+    fun test_checkingValues_checksOrder() {
+        val values = listOf(Object(), Object(), Object())
+
+        values.forEach(testConsumer::accept)
+
+        testConsumer.assertValues(values.reversed())
+    }
+}
\ No newline at end of file
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index 3521e78..45453ce 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -793,6 +793,7 @@
     method public void deleteNotificationChannelGroup(String);
     method public void deleteUnlistedNotificationChannels(java.util.Collection<java.lang.String!>);
     method public static androidx.core.app.NotificationManagerCompat from(android.content.Context);
+    method public java.util.List<android.service.notification.StatusBarNotification!> getActiveNotifications();
     method public static java.util.Set<java.lang.String!> getEnabledListenerPackages(android.content.Context);
     method public int getImportance();
     method public android.app.NotificationChannel? getNotificationChannel(String);
diff --git a/core/core/api/public_plus_experimental_current.txt b/core/core/api/public_plus_experimental_current.txt
index eb3d438..7910e34 100644
--- a/core/core/api/public_plus_experimental_current.txt
+++ b/core/core/api/public_plus_experimental_current.txt
@@ -793,6 +793,7 @@
     method public void deleteNotificationChannelGroup(String);
     method public void deleteUnlistedNotificationChannels(java.util.Collection<java.lang.String!>);
     method public static androidx.core.app.NotificationManagerCompat from(android.content.Context);
+    method public java.util.List<android.service.notification.StatusBarNotification!> getActiveNotifications();
     method public static java.util.Set<java.lang.String!> getEnabledListenerPackages(android.content.Context);
     method public int getImportance();
     method public android.app.NotificationChannel? getNotificationChannel(String);
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index a15cb01..20329ba 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -792,6 +792,21 @@
     field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected androidx.core.app.NotificationCompat.Builder! mBuilder;
   }
 
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final class NotificationCompat.TvExtender implements androidx.core.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.TvExtender();
+    ctor public NotificationCompat.TvExtender(android.app.Notification);
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+    method public String? getChannelId();
+    method public android.app.PendingIntent? getContentIntent();
+    method public android.app.PendingIntent? getDeleteIntent();
+    method public boolean getSuppressShowOverApps();
+    method public boolean isAvailableOnTv();
+    method public androidx.core.app.NotificationCompat.TvExtender setChannelId(String?);
+    method public androidx.core.app.NotificationCompat.TvExtender setContentIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.TvExtender setDeleteIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.TvExtender setSuppressShowOverApps(boolean);
+  }
+
   public static final class NotificationCompat.WearableExtender implements androidx.core.app.NotificationCompat.Extender {
     ctor public NotificationCompat.WearableExtender();
     ctor public NotificationCompat.WearableExtender(android.app.Notification);
@@ -887,6 +902,7 @@
     method public void deleteNotificationChannelGroup(String);
     method public void deleteUnlistedNotificationChannels(java.util.Collection<java.lang.String!>);
     method public static androidx.core.app.NotificationManagerCompat from(android.content.Context);
+    method public java.util.List<android.service.notification.StatusBarNotification!> getActiveNotifications();
     method public static java.util.Set<java.lang.String!> getEnabledListenerPackages(android.content.Context);
     method public int getImportance();
     method public android.app.NotificationChannel? getNotificationChannel(String);
diff --git a/core/core/build.gradle b/core/core/build.gradle
index b9743ef..fa351a1 100644
--- a/core/core/build.gradle
+++ b/core/core/build.gradle
@@ -8,9 +8,10 @@
 }
 
 dependencies {
-    // Atomic group
+    // Atomically versioned.
     constraints {
         implementation(project(":core:core-ktx"))
+        implementation(project(":core:core-testing"))
     }
 
     api("androidx.annotation:annotation:1.6.0")
@@ -34,6 +35,7 @@
     androidTestImplementation(libs.espressoCore, excludes.espresso)
     androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(libs.multidex)
+    androidTestImplementation(libs.testUiautomator)
 
     androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.3.1")
 
diff --git a/core/core/src/androidTest/AndroidManifest.xml b/core/core/src/androidTest/AndroidManifest.xml
index 41caed87..87ce490 100644
--- a/core/core/src/androidTest/AndroidManifest.xml
+++ b/core/core/src/androidTest/AndroidManifest.xml
@@ -14,7 +14,10 @@
   ~ 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <uses-sdk tools:overrideLibrary="android_libs.ub_uiautomator, androidx.test.uiautomator" />
 
     <application
         android:name="androidx.multidex.MultiDexApplication"
@@ -123,6 +126,14 @@
             android:name="androidx.core.app.GetSystemLocalesActivity"
             android:exported="true" />
 
+        <activity
+            android:name="androidx.core.view.inputmethod.ImeBaseSplitTestActivity"
+            android:exported="true" />
+
+        <activity
+            android:name="androidx.core.view.inputmethod.ImeSecondarySplitTestActivity"
+            android:exported="true" />
+
         <activity-alias
             android:name="androidx.core.app.NavUtilsAliasActivity"
             android:targetActivity="androidx.core.app.NavUtilsActivity">
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 c8fb297..eb5c03d 100644
--- a/core/core/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
@@ -3297,6 +3297,114 @@
     }
 
     @Test
+    public void tvExtenderSetGetChannelId() {
+        NotificationCompat.TvExtender tvExtender = new NotificationCompat.TvExtender();
+        tvExtender.setChannelId("My cool channel");
+        assertEquals("My cool channel", tvExtender.getChannelId());
+    }
+
+    @Test
+    public void tvExtenderSetGetContentIntent() {
+        NotificationCompat.TvExtender tvExtender = new NotificationCompat.TvExtender();
+        PendingIntent intent =
+                PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE);
+        tvExtender.setContentIntent(intent);
+        assertEquals(intent, tvExtender.getContentIntent());
+    }
+
+    @Test
+    public void tvExtenderSetGetDeleteIntent() {
+        NotificationCompat.TvExtender tvExtender = new NotificationCompat.TvExtender();
+        PendingIntent intent =
+                PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE);
+        tvExtender.setDeleteIntent(intent);
+        assertEquals(intent, tvExtender.getDeleteIntent());
+    }
+
+    @Test
+    public void tvExtenderSetGetSuppressShowOverApps() {
+        NotificationCompat.TvExtender tvExtender = new NotificationCompat.TvExtender();
+        tvExtender.setSuppressShowOverApps(true);
+        assertTrue(tvExtender.getSuppressShowOverApps());
+        tvExtender.setSuppressShowOverApps(false);
+        assertFalse(tvExtender.getSuppressShowOverApps());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 26)
+    public void tvExtenderSetsExtras() {
+        PendingIntent contentIntent = createIntent("content");
+        PendingIntent deleteIntent = createIntent("delete");
+
+        NotificationCompat.TvExtender tvExtender = new NotificationCompat.TvExtender()
+                .setChannelId("My cool channel")
+                .setContentIntent(contentIntent)
+                .setDeleteIntent(deleteIntent)
+                .setSuppressShowOverApps(true);
+        NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext,
+                "test channel")
+                .setSmallIcon(0)
+                .setContentTitle("title")
+                .setContentText("text");
+
+        builder = tvExtender.extend(builder);
+        Notification notification = builder.build();
+
+        Bundle tvExtensions =
+                notification.extras.getBundle(NotificationCompat.TvExtender.EXTRA_TV_EXTENDER);
+        assertNotNull(tvExtensions);
+        assertEquals("My cool channel",
+                tvExtensions.getString(NotificationCompat.TvExtender.EXTRA_CHANNEL_ID));
+        assertEquals(contentIntent,
+                ((PendingIntent) tvExtensions.getParcelable(
+                        NotificationCompat.TvExtender.EXTRA_CONTENT_INTENT)));
+        assertEquals(deleteIntent,
+                ((PendingIntent) tvExtensions.getParcelable(
+                        NotificationCompat.TvExtender.EXTRA_DELETE_INTENT)));
+        assertTrue(tvExtensions.getBoolean(
+                NotificationCompat.TvExtender.EXTRA_SUPPRESS_SHOW_OVER_APPS));
+        assertTrue(tvExtender.isAvailableOnTv());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 26)
+    public void tvExtenderFromNotification() {
+        // Use TvExtender to set all the information in the Notification.
+        // Note that this relies on the tvExtenderSetsExtras to assure us of correctness.
+        PendingIntent contentIntent = createIntent("content");
+        PendingIntent deleteIntent = createIntent("delete");
+
+        NotificationCompat.TvExtender tvExtender = new NotificationCompat.TvExtender()
+                .setChannelId("My cool channel")
+                .setContentIntent(contentIntent)
+                .setDeleteIntent(deleteIntent)
+                .setSuppressShowOverApps(true);
+        NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext,
+                "test channel")
+                .setSmallIcon(0)
+                .setContentTitle("title")
+                .setContentText("text");
+
+        builder = tvExtender.extend(builder);
+        Notification notification = builder.build();
+
+        // Recovers the tvExtender values from an already-extended Notification.
+        NotificationCompat.TvExtender recoveredExtender =
+                new NotificationCompat.TvExtender(notification);
+        assertEquals("My cool channel", recoveredExtender.getChannelId());
+        assertEquals(contentIntent, recoveredExtender.getContentIntent());
+        assertEquals(deleteIntent, recoveredExtender.getDeleteIntent());
+        assertTrue(recoveredExtender.getSuppressShowOverApps());
+        assertTrue(recoveredExtender.isAvailableOnTv());
+    }
+
+    @Test
+    public void emptyTvExtender() {
+        NotificationCompat.TvExtender tvExtender = new NotificationCompat.TvExtender();
+        assertTrue(tvExtender.isAvailableOnTv());
+    }
+
+    @Test
     public void setBubbleMetadataIntent() {
         IconCompat icon = IconCompat.createWithAdaptiveBitmap(BitmapFactory.decodeResource(
                 mContext.getResources(),
diff --git a/core/core/src/androidTest/java/androidx/core/app/NotificationManagerCompatTest.java b/core/core/src/androidTest/java/androidx/core/app/NotificationManagerCompatTest.java
index c926c0e..6a245b7 100644
--- a/core/core/src/androidTest/java/androidx/core/app/NotificationManagerCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/app/NotificationManagerCompatTest.java
@@ -31,6 +31,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -43,6 +44,7 @@
 import android.media.AudioAttributes;
 import android.net.Uri;
 import android.os.Build;
+import android.service.notification.StatusBarNotification;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -1144,10 +1146,10 @@
                         .build();
         NotificationManagerCompat.NotificationWithIdAndTag n1 =
                 new NotificationManagerCompat.NotificationWithIdAndTag("tag1", 1,
-                notification);
+                        notification);
         NotificationManagerCompat.NotificationWithIdAndTag n2 =
                 new NotificationManagerCompat.NotificationWithIdAndTag(2,
-                notification2);
+                        notification2);
         List<NotificationManagerCompat.NotificationWithIdAndTag> notifications =
                 Arrays.asList(n1, n2);
 
@@ -1157,4 +1159,18 @@
         verify(fakeManager, times(1)).notify(null, 2, notification2);
     }
 
+    @Test
+    public void testGetActiveNotifications() {
+        NotificationManager fakeManager = mock(NotificationManager.class);
+        NotificationManagerCompat notificationManager =
+                new NotificationManagerCompat(fakeManager, mContext);
+
+        List<StatusBarNotification> notifs = notificationManager.getActiveNotifications();
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            verify(fakeManager, times(1)).getActiveNotifications();
+        } else {
+            assertEquals(0, notifs.size());
+        }
+    }
 }
\ No newline at end of file
diff --git a/core/core/src/androidTest/java/androidx/core/location/LocationManagerCompatTest.java b/core/core/src/androidTest/java/androidx/core/location/LocationManagerCompatTest.java
index 324ff49..087a63f 100644
--- a/core/core/src/androidTest/java/androidx/core/location/LocationManagerCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/location/LocationManagerCompatTest.java
@@ -152,58 +152,49 @@
     @SdkSuppress(minSdkVersion = 24)
     @Test
     public void testRegisterGnssMeasurementsCallback_handler() {
+        if (VERSION.SDK_INT == VERSION_CODES.Q) {
+            // Q is very flaky
+            return;
+        }
+
         GnssMeasurementsEvent.Callback callback = new GnssMeasurementsEvent.Callback() {};
 
         // can't do much to test this except check it doesn't crash
-        if (VERSION.SDK_INT == VERSION_CODES.Q) {
-            // Q can be flaky with actual registrations - don't test the result
-            LocationManagerCompat.registerGnssMeasurementsCallback(mLocationManager,
-                    callback, new Handler(Looper.getMainLooper()));
-            LocationManagerCompat.registerGnssMeasurementsCallback(mLocationManager,
-                    Runnable::run,
-                    callback);
-            LocationManagerCompat.registerGnssMeasurementsCallback(mLocationManager,
-                    callback, new Handler(Looper.getMainLooper()));
-        } else {
-            assertTrue(LocationManagerCompat.registerGnssMeasurementsCallback(mLocationManager,
-                    callback, new Handler(Looper.getMainLooper())));
+        assertTrue(LocationManagerCompat.registerGnssMeasurementsCallback(mLocationManager,
+                callback, new Handler(Looper.getMainLooper())));
+        try {
             assertTrue(LocationManagerCompat.registerGnssMeasurementsCallback(mLocationManager,
                     Runnable::run,
                     callback));
             assertTrue(LocationManagerCompat.registerGnssMeasurementsCallback(mLocationManager,
                     callback, new Handler(Looper.getMainLooper())));
+        } finally {
+            LocationManagerCompat.unregisterGnssMeasurementsCallback(mLocationManager, callback);
         }
-
-        LocationManagerCompat.unregisterGnssMeasurementsCallback(mLocationManager, callback);
     }
 
     @SdkSuppress(minSdkVersion = 24)
     @Test
     public void testRegisterGnssMeasurementsCallback_executor() {
+        if (VERSION.SDK_INT == VERSION_CODES.Q) {
+            // Q is very flaky
+            return;
+        }
+
         GnssMeasurementsEvent.Callback callback = new GnssMeasurementsEvent.Callback() {};
 
         // can't do much to test this except check it doesn't crash
-        if (VERSION.SDK_INT == VERSION_CODES.Q) {
-            // Q can be flaky with actual registrations - don't test the result
-            LocationManagerCompat.registerGnssMeasurementsCallback(mLocationManager,
-                    Runnable::run,
-                    callback);
-            LocationManagerCompat.registerGnssMeasurementsCallback(mLocationManager,
-                    callback, new Handler(Looper.getMainLooper()));
-            LocationManagerCompat.registerGnssMeasurementsCallback(mLocationManager,
-                    Runnable::run,
-                    callback);
-        } else {
-            assertTrue(LocationManagerCompat.registerGnssMeasurementsCallback(mLocationManager,
-                    Runnable::run,
-                    callback));
+        assertTrue(LocationManagerCompat.registerGnssMeasurementsCallback(mLocationManager,
+                Runnable::run,
+                callback));
+        try {
             assertTrue(LocationManagerCompat.registerGnssMeasurementsCallback(mLocationManager,
                     callback, new Handler(Looper.getMainLooper())));
             assertTrue(LocationManagerCompat.registerGnssMeasurementsCallback(mLocationManager,
                     Runnable::run,
                     callback));
+        } finally {
+            LocationManagerCompat.unregisterGnssMeasurementsCallback(mLocationManager, callback);
         }
-
-        LocationManagerCompat.unregisterGnssMeasurementsCallback(mLocationManager, callback);
     }
 }
diff --git a/core/core/src/androidTest/java/androidx/core/view/inputmethod/ImeBaseSplitTestActivity.java b/core/core/src/androidTest/java/androidx/core/view/inputmethod/ImeBaseSplitTestActivity.java
new file mode 100644
index 0000000..279d763
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/view/inputmethod/ImeBaseSplitTestActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.view.inputmethod;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.core.R;
+
+@RequiresApi(30)
+public class ImeBaseSplitTestActivity extends Activity {
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ime_base_split_test_activity);
+    }
+}
diff --git a/core/core/src/androidTest/java/androidx/core/view/inputmethod/ImeMultiWindowTest.java b/core/core/src/androidTest/java/androidx/core/view/inputmethod/ImeMultiWindowTest.java
new file mode 100644
index 0000000..1076dfd
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/view/inputmethod/ImeMultiWindowTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.view.inputmethod;
+
+import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Build;
+import android.os.RemoteException;
+import android.support.v4.BaseInstrumentationTestCase;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+
+import androidx.core.view.WindowInsetsCompat;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+import androidx.testutils.PollingCheck;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+@SdkSuppress(minSdkVersion = 30)
+public class ImeMultiWindowTest extends BaseInstrumentationTestCase<ImeBaseSplitTestActivity> {
+
+    private static final long ACTIVITY_LAUNCH_TIMEOUT_MS = 10000;
+    private static final long VISIBILITY_TIMEOUT_MS = 2000;
+    private static final long FIND_OBJECT_TIMEOUT_MS = 5000;
+    private static final long CLICK_DURATION_MS = 200;
+
+    private static final String TEST_APP = "androidx.core.test";
+
+    private Activity mActivity;
+
+    private UiDevice mDevice;
+
+    public ImeMultiWindowTest() {
+        super(ImeBaseSplitTestActivity.class);
+    }
+
+    @Before
+    public void setup() throws RemoteException {
+        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        mDevice.wakeUp();
+        mActivity = mActivityTestRule.getActivity();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 30)
+    public void testImeShowAndHide_splitScreen() {
+        if (Build.VERSION.SDK_INT < 32) {
+            // FLAG_ACTIVITY_LAUNCH_ADJACENT is not support before Sdk 32, using the
+            // GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN instead.
+            InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                    .performGlobalAction(GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
+        }
+
+        // Launch ime test activity in secondary split.
+        Intent intent = new Intent(mActivity, ImeSecondarySplitTestActivity.class)
+                .addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+        mActivity.startActivity(intent);
+
+        assertTrue("Test app is not visible after launching activity",
+                mDevice.wait(Until.hasObject(By.pkg(TEST_APP)), ACTIVITY_LAUNCH_TIMEOUT_MS));
+
+        UiObject2 editText = waitForFindObject("edit_text_id");
+        editText.click(CLICK_DURATION_MS);
+
+        WindowManager wm = mActivity.getSystemService(WindowManager.class);
+        PollingCheck.waitFor(VISIBILITY_TIMEOUT_MS, () -> {
+            WindowInsets insets = wm.getCurrentWindowMetrics().getWindowInsets();
+            return insets.isVisible(WindowInsetsCompat.Type.ime());
+        });
+
+        UiObject2 hideImeButton = waitForFindObject("hide_ime_id");
+        hideImeButton.click();
+
+        PollingCheck.waitFor(VISIBILITY_TIMEOUT_MS, () -> {
+            WindowInsets insets = wm.getCurrentWindowMetrics().getWindowInsets();
+            return !insets.isVisible(WindowInsetsCompat.Type.ime());
+        });
+    }
+
+    private UiObject2 waitForFindObject(String resId) {
+        final UiObject2 object =
+                mDevice.wait(Until.findObject(By.res(TEST_APP, resId)), FIND_OBJECT_TIMEOUT_MS);
+        assertNotNull("Find object fail", object);
+        return object;
+    }
+}
diff --git a/core/core/src/androidTest/java/androidx/core/view/inputmethod/ImeSecondarySplitTestActivity.java b/core/core/src/androidTest/java/androidx/core/view/inputmethod/ImeSecondarySplitTestActivity.java
new file mode 100644
index 0000000..1820e0e
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/view/inputmethod/ImeSecondarySplitTestActivity.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.view.inputmethod;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.EditText;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.core.R;
+import androidx.core.view.WindowCompat;
+import androidx.core.view.WindowInsetsCompat;
+import androidx.core.view.WindowInsetsControllerCompat;
+
+@RequiresApi(30)
+public class ImeSecondarySplitTestActivity extends Activity {
+
+    EditText mEditText;
+
+    Button mHideImeButton;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ime_secondary_split_test_activity);
+        mEditText = findViewById(R.id.edit_text_id);
+        mHideImeButton = findViewById(R.id.hide_ime_id);
+        mHideImeButton.setOnClickListener(view -> hideIme());
+    }
+
+    private void hideIme() {
+        // Use WindowInsetsControllerCompat to hide ime.
+        WindowInsetsControllerCompat insetsController =
+                WindowCompat.getInsetsController(getWindow(), mEditText);
+        insetsController.hide(WindowInsetsCompat.Type.ime());
+    }
+}
diff --git a/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewWithCollapsingToolbarTest.java b/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewWithCollapsingToolbarTest.java
index 74a1306..2885cc3 100644
--- a/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewWithCollapsingToolbarTest.java
+++ b/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewWithCollapsingToolbarTest.java
@@ -28,6 +28,7 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
@@ -277,7 +278,229 @@
         // Assert
         // Should trigger a scroll event in parent. Note: OnStartNestedScroll is triggered on
         // key action down only, not key action up, so that is why the count is one.
-        // Should trigger in parent of scroll event.
+        assertEquals(1, mParentNestedScrollView.getOnStartNestedScrollCount());
+        // Should not trigger in child (because child doesn't have its own inner NestedScrollView).
+        assertEquals(0, mChildNestedScrollView.getOnStartNestedScrollCount());
+    }
+
+    @Test
+    public void isOnStartNestedScrollCalled_keyboardPageDownInChild_calledInParent() {
+        // Arrange
+        setupNestedScrollViewInNestedScrollView(
+                ApplicationProvider.getApplicationContext(),
+                100,
+                600);
+
+        // Act
+        mChildNestedScrollView.requestFocus();
+        KeyEvent keyEventPressDown = new KeyEvent(
+                0,
+                0,
+                KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_PAGE_DOWN,
+                0);
+        mChildNestedScrollView.executeKeyEvent(keyEventPressDown);
+
+        KeyEvent keyEventPressUp = new KeyEvent(
+                0,
+                0,
+                KeyEvent.ACTION_UP,
+                KeyEvent.KEYCODE_PAGE_DOWN,
+                0);
+        mChildNestedScrollView.executeKeyEvent(keyEventPressUp);
+
+        // Assert
+        // Should trigger a scroll event in parent. Note: OnStartNestedScroll is triggered on
+        // key action down only, not key action up, so that is why the count is one.
+        assertEquals(1, mParentNestedScrollView.getOnStartNestedScrollCount());
+        // Should not trigger in child (because child doesn't have its own inner NestedScrollView).
+        assertEquals(0, mChildNestedScrollView.getOnStartNestedScrollCount());
+    }
+
+    @Test
+    public void isOnStartNestedScrollCalled_keyboardPageUpInChild_calledInParent() {
+        // Arrange
+        setupNestedScrollViewInNestedScrollView(
+                ApplicationProvider.getApplicationContext(),
+                100,
+                600);
+
+        // Move to bottom of the child NestedScrollView, so we can scroll up and not go past child.
+        int scrollRange = mChildNestedScrollView.getScrollRange();
+        mChildNestedScrollView.scrollTo(0, scrollRange);
+
+        // Act
+        mChildNestedScrollView.requestFocus();
+        KeyEvent keyEventPressDown = new KeyEvent(
+                0,
+                0,
+                KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_PAGE_UP,
+                0);
+        mChildNestedScrollView.executeKeyEvent(keyEventPressDown);
+
+        KeyEvent keyEventPressUp = new KeyEvent(
+                0,
+                0,
+                KeyEvent.ACTION_UP,
+                KeyEvent.KEYCODE_PAGE_UP,
+                0);
+        mChildNestedScrollView.executeKeyEvent(keyEventPressUp);
+
+        // Assert
+        // Should trigger a scroll event in parent. Note: OnStartNestedScroll is triggered on
+        // key action down only, not key action up, so that is why the count is one.
+        assertEquals(1, mParentNestedScrollView.getOnStartNestedScrollCount());
+        // Should not trigger in child (because child doesn't have its own inner NestedScrollView).
+        assertEquals(0, mChildNestedScrollView.getOnStartNestedScrollCount());
+    }
+
+    @Test
+    public void isOnStartNestedScrollCalled_keyboardMoveEndInChild_calledInParent() {
+        // Arrange
+        setupNestedScrollViewInNestedScrollView(
+                ApplicationProvider.getApplicationContext(),
+                100,
+                600);
+
+        // Act
+        mChildNestedScrollView.requestFocus();
+        KeyEvent keyEventPressDown = new KeyEvent(
+                0,
+                0,
+                KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_MOVE_END,
+                0);
+        mChildNestedScrollView.executeKeyEvent(keyEventPressDown);
+
+        KeyEvent keyEventPressUp = new KeyEvent(
+                0,
+                0,
+                KeyEvent.ACTION_UP,
+                KeyEvent.KEYCODE_MOVE_END,
+                0);
+        mChildNestedScrollView.executeKeyEvent(keyEventPressUp);
+
+        // Assert
+        // Should trigger a scroll event in parent. Note: OnStartNestedScroll is triggered on
+        // key action down only, not key action up, so that is why the count is one.
+        assertEquals(1, mParentNestedScrollView.getOnStartNestedScrollCount());
+        // Should not trigger in child (because child doesn't have its own inner NestedScrollView).
+        assertEquals(0, mChildNestedScrollView.getOnStartNestedScrollCount());
+    }
+
+
+    @Test
+    public void isOnStartNestedScrollCalled_keyboardMoveHomeInChild_calledInParent() {
+        // Arrange
+        setupNestedScrollViewInNestedScrollView(
+                ApplicationProvider.getApplicationContext(),
+                100,
+                600);
+
+        // Move to bottom of the child NestedScrollView, so we can scroll up and not go past child.
+        int scrollRange = mChildNestedScrollView.getScrollRange();
+        mChildNestedScrollView.scrollTo(0, scrollRange);
+
+        // Act
+        mChildNestedScrollView.requestFocus();
+        KeyEvent keyEventPressDown = new KeyEvent(
+                0,
+                0,
+                KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_MOVE_HOME,
+                0);
+        mChildNestedScrollView.executeKeyEvent(keyEventPressDown);
+
+        KeyEvent keyEventPressUp = new KeyEvent(
+                0,
+                0,
+                KeyEvent.ACTION_UP,
+                KeyEvent.KEYCODE_MOVE_HOME,
+                0);
+        mChildNestedScrollView.executeKeyEvent(keyEventPressUp);
+
+        // Assert
+        // Should trigger a scroll event in parent. Note: OnStartNestedScroll is triggered on
+        // key action down only, not key action up, so that is why the count is one.
+        assertEquals(1, mParentNestedScrollView.getOnStartNestedScrollCount());
+        // Should not trigger in child (because child doesn't have its own inner NestedScrollView).
+        assertEquals(0, mChildNestedScrollView.getOnStartNestedScrollCount());
+    }
+
+
+    @Test
+    public void isOnStartNestedScrollCalled_keyboardSpaceBarInChild_calledInParent() {
+        // Arrange
+        setupNestedScrollViewInNestedScrollView(
+                ApplicationProvider.getApplicationContext(),
+                100,
+                600);
+
+        // Act
+        mChildNestedScrollView.requestFocus();
+        KeyEvent keyEventPressDown = new KeyEvent(
+                0,
+                0,
+                KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_SPACE,
+                0);
+        mChildNestedScrollView.executeKeyEvent(keyEventPressDown);
+
+        KeyEvent keyEventPressUp = new KeyEvent(
+                0,
+                0,
+                KeyEvent.ACTION_UP,
+                KeyEvent.KEYCODE_SPACE,
+                0);
+        mChildNestedScrollView.executeKeyEvent(keyEventPressUp);
+
+        // Assert
+        // Should trigger a scroll event in parent. Note: OnStartNestedScroll is triggered on
+        // key action down only, not key action up, so that is why the count is one.
+        assertEquals(1, mParentNestedScrollView.getOnStartNestedScrollCount());
+        // Should not trigger in child (because child doesn't have its own inner NestedScrollView).
+        assertEquals(0, mChildNestedScrollView.getOnStartNestedScrollCount());
+    }
+
+
+    @Test
+    public void isOnStartNestedScrollCalled_keyboardShiftSpaceBarInChild_calledInParent() {
+        // Arrange
+        setupNestedScrollViewInNestedScrollView(
+                ApplicationProvider.getApplicationContext(),
+                100,
+                600);
+
+        // Move to bottom of the child NestedScrollView, so we can scroll up and not go past child.
+        int scrollRange = mChildNestedScrollView.getScrollRange();
+        mChildNestedScrollView.scrollTo(0, scrollRange);
+
+        // Act
+        mChildNestedScrollView.requestFocus();
+        KeyEvent keyEventPressDown = new KeyEvent(
+                0,
+                0,
+                KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_SPACE,
+                0,
+                KeyEvent.META_SHIFT_ON | KeyEvent.META_SHIFT_LEFT_ON
+        );
+        mChildNestedScrollView.executeKeyEvent(keyEventPressDown);
+
+        KeyEvent keyEventPressUp = new KeyEvent(
+                0,
+                0,
+                KeyEvent.ACTION_UP,
+                KeyEvent.KEYCODE_SPACE,
+                0,
+                KeyEvent.META_SHIFT_ON | KeyEvent.META_SHIFT_LEFT_ON
+        );
+        mChildNestedScrollView.executeKeyEvent(keyEventPressUp);
+
+        // Assert
+        // Should trigger a scroll event in parent. Note: OnStartNestedScroll is triggered on
+        // key action down only, not key action up, so that is why the count is one.
         assertEquals(1, mParentNestedScrollView.getOnStartNestedScrollCount());
         // Should not trigger in child (because child doesn't have its own inner NestedScrollView).
         assertEquals(0, mChildNestedScrollView.getOnStartNestedScrollCount());
@@ -321,8 +544,6 @@
         mChildNestedScrollView.executeKeyEvent(keyEventPressUp);
 
         // Assert
-        // Should trigger in parent of scroll event. Note: OnStartNestedScroll is triggered on
-        // key action down only, not key action up, so that is why the count is one.
         assertEquals(0, mParentNestedScrollView.getOnStartNestedScrollCount());
         // Should not trigger in child (because child doesn't have its own inner NestedScrollView).
         assertEquals(0, mChildNestedScrollView.getOnStartNestedScrollCount());
@@ -358,9 +579,6 @@
         mChildNestedScrollView.executeKeyEvent(keyEventPressUp);
 
         // Assert
-        // Should trigger in parent of scroll event. Note: OnStartNestedScroll is triggered on
-        // key action down only, not key action up, so that is why the count is one.
-        // Should trigger in parent of scroll event.
         assertEquals(0, mParentNestedScrollView.getOnStartNestedScrollCount());
         // Should not trigger in child (because child doesn't have its own inner NestedScrollView).
         assertEquals(0, mChildNestedScrollView.getOnStartNestedScrollCount());
@@ -397,9 +615,6 @@
         mChildNestedScrollView.executeKeyEvent(keyEventPressUp);
 
         // Assert
-        // Should trigger in parent of scroll event. Note: OnStartNestedScroll is triggered on
-        // key action down only, not key action up, so that is why the count is one.
-        // Should trigger in parent of scroll event.
         assertEquals(0, mParentNestedScrollView.getOnStartNestedScrollCount());
         // Should not trigger in child (because child doesn't have its own inner NestedScrollView).
         assertEquals(0, mChildNestedScrollView.getOnStartNestedScrollCount());
@@ -438,9 +653,77 @@
         mChildNestedScrollView.executeKeyEvent(keyEventPressUp);
 
         // Assert
-        // Should trigger in parent of scroll event. Note: OnStartNestedScroll is triggered on
-        // key action down only, not key action up, so that is why the count is one.
-        // Should trigger in parent of scroll event.
+        assertEquals(0, mParentNestedScrollView.getOnStartNestedScrollCount());
+        // Should not trigger in child (because child doesn't have its own inner NestedScrollView).
+        assertEquals(0, mChildNestedScrollView.getOnStartNestedScrollCount());
+    }
+
+
+    @Test
+    public void isOnStartNestedScrollCalled_keyboardPageUpInChildPastTop_notCalledInParent() {
+        // Arrange
+        setupNestedScrollViewInNestedScrollView(
+                ApplicationProvider.getApplicationContext(),
+                100,
+                600);
+
+        // Act
+        mChildNestedScrollView.requestFocus();
+        KeyEvent keyEventPressPageDown = new KeyEvent(
+                0,
+                0,
+                KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_PAGE_UP,
+                0
+        );
+        mChildNestedScrollView.executeKeyEvent(keyEventPressPageDown);
+
+        KeyEvent keyEventPressPageUp = new KeyEvent(
+                0,
+                0,
+                KeyEvent.ACTION_UP,
+                KeyEvent.KEYCODE_PAGE_UP,
+                0
+        );
+        mChildNestedScrollView.executeKeyEvent(keyEventPressPageUp);
+
+        // Assert
+        assertEquals(0, mParentNestedScrollView.getOnStartNestedScrollCount());
+        // Should not trigger in child (because child doesn't have its own inner NestedScrollView).
+        assertEquals(0, mChildNestedScrollView.getOnStartNestedScrollCount());
+    }
+
+    @Test
+    public void isOnStartNestedScrollCalled_keyboardPageDownInChildPastBottom_notCalledInParent() {
+        // Arrange
+        setupNestedScrollViewInNestedScrollView(
+                ApplicationProvider.getApplicationContext(),
+                100,
+                600);
+        // Move to bottom of the child NestedScrollView, so we can try scrolling past it.
+        int scrollRange = mChildNestedScrollView.getScrollRange();
+        mChildNestedScrollView.scrollTo(0, scrollRange);
+
+        mChildNestedScrollView.requestFocus();
+        KeyEvent keyEventPressDown = new KeyEvent(
+                0,
+                0,
+                KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_PAGE_DOWN,
+                0
+        );
+        mChildNestedScrollView.executeKeyEvent(keyEventPressDown);
+
+        KeyEvent keyEventPressUp = new KeyEvent(
+                0,
+                0,
+                KeyEvent.ACTION_UP,
+                KeyEvent.KEYCODE_PAGE_DOWN,
+                0
+        );
+        mChildNestedScrollView.executeKeyEvent(keyEventPressUp);
+
+        // Assert
         assertEquals(0, mParentNestedScrollView.getOnStartNestedScrollCount());
         // Should not trigger in child (because child doesn't have its own inner NestedScrollView).
         assertEquals(0, mChildNestedScrollView.getOnStartNestedScrollCount());
@@ -475,9 +758,6 @@
         mChildNestedScrollView.executeKeyEvent(keyEventPressUp);
 
         // Assert
-        // Should trigger in parent of scroll event. Note: OnStartNestedScroll is triggered on
-        // key action down only, not key action up, so that is why the count is one.
-        // Should trigger in parent of scroll event.
         assertEquals(0, mParentNestedScrollView.getOnStartNestedScrollCount());
         // Should not trigger in child (because child doesn't have its own inner NestedScrollView).
         assertEquals(0, mChildNestedScrollView.getOnStartNestedScrollCount());
@@ -513,9 +793,6 @@
         mChildNestedScrollView.executeKeyEvent(keyEventPressUp);
 
         // Assert
-        // Should trigger in parent of scroll event. Note: OnStartNestedScroll is triggered on
-        // key action down only, not key action up, so that is why the count is one.
-        // Should trigger in parent of scroll event.
         assertEquals(0, mParentNestedScrollView.getOnStartNestedScrollCount());
         // Should not trigger in child (because child doesn't have its own inner NestedScrollView).
         assertEquals(0, mChildNestedScrollView.getOnStartNestedScrollCount());
@@ -701,7 +978,12 @@
         }
 
         @Override
-        public boolean onStartNestedScroll(View child, View target, int axes, int type) {
+        public boolean onStartNestedScroll(
+                @NonNull View child,
+                @NonNull View target,
+                int axes,
+                int type
+        ) {
             mOnStartNestedScrollCount++;
             return super.onStartNestedScroll(child, target, axes, type);
         }
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 124fa91..745fdaf 100644
--- a/core/core/src/main/java/androidx/core/app/NotificationCompat.java
+++ b/core/core/src/main/java/androidx/core/app/NotificationCompat.java
@@ -4280,12 +4280,14 @@
              * where the platform doesn't support the MIME type, the original text provided in the
              * constructor will be used.
              *
-             * @param dataMimeType The MIME type of the content
+             * @param dataMimeType The MIME type of the content. See
+             * {@link android.graphics.ImageDecoder#isMimeTypeSupported(String)}
+             * for a list of supported image MIME types.
              * @param dataUri The uri containing the content whose type is given by the MIME type.
              * <p class="note">
+             * Notification Listeners including the System UI need permission to access the
+             * data the Uri points to. The recommended ways to do this are:
              * <ol>
-             *   <li>Notification Listeners including the System UI need permission to access the
-             *       data the Uri points to. The recommended ways to do this are:</li>
              *   <li>Store the data in your own ContentProvider, making sure that other apps have
              *       the correct permission to access your provider. The preferred mechanism for
              *       providing access is to use per-URI permissions which are temporary and only
@@ -8429,6 +8431,217 @@
     }
 
     /**
+     * <p>Helper class to add Android TV extensions to notifications. To create a notification
+     * with a TV extension:
+     *
+     * <ol>
+     *  <li>Create an {@link NotificationCompat.Builder}, setting any desired properties.
+     *  <li>Create a {@link TvExtender}.
+     *  <li>Set TV-specific properties using the {@code set} methods of
+     *  {@link TvExtender}.
+     *  <li>Call {@link NotificationCompat.Builder#extend(NotificationCompat.Extender)}
+     *  to apply the extension to a notification.
+     * </ol>
+     *
+     * <pre class="prettyprint">
+     * Notification notification = new NotificationCompat.Builder(context)
+     *         ...
+     *         .extend(new TvExtender()
+     *                 .set*(...))
+     *         .build();
+     * </pre>
+     *
+     * <p>TV extensions can be accessed on an existing notification by using the
+     * {@code TvExtender(Notification)} constructor, and then using the {@code get} methods
+     * to access values.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    public static final class TvExtender implements Extender {
+        private static final String TAG = "TvExtender";
+
+        /** @hide **/
+        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
+
+        /** @hide **/
+        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        private static final String EXTRA_FLAGS = "flags";
+        /** @hide **/
+        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        static final String EXTRA_CONTENT_INTENT = "content_intent";
+        /** @hide **/
+        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        static final String EXTRA_DELETE_INTENT = "delete_intent";
+        /** @hide **/
+        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        static final String EXTRA_CHANNEL_ID = "channel_id";
+        /** @hide **/
+        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        static final String EXTRA_SUPPRESS_SHOW_OVER_APPS = "suppressShowOverApps";
+
+        // Flags bitwise-ored to mFlags
+        private static final int FLAG_AVAILABLE_ON_TV = 0x1;
+
+        private int mFlags;
+        private String mChannelId;
+        private PendingIntent mContentIntent;
+        private PendingIntent mDeleteIntent;
+        private boolean mSuppressShowOverApps;
+
+        /**
+         * Create a {@link TvExtender} with default options.
+         */
+        public TvExtender() {
+            mFlags = FLAG_AVAILABLE_ON_TV;
+        }
+
+        /**
+         * Create a {@link TvExtender} from the TvExtender options of an existing Notification.
+         *
+         * @param notif The notification from which to copy options.
+         */
+        public TvExtender(@NonNull Notification notif) {
+            // TvExtender was introduced in API level 26; note that before API level 26, the extras
+            // added by TvExtender are not expected to be used; thus, we avoid setting them to save
+            // memory.
+            if (Build.VERSION.SDK_INT < 26) {
+                return;
+            }
+
+            Bundle tvBundle = notif.extras == null
+                    ? null : notif.extras.getBundle(EXTRA_TV_EXTENDER);
+            if (tvBundle != null) {
+                mFlags = tvBundle.getInt(EXTRA_FLAGS);
+                mChannelId = tvBundle.getString(EXTRA_CHANNEL_ID);
+                mSuppressShowOverApps = tvBundle.getBoolean(EXTRA_SUPPRESS_SHOW_OVER_APPS);
+                mContentIntent = (PendingIntent) tvBundle.getParcelable(EXTRA_CONTENT_INTENT);
+                mDeleteIntent = (PendingIntent) tvBundle.getParcelable(EXTRA_DELETE_INTENT);
+            }
+        }
+
+        /**
+         * Apply a TV extension to a notification that is being built. This is typically called by
+         * the
+         * {@link androidx.core.app.NotificationCompat.Builder#extend(NotificationCompat.Extender)}
+         * method of {@link NotificationCompat.Builder}.
+         */
+        @Override
+        @NonNull
+        public NotificationCompat.Builder extend(@NonNull NotificationCompat.Builder builder) {
+            // TvExtender was introduced in API level 26; note that before API level 26, the extras
+            // added by TvExtender are not expected to be used; thus, we avoid setting them to save
+            // memory.
+            if (Build.VERSION.SDK_INT < 26) {
+                return builder;
+            }
+
+            Bundle tvExtensions = new Bundle();
+
+            tvExtensions.putInt(EXTRA_FLAGS, mFlags);
+            tvExtensions.putString(EXTRA_CHANNEL_ID, mChannelId);
+            tvExtensions.putBoolean(EXTRA_SUPPRESS_SHOW_OVER_APPS, mSuppressShowOverApps);
+            if (mContentIntent != null) {
+                tvExtensions.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
+            }
+
+            if (mDeleteIntent != null) {
+                tvExtensions.putParcelable(EXTRA_DELETE_INTENT, mDeleteIntent);
+            }
+
+            // Extras was added in API level 19.
+            builder.getExtras().putBundle(EXTRA_TV_EXTENDER, tvExtensions);
+            return builder;
+        }
+
+        /**
+         * Returns true if this notification should be shown on TV. This method return true
+         * if the notification was extended with a TvExtender.
+         */
+        public boolean isAvailableOnTv() {
+            return (mFlags & FLAG_AVAILABLE_ON_TV) != 0;
+        }
+
+        /**
+         * Specifies the channel the notification should be delivered on when shown on TV.
+         * It can be different from the channel that the notification is delivered to when
+         * posting on a non-TV device.
+         *
+         * @param channelId The channelId to use in the tv notification.
+         * @return The object for method chaining.
+         */
+        public @NonNull TvExtender setChannelId(@Nullable String channelId) {
+            mChannelId = channelId;
+            return this;
+        }
+
+        /**
+         * Returns the id of the channel this notification posts to on TV.
+         */
+        public @Nullable String getChannelId() {
+            return mChannelId;
+        }
+
+        /**
+         * Supplies a {@link PendingIntent} to be sent when the notification is selected on TV.
+         * If provided, it is used instead of the content intent specified
+         * at the level of Notification.
+         */
+        public @NonNull TvExtender setContentIntent(@Nullable PendingIntent intent) {
+            mContentIntent = intent;
+            return this;
+        }
+
+        /**
+         * Returns the TV-specific content intent.  If this method returns null, the
+         * main content intent on the notification should be used.
+         *
+         * @see {@link Notification#contentIntent}
+         */
+        public @Nullable PendingIntent getContentIntent() {
+            return mContentIntent;
+        }
+
+        /**
+         * Supplies a {@link PendingIntent} to send when the notification is cleared explicitly
+         * by the user on TV.  If provided, it is used instead of the delete intent specified
+         * at the level of Notification.
+         */
+        public @NonNull TvExtender setDeleteIntent(@Nullable PendingIntent intent) {
+            mDeleteIntent = intent;
+            return this;
+        }
+
+        /**
+         * Returns the TV-specific delete intent.  If this method returns null, the
+         * main delete intent on the notification should be used.
+         *
+         * @see {@link Notification#deleteIntent}
+         */
+        public @Nullable PendingIntent getDeleteIntent() {
+            return mDeleteIntent;
+        }
+
+        /**
+         * Specifies whether this notification should suppress showing a message over top of apps
+         * outside of the launcher.
+         */
+        public @NonNull TvExtender setSuppressShowOverApps(boolean suppress) {
+            mSuppressShowOverApps = suppress;
+            return this;
+        }
+
+        /**
+         * Returns true if this notification should not show messages over top of apps
+         * outside of the launcher.
+         */
+        public boolean getSuppressShowOverApps() {
+            return mSuppressShowOverApps;
+        }
+    }
+
+    /**
      * Encapsulates the information needed to display a notification as a bubble.
      *
      * <p>A bubble is used to display app content in a floating window over the existing
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 973c9ef..4e5e902 100644
--- a/core/core/src/main/java/androidx/core/app/NotificationManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/app/NotificationManagerCompat.java
@@ -42,6 +42,7 @@
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
 import android.support.v4.app.INotificationSideChannel;
 import android.util.Log;
 
@@ -59,6 +60,7 @@
 import java.lang.reflect.Method;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -284,6 +286,36 @@
     }
 
     /**
+     * Recover a list of active notifications: ones that have been posted by the calling app that
+     * have not yet been dismissed by the user or {@link #cancel(String, int)}ed by the app.
+     *
+     * <p><Each notification is embedded in a {@link StatusBarNotification} object, including the
+     * original <code>tag</code> and <code>id</code> supplied to
+     * {@link #notify(String, int, Notification) notify()}
+     * (via {@link StatusBarNotification#getTag() getTag()} and
+     * {@link StatusBarNotification#getId() getId()}) as well as a copy of the original
+     * {@link Notification} object (via {@link StatusBarNotification#getNotification()}).
+     * </p>
+     * <p>From {@link Build.VERSION_CODES#Q}, will also return notifications you've posted as an
+     * app's notification delegate via
+     * {@link NotificationManager#notifyAsPackage(String, String, int, Notification)}.
+     * </p>
+     * <p>
+     *     Returns an empty list on {@link Build.VERSION_CODES#LOLLIPOP_MR1} and earlier.
+     * </p>
+     *
+     * @return A list of {@link StatusBarNotification}.
+     */
+    @NonNull
+    public List<StatusBarNotification> getActiveNotifications() {
+        if (Build.VERSION.SDK_INT >= 23) {
+            return Api23Impl.getActiveNotifications(mNotificationManager);
+        } else {
+            return new ArrayList<>();
+        }
+    }
+
+    /**
      * Returns whether notifications from the calling package are not blocked.
      */
     public boolean areNotificationsEnabled() {
@@ -1111,7 +1143,27 @@
     }
 
     /**
-     * A class for wrapping calls to {@link Notification.Builder} methods which
+     * A class for wrapping calls to {@link NotificationManager} methods which
+     * were added in API 23; these calls must be wrapped to avoid performance issues.
+     * See the UnsafeNewApiCall lint rule for more details.
+     */
+    @RequiresApi(23)
+    static class Api23Impl {
+        private Api23Impl() { }
+
+        @DoNotInline
+        static List<StatusBarNotification> getActiveNotifications(
+                NotificationManager notificationManager) {
+            StatusBarNotification[] notifs = notificationManager.getActiveNotifications();
+            if (notifs == null) {
+                return new ArrayList<>();
+            }
+            return Arrays.asList(notifs);
+        }
+    }
+
+    /**
+     * A class for wrapping calls to {@link NotificationManager} methods which
      * were added in API 24; these calls must be wrapped to avoid performance issues.
      * See the UnsafeNewApiCall lint rule for more details.
      */
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 c7e6448..ccec82c 100644
--- a/core/core/src/main/java/androidx/core/location/LocationManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/location/LocationManagerCompat.java
@@ -400,14 +400,7 @@
                     callback);
         } else {
             synchronized (GnssListenersHolder.sGnssMeasurementListeners) {
-                GnssMeasurementsEvent.Callback oldTransport =
-                        GnssListenersHolder.sGnssMeasurementListeners.remove(callback);
-                if (oldTransport != null) {
-                    if (oldTransport instanceof GnssMeasurementsTransport) {
-                        ((GnssMeasurementsTransport) oldTransport).unregister();
-                    }
-                    Api24Impl.unregisterGnssMeasurementsCallback(locationManager, oldTransport);
-                }
+                unregisterGnssMeasurementsCallback(locationManager, callback);
                 if (Api24Impl.registerGnssMeasurementsCallback(locationManager, callback,
                         handler)) {
                     GnssListenersHolder.sGnssMeasurementListeners.put(callback, callback);
@@ -443,14 +436,7 @@
             synchronized (GnssListenersHolder.sGnssMeasurementListeners) {
                 GnssMeasurementsTransport newTransport = new GnssMeasurementsTransport(callback,
                         executor);
-                GnssMeasurementsEvent.Callback oldTransport =
-                        GnssListenersHolder.sGnssMeasurementListeners.remove(callback);
-                if (oldTransport != null) {
-                    if (oldTransport instanceof GnssMeasurementsTransport) {
-                        ((GnssMeasurementsTransport) oldTransport).unregister();
-                    }
-                    Api24Impl.unregisterGnssMeasurementsCallback(locationManager, oldTransport);
-                }
+                unregisterGnssMeasurementsCallback(locationManager, callback);
                 if (Api24Impl.registerGnssMeasurementsCallback(locationManager, newTransport)) {
                     GnssListenersHolder.sGnssMeasurementListeners.put(callback, newTransport);
                     return true;
@@ -468,7 +454,20 @@
     @RequiresApi(VERSION_CODES.N)
     public static void unregisterGnssMeasurementsCallback(@NonNull LocationManager locationManager,
             @NonNull GnssMeasurementsEvent.Callback callback) {
-        Api24Impl.unregisterGnssMeasurementsCallback(locationManager, callback);
+        if (VERSION.SDK_INT >= VERSION_CODES.R) {
+            Api24Impl.unregisterGnssMeasurementsCallback(locationManager, callback);
+        } else {
+            synchronized (GnssListenersHolder.sGnssMeasurementListeners) {
+                GnssMeasurementsEvent.Callback transport =
+                        GnssListenersHolder.sGnssMeasurementListeners.remove(callback);
+                if (transport != null) {
+                    if (transport instanceof GnssMeasurementsTransport) {
+                        ((GnssMeasurementsTransport) transport).unregister();
+                    }
+                    Api24Impl.unregisterGnssMeasurementsCallback(locationManager, transport);
+                }
+            }
+        }
     }
 
     // Android R without QPR1 has a bug where the default version of this method will always
diff --git a/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java b/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java
index 733521d..6dae4cb 100644
--- a/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java
+++ b/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java
@@ -44,6 +44,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Provide simple controls of windows that generate insets.
@@ -660,6 +661,24 @@
 
         @Override
         void hide(@InsetsType int types) {
+            if (mWindow != null && (types & WindowInsetsCompat.Type.IME) != 0 && SDK_INT <= 33) {
+                final AtomicBoolean isImeInsetsControllable = new AtomicBoolean(false);
+                final WindowInsetsController.OnControllableInsetsChangedListener listener =
+                        (windowInsetsController, typeMask) -> isImeInsetsControllable.set(
+                                (typeMask & WindowInsetsCompat.Type.IME) != 0);
+                // Register the OnControllableInsetsChangedListener would synchronously callback
+                // current controllable insets. Adding the listener here to check if ime inset is
+                // controllable.
+                mInsetsController.addOnControllableInsetsChangedListener(listener);
+                if (!isImeInsetsControllable.get()) {
+                    final InputMethodManager imm = (InputMethodManager) mWindow.getContext()
+                                    .getSystemService(Context.INPUT_METHOD_SERVICE);
+                    // This is a backport when the app is in multi-windowing mode, it cannot control
+                    // the ime insets. Use the InputMethodManager instead.
+                    imm.hideSoftInputFromWindow(mWindow.getDecorView().getWindowToken(), 0);
+                }
+                mInsetsController.removeOnControllableInsetsChangedListener(listener);
+            }
             mInsetsController.hide(types);
         }
 
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 dd19e2d..2b0cdce 100644
--- a/core/core/src/main/java/androidx/core/widget/NestedScrollView.java
+++ b/core/core/src/main/java/androidx/core/widget/NestedScrollView.java
@@ -704,22 +704,34 @@
         if (event.getAction() == KeyEvent.ACTION_DOWN) {
             switch (event.getKeyCode()) {
                 case KeyEvent.KEYCODE_DPAD_UP:
-                    if (!event.isAltPressed()) {
-                        handled = arrowScroll(View.FOCUS_UP);
-                    } else {
+                    if (event.isAltPressed()) {
                         handled = fullScroll(View.FOCUS_UP);
+                    } else {
+                        handled = arrowScroll(View.FOCUS_UP);
                     }
                     break;
                 case KeyEvent.KEYCODE_DPAD_DOWN:
-                    if (!event.isAltPressed()) {
-                        handled = arrowScroll(View.FOCUS_DOWN);
-                    } else {
+                    if (event.isAltPressed()) {
                         handled = fullScroll(View.FOCUS_DOWN);
+                    } else {
+                        handled = arrowScroll(View.FOCUS_DOWN);
                     }
                     break;
+                case KeyEvent.KEYCODE_PAGE_UP:
+                    handled = fullScroll(View.FOCUS_UP);
+                    break;
+                case KeyEvent.KEYCODE_PAGE_DOWN:
+                    handled = fullScroll(View.FOCUS_DOWN);
+                    break;
                 case KeyEvent.KEYCODE_SPACE:
                     pageScroll(event.isShiftPressed() ? View.FOCUS_UP : View.FOCUS_DOWN);
                     break;
+                case KeyEvent.KEYCODE_MOVE_HOME:
+                    pageScroll(View.FOCUS_UP);
+                    break;
+                case KeyEvent.KEYCODE_MOVE_END:
+                    pageScroll(View.FOCUS_DOWN);
+                    break;
             }
         }
 
diff --git a/core/core/src/main/res/layout/ime_base_split_test_activity.xml b/core/core/src/main/res/layout/ime_base_split_test_activity.xml
new file mode 100644
index 0000000..43d4d7c
--- /dev/null
+++ b/core/core/src/main/res/layout/ime_base_split_test_activity.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+</LinearLayout>
\ No newline at end of file
diff --git a/core/core/src/main/res/layout/ime_secondary_split_test_activity.xml b/core/core/src/main/res/layout/ime_secondary_split_test_activity.xml
new file mode 100644
index 0000000..8aeaae7
--- /dev/null
+++ b/core/core/src/main/res/layout/ime_secondary_split_test_activity.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <EditText
+        android:id="@+id/edit_text_id"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minHeight="200px"
+        android:gravity="center"
+        android:text="EditText"/>
+
+    <Button
+        android:id="@+id/hide_ime_id"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textAllCaps="false"
+        android:text="Hide Ime"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/core/uwb/uwb-rxjava3/build.gradle b/core/uwb/uwb-rxjava3/build.gradle
index 9c1379f..95d0b38 100644
--- a/core/uwb/uwb-rxjava3/build.gradle
+++ b/core/uwb/uwb-rxjava3/build.gradle
@@ -36,7 +36,7 @@
     androidTestImplementation(libs.truth)
     androidTestImplementation(libs.espressoCore)
     androidTestImplementation('com.google.android.gms:play-services-base:18.0.1')
-    androidTestImplementation('com.google.android.gms:play-services-nearby:18.3.0', {
+    androidTestImplementation('com.google.android.gms:play-services-nearby:18.5.0', {
         exclude group: "androidx.core"
     })
     androidTestImplementation(libs.multidex)
diff --git a/core/uwb/uwb-rxjava3/src/androidTest/java/androidx/core/uwb/rxjava3/mock/TestUwbClient.kt b/core/uwb/uwb-rxjava3/src/androidTest/java/androidx/core/uwb/rxjava3/mock/TestUwbClient.kt
index 2862833..522e730 100644
--- a/core/uwb/uwb-rxjava3/src/androidTest/java/androidx/core/uwb/rxjava3/mock/TestUwbClient.kt
+++ b/core/uwb/uwb-rxjava3/src/androidTest/java/androidx/core/uwb/rxjava3/mock/TestUwbClient.kt
@@ -46,7 +46,7 @@
     private var startedRanging = false
     companion object {
         val rangingPosition = RangingPosition(
-            RangingMeasurement(1, 1.0F), null, null, 20)
+            RangingMeasurement(1, 1.0F), null, null, 20, -50)
     }
     override fun getApiKey(): ApiKey<zze> {
         TODO("Not yet implemented")
diff --git a/core/uwb/uwb-rxjava3/src/androidTest/java/androidx/core/uwb/rxjava3/mock/TestUwbClientSessionScope.kt b/core/uwb/uwb-rxjava3/src/androidTest/java/androidx/core/uwb/rxjava3/mock/TestUwbClientSessionScope.kt
index a46f50e..6c2a4fb 100644
--- a/core/uwb/uwb-rxjava3/src/androidTest/java/androidx/core/uwb/rxjava3/mock/TestUwbClientSessionScope.kt
+++ b/core/uwb/uwb-rxjava3/src/androidTest/java/androidx/core/uwb/rxjava3/mock/TestUwbClientSessionScope.kt
@@ -63,7 +63,6 @@
             .setSessionId(defaultRangingParameters.sessionId)
             .setUwbConfigId(configId)
             .setRangingUpdateRate(updateRate)
-            .setSessionKeyInfo(defaultRangingParameters.sessionKeyInfo)
         parametersBuilder.addPeerDevice(UwbDevice.createForAddress(uwbDevice.address.address))
         val callback =
             object : RangingSessionCallback {
diff --git a/core/uwb/uwb-rxjava3/src/androidTest/java/androidx/core/uwb/rxjava3/mock/TestUwbManager.kt b/core/uwb/uwb-rxjava3/src/androidTest/java/androidx/core/uwb/rxjava3/mock/TestUwbManager.kt
index 74d6b6e..d4769cc 100644
--- a/core/uwb/uwb-rxjava3/src/androidTest/java/androidx/core/uwb/rxjava3/mock/TestUwbManager.kt
+++ b/core/uwb/uwb-rxjava3/src/androidTest/java/androidx/core/uwb/rxjava3/mock/TestUwbManager.kt
@@ -51,7 +51,8 @@
         val localAddress = com.google.android.gms.nearby.uwb.UwbAddress(DEVICE_ADDRESS)
 
         val rangingCapabilities =
-            com.google.android.gms.nearby.uwb.RangingCapabilities(true, false, false, 200)
+            com.google.android.gms.nearby.uwb.RangingCapabilities(true, false, false, 200,
+                listOf(9), listOf(1, 2, 3), 2F)
         val uwbClient = TestUwbClient(complexChannel, localAddress, rangingCapabilities, true)
         return if (isController) {
              TestUwbControllerSessionScope(
diff --git a/core/uwb/uwb/build.gradle b/core/uwb/uwb/build.gradle
index 1c781ab..8a23173 100644
--- a/core/uwb/uwb/build.gradle
+++ b/core/uwb/uwb/build.gradle
@@ -30,7 +30,7 @@
     implementation(libs.guavaAndroid)
     implementation('com.google.android.gms:play-services-base:18.0.1')
     implementation(libs.kotlinCoroutinesPlayServices)
-    implementation('com.google.android.gms:play-services-nearby:18.3.0', {
+    implementation('com.google.android.gms:play-services-nearby:18.5.0', {
         exclude group: "androidx.core"
     })
 
diff --git a/core/uwb/uwb/src/androidTest/java/androidx/core/uwb/common/TestCommons.kt b/core/uwb/uwb/src/androidTest/java/androidx/core/uwb/common/TestCommons.kt
index ce0dab8..3290b14 100644
--- a/core/uwb/uwb/src/androidTest/java/androidx/core/uwb/common/TestCommons.kt
+++ b/core/uwb/uwb/src/androidTest/java/androidx/core/uwb/common/TestCommons.kt
@@ -29,7 +29,8 @@
             .setChannel(10)
             .build()
         val LOCAL_ADDRESS = UwbAddress(byteArrayOf(0xB0.toByte()))
-        val RANGING_CAPABILITIES = RangingCapabilities(true, false, false, 200)
+        val RANGING_CAPABILITIES = RangingCapabilities(true, false, false,
+            200, listOf(9), listOf(1, 2, 3), 2F)
         val NEIGHBOR_1 = byteArrayOf(0xA1.toByte())
         val NEIGHBOR_2 = byteArrayOf(0xA5.toByte())
         val UWB_DEVICE = UwbDevice.createForAddress(NEIGHBOR_1)
diff --git a/core/uwb/uwb/src/androidTest/java/androidx/core/uwb/mock/TestUwbClient.kt b/core/uwb/uwb/src/androidTest/java/androidx/core/uwb/mock/TestUwbClient.kt
index c3f90ad..db0ff5b 100644
--- a/core/uwb/uwb/src/androidTest/java/androidx/core/uwb/mock/TestUwbClient.kt
+++ b/core/uwb/uwb/src/androidTest/java/androidx/core/uwb/mock/TestUwbClient.kt
@@ -48,7 +48,7 @@
     private var startedRanging = false
     companion object {
         val rangingPosition = RangingPosition(
-            RangingMeasurement(1, 1.0F), null, null, 20)
+            RangingMeasurement(1, 1.0F), null, null, 20, -50)
     }
     override fun getApiKey(): ApiKey<zze> {
         TODO("Not yet implemented")
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbClientSessionScopeImpl.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbClientSessionScopeImpl.kt
index fa19f31..86c1559 100644
--- a/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbClientSessionScopeImpl.kt
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbClientSessionScopeImpl.kt
@@ -79,7 +79,6 @@
             .setSessionId(parameters.sessionId)
             .setUwbConfigId(configId)
             .setRangingUpdateRate(updateRate)
-            .setSessionKeyInfo(parameters.sessionKeyInfo)
             .setComplexChannel(
                 parameters.complexChannel?.let {
                     UwbComplexChannel.Builder()
@@ -87,6 +86,9 @@
                         .setPreambleIndex(it.preambleIndex)
                         .build()
                 })
+        if (parameters.sessionKeyInfo != null) {
+            parametersBuilder.setSessionKeyInfo(parameters.sessionKeyInfo)
+        }
         for (peer in parameters.peerDevices) {
             parametersBuilder.addPeerDevice(UwbDevice.createForAddress(peer.address.address))
         }
diff --git a/credentials/credentials-play-services-auth/build.gradle b/credentials/credentials-play-services-auth/build.gradle
index f2ecf59..7fd1e0e 100644
--- a/credentials/credentials-play-services-auth/build.gradle
+++ b/credentials/credentials-play-services-auth/build.gradle
@@ -26,7 +26,7 @@
     api(libs.kotlinStdlib)
     api project(":credentials:credentials")
 
-    implementation("com.google.android.libraries.identity.googleid:googleid:0.0.2"){
+    implementation("com.google.android.libraries.identity.googleid:googleid:1.0.0"){
         exclude group: "androidx.credentials"
     }
 
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java
index 6689b7a..969f941 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java
@@ -29,6 +29,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.google.android.gms.auth.api.identity.BeginSignInRequest;
+import com.google.android.libraries.identity.googleid.GetGoogleIdOption;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -109,8 +110,6 @@
 
     @Test
     public void convertRequestToPlayServices_setGoogleIdOptionRequestAndTrueAutoSelect_success() {
-        // TODO(b/270239625) fix pre u test cases for new GoogleIdOption signature
-        /*
         ActivityScenario<TestCredentialsActivity> activityScenario =
                 ActivityScenario.launch(TestCredentialsActivity.class);
 
@@ -119,7 +118,8 @@
                 .setNonce("nonce")
                 .setFilterByAuthorizedAccounts(true)
                 .setRequestVerifiedPhoneNumber(false)
-                .associatedLinkedAccounts("link_service_id", List.of("a", "b", "c"))
+                .associateLinkedAccounts("link_service_id", List.of("a", "b", "c"))
+                .setAutoSelectEnabled(true)
                 .build();
 
         activityScenario.onActivity(activity -> {
@@ -147,6 +147,5 @@
                     option.getIdTokenDepositionScopes());
 
         });
-        */
     }
 }
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt
index f9cfd2c..9652061 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt
@@ -28,6 +28,7 @@
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
+import com.google.android.libraries.identity.googleid.GetGoogleIdOption
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
@@ -78,8 +79,6 @@
 
     @Test
     fun convertRequestToPlayServices_setGoogleIdOptionRequest_success() {
-        // TODO(b/270239625) fix pre u test cases for new GoogleIdOption signature
-        /*
         val activityScenario = ActivityScenario.launch(
             TestCredentialsActivity::class.java
         )
@@ -89,7 +88,8 @@
             .setNonce("nonce")
             .setFilterByAuthorizedAccounts(true)
             .setRequestVerifiedPhoneNumber(false)
-            .associatedLinkedAccounts("link_service_id", listOf("a", "b", "c"))
+            .associateLinkedAccounts("link_service_id", listOf("a", "b", "c"))
+            .setAutoSelectEnabled(true)
             .build()
 
         activityScenario.onActivity { activity: TestCredentialsActivity? ->
@@ -116,6 +116,5 @@
             assertThat(actualOption.idTokenDepositionScopes)
                 .isEqualTo(option.idTokenDepositionScopes)
         }
-         */
     }
 }
\ No newline at end of file
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/BeginSignInControllerUtility.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/BeginSignInControllerUtility.kt
index e3e7c4f..b6416f5 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/BeginSignInControllerUtility.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/BeginSignInControllerUtility.kt
@@ -72,8 +72,7 @@
                 } else if (option is GetGoogleIdOption) {
                     requestBuilder.setGoogleIdTokenRequestOptions(
                         convertToGoogleIdTokenOption(option))
-                    // TODO(b/270239625) add this bit to GID
-                    // autoSelect = autoSelect || option.isAutoSelectEnabled
+                    autoSelect = autoSelect || option.autoSelectEnabled
                 }
             }
             return requestBuilder
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
index e1ca866..7b6ac57 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
@@ -228,7 +228,7 @@
         }
 
         if (response.profilePictureUri != null) {
-            cred.setProfilePictureUri(response.profilePictureUri.toString())
+            cred.setProfilePictureUri(response.profilePictureUri)
         }
 
         return cred.build()
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoJavaTest.java
index 4d0a31a..40acdb0 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoJavaTest.java
@@ -23,6 +23,7 @@
 import static org.junit.Assert.assertThrows;
 
 import android.content.Context;
+import android.graphics.drawable.Icon;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SdkSuppress;
@@ -77,6 +78,25 @@
         assertThat(displayInfo.getUserId()).isEqualTo(expectedUserId);
         assertThat(displayInfo.getUserDisplayName()).isEqualTo(expectedDisplayName);
         assertThat(displayInfo.getCredentialTypeIcon()).isNull();
+        assertThat(displayInfo.getDefaultProvider()).isNull();
+    }
+
+    @SdkSuppress(minSdkVersion = 28)
+    @Test
+    public void constructWithOptionalParameters_success() {
+        CharSequence expectedUserId = "userId";
+        CharSequence expectedDisplayName = "displayName";
+        Icon expectedIcon = Icon.createWithResource(mContext, R.drawable.ic_passkey);
+        String expectedDefaultProvider = "defaultProvider";
+
+        CreateCredentialRequest.DisplayInfo displayInfo =
+                new CreateCredentialRequest.DisplayInfo(expectedUserId,
+                        expectedDisplayName, expectedIcon, expectedDefaultProvider);
+
+        assertThat(displayInfo.getUserId()).isEqualTo(expectedUserId);
+        assertThat(displayInfo.getUserDisplayName()).isEqualTo(expectedDisplayName);
+        assertThat(displayInfo.getCredentialTypeIcon()).isEqualTo(expectedIcon);
+        assertThat(displayInfo.getDefaultProvider()).isEqualTo(expectedDefaultProvider);
     }
 
     @SdkSuppress(minSdkVersion = 28)
@@ -95,5 +115,6 @@
         assertThat(displayInfo.getUserDisplayName()).isNull();
         assertThat(displayInfo.getCredentialTypeIcon().getResId()).isEqualTo(
                 R.drawable.ic_password);
+        assertThat(displayInfo.getDefaultProvider()).isNull();
     }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoTest.kt
index 2fb3e1d..bfde3e9 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreateCredentialRequestDisplayInfoTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.credentials
 
+import android.graphics.drawable.Icon
 import androidx.credentials.CreateCredentialRequest.DisplayInfo
 import androidx.credentials.CreateCredentialRequest.DisplayInfo.Companion.parseFromCredentialDataBundle
 import androidx.credentials.internal.FrameworkImplHelper.Companion.getFinalCreateCredentialData
@@ -54,8 +55,8 @@
 
     @Test
     fun constructWithUserIdAndDisplayName_success() {
-        val expectedUserId = "userId"
-        val expectedDisplayName = "displayName"
+        val expectedUserId: CharSequence = "userId"
+        val expectedDisplayName: CharSequence = "displayName"
 
         val displayInfo = DisplayInfo(
             expectedUserId,
@@ -65,6 +66,26 @@
         assertThat(displayInfo.userId).isEqualTo(expectedUserId)
         assertThat(displayInfo.userDisplayName).isEqualTo(expectedDisplayName)
         assertThat(displayInfo.credentialTypeIcon).isNull()
+        assertThat(displayInfo.defaultProvider).isNull()
+    }
+
+    @SdkSuppress(minSdkVersion = 28)
+    @Test
+    fun constructWithOptionalParameters_success() {
+        val expectedUserId: CharSequence = "userId"
+        val expectedDisplayName: CharSequence = "displayName"
+        val expectedIcon = Icon.createWithResource(mContext, R.drawable.ic_passkey)
+        val expectedDefaultProvider = "defaultProvider"
+
+        val displayInfo = DisplayInfo(
+            expectedUserId,
+            expectedDisplayName, expectedIcon, expectedDefaultProvider
+        )
+
+        assertThat(displayInfo.userId).isEqualTo(expectedUserId)
+        assertThat(displayInfo.userDisplayName).isEqualTo(expectedDisplayName)
+        assertThat(displayInfo.credentialTypeIcon).isEqualTo(expectedIcon)
+        assertThat(displayInfo.defaultProvider).isEqualTo(expectedDefaultProvider)
     }
 
     @SdkSuppress(minSdkVersion = 28)
@@ -84,5 +105,6 @@
         assertThat(displayInfo.credentialTypeIcon?.resId).isEqualTo(
             R.drawable.ic_password
         )
+        assertThat(displayInfo.defaultProvider).isNull()
     }
 }
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialRequest.kt
index 4334cc0..10c6a67 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialRequest.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreateCredentialRequest.kt
@@ -67,13 +67,15 @@
      * @property userId the user identifier of the created credential
      * @property userDisplayName an optional display name in addition to the [userId] that may be
      * displayed next to the `userId` during the user consent to help your user better understand
-     * the credential being created.
+     * the credential being created
      */
     class DisplayInfo internal /** @hide */ constructor(
         val userId: CharSequence,
         val userDisplayName: CharSequence?,
         /** @hide */
-        val credentialTypeIcon: Icon?
+        val credentialTypeIcon: Icon?,
+        /** @hide */
+        val defaultProvider: String?,
     ) {
 
         /**
@@ -82,7 +84,7 @@
          * @param userId the user id of the created credential
          * @param userDisplayName an optional display name in addition to the [userId] that may be
          * displayed next to the `userId` during the user consent to help your user better
-         * understand the credential being created.
+         * understand the credential being created
          * @throws IllegalArgumentException If [userId] is empty
          */
         @JvmOverloads constructor(
@@ -91,7 +93,8 @@
         ) : this(
             userId,
             userDisplayName,
-            null
+            null,
+            null,
         )
 
         init {
@@ -106,6 +109,9 @@
             if (!TextUtils.isEmpty(userDisplayName)) {
                 bundle.putCharSequence(BUNDLE_KEY_USER_DISPLAY_NAME, userDisplayName)
             }
+            if (!TextUtils.isEmpty(defaultProvider)) {
+                bundle.putString(BUNDLE_KEY_DEFAULT_PROVIDER, defaultProvider)
+            }
             // Today the type icon is determined solely within this library right before the
             // request is passed into the framework. Later if needed a new API can be added for
             // custom SDKs to supply their own credential type icons.
@@ -132,6 +138,10 @@
             const val BUNDLE_KEY_CREDENTIAL_TYPE_ICON =
                 "androidx.credentials.BUNDLE_KEY_CREDENTIAL_TYPE_ICON"
 
+            /** @hide */
+            const val BUNDLE_KEY_DEFAULT_PROVIDER =
+                "androidx.credentials.BUNDLE_KEY_DEFAULT_PROVIDER"
+
             /**
              * Returns a RequestDisplayInfo from a `credentialData` Bundle, or otherwise `null` if
              * parsing fails.
@@ -149,7 +159,9 @@
                         displayInfoBundle.getCharSequence(BUNDLE_KEY_USER_DISPLAY_NAME)
                     val icon: Icon? =
                         displayInfoBundle.getParcelable(BUNDLE_KEY_CREDENTIAL_TYPE_ICON)
-                    DisplayInfo(userId!!, displayName, icon)
+                    val defaultProvider: String? =
+                        displayInfoBundle.getString(BUNDLE_KEY_DEFAULT_PROVIDER)
+                    DisplayInfo(userId!!, displayName, icon, defaultProvider)
                 } catch (e: Exception) {
                     null
                 }
diff --git a/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/DataStoreImpl.kt b/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/DataStoreImpl.kt
index 5612ced..386a01a 100644
--- a/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/DataStoreImpl.kt
+++ b/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/DataStoreImpl.kt
@@ -29,6 +29,7 @@
 import kotlinx.coroutines.SupervisorJob
 import kotlinx.coroutines.completeWith
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.conflate
 import kotlinx.coroutines.flow.dropWhile
 import kotlinx.coroutines.flow.emitAll
 import kotlinx.coroutines.flow.flow
@@ -76,14 +77,15 @@
          * Final. ReadException can transition to another ReadException, Data or Final.
          * Data can transition to another Data or Final. Final will not change.
          */
-        val latestVersionAtRead = coordinator.getVersion()
-        val currentDownStreamFlowState = inMemoryCache.currentState
-
-        if ((currentDownStreamFlowState !is Data) ||
-            (currentDownStreamFlowState.version < latestVersionAtRead)
-        ) {
-            // We need to send a read request because we don't have data yet / cached data is stale.
-            readActor.offer(Message.Read(currentDownStreamFlowState))
+        // the first read should not be blocked by ongoing writes, so it can be dirty read. If it is
+        // a unlocked read, the same value might be emitted to the flow again
+        val startState = readState(requireLock = false)
+        when (startState) {
+            is Data<T> -> emit(startState.value)
+            is UnInitialized -> error(BUG_MESSAGE)
+            is ReadException<T> -> throw startState.readException
+            // TODO(b/273990827): decide the contract of accessing when state is Final
+            is Final -> return@flow
         }
 
         emitAll(
@@ -91,16 +93,7 @@
                 // end the flow if we reach the final value
                 it !is Final
             }.dropWhile {
-                if (currentDownStreamFlowState is Data<T> && it is Data) {
-                    // we need to drop until initTasks are completed and set to null, and data
-                    // version >= the current version when entering flow
-                    it.version < latestVersionAtRead
-                } else {
-                    // we need to drop the last seen state since it was either an exception or
-                    // wasn't yet initialized. Since we sent a message to actor, we *will* see a
-                    // new value.
-                    it === currentDownStreamFlowState
-                }
+                it is Data && it.version <= startState.version
             }.map {
                 when (it) {
                     is ReadException<T> -> throw it.readException
@@ -125,6 +118,7 @@
         return ack.await()
     }
 
+    // cache is only set by the reads who have file lock, so cache always has stable data
     private val inMemoryCache = DataStoreInMemoryCache<T>()
 
     private val readAndInit = InitDataStore(initTasksList)
@@ -141,6 +135,11 @@
     private val writeActor = SimpleActor<Message.Update<T>>(
         scope = scope,
         onComplete = {
+            // TODO(b/267792241): remove it if updateCollector is better scoped
+            // no more reads so stop listening to file changes
+            if (::updateCollector.isInitialized) {
+                updateCollector.cancel()
+            }
             it?.let {
                 inMemoryCache.tryUpdate(Final(it))
             }
@@ -160,32 +159,25 @@
         handleUpdate(msg)
     }
 
-    private val readActor = SimpleActor<Message.Read<T>>(
-        scope = scope,
-        onComplete = {
-            // TODO(b/267792241): remove it if updateCollector is better scoped
-            // no more reads so stop listening to file changes
-            if (::updateCollector.isInitialized) {
-                updateCollector.cancel()
+    private suspend fun readState(requireLock: Boolean): State<T> =
+        withContext(scope.coroutineContext) {
+            if (inMemoryCache.currentState is Final) {
+                // if state is Final, just return it
+                inMemoryCache.currentState
+            } else {
+                try {
+                    // make sure we initialize properly before reading from file.
+                    readAndInitOrPropagateAndThrowFailure()
+                } catch (throwable: Throwable) {
+                    // init or read failed, it is already updated in the cached value
+                    // so we don't need to do anything.
+                    return@withContext ReadException(throwable, -1)
+                }
+                // after init, try to read again. If the init run for this block, it won't re-read
+                // the file and use cache, so this is an OK call to make wrt performance.
+                readDataAndUpdateCache(requireLock)
             }
-        },
-        onUndeliveredElement = { _, _ -> }
-    ) {
-        handleRead()
-    }
-
-    private suspend fun handleRead() {
-        try {
-            // make sure we initialize properly before reading from file.
-            readAndInitOrPropagateAndThrowFailure()
-            // after init, try to read again. If the init run for this block, it won't
-            // re-read the file and use cache, so this is an OK call to make wrt performance.
-            readDataAndUpdateCache()
-        } catch (throwable: Throwable) {
-            // init or read failed, it is already updated in the cached value
-            // so we don't need to do anything.
         }
-    }
 
     private suspend fun handleUpdate(update: Message.Update<T>) {
         update.ack.completeWith(
@@ -231,41 +223,64 @@
     }
 
     /**
-     * Reads the file and updates the cache unless current cached value is Data and
-     * its version is equal to the latest version.
+     * Reads the file and updates the cache unless current cached value is Data and its version is
+     * equal to the latest version, or it is unable to get lock.
      *
-     * Calling this method when state is UnInitialized is a bug and this method
-     * will throw if that happens.
+     * Calling this method when state is UnInitialized is a bug and this method will throw if that
+     * happens.
      */
-    private suspend fun readDataAndUpdateCache() {
+    private suspend fun readDataAndUpdateCache(requireLock: Boolean): State<T> {
         // Check if the cached version matches with shared memory counter
         val currentState = inMemoryCache.currentState
         // should not call this without initialization first running
         check(currentState !is UnInitialized) {
             BUG_MESSAGE
         }
-        val version = coordinator.getVersion()
+        val latestVersion = coordinator.getVersion()
         val cachedVersion = if (currentState is Data) currentState.version else -1
 
         // Return cached value if cached version is latest
-        if (currentState is Data && version == cachedVersion) {
-            return
+        if (currentState is Data && latestVersion == cachedVersion) {
+            return currentState
         }
-        val data = try {
-            coordinator.tryLock { locked ->
-                val result = readDataFromFileOrDefault()
-                Data(
-                    result,
-                    result.hashCode(),
-                    // use the latest version if we have the lock. Otherwise, use the
-                    // previous version that we read before entering the tryLock
-                    if (locked) coordinator.getVersion() else version
-                )
+        val (newState, acquiredLock) =
+            if (requireLock) {
+                coordinator.lock { attemptRead(acquiredLock = true) to true }
+            } else {
+                coordinator.tryLock { locked ->
+                    attemptRead(locked) to locked
+                }
             }
-        } catch (throwable: Throwable) {
-            ReadException(throwable, version)
+        if (acquiredLock) {
+            inMemoryCache.tryUpdate(newState)
         }
-        inMemoryCache.tryUpdate(data)
+        return newState
+    }
+
+    /**
+     * Caller is responsible to lock or tryLock, and pass the [acquiredLock] parameter to indicate
+     * if it has acquired lock.
+     */
+    private suspend fun attemptRead(acquiredLock: Boolean): State<T> {
+        // read version before file
+        val currentVersion = coordinator.getVersion()
+        // use current version if it has lock, otherwise use the older version between current and
+        // cached version, which guarantees correctness
+        val readVersion = if (acquiredLock) {
+            currentVersion
+        } else {
+            inMemoryCache.currentState.version
+        }
+        val readResult = runCatching { readDataFromFileOrDefault() }
+        return if (readResult.isSuccess) {
+            Data(
+                readResult.getOrThrow(),
+                readResult.getOrThrow().hashCode(),
+                readVersion
+            )
+        } else {
+            ReadException<T>(readResult.exceptionOrNull()!!, readVersion)
+        }
     }
 
     // Caller is responsible for (try to) getting file lock. It reads from the file directly without
@@ -298,8 +313,11 @@
         // The code in `writeScope` is run synchronously, i.e. the newVersion isn't returned until
         // the code in `writeScope` completes.
         storageConnection.writeScope {
-            writeData(newData)
+            // update version before write to file to avoid the case where if update version after
+            // file write, the process can crash after file write but before version increment, so
+            // the readers might skip reading forever because the version isn't changed
             newVersion = coordinator.incrementAndGetVersion()
+            writeData(newData)
             if (updateCache) {
                 inMemoryCache.tryUpdate(Data(newData, newData.hashCode(), newVersion))
             }
@@ -363,10 +381,11 @@
             inMemoryCache.tryUpdate(initData)
             if (!::updateCollector.isInitialized) {
                 updateCollector = scope.launch {
-                    coordinator.updateNotifications.collect {
+                    coordinator.updateNotifications.conflate().collect {
                         val currentState = inMemoryCache.currentState
                         if (currentState !is Final) {
-                            readActor.offer(Message.Read(currentState))
+                            // update triggered reads should always wait for lock
+                            readDataAndUpdateCache(requireLock = true)
                         }
                     }
                 }
diff --git a/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/DataStoreInMemoryCache.kt b/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/DataStoreInMemoryCache.kt
index 29f0c14..0fe02e1 100644
--- a/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/DataStoreInMemoryCache.kt
+++ b/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/DataStoreInMemoryCache.kt
@@ -56,17 +56,10 @@
                     // if version changed, and it will arrive here as either new data or
                     // new error with its new version.
                     //
-                    // The only other case that might happen is when a read happens in
-                    // parallel to a write.
-                    // In that case, read either has:
-                    // old version, old data
-                    // old version, new data
-                    // new version, new data
-                    // Since the write will send (new version, new data); it is OK to ignore
-                    // what read sent if it has old version (or ignore what write sent
-                    // if read already sent (new version, new data).
-                    // The key constraint here is that, we will never receive
-                    // (new version, old data) as version updates happen after data is written.
+                    // If a read happens in parallel to a write ("dirty read"), we will not update
+                    // the cache here but make it local in the flow that does the dirty read. In
+                    // this cache we guarantee the version matches with the data because only reads
+                    // that have file lock can set the cache.
                     if (newState.version > cached.version) {
                         newState
                     } else {
diff --git a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/SingleProcessDataStoreTest.kt b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/SingleProcessDataStoreTest.kt
index 79c682d..454ed64 100644
--- a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/SingleProcessDataStoreTest.kt
+++ b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/SingleProcessDataStoreTest.kt
@@ -912,6 +912,22 @@
         assertThat(asyncCollector.await()).containsExactly(2.toByte(), 3.toByte()).inOrder()
     }
 
+    @Test
+    fun testCancelledDataStoreScopeCantRead() = doTest {
+        // TODO(b/273990827): decide the contract of accessing when state is Final
+        dataStoreScope.cancel()
+
+        val flowCollector = async {
+            store.data.toList()
+        }
+        runCurrent()
+        assertThrows<CancellationException> { flowCollector.await() }
+
+        assertThrows<CancellationException> {
+            store.data.first()
+        }
+    }
+
     private class TestingCorruptionHandler(
         private val replaceWith: Byte? = null
     ) : CorruptionHandler<Byte> {
diff --git a/development/build_log_simplifier/messages.ignore b/development/build_log_simplifier/messages.ignore
index 99a400b..acd42b2 100644
--- a/development/build_log_simplifier/messages.ignore
+++ b/development/build_log_simplifier/messages.ignore
@@ -181,6 +181,7 @@
 WARN: Missing @param tag for parameter `content` of function androidx\.wear\.compose\.material//Stepper/\#kotlin\.Float\#kotlin\.Function[0-9]+\[kotlin\.Float,kotlin\.Unit\]\#kotlin\.Int\#kotlin\.Function[0-9]+\[kotlin\.Unit\]\#kotlin\.Function[0-9]+\[kotlin\.Unit\]\#androidx\.compose\.ui\.Modifier\#kotlin\.ranges\.ClosedFloatingPointRange\[kotlin\.Float\]\#androidx\.compose\.ui\.graphics\.Color\#androidx\.compose\.ui\.graphics\.Color\#androidx\.compose\.ui\.graphics\.Color\#kotlin\.Function[0-9]+\[androidx\.compose\.foundation\.layout\.BoxScope,kotlin\.Unit\]/PointingToDeclaration/
 WARN: Missing @param tag for parameter `content` of function androidx\.wear\.compose\.material//Stepper/\#kotlin\.Int\#kotlin\.Function[0-9]+\[kotlin\.Int,kotlin\.Unit\]\#kotlin\.ranges\.IntProgression\#kotlin\.Function[0-9]+\[kotlin\.Unit\]\#kotlin\.Function[0-9]+\[kotlin\.Unit\]\#androidx\.compose\.ui\.Modifier\#androidx\.compose\.ui\.graphics\.Color\#androidx\.compose\.ui\.graphics\.Color\#androidx\.compose\.ui\.graphics\.Color\#kotlin\.Function[0-9]+\[androidx\.compose\.foundation\.layout\.BoxScope,kotlin\.Unit\]/PointingToDeclaration/
 WARN: Missing @param tag for parameter `content` of function androidx\.wear\.compose\.material//TitleCard/\#kotlin\.Function[0-9]+\[kotlin\.Unit\]\#kotlin\.Function[0-9]+\[androidx\.compose\.foundation\.layout\.RowScope,kotlin\.Unit\]\#androidx\.compose\.ui\.Modifier\#kotlin\.Boolean\#kotlin\.Function[0-9]+\[androidx\.compose\.foundation\.layout\.RowScope,kotlin\.Unit\]\?\#androidx\.compose\.ui\.graphics\.painter\.Painter\#androidx\.compose\.ui\.graphics\.Color\#androidx\.compose\.ui\.graphics\.Color\#androidx\.compose\.ui\.graphics\.Color\#kotlin\.Function[0-9]+\[androidx\.compose\.foundation\.layout\.ColumnScope,kotlin\.Unit\]/PointingToDeclaration/
+WARN: Missing @param tag for parameter `content` of function androidx\.wear\.compose\.material[0-9]+//MaterialTheme/\#androidx\.wear\.compose\.material[0-9]+\.ColorScheme\#androidx\.wear\.compose\.material[0-9]+\.Typography\#androidx\.wear\.compose\.material[0-9]+\.Shapes\#kotlin\.Function[0-9]+\[kotlin\.Unit\]/PointingToDeclaration/
 WARN: Unable to find what is referred to by
 # > Task :docs-tip-of-tree:dackkaDocs
 WARN\: Failed to resolve \`\@see SplitAttributes\.shouldExpandSecondaryContainer\`\!
@@ -233,6 +234,7 @@
 WARNING: link to @throws type Renderer\.GlesException does not resolve\. Is it from a package that the containing file does not import\? Is docs inherited to 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\. This was observed in Throws\(root=CustomDocTag\(children=\[P\(children=\[Text\(body=If any GL calls fail during initialization\., children=\[\], params=\{\}\)\], params=\{\}\)\], params=\{\}, name=MARKDOWN_FILE\), name=Renderer\.GlesException, exceptionAddress=null\)\.`
 WARNING: link to @throws type ServiceStartFailureException does not resolve\. Is it from a package that the containing file does not import\? Is docs inherited to 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\. This was observed in Throws\(root=CustomDocTag\(children=\[P\(children=\[Text\(body=if the watchface dies during startup\., children=\[\], params=\{\}\)\], params=\{\}\)\], params=\{\}, name=MARKDOWN_FILE\), name=ServiceStartFailureException, exceptionAddress=null\)\.`
 WARN: Sources for .+ is empty
+WARN\: Multiple sources exist for IOException\. Artifact ID metadata will not be displayed
 WARN: Missing @param tag for parameter `startActivityIntent` of function androidx\.test\.core\.app/ActivityScenario/launch/\#android\.content\.Intent\#android\.os\.Bundle/PointingToDeclaration/
 WARN: Missing @param tag for parameter `activityClass` of function androidx\.test\.core\.app/ActivityScenario/launch/\#java\.lang\.Class<A>\#android\.os\.Bundle/PointingToDeclaration/
 WARN: Missing @param tag for parameter `startActivityIntent` of function androidx\.test\.core\.app/ActivityScenario/launchActivityForResult/\#android\.content\.Intent\#android\.os\.Bundle/PointingToDeclaration/
@@ -462,6 +464,7 @@
 WARN: Missing @param tag for parameter `serializer` of function androidx\.datastore\.rxjava[0-9]+/RxDataStoreDelegateKt/rxDataStore/\#kotlin\.String\#androidx\.datastore\.core\.Serializer\[TypeParam\(bounds=\[kotlin\.Any\]\)\]\#androidx\.datastore\.core\.handlers\.ReplaceFileCorruptionHandler\[TypeParam\(bounds=\[kotlin\.Any\]\)\]\?\#kotlin\.Function[0-9]+\[android\.content\.Context,kotlin\.collections\.List\[androidx\.datastore\.core\.DataMigration\[TypeParam\(bounds=\[kotlin\.Any\]\)\]\]\]\#io\.reactivex\.rxjava[0-9]+\.core\.Scheduler/PointingToDeclaration/
 WARN: Missing @param tag for parameter `context` of function androidx\.documentfile\.provider/DocumentFile/fromSingleUri/\#android\.content\.Context\#android\.net\.Uri/PointingToDeclaration/
 WARN: Missing @param tag for parameter `context` of function androidx\.documentfile\.provider/DocumentFile/fromTreeUri/\#android\.content\.Context\#android\.net\.Uri/PointingToDeclaration/
+WARNING\: link to \@throws type kotlin\.IllegalArgumentException does not resolve\. Is it from a package that the containing file does not import\? Is docs inherited to 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\. This was observed in Throws\(root\=CustomDocTag\(children\=\[P\(children\=\[Text\(body\=if this enum type has no constant with the specified name\, children\=\[\]\, params\=\{\}\)\]\, params\=\{\}\)\]\, params\=\{\}\, name\=MARKDOWN_FILE\)\, name\=kotlin\.IllegalArgumentException\, exceptionAddress\=null\)\.\`
 WARN: Failed to resolve `@see <a href="https://developer\.android\.com/guide/topics/ui/drag\-drop">Drag and drop</a>`!
 WARN: Missing @param tag for parameter `useEmojiAsDefaultStyle` of function androidx\.emoji\.text/EmojiCompat\.Config/setUseEmojiAsDefaultStyle/\#boolean\#java\.util\.List<java\.lang\.Integer>/PointingToDeclaration/
 WARN: Missing @param tag for parameter `useEmojiAsDefaultStyle` of function androidx\.emoji[0-9]+\.text/EmojiCompat\.Config/setUseEmojiAsDefaultStyle/\#boolean\#java\.util\.List<java\.lang\.Integer>/PointingToDeclaration/
@@ -799,6 +802,124 @@
 public abstract java\.util\.List<androidx\.room\.integration\.kotlintestapp\.test\.JvmNameInDaoTest\.JvmNameEntity> jvmQuery\(\);
 public abstract androidx\.room\.integration\.kotlintestapp\.test\.JvmNameInDaoTest\.JvmNameDao jvmDao\(\);
 \^
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/layouts/PrimaryLayout\.java:[0-9]+: warning: \[deprecation\] CompactChip in androidx\.wear\.tiles\.material has been deprecated
+Note: \$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/Button\.java has additional uses or overrides of a deprecated API\.
+\$SUPPORT/wear/tiles/tiles\-material/src/androidTest/java/androidx/wear/tiles/material/layouts/TestCasesGenerator\.java:[0-9]+: warning: \[deprecation\] ButtonDefaults in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/androidTest/java/androidx/wear/tiles/material/layouts/TestCasesGenerator\.java:[0-9]+: warning: \[deprecation\] Chip in androidx\.wear\.tiles\.material has been deprecated
+import androidx\.wear\.tiles\.material\.Chip;
+\$SUPPORT/wear/tiles/tiles\-material/src/androidTest/java/androidx/wear/tiles/material/layouts/TestCasesGenerator\.java:[0-9]+: warning: \[deprecation\] ChipColors in androidx\.wear\.tiles\.material has been deprecated
+import androidx\.wear\.tiles\.material\.ChipColors;
+\$SUPPORT/wear/tiles/tiles\-material/src/androidTest/java/androidx/wear/tiles/material/layouts/TestCasesGenerator\.java:[0-9]+: warning: \[deprecation\] CircularProgressIndicator in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/androidTest/java/androidx/wear/tiles/material/layouts/TestCasesGenerator\.java:[0-9]+: warning: \[deprecation\] Colors in androidx\.wear\.tiles\.material has been deprecated
+import androidx\.wear\.tiles\.material\.Colors;
+\$SUPPORT/wear/tiles/tiles\-material/src/androidTest/java/androidx/wear/tiles/material/layouts/TestCasesGenerator\.java:[0-9]+: warning: \[deprecation\] CompactChip in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/androidTest/java/androidx/wear/tiles/material/layouts/TestCasesGenerator\.java:[0-9]+: warning: \[deprecation\] ProgressIndicatorColors in androidx\.wear\.tiles\.material has been deprecated
+import androidx\.wear\.tiles\.material\.ProgressIndicatorColors;
+\$SUPPORT/wear/tiles/tiles\-material/src/androidTest/java/androidx/wear/tiles/material/layouts/TestCasesGenerator\.java:[0-9]+: warning: \[deprecation\] ProgressIndicatorDefaults in androidx\.wear\.tiles\.material has been deprecated
+import androidx\.wear\.tiles\.material\.ProgressIndicatorDefaults;
+\$SUPPORT/wear/tiles/tiles\-material/src/androidTest/java/androidx/wear/tiles/material/layouts/TestCasesGenerator\.java:[0-9]+: warning: \[deprecation\] Text in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/androidTest/java/androidx/wear/tiles/material/layouts/TestCasesGenerator\.java:[0-9]+: warning: \[deprecation\] TitleChip in androidx\.wear\.tiles\.material has been deprecated
+import androidx\.wear\.tiles\.material\.TitleChip;
+\$SUPPORT/wear/tiles/tiles\-material/src/androidTest/java/androidx/wear/tiles/material/layouts/TestCasesGenerator\.java:[0-9]+: warning: \[deprecation\] Typography in androidx\.wear\.tiles\.material has been deprecated
+import androidx\.wear\.tiles\.material\.Typography;
+import static androidx\.wear\.tiles\.material\.Typography\.TYPOGRAPHY_CAPTION[0-9]+;
+import static androidx\.wear\.tiles\.material\.Typography\.TYPOGRAPHY_TITLE[0-9]+;
+\$SUPPORT/wear/tiles/tiles\-material/src/test/java/androidx/wear/tiles/material/layouts/MultiButtonLayoutTest\.java:[0-9]+: warning: \[deprecation\] Button in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/test/java/androidx/wear/tiles/material/layouts/MultiButtonLayoutTest\.java:[0-9]+: warning: \[deprecation\] ButtonDefaults in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/test/java/androidx/wear/tiles/material/layouts/PrimaryLayoutTest\.java:[0-9]+: warning: \[deprecation\] LayoutDefaults in androidx\.wear\.tiles\.material\.layouts has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/test/java/androidx/wear/tiles/material/layouts/PrimaryLayoutTest\.java:[0-9]+: warning: \[deprecation\] CompactChip in androidx\.wear\.tiles\.material has been deprecated
+import androidx\.wear\.tiles\.material\.CompactChip;
+\$SUPPORT/wear/tiles/tiles\-material/src/test/java/androidx/wear/tiles/material/layouts/PrimaryLayoutTest\.java:[0-9]+: warning: \[deprecation\] Text in androidx\.wear\.tiles\.material has been deprecated
+import androidx\.wear\.tiles\.material\.Text;
+\$SUPPORT/wear/tiles/tiles\-material/src/test/java/androidx/wear/tiles/material/layouts/EdgeContentLayoutTest\.java:[0-9]+: warning: \[deprecation\] CircularProgressIndicator in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/test/java/androidx/wear/tiles/material/layouts/EdgeContentLayoutTest\.java:[0-9]+: warning: \[deprecation\] Text in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/test/java/androidx/wear/tiles/material/ButtonTest\.java:[0-9]+: warning: \[deprecation\] ButtonDefaults in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/test/java/androidx/wear/tiles/material/CircularProgressIndicatorTest\.java:[0-9]+: warning: \[deprecation\] ProgressIndicatorDefaults in androidx\.wear\.tiles\.material has been deprecated
+import static androidx\.wear\.tiles\.material\.Helper\.checkTag;
+import static androidx\.wear\.tiles\.material\.Helper\.getMetadataTagName;
+import static androidx\.wear\.tiles\.material\.Helper\.getTagBytes;
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/CircularProgressIndicator\.java:[0-9]+: warning: \[deprecation\] ProgressIndicatorDefaults in androidx\.wear\.tiles\.material has been deprecated
+import static androidx\.wear\.tiles\.material\.ProgressIndicatorDefaults\.DEFAULT_COLORS;
+import static androidx\.wear\.tiles\.material\.ProgressIndicatorDefaults\.DEFAULT_END_ANGLE;
+import static androidx\.wear\.tiles\.material\.ProgressIndicatorDefaults\.DEFAULT_PADDING;
+import static androidx\.wear\.tiles\.material\.ProgressIndicatorDefaults\.DEFAULT_START_ANGLE;
+import static androidx\.wear\.tiles\.material\.ProgressIndicatorDefaults\.DEFAULT_STROKE_WIDTH;
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/CompactChip\.java:[0-9]+: warning: \[deprecation\] ChipDefaults in androidx\.wear\.tiles\.material has been deprecated
+import static androidx\.wear\.tiles\.material\.ChipDefaults\.COMPACT_HEIGHT;
+import static androidx\.wear\.tiles\.material\.ChipDefaults\.COMPACT_HEIGHT_TAPPABLE;
+import static androidx\.wear\.tiles\.material\.ChipDefaults\.COMPACT_HORIZONTAL_PADDING;
+import static androidx\.wear\.tiles\.material\.ChipDefaults\.COMPACT_PRIMARY_COLORS;
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/CompactChip\.java:[0-9]+: warning: \[deprecation\] Helper in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/Chip\.java:[0-9]+: warning: \[deprecation\] ChipDefaults in androidx\.wear\.tiles\.material has been deprecated
+import static androidx\.wear\.tiles\.material\.ChipDefaults\.DEFAULT_HEIGHT;
+import static androidx\.wear\.tiles\.material\.ChipDefaults\.DEFAULT_MARGIN_PERCENT;
+import static androidx\.wear\.tiles\.material\.ChipDefaults\.HORIZONTAL_PADDING;
+import static androidx\.wear\.tiles\.material\.ChipDefaults\.ICON_SIZE;
+import static androidx\.wear\.tiles\.material\.ChipDefaults\.ICON_SPACER_WIDTH;
+import static androidx\.wear\.tiles\.material\.ChipDefaults\.PRIMARY_COLORS;
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/Chip\.java:[0-9]+: warning: \[deprecation\] Helper in androidx\.wear\.tiles\.material has been deprecated
+import static androidx\.wear\.tiles\.material\.Helper\.radiusOf;
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/Typography\.java:[0-9]+: warning: \[deprecation\] Helper in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/Chip\.java:[0-9]+: warning: \[deprecation\] Typography in androidx\.wear\.tiles\.material has been deprecated
+import androidx\.wear\.tiles\.material\.Typography\.TypographyName;
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/Text\.java:[0-9]+: warning: \[deprecation\] Helper in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/Text\.java:[0-9]+: warning: \[deprecation\] Typography in androidx\.wear\.tiles\.material has been deprecated
+import static androidx\.wear\.tiles\.material\.Typography\.TYPOGRAPHY_DISPLAY[0-9]+;
+import static androidx\.wear\.tiles\.material\.Typography\.getFontStyleBuilder;
+import static androidx\.wear\.tiles\.material\.Typography\.getLineHeightForTypography;
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/Button\.java:[0-9]+: warning: \[deprecation\] ButtonDefaults in androidx\.wear\.tiles\.material has been deprecated
+import static androidx\.wear\.tiles\.material\.ButtonDefaults\.DEFAULT_SIZE;
+import static androidx\.wear\.tiles\.material\.ButtonDefaults\.EXTRA_LARGE_SIZE;
+import static androidx\.wear\.tiles\.material\.ButtonDefaults\.LARGE_SIZE;
+import static androidx\.wear\.tiles\.material\.ButtonDefaults\.PRIMARY_COLORS;
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/Button\.java:[0-9]+: warning: \[deprecation\] Helper in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/Button\.java:[0-9]+: warning: \[deprecation\] Typography in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/TitleChip\.java:[0-9]+: warning: \[deprecation\] ChipDefaults in androidx\.wear\.tiles\.material has been deprecated
+import static androidx\.wear\.tiles\.material\.ChipDefaults\.TITLE_HEIGHT;
+import static androidx\.wear\.tiles\.material\.ChipDefaults\.TITLE_HORIZONTAL_PADDING;
+import static androidx\.wear\.tiles\.material\.ChipDefaults\.TITLE_PRIMARY_COLORS;
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/TitleChip\.java:[0-9]+: warning: \[deprecation\] Helper in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/layouts/MultiSlotLayout\.java:[0-9]+: warning: \[deprecation\] Helper in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/layouts/LayoutDefaults\.java:[0-9]+: warning: \[deprecation\] ButtonDefaults in androidx\.wear\.tiles\.material has been deprecated
+import androidx\.wear\.tiles\.material\.ButtonDefaults;
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/layouts/MultiSlotLayout\.java:[0-9]+: warning: \[deprecation\] LayoutDefaults in androidx\.wear\.tiles\.material\.layouts has been deprecated
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH;
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/layouts/EdgeContentLayout\.java:[0-9]+: warning: \[deprecation\] Helper in androidx\.wear\.tiles\.material has been deprecated
+import static androidx\.wear\.tiles\.material\.Helper\.getMetadataTagBytes;
+import static androidx\.wear\.tiles\.material\.Helper\.isRoundDevice;
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/layouts/EdgeContentLayout\.java:[0-9]+: warning: \[deprecation\] ProgressIndicatorDefaults in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/layouts/EdgeContentLayout\.java:[0-9]+: warning: \[deprecation\] LayoutDefaults in androidx\.wear\.tiles\.material\.layouts has been deprecated
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.EDGE_CONTENT_LAYOUT_MARGIN_HORIZONTAL_ROUND_DP;
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.EDGE_CONTENT_LAYOUT_MARGIN_HORIZONTAL_SQUARE_DP;
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.EDGE_CONTENT_LAYOUT_PADDING_ABOVE_MAIN_CONTENT_DP;
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.EDGE_CONTENT_LAYOUT_PADDING_BELOW_MAIN_CONTENT_DP;
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/layouts/EdgeContentLayout\.java:[0-9]+: warning: \[deprecation\] CircularProgressIndicator in androidx\.wear\.tiles\.material has been deprecated
+import androidx\.wear\.tiles\.material\.CircularProgressIndicator;
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/layouts/MultiButtonLayout\.java:[0-9]+: warning: \[deprecation\] Helper in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/layouts/MultiButtonLayout\.java:[0-9]+: warning: \[deprecation\] LayoutDefaults in androidx\.wear\.tiles\.material\.layouts has been deprecated
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.MULTI_BUTTON_[0-9]+_SIZE;
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.MULTI_BUTTON_[0-9]+_PLUS_SIZE;
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.MULTI_BUTTON_MAX_NUMBER;
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.MULTI_BUTTON_SPACER_HEIGHT;
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.MULTI_BUTTON_SPACER_WIDTH;
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/layouts/MultiButtonLayout\.java:[0-9]+: warning: \[deprecation\] Button in androidx\.wear\.tiles\.material has been deprecated
+import androidx\.wear\.tiles\.material\.Button;
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/layouts/PrimaryLayout\.java:[0-9]+: warning: \[deprecation\] ChipDefaults in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/layouts/PrimaryLayout\.java:[0-9]+: warning: \[deprecation\] Helper in androidx\.wear\.tiles\.material has been deprecated
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/layouts/PrimaryLayout\.java:[0-9]+: warning: \[deprecation\] LayoutDefaults in androidx\.wear\.tiles\.material\.layouts has been deprecated
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.DEFAULT_VERTICAL_SPACER_HEIGHT;
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.PRIMARY_LAYOUT_CHIP_HORIZONTAL_PADDING_ROUND_DP;
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.PRIMARY_LAYOUT_CHIP_HORIZONTAL_PADDING_SQUARE_DP;
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.PRIMARY_LAYOUT_MARGIN_BOTTOM_ROUND_PERCENT;
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.PRIMARY_LAYOUT_MARGIN_BOTTOM_SQUARE_PERCENT;
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.PRIMARY_LAYOUT_MARGIN_HORIZONTAL_ROUND_PERCENT;
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.PRIMARY_LAYOUT_MARGIN_HORIZONTAL_SQUARE_PERCENT;
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.PRIMARY_LAYOUT_MARGIN_TOP_ROUND_PERCENT;
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.PRIMARY_LAYOUT_MARGIN_TOP_SQUARE_PERCENT;
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.PRIMARY_LAYOUT_PRIMARY_LABEL_SPACER_HEIGHT_ROUND_DP;
+import static androidx\.wear\.tiles\.material\.layouts\.LayoutDefaults\.PRIMARY_LAYOUT_PRIMARY_LABEL_SPACER_HEIGHT_SQUARE_DP;
+Note: \$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/layouts/PrimaryLayout\.java has additional uses or overrides of a deprecated API\.
+[0-9]+ warnings
 # Gradle will log if you are not authenticated to upload scans
 A build scan was not published as you have not authenticated with server 'ge\.androidx\.dev'\.
 For more information, please see https://gradle\.com/help/gradle\-authenticating\-with\-gradle\-enterprise\.
@@ -858,7 +979,6 @@
 # > Task :concurrent:concurrent-futures:compileTestJava b/242311027
 \$SUPPORT/concurrent/concurrent\-futures/src/test/java/androidx/concurrent/futures/AbstractResolvableFutureTest\.java:[0-9]+: warning: \[removal\] (resume|suspend)\(\) in Thread has been deprecated and marked for removal
 thread\.(resume|suspend)\(\);
-2 warnings
 # AGP warning that will go away soon
 WARNING:Software Components will not be created automatically for Maven publishing from Android Gradle Plugin 8\.0\. To opt\-in to the future behavior, set the Gradle property android\.disableAutomaticComponentCreation=true in the `gradle\.properties` file or use the new publishing DSL\.
 # > Task :graphics:graphics-path:compileDebugKotlin
@@ -876,4 +996,12 @@
 # > Configure project :internal-testutils-ktx
 WARNING:.*The option setting 'android\.r8\.maxWorkers=[0-9]+' is experimental\.
 # Building XCFrameworks (b/260140834) and iOS benchmark invocation
-.*xcodebuild.*
\ No newline at end of file
+.*xcodebuild.*
+# > Task :wear:tiles:tiles-material:compileDebugJavaWithJavac
+\$SUPPORT/wear/tiles/tiles\-material/src/main/java/androidx/wear/tiles/material/CircularProgressIndicator\.java:[0-9]+: warning: \[deprecation\] Helper in androidx\.wear\.tiles\.material has been deprecated
+import static androidx\.wear\.tiles\.material\.Helper\.checkNotNull;
+# > Task :wear:tiles:tiles-material:compileDebugUnitTestJavaWithJavac
+\$SUPPORT/wear/tiles/tiles\-material/src/test/java/androidx/wear/tiles/material/TextTest\.java:[0-9]+: warning: \[deprecation\] Typography in androidx\.wear\.tiles\.material has been deprecated
+import static androidx\.wear\.tiles\.material\.Typography\.TYPOGRAPHY_BODY[0-9]+;
+# > Task :wear:tiles:tiles-material:compileDebugAndroidTestJavaWithJavac
+\$SUPPORT/wear/tiles/tiles\-material/src/androidTest/java/androidx/wear/tiles/material/layouts/TestCasesGenerator\.java:[0-9]+: warning: \[deprecation\] Button in androidx\.wear\.tiles\.material has been deprecated
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index 4b406d6..91bac10e 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -15,8 +15,8 @@
     docs("androidx.ads:ads-identifier:1.0.0-alpha05")
     docs("androidx.ads:ads-identifier-common:1.0.0-alpha05")
     docs("androidx.ads:ads-identifier-provider:1.0.0-alpha05")
-    docs("androidx.annotation:annotation:1.6.0-rc01")
-    docs("androidx.annotation:annotation-experimental:1.3.0")
+    kmpDocs("androidx.annotation:annotation:1.7.0-alpha01")
+    docs("androidx.annotation:annotation-experimental:1.4.0-alpha01")
     docs("androidx.appcompat:appcompat:1.7.0-alpha02")
     docs("androidx.appcompat:appcompat-resources:1.7.0-alpha02")
     docs("androidx.appsearch:appsearch:1.1.0-alpha02")
@@ -30,85 +30,85 @@
     docs("androidx.asynclayoutinflater:asynclayoutinflater:1.1.0-alpha01")
     docs("androidx.asynclayoutinflater:asynclayoutinflater-appcompat:1.1.0-alpha01")
     docs("androidx.autofill:autofill:1.2.0-beta01")
-    docs("androidx.benchmark:benchmark-common:1.2.0-alpha11")
-    docs("androidx.benchmark:benchmark-junit4:1.2.0-alpha11")
-    docs("androidx.benchmark:benchmark-macro:1.2.0-alpha11")
-    docs("androidx.benchmark:benchmark-macro-junit4:1.2.0-alpha11")
+    docs("androidx.benchmark:benchmark-common:1.2.0-alpha12")
+    docs("androidx.benchmark:benchmark-junit4:1.2.0-alpha12")
+    docs("androidx.benchmark:benchmark-macro:1.2.0-alpha12")
+    docs("androidx.benchmark:benchmark-macro-junit4:1.2.0-alpha12")
     docs("androidx.biometric:biometric:1.2.0-alpha05")
     docs("androidx.biometric:biometric-ktx:1.2.0-alpha05")
     samples("androidx.biometric:biometric-ktx-samples:1.2.0-alpha05")
     docs("androidx.browser:browser:1.5.0")
-    docs("androidx.camera:camera-camera2:1.3.0-alpha04")
-    docs("androidx.camera:camera-core:1.3.0-alpha04")
-    docs("androidx.camera:camera-extensions:1.3.0-alpha04")
+    docs("androidx.camera:camera-camera2:1.3.0-alpha05")
+    docs("androidx.camera:camera-core:1.3.0-alpha05")
+    docs("androidx.camera:camera-extensions:1.3.0-alpha05")
     stubs(fileTree(dir: "../camera/camera-extensions-stub", include: ["camera-extensions-stub.jar"]))
-    docs("androidx.camera:camera-lifecycle:1.3.0-alpha04")
-    docs("androidx.camera:camera-mlkit-vision:1.3.0-alpha04")
+    docs("androidx.camera:camera-lifecycle:1.3.0-alpha05")
+    docs("androidx.camera:camera-mlkit-vision:1.3.0-alpha05")
     docs("androidx.camera:camera-previewview:1.1.0-beta02")
-    docs("androidx.camera:camera-video:1.3.0-alpha04")
-    docs("androidx.camera:camera-view:1.3.0-alpha04")
-    docs("androidx.camera:camera-viewfinder:1.3.0-alpha04")
+    docs("androidx.camera:camera-video:1.3.0-alpha05")
+    docs("androidx.camera:camera-view:1.3.0-alpha05")
+    docs("androidx.camera:camera-viewfinder:1.3.0-alpha05")
     docs("androidx.car.app:app:1.4.0-alpha01")
     docs("androidx.car.app:app-automotive:1.4.0-alpha01")
     docs("androidx.car.app:app-projected:1.4.0-alpha01")
     docs("androidx.car.app:app-testing:1.4.0-alpha01")
     docs("androidx.cardview:cardview:1.0.0")
-    docs("androidx.collection:collection:1.3.0-alpha02")
-    docs("androidx.collection:collection-ktx:1.3.0-alpha02")
-    docs("androidx.compose.animation:animation:1.4.0-rc01")
-    docs("androidx.compose.animation:animation-core:1.4.0-rc01")
-    docs("androidx.compose.animation:animation-graphics:1.4.0-rc01")
-    samples("androidx.compose.animation:animation-samples:1.4.0-rc01")
-    samples("androidx.compose.animation:animation-core-samples:1.4.0-rc01")
-    samples("androidx.compose.animation:animation-graphics-samples:1.4.0-rc01")
-    docs("androidx.compose.foundation:foundation:1.4.0-rc01")
-    docs("androidx.compose.foundation:foundation-layout:1.4.0-rc01")
-    samples("androidx.compose.foundation:foundation-layout-samples:1.4.0-rc01")
-    samples("androidx.compose.foundation:foundation-samples:1.4.0-rc01")
-    docs("androidx.compose.material3:material3:1.1.0-alpha08")
-    samples("androidx.compose.material3:material3-samples:1.1.0-alpha08")
-    docs("androidx.compose.material3:material3-window-size-class:1.1.0-alpha08")
-    samples("androidx.compose.material3:material3-window-size-class-samples:1.1.0-alpha08")
-    docs("androidx.compose.material:material:1.4.0-rc01")
-    docs("androidx.compose.material:material-icons-core:1.4.0-rc01")
-    samples("androidx.compose.material:material-icons-core-samples:1.4.0-rc01")
-    docs("androidx.compose.material:material-ripple:1.4.0-rc01")
-    samples("androidx.compose.material:material-samples:1.4.0-rc01")
-    docs("androidx.compose.runtime:runtime:1.4.0-rc01")
-    docs("androidx.compose.runtime:runtime-livedata:1.4.0-rc01")
-    samples("androidx.compose.runtime:runtime-livedata-samples:1.4.0-rc01")
-    docs("androidx.compose.runtime:runtime-rxjava2:1.4.0-rc01")
-    samples("androidx.compose.runtime:runtime-rxjava2-samples:1.4.0-rc01")
-    docs("androidx.compose.runtime:runtime-rxjava3:1.4.0-rc01")
-    samples("androidx.compose.runtime:runtime-rxjava3-samples:1.4.0-rc01")
-    docs("androidx.compose.runtime:runtime-saveable:1.4.0-rc01")
-    samples("androidx.compose.runtime:runtime-saveable-samples:1.4.0-rc01")
-    samples("androidx.compose.runtime:runtime-samples:1.4.0-rc01")
+    kmpDocs("androidx.collection:collection:1.3.0-alpha03")
+    docs("androidx.collection:collection-ktx:1.3.0-alpha03")
+    docs("androidx.compose.animation:animation:1.5.0-alpha01")
+    docs("androidx.compose.animation:animation-core:1.5.0-alpha01")
+    docs("androidx.compose.animation:animation-graphics:1.5.0-alpha01")
+    samples("androidx.compose.animation:animation-samples:1.5.0-alpha01")
+    samples("androidx.compose.animation:animation-core-samples:1.5.0-alpha01")
+    samples("androidx.compose.animation:animation-graphics-samples:1.5.0-alpha01")
+    docs("androidx.compose.foundation:foundation:1.5.0-alpha01")
+    docs("androidx.compose.foundation:foundation-layout:1.5.0-alpha01")
+    samples("androidx.compose.foundation:foundation-layout-samples:1.5.0-alpha01")
+    samples("androidx.compose.foundation:foundation-samples:1.5.0-alpha01")
+    docs("androidx.compose.material3:material3:1.1.0-beta01")
+    samples("androidx.compose.material3:material3-samples:1.1.0-beta01")
+    docs("androidx.compose.material3:material3-window-size-class:1.1.0-beta01")
+    samples("androidx.compose.material3:material3-window-size-class-samples:1.1.0-beta01")
+    docs("androidx.compose.material:material:1.5.0-alpha01")
+    docs("androidx.compose.material:material-icons-core:1.5.0-alpha01")
+    samples("androidx.compose.material:material-icons-core-samples:1.5.0-alpha01")
+    docs("androidx.compose.material:material-ripple:1.5.0-alpha01")
+    samples("androidx.compose.material:material-samples:1.5.0-alpha01")
+    docs("androidx.compose.runtime:runtime:1.5.0-alpha01")
+    docs("androidx.compose.runtime:runtime-livedata:1.5.0-alpha01")
+    samples("androidx.compose.runtime:runtime-livedata-samples:1.5.0-alpha01")
+    docs("androidx.compose.runtime:runtime-rxjava2:1.5.0-alpha01")
+    samples("androidx.compose.runtime:runtime-rxjava2-samples:1.5.0-alpha01")
+    docs("androidx.compose.runtime:runtime-rxjava3:1.5.0-alpha01")
+    samples("androidx.compose.runtime:runtime-rxjava3-samples:1.5.0-alpha01")
+    docs("androidx.compose.runtime:runtime-saveable:1.5.0-alpha01")
+    samples("androidx.compose.runtime:runtime-saveable-samples:1.5.0-alpha01")
+    samples("androidx.compose.runtime:runtime-samples:1.5.0-alpha01")
     docs("androidx.compose.runtime:runtime-tracing:1.0.0-alpha03")
-    docs("androidx.compose.ui:ui:1.4.0-rc01")
-    docs("androidx.compose.ui:ui-geometry:1.4.0-rc01")
-    docs("androidx.compose.ui:ui-graphics:1.4.0-rc01")
-    samples("androidx.compose.ui:ui-graphics-samples:1.4.0-rc01")
-    docs("androidx.compose.ui:ui-test:1.4.0-rc01")
-    docs("androidx.compose.ui:ui-test-junit4:1.4.0-rc01")
-    samples("androidx.compose.ui:ui-test-samples:1.4.0-rc01")
-    docs("androidx.compose.ui:ui-text:1.4.0-rc01")
-    docs("androidx.compose.ui:ui-text-google-fonts:1.4.0-rc01")
-    samples("androidx.compose.ui:ui-text-samples:1.4.0-rc01")
-    docs("androidx.compose.ui:ui-tooling:1.4.0-rc01")
-    docs("androidx.compose.ui:ui-tooling-data:1.4.0-rc01")
-    docs("androidx.compose.ui:ui-tooling-preview:1.4.0-rc01")
-    docs("androidx.compose.ui:ui-unit:1.4.0-rc01")
-    samples("androidx.compose.ui:ui-unit-samples:1.4.0-rc01")
-    docs("androidx.compose.ui:ui-util:1.4.0-rc01")
-    docs("androidx.compose.ui:ui-viewbinding:1.4.0-rc01")
-    samples("androidx.compose.ui:ui-viewbinding-samples:1.4.0-rc01")
-    samples("androidx.compose.ui:ui-samples:1.4.0-rc01")
+    docs("androidx.compose.ui:ui:1.5.0-alpha01")
+    docs("androidx.compose.ui:ui-geometry:1.5.0-alpha01")
+    docs("androidx.compose.ui:ui-graphics:1.5.0-alpha01")
+    samples("androidx.compose.ui:ui-graphics-samples:1.5.0-alpha01")
+    docs("androidx.compose.ui:ui-test:1.5.0-alpha01")
+    docs("androidx.compose.ui:ui-test-junit4:1.5.0-alpha01")
+    samples("androidx.compose.ui:ui-test-samples:1.5.0-alpha01")
+    docs("androidx.compose.ui:ui-text:1.5.0-alpha01")
+    docs("androidx.compose.ui:ui-text-google-fonts:1.5.0-alpha01")
+    samples("androidx.compose.ui:ui-text-samples:1.5.0-alpha01")
+    docs("androidx.compose.ui:ui-tooling:1.5.0-alpha01")
+    docs("androidx.compose.ui:ui-tooling-data:1.5.0-alpha01")
+    docs("androidx.compose.ui:ui-tooling-preview:1.5.0-alpha01")
+    docs("androidx.compose.ui:ui-unit:1.5.0-alpha01")
+    samples("androidx.compose.ui:ui-unit-samples:1.5.0-alpha01")
+    docs("androidx.compose.ui:ui-util:1.5.0-alpha01")
+    docs("androidx.compose.ui:ui-viewbinding:1.5.0-alpha01")
+    samples("androidx.compose.ui:ui-viewbinding-samples:1.5.0-alpha01")
+    samples("androidx.compose.ui:ui-samples:1.5.0-alpha01")
     docs("androidx.concurrent:concurrent-futures:1.2.0-alpha01")
     docs("androidx.concurrent:concurrent-futures-ktx:1.2.0-alpha01")
-    docs("androidx.constraintlayout:constraintlayout:2.2.0-alpha08")
-    docs("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha08")
-    docs("androidx.constraintlayout:constraintlayout-core:1.1.0-alpha08")
+    docs("androidx.constraintlayout:constraintlayout:2.2.0-alpha09")
+    docs("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha09")
+    docs("androidx.constraintlayout:constraintlayout-core:1.1.0-alpha09")
     docs("androidx.contentpager:contentpager:1.0.0")
     docs("androidx.coordinatorlayout:coordinatorlayout:1.2.0")
     docs("androidx.core.uwb:uwb:1.0.0-alpha04")
@@ -129,34 +129,34 @@
     docs("androidx.cursoradapter:cursoradapter:1.0.0")
     docs("androidx.customview:customview:1.2.0-alpha02")
     docs("androidx.customview:customview-poolingcontainer:1.0.0-rc01")
-    docs("androidx.datastore:datastore:1.1.0-alpha01")
-    docs("androidx.datastore:datastore-core:1.1.0-alpha01")
-    docs("androidx.datastore:datastore-core-okio:1.1.0-alpha01")
-    docs("androidx.datastore:datastore-preferences:1.1.0-alpha01")
-    docs("androidx.datastore:datastore-preferences-core:1.1.0-alpha01")
-    docs("androidx.datastore:datastore-preferences-rxjava2:1.1.0-alpha01")
-    docs("androidx.datastore:datastore-preferences-rxjava3:1.1.0-alpha01")
-    docs("androidx.datastore:datastore-rxjava2:1.1.0-alpha01")
-    docs("androidx.datastore:datastore-rxjava3:1.1.0-alpha01")
+    kmpDocs("androidx.datastore:datastore:1.1.0-alpha02")
+    kmpDocs("androidx.datastore:datastore-core:1.1.0-alpha02")
+    kmpDocs("androidx.datastore:datastore-core-okio:1.1.0-alpha02")
+    kmpDocs("androidx.datastore:datastore-preferences:1.1.0-alpha02")
+    kmpDocs("androidx.datastore:datastore-preferences-core:1.1.0-alpha02")
+    docs("androidx.datastore:datastore-preferences-rxjava2:1.1.0-alpha02")
+    docs("androidx.datastore:datastore-preferences-rxjava3:1.1.0-alpha02")
+    docs("androidx.datastore:datastore-rxjava2:1.1.0-alpha02")
+    docs("androidx.datastore:datastore-rxjava3:1.1.0-alpha02")
     docs("androidx.documentfile:documentfile:1.1.0-alpha01")
     docs("androidx.draganddrop:draganddrop:1.0.0")
-    docs("androidx.drawerlayout:drawerlayout:1.2.0-rc01")
+    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.3.0-rc01")
-    docs("androidx.emoji2:emoji2-bundled:1.3.0-rc01")
+    docs("androidx.emoji2:emoji2:1.4.0-alpha01")
+    docs("androidx.emoji2:emoji2-bundled:1.4.0-alpha01")
     docs("androidx.emoji2:emoji2-emojipicker:1.0.0-alpha03")
-    docs("androidx.emoji2:emoji2-views:1.3.0-rc01")
-    docs("androidx.emoji2:emoji2-views-helper:1.3.0-rc01")
+    docs("androidx.emoji2:emoji2-views:1.4.0-alpha01")
+    docs("androidx.emoji2:emoji2-views-helper:1.4.0-alpha01")
     docs("androidx.emoji:emoji:1.2.0-alpha03")
     docs("androidx.emoji:emoji-appcompat:1.2.0-alpha03")
     docs("androidx.emoji:emoji-bundled:1.2.0-alpha03")
     docs("androidx.enterprise:enterprise-feedback:1.1.0")
     docs("androidx.enterprise:enterprise-feedback-testing:1.1.0")
     docs("androidx.exifinterface:exifinterface:1.3.6")
-    docs("androidx.fragment:fragment:1.6.0-alpha07")
-    docs("androidx.fragment:fragment-ktx:1.6.0-alpha07")
-    docs("androidx.fragment:fragment-testing:1.6.0-alpha07")
+    docs("androidx.fragment:fragment:1.6.0-alpha08")
+    docs("androidx.fragment:fragment-ktx:1.6.0-alpha08")
+    docs("androidx.fragment:fragment-testing:1.6.0-alpha08")
     docs("androidx.glance:glance:1.0.0-alpha05")
     docs("androidx.glance:glance-appwidget:1.0.0-alpha05")
     docs("androidx.glance:glance-appwidget-preview:1.0.0-alpha05")
@@ -164,8 +164,8 @@
     docs("androidx.glance:glance-preview:1.0.0-alpha05")
     docs("androidx.glance:glance-wear-tiles:1.0.0-alpha05")
     docs("androidx.glance:glance-wear-tiles-preview:1.0.0-alpha05")
-    docs("androidx.graphics:graphics-core:1.0.0-alpha02")
-    docs("androidx.gridlayout:gridlayout:1.0.0")
+    docs("androidx.graphics:graphics-core:1.0.0-alpha03")
+    docs("androidx.gridlayout:gridlayout:1.1.0-alpha01")
     docs("androidx.health.connect:connect-client:1.0.0-alpha11")
     samples("androidx.health.connect:connect-client-samples:1.0.0-alpha11")
     docs("androidx.health:health-services-client:1.0.0-beta02")
@@ -176,7 +176,7 @@
     samples("androidx.hilt:hilt-navigation-compose-samples:1.1.0-alpha01")
     docs("androidx.hilt:hilt-navigation-fragment:1.0.0-beta01")
     docs("androidx.hilt:hilt-work:1.0.0-beta01")
-    docs("androidx.input:input-motionprediction:1.0.0-alpha02")
+    docs("androidx.input:input-motionprediction:1.0.0-beta01")
     docs("androidx.interpolator:interpolator:1.0.0")
     docs("androidx.javascriptengine:javascriptengine:1.0.0-alpha04")
     docs("androidx.leanback:leanback:1.2.0-alpha02")
@@ -212,45 +212,45 @@
     docs("androidx.media2:media2-session:1.2.1")
     docs("androidx.media2:media2-widget:1.2.1")
     docs("androidx.media:media:1.6.0")
-    docs("androidx.media3:media3-cast:1.0.0-rc02")
-    docs("androidx.media3:media3-common:1.0.0-rc02")
-    docs("androidx.media3:media3-database:1.0.0-rc02")
-    docs("androidx.media3:media3-datasource:1.0.0-rc02")
-    docs("androidx.media3:media3-datasource-cronet:1.0.0-rc02")
-    docs("androidx.media3:media3-datasource-okhttp:1.0.0-rc02")
-    docs("androidx.media3:media3-datasource-rtmp:1.0.0-rc02")
-    docs("androidx.media3:media3-decoder:1.0.0-rc02")
-    docs("androidx.media3:media3-effect:1.0.0-rc02")
-    docs("androidx.media3:media3-exoplayer:1.0.0-rc02")
-    docs("androidx.media3:media3-exoplayer-dash:1.0.0-rc02")
-    docs("androidx.media3:media3-exoplayer-hls:1.0.0-rc02")
-    docs("androidx.media3:media3-exoplayer-ima:1.0.0-rc02")
-    docs("androidx.media3:media3-exoplayer-rtsp:1.0.0-rc02")
-    docs("androidx.media3:media3-exoplayer-smoothstreaming:1.0.0-rc02")
-    docs("androidx.media3:media3-exoplayer-workmanager:1.0.0-rc02")
-    docs("androidx.media3:media3-extractor:1.0.0-rc02")
-    docs("androidx.media3:media3-session:1.0.0-rc02")
-    docs("androidx.media3:media3-test-utils:1.0.0-rc02")
-    docs("androidx.media3:media3-test-utils-robolectric:1.0.0-rc02")
-    docs("androidx.media3:media3-transformer:1.0.0-rc02")
-    docs("androidx.media3:media3-ui:1.0.0-rc02")
-    docs("androidx.media3:media3-ui-leanback:1.0.0-rc02")
+    docs("androidx.media3:media3-cast:1.0.0")
+    docs("androidx.media3:media3-common:1.0.0")
+    docs("androidx.media3:media3-database:1.0.0")
+    docs("androidx.media3:media3-datasource:1.0.0")
+    docs("androidx.media3:media3-datasource-cronet:1.0.0")
+    docs("androidx.media3:media3-datasource-okhttp:1.0.0")
+    docs("androidx.media3:media3-datasource-rtmp:1.0.0")
+    docs("androidx.media3:media3-decoder:1.0.0")
+    docs("androidx.media3:media3-effect:1.0.0")
+    docs("androidx.media3:media3-exoplayer:1.0.0")
+    docs("androidx.media3:media3-exoplayer-dash:1.0.0")
+    docs("androidx.media3:media3-exoplayer-hls:1.0.0")
+    docs("androidx.media3:media3-exoplayer-ima:1.0.0")
+    docs("androidx.media3:media3-exoplayer-rtsp:1.0.0")
+    docs("androidx.media3:media3-exoplayer-smoothstreaming:1.0.0")
+    docs("androidx.media3:media3-exoplayer-workmanager:1.0.0")
+    docs("androidx.media3:media3-extractor:1.0.0")
+    docs("androidx.media3:media3-session:1.0.0")
+    docs("androidx.media3:media3-test-utils:1.0.0")
+    docs("androidx.media3:media3-test-utils-robolectric:1.0.0")
+    docs("androidx.media3:media3-transformer:1.0.0")
+    docs("androidx.media3:media3-ui:1.0.0")
+    docs("androidx.media3:media3-ui-leanback:1.0.0")
     docs("androidx.mediarouter:mediarouter:1.6.0-alpha02")
     docs("androidx.mediarouter:mediarouter-testing:1.6.0-alpha02")
     docs("androidx.metrics:metrics-performance:1.0.0-alpha03")
-    docs("androidx.navigation:navigation-common:2.6.0-alpha07")
-    docs("androidx.navigation:navigation-common-ktx:2.6.0-alpha07")
-    docs("androidx.navigation:navigation-compose:2.6.0-alpha07")
-    samples("androidx.navigation:navigation-compose-samples:2.6.0-alpha07")
-    docs("androidx.navigation:navigation-dynamic-features-fragment:2.6.0-alpha07")
-    docs("androidx.navigation:navigation-dynamic-features-runtime:2.6.0-alpha07")
-    docs("androidx.navigation:navigation-fragment:2.6.0-alpha07")
-    docs("androidx.navigation:navigation-fragment-ktx:2.6.0-alpha07")
-    docs("androidx.navigation:navigation-runtime:2.6.0-alpha07")
-    docs("androidx.navigation:navigation-runtime-ktx:2.6.0-alpha07")
-    docs("androidx.navigation:navigation-testing:2.6.0-alpha07")
-    docs("androidx.navigation:navigation-ui:2.6.0-alpha07")
-    docs("androidx.navigation:navigation-ui-ktx:2.6.0-alpha07")
+    docs("androidx.navigation:navigation-common:2.6.0-alpha08")
+    docs("androidx.navigation:navigation-common-ktx:2.6.0-alpha08")
+    docs("androidx.navigation:navigation-compose:2.6.0-alpha08")
+    samples("androidx.navigation:navigation-compose-samples:2.6.0-alpha08")
+    docs("androidx.navigation:navigation-dynamic-features-fragment:2.6.0-alpha08")
+    docs("androidx.navigation:navigation-dynamic-features-runtime:2.6.0-alpha08")
+    docs("androidx.navigation:navigation-fragment:2.6.0-alpha08")
+    docs("androidx.navigation:navigation-fragment-ktx:2.6.0-alpha08")
+    docs("androidx.navigation:navigation-runtime:2.6.0-alpha08")
+    docs("androidx.navigation:navigation-runtime-ktx:2.6.0-alpha08")
+    docs("androidx.navigation:navigation-testing:2.6.0-alpha08")
+    docs("androidx.navigation:navigation-ui:2.6.0-alpha08")
+    docs("androidx.navigation:navigation-ui-ktx:2.6.0-alpha08")
     docs("androidx.paging:paging-common:3.2.0-alpha04")
     docs("androidx.paging:paging-common-ktx:3.2.0-alpha04")
     docs("androidx.paging:paging-compose:1.0.0-alpha18")
@@ -269,34 +269,34 @@
     docs("androidx.preference:preference:1.2.0")
     docs("androidx.preference:preference-ktx:1.2.0")
     docs("androidx.print:print:1.1.0-beta01")
-    docs("androidx.privacysandbox.ads:ads-adservices:1.0.0-beta01")
-    docs("androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta01")
+    docs("androidx.privacysandbox.ads:ads-adservices:1.0.0-beta02")
+    docs("androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta02")
     docs("androidx.privacysandbox.tools:tools:1.0.0-alpha03")
     docs("androidx.privacysandbox.tools:tools-apigenerator:1.0.0-alpha03")
     docs("androidx.privacysandbox.tools:tools-apipackager:1.0.0-alpha03")
     docs("androidx.privacysandbox.tools:tools-core:1.0.0-alpha03")
-    docs("androidx.privacysandbox.ui:ui-client:1.0.0-alpha01")
-    docs("androidx.privacysandbox.ui:ui-core:1.0.0-alpha01")
-    docs("androidx.privacysandbox.ui:ui-provider:1.0.0-alpha01")
-    docs("androidx.profileinstaller:profileinstaller:1.3.0-rc01")
+    docs("androidx.privacysandbox.ui:ui-client:1.0.0-alpha02")
+    docs("androidx.privacysandbox.ui:ui-core:1.0.0-alpha02")
+    docs("androidx.privacysandbox.ui:ui-provider:1.0.0-alpha02")
+    docs("androidx.profileinstaller:profileinstaller:1.3.0")
     docs("androidx.recommendation:recommendation:1.0.0")
     docs("androidx.recyclerview:recyclerview:1.3.0")
     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")
     docs("androidx.resourceinspection:resourceinspection-processor:1.0.1")
-    docs("androidx.room:room-common:2.5.0")
-    docs("androidx.room:room-guava:2.5.0")
-    docs("androidx.room:room-ktx:2.5.0")
-    docs("androidx.room:room-migration:2.5.0")
-    docs("androidx.room:room-paging:2.5.0")
-    docs("androidx.room:room-paging-guava:2.5.0")
-    docs("androidx.room:room-paging-rxjava2:2.5.0")
-    docs("androidx.room:room-paging-rxjava3:2.5.0")
-    docs("androidx.room:room-runtime:2.5.0")
-    docs("androidx.room:room-rxjava2:2.5.0")
-    docs("androidx.room:room-rxjava3:2.5.0")
-    docs("androidx.room:room-testing:2.5.0")
+    docs("androidx.room:room-common:2.6.0-alpha01")
+    docs("androidx.room:room-guava:2.6.0-alpha01")
+    docs("androidx.room:room-ktx:2.6.0-alpha01")
+    docs("androidx.room:room-migration:2.6.0-alpha01")
+    docs("androidx.room:room-paging:2.6.0-alpha01")
+    docs("androidx.room:room-paging-guava:2.6.0-alpha01")
+    docs("androidx.room:room-paging-rxjava2:2.6.0-alpha01")
+    docs("androidx.room:room-paging-rxjava3:2.6.0-alpha01")
+    docs("androidx.room:room-runtime:2.6.0-alpha01")
+    docs("androidx.room:room-rxjava2:2.6.0-alpha01")
+    docs("androidx.room:room-rxjava3:2.6.0-alpha01")
+    docs("androidx.room:room-testing:2.6.0-alpha01")
     docs("androidx.savedstate:savedstate:1.2.1")
     docs("androidx.savedstate:savedstate-ktx:1.2.1")
     docs("androidx.security:security-app-authenticator:1.0.0-alpha02")
@@ -310,9 +310,9 @@
     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")
-    docs("androidx.sqlite:sqlite:2.3.0")
-    docs("androidx.sqlite:sqlite-framework:2.3.0")
-    docs("androidx.sqlite:sqlite-ktx:2.3.0")
+    docs("androidx.sqlite:sqlite:2.4.0-alpha01")
+    docs("androidx.sqlite:sqlite-framework:2.4.0-alpha01")
+    docs("androidx.sqlite:sqlite-ktx:2.4.0-alpha01")
     docs("androidx.startup:startup-runtime:1.2.0-alpha02")
     docs("androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01")
     docs("androidx.test:core:1.5.0")
@@ -336,15 +336,15 @@
     docs("androidx.test.services:storage:1.4.2")
     docs("androidx.test.uiautomator:uiautomator:2.3.0-alpha02")
     docs("androidx.textclassifier:textclassifier:1.0.0-alpha04")
-    docs("androidx.tracing:tracing:1.2.0-beta01")
-    docs("androidx.tracing:tracing-ktx:1.2.0-beta01")
-    docs("androidx.tracing:tracing-perfetto:1.0.0-alpha12")
-    docs("androidx.tracing:tracing-perfetto-common:1.0.0-alpha12")
+    docs("androidx.tracing:tracing:1.2.0-beta02")
+    docs("androidx.tracing:tracing-ktx:1.2.0-beta02")
+    docs("androidx.tracing:tracing-perfetto:1.0.0-alpha13")
+    docs("androidx.tracing:tracing-perfetto-common:1.0.0-alpha13")
     docs("androidx.transition:transition:1.4.1")
     docs("androidx.transition:transition-ktx:1.4.1")
-    docs("androidx.tv:tv-foundation:1.0.0-alpha04")
-    docs("androidx.tv:tv-material:1.0.0-alpha04")
-    samples("androidx.tv:tv-samples:1.0.0-alpha04")
+    docs("androidx.tv:tv-foundation:1.0.0-alpha05")
+    docs("androidx.tv:tv-material:1.0.0-alpha05")
+    samples("androidx.tv:tv-samples:1.0.0-alpha05")
     docs("androidx.tvprovider:tvprovider:1.1.0-alpha01")
     docs("androidx.vectordrawable:vectordrawable:1.2.0-beta01")
     docs("androidx.vectordrawable:vectordrawable-animated:1.2.0-alpha01")
@@ -352,25 +352,27 @@
     docs("androidx.versionedparcelable:versionedparcelable:1.1.1")
     docs("androidx.viewpager2:viewpager2:1.1.0-beta01")
     docs("androidx.viewpager:viewpager:1.1.0-alpha01")
-    docs("androidx.wear.compose:compose-foundation:1.2.0-alpha06")
-    samples("androidx.wear.compose:compose-foundation-samples:1.2.0-alpha06")
-    docs("androidx.wear.compose:compose-material:1.2.0-alpha06")
-    docs("androidx.wear.compose:compose-material-core:1.2.0-alpha06")
-    samples("androidx.wear.compose:compose-material-samples:1.2.0-alpha06")
+    docs("androidx.wear.compose:compose-foundation:1.2.0-alpha07")
+    samples("androidx.wear.compose:compose-foundation-samples:1.2.0-alpha07")
+    docs("androidx.wear.compose:compose-material:1.2.0-alpha07")
+    docs("androidx.wear.compose:compose-material-core:1.2.0-alpha07")
+    samples("androidx.wear.compose:compose-material-samples:1.2.0-alpha07")
     docs("androidx.wear.compose:compose-material3:1.0.0-alpha01")
-    samples("androidx.wear.compose:compose-material3-samples:1.2.0-alpha06")
-    docs("androidx.wear.compose:compose-navigation:1.2.0-alpha06")
-    samples("androidx.wear.compose:compose-navigation-samples:1.2.0-alpha06")
-    docs("androidx.wear.protolayout:protolayout:1.0.0-alpha05")
-    docs("androidx.wear.protolayout:protolayout-expression:1.0.0-alpha05")
-    docs("androidx.wear.protolayout:protolayout-material:1.0.0-alpha05")
-    docs("androidx.wear.protolayout:protolayout-proto:1.0.0-alpha05")
-    docs("androidx.wear.protolayout:protolayout-renderer:1.0.0-alpha05")
-    docs("androidx.wear.tiles:tiles:1.2.0-alpha01")
-    docs("androidx.wear.tiles:tiles-material:1.2.0-alpha01")
-    docs("androidx.wear.tiles:tiles-proto:1.2.0-alpha01")
-    docs("androidx.wear.tiles:tiles-renderer:1.2.0-alpha01")
-    docs("androidx.wear.tiles:tiles-testing:1.2.0-alpha01")
+    samples("androidx.wear.compose:compose-material3-samples:1.2.0-alpha07")
+    docs("androidx.wear.compose:compose-navigation:1.2.0-alpha07")
+    samples("androidx.wear.compose:compose-navigation-samples:1.2.0-alpha07")
+    docs("androidx.wear.compose:compose-ui-tooling:1.2.0-alpha07")
+    docs("androidx.wear.protolayout:protolayout:1.0.0-alpha06")
+    docs("androidx.wear.protolayout:protolayout-expression:1.0.0-alpha06")
+    docs("androidx.wear.protolayout:protolayout-material:1.0.0-alpha06")
+    docs("androidx.wear.protolayout:protolayout-proto:1.0.0-alpha06")
+    docs("androidx.wear.protolayout:protolayout-renderer:1.0.0-alpha06")
+    docs("androidx.wear.tiles:tiles:1.2.0-alpha02")
+    docs("androidx.wear.tiles:tiles-material:1.2.0-alpha02")
+    docs("androidx.wear.tiles:tiles-proto:1.2.0-alpha02")
+    docs("androidx.wear.tiles:tiles-renderer:1.2.0-alpha02")
+    docs("androidx.wear.tiles:tiles-testing:1.2.0-alpha02")
+    docs("androidx.wear.tiles:tiles-tooling:1.2.0-alpha02")
     docs("androidx.wear.watchface:watchface:1.2.0-alpha07")
     docs("androidx.wear.watchface:watchface-client:1.2.0-alpha07")
     docs("androidx.wear.watchface:watchface-client-guava:1.2.0-alpha07")
@@ -395,22 +397,22 @@
     docs("androidx.wear:wear-input:1.2.0-alpha02")
     samples("androidx.wear:wear-input-samples:1.2.0-alpha01")
     docs("androidx.wear:wear-input-testing:1.2.0-alpha02")
-    docs("androidx.webkit:webkit:1.7.0-alpha03")
-    docs("androidx.window.extensions.core:core:1.0.0-alpha01")
-    docs("androidx.window:window:1.1.0-alpha06")
+    docs("androidx.webkit:webkit:1.7.0-beta01")
+    docs("androidx.window.extensions.core:core:1.0.0-beta01")
+    docs("androidx.window:window:1.1.0-beta01")
     stubs(fileTree(dir: "../window/stubs/", include: ["window-sidecar-release-0.1.0-alpha01.aar"]))
-    docs("androidx.window:window-core:1.1.0-alpha06")
+    docs("androidx.window:window-core:1.1.0-beta01")
     stubs("androidx.window:window-extensions:1.0.0-alpha01")
-    docs("androidx.window:window-java:1.1.0-alpha06")
-    docs("androidx.window:window-rxjava2:1.1.0-alpha06")
-    docs("androidx.window:window-rxjava3:1.1.0-alpha06")
-    samples("androidx.window:window-samples:1.1.0-alpha06")
-    docs("androidx.window:window-testing:1.1.0-alpha06")
-    docs("androidx.work:work-gcm:2.8.0")
-    docs("androidx.work:work-multiprocess:2.8.0")
-    docs("androidx.work:work-runtime:2.8.0")
-    docs("androidx.work:work-runtime-ktx:2.8.0")
-    docs("androidx.work:work-rxjava2:2.8.0")
-    docs("androidx.work:work-rxjava3:2.8.0")
-    docs("androidx.work:work-testing:2.8.0")
+    docs("androidx.window:window-java:1.1.0-beta01")
+    docs("androidx.window:window-rxjava2:1.1.0-beta01")
+    docs("androidx.window:window-rxjava3:1.1.0-beta01")
+    samples("androidx.window:window-samples:1.1.0-beta01")
+    docs("androidx.window:window-testing:1.1.0-beta01")
+    docs("androidx.work:work-gcm:2.8.1")
+    docs("androidx.work:work-multiprocess:2.8.1")
+    docs("androidx.work:work-runtime:2.8.1")
+    docs("androidx.work:work-runtime-ktx:2.8.1")
+    docs("androidx.work:work-rxjava2:2.8.1")
+    docs("androidx.work:work-rxjava3:2.8.1")
+    docs("androidx.work:work-testing:2.8.1")
 }
diff --git a/docs-tip-of-tree/build.gradle b/docs-tip-of-tree/build.gradle
index 08d11c4..fe0253b 100644
--- a/docs-tip-of-tree/build.gradle
+++ b/docs-tip-of-tree/build.gradle
@@ -12,10 +12,6 @@
     docs(project(":activity:activity-compose"))
     samples(project(":activity:activity-compose:activity-compose-samples"))
     docs(project(":activity:activity-ktx"))
-    docs(project(":ads:ads-identifier"))
-    docs(project(":ads:ads-identifier-common"))
-    docs(project(":ads:ads-identifier-provider"))
-    docs(project(":ads:ads-identifier-testing"))
     kmpDocs(project(":annotation:annotation"))
     docs(project(":annotation:annotation-experimental"))
     docs(project(":appactions:interaction:interaction-capabilities-communication"))
@@ -23,6 +19,7 @@
     docs(project(":appactions:interaction:interaction-capabilities-productivity"))
     docs(project(":appactions:interaction:interaction-capabilities-safety"))
     docs(project(":appactions:interaction:interaction-capabilities-fitness"))
+    docs(project(":appactions:interaction:interaction-capabilities-testing"))
     docs(project(":appactions:interaction:interaction-proto"))
     docs(project(":appactions:interaction:interaction-service"))
     docs(project(":appactions:interaction:interaction-service-proto"))
@@ -131,6 +128,7 @@
     docs(project(":core:core-remoteviews"))
     docs(project(":core:core-splashscreen"))
     docs(project(":core:core-role"))
+    docs(project(":core:core-testing"))
     docs(project(":core:uwb:uwb"))
     docs(project(":core:uwb:uwb-rxjava3"))
     docs(project(":credentials:credentials"))
diff --git a/emoji2/emoji2-bundled/api/1.4.0-beta01.txt b/emoji2/emoji2-bundled/api/1.4.0-beta01.txt
new file mode 100644
index 0000000..8749c28
--- /dev/null
+++ b/emoji2/emoji2-bundled/api/1.4.0-beta01.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.emoji2.bundled {
+
+  public class BundledEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+    ctor public BundledEmojiCompatConfig(android.content.Context);
+  }
+
+}
+
diff --git a/emoji2/emoji2-bundled/api/public_plus_experimental_1.4.0-beta01.txt b/emoji2/emoji2-bundled/api/public_plus_experimental_1.4.0-beta01.txt
new file mode 100644
index 0000000..8749c28
--- /dev/null
+++ b/emoji2/emoji2-bundled/api/public_plus_experimental_1.4.0-beta01.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.emoji2.bundled {
+
+  public class BundledEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+    ctor public BundledEmojiCompatConfig(android.content.Context);
+  }
+
+}
+
diff --git a/ads/ads-identifier-common/api/res-current.txt b/emoji2/emoji2-bundled/api/res-1.4.0-beta01.txt
similarity index 100%
copy from ads/ads-identifier-common/api/res-current.txt
copy to emoji2/emoji2-bundled/api/res-1.4.0-beta01.txt
diff --git a/emoji2/emoji2-bundled/api/restricted_1.4.0-beta01.txt b/emoji2/emoji2-bundled/api/restricted_1.4.0-beta01.txt
new file mode 100644
index 0000000..8749c28
--- /dev/null
+++ b/emoji2/emoji2-bundled/api/restricted_1.4.0-beta01.txt
@@ -0,0 +1,9 @@
+// Signature format: 4.0
+package androidx.emoji2.bundled {
+
+  public class BundledEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+    ctor public BundledEmojiCompatConfig(android.content.Context);
+  }
+
+}
+
diff --git a/emoji2/emoji2-emojipicker/api/1.4.0-beta01.txt b/emoji2/emoji2-emojipicker/api/1.4.0-beta01.txt
new file mode 100644
index 0000000..e2360fa
--- /dev/null
+++ b/emoji2/emoji2-emojipicker/api/1.4.0-beta01.txt
@@ -0,0 +1,43 @@
+// Signature format: 4.0
+package androidx.emoji2.emojipicker {
+
+  public final class EmojiPickerView extends android.widget.FrameLayout {
+    ctor public EmojiPickerView(android.content.Context context, optional android.util.AttributeSet? attrs, optional int defStyleAttr);
+    ctor public EmojiPickerView(android.content.Context context, optional android.util.AttributeSet? attrs);
+    ctor public EmojiPickerView(android.content.Context context);
+    method public int getEmojiGridColumns();
+    method public float getEmojiGridRows();
+    method public void setEmojiGridColumns(int);
+    method public void setEmojiGridRows(float);
+    method public void setOnEmojiPickedListener(androidx.core.util.Consumer<androidx.emoji2.emojipicker.EmojiViewItem>? onEmojiPickedListener);
+    method public void setRecentEmojiProvider(androidx.emoji2.emojipicker.RecentEmojiProvider recentEmojiProvider);
+    property public final int emojiGridColumns;
+    property public final float emojiGridRows;
+  }
+
+  public final class EmojiViewItem {
+    ctor public EmojiViewItem(String emoji, java.util.List<java.lang.String> variants);
+    method public String getEmoji();
+    method public java.util.List<java.lang.String> getVariants();
+    property public final String emoji;
+    property public final java.util.List<java.lang.String> variants;
+  }
+
+  public interface RecentEmojiAsyncProvider {
+    method public com.google.common.util.concurrent.ListenableFuture<java.util.List<java.lang.String>> getRecentEmojiListAsync();
+    method public void recordSelection(String emoji);
+  }
+
+  public interface RecentEmojiProvider {
+    method public suspend Object? getRecentEmojiList(kotlin.coroutines.Continuation<? super java.util.List<? extends java.lang.String>>);
+    method public void recordSelection(String emoji);
+  }
+
+  public final class RecentEmojiProviderAdapter implements androidx.emoji2.emojipicker.RecentEmojiProvider {
+    ctor public RecentEmojiProviderAdapter(androidx.emoji2.emojipicker.RecentEmojiAsyncProvider recentEmojiAsyncProvider);
+    method public suspend Object? getRecentEmojiList(kotlin.coroutines.Continuation<? super java.util.List<? extends java.lang.String>>);
+    method public void recordSelection(String emoji);
+  }
+
+}
+
diff --git a/emoji2/emoji2-emojipicker/api/public_plus_experimental_1.4.0-beta01.txt b/emoji2/emoji2-emojipicker/api/public_plus_experimental_1.4.0-beta01.txt
new file mode 100644
index 0000000..e2360fa
--- /dev/null
+++ b/emoji2/emoji2-emojipicker/api/public_plus_experimental_1.4.0-beta01.txt
@@ -0,0 +1,43 @@
+// Signature format: 4.0
+package androidx.emoji2.emojipicker {
+
+  public final class EmojiPickerView extends android.widget.FrameLayout {
+    ctor public EmojiPickerView(android.content.Context context, optional android.util.AttributeSet? attrs, optional int defStyleAttr);
+    ctor public EmojiPickerView(android.content.Context context, optional android.util.AttributeSet? attrs);
+    ctor public EmojiPickerView(android.content.Context context);
+    method public int getEmojiGridColumns();
+    method public float getEmojiGridRows();
+    method public void setEmojiGridColumns(int);
+    method public void setEmojiGridRows(float);
+    method public void setOnEmojiPickedListener(androidx.core.util.Consumer<androidx.emoji2.emojipicker.EmojiViewItem>? onEmojiPickedListener);
+    method public void setRecentEmojiProvider(androidx.emoji2.emojipicker.RecentEmojiProvider recentEmojiProvider);
+    property public final int emojiGridColumns;
+    property public final float emojiGridRows;
+  }
+
+  public final class EmojiViewItem {
+    ctor public EmojiViewItem(String emoji, java.util.List<java.lang.String> variants);
+    method public String getEmoji();
+    method public java.util.List<java.lang.String> getVariants();
+    property public final String emoji;
+    property public final java.util.List<java.lang.String> variants;
+  }
+
+  public interface RecentEmojiAsyncProvider {
+    method public com.google.common.util.concurrent.ListenableFuture<java.util.List<java.lang.String>> getRecentEmojiListAsync();
+    method public void recordSelection(String emoji);
+  }
+
+  public interface RecentEmojiProvider {
+    method public suspend Object? getRecentEmojiList(kotlin.coroutines.Continuation<? super java.util.List<? extends java.lang.String>>);
+    method public void recordSelection(String emoji);
+  }
+
+  public final class RecentEmojiProviderAdapter implements androidx.emoji2.emojipicker.RecentEmojiProvider {
+    ctor public RecentEmojiProviderAdapter(androidx.emoji2.emojipicker.RecentEmojiAsyncProvider recentEmojiAsyncProvider);
+    method public suspend Object? getRecentEmojiList(kotlin.coroutines.Continuation<? super java.util.List<? extends java.lang.String>>);
+    method public void recordSelection(String emoji);
+  }
+
+}
+
diff --git a/ads/ads-identifier-provider/api/res-current.txt b/emoji2/emoji2-emojipicker/api/res-1.4.0-beta01.txt
similarity index 100%
rename from ads/ads-identifier-provider/api/res-current.txt
rename to emoji2/emoji2-emojipicker/api/res-1.4.0-beta01.txt
diff --git a/emoji2/emoji2-emojipicker/api/restricted_1.4.0-beta01.txt b/emoji2/emoji2-emojipicker/api/restricted_1.4.0-beta01.txt
new file mode 100644
index 0000000..e2360fa
--- /dev/null
+++ b/emoji2/emoji2-emojipicker/api/restricted_1.4.0-beta01.txt
@@ -0,0 +1,43 @@
+// Signature format: 4.0
+package androidx.emoji2.emojipicker {
+
+  public final class EmojiPickerView extends android.widget.FrameLayout {
+    ctor public EmojiPickerView(android.content.Context context, optional android.util.AttributeSet? attrs, optional int defStyleAttr);
+    ctor public EmojiPickerView(android.content.Context context, optional android.util.AttributeSet? attrs);
+    ctor public EmojiPickerView(android.content.Context context);
+    method public int getEmojiGridColumns();
+    method public float getEmojiGridRows();
+    method public void setEmojiGridColumns(int);
+    method public void setEmojiGridRows(float);
+    method public void setOnEmojiPickedListener(androidx.core.util.Consumer<androidx.emoji2.emojipicker.EmojiViewItem>? onEmojiPickedListener);
+    method public void setRecentEmojiProvider(androidx.emoji2.emojipicker.RecentEmojiProvider recentEmojiProvider);
+    property public final int emojiGridColumns;
+    property public final float emojiGridRows;
+  }
+
+  public final class EmojiViewItem {
+    ctor public EmojiViewItem(String emoji, java.util.List<java.lang.String> variants);
+    method public String getEmoji();
+    method public java.util.List<java.lang.String> getVariants();
+    property public final String emoji;
+    property public final java.util.List<java.lang.String> variants;
+  }
+
+  public interface RecentEmojiAsyncProvider {
+    method public com.google.common.util.concurrent.ListenableFuture<java.util.List<java.lang.String>> getRecentEmojiListAsync();
+    method public void recordSelection(String emoji);
+  }
+
+  public interface RecentEmojiProvider {
+    method public suspend Object? getRecentEmojiList(kotlin.coroutines.Continuation<? super java.util.List<? extends java.lang.String>>);
+    method public void recordSelection(String emoji);
+  }
+
+  public final class RecentEmojiProviderAdapter implements androidx.emoji2.emojipicker.RecentEmojiProvider {
+    ctor public RecentEmojiProviderAdapter(androidx.emoji2.emojipicker.RecentEmojiAsyncProvider recentEmojiAsyncProvider);
+    method public suspend Object? getRecentEmojiList(kotlin.coroutines.Continuation<? super java.util.List<? extends java.lang.String>>);
+    method public void recordSelection(String emoji);
+  }
+
+}
+
diff --git a/emoji2/emoji2-emojipicker/build.gradle b/emoji2/emoji2-emojipicker/build.gradle
index 77d9424..2ab0c6a 100644
--- a/emoji2/emoji2-emojipicker/build.gradle
+++ b/emoji2/emoji2-emojipicker/build.gradle
@@ -59,7 +59,6 @@
 androidx {
     name = "androidx.emoji2:emoji2-emojipicker"
     type = LibraryType.PUBLISHED_LIBRARY
-    mavenVersion = LibraryVersions.EMOJI2_QUARANTINE
     inceptionYear = "2022"
     description = "This library provides the latest emoji support and emoji picker UI to input " +
             "emoji in current and older devices"
diff --git a/emoji2/emoji2-emojipicker/src/androidTest/java/androidx/emoji2/emojipicker/EmojiPickerViewTest.kt b/emoji2/emoji2-emojipicker/src/androidTest/java/androidx/emoji2/emojipicker/EmojiPickerViewTest.kt
index 57c0bc3..f6bbac1 100644
--- a/emoji2/emoji2-emojipicker/src/androidTest/java/androidx/emoji2/emojipicker/EmojiPickerViewTest.kt
+++ b/emoji2/emoji2-emojipicker/src/androidTest/java/androidx/emoji2/emojipicker/EmojiPickerViewTest.kt
@@ -39,6 +39,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
 import org.hamcrest.Description
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotNull
@@ -120,6 +121,8 @@
         activityTestRule.scenario.onActivity {
             view = it.findViewById(R.id.emojiPickerTest)
         }
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+
         // Scroll to the nose emoji, long click then select nose in dark skin tone
         findViewByEmoji(view, NOSE_EMOJI)
             ?: onView(withId(EmojiPickerViewR.id.emoji_picker_body))
@@ -143,7 +146,6 @@
     @Test
     fun testHeader_highlightCurrentCategory() {
         disableRecent()
-
         assertSelectedHeaderIndex(0)
         scrollToEmoji(NOSE_EMOJI)
         assertSelectedHeaderIndex(1)
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerHeaderAdapter.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerHeaderAdapter.kt
index 5e1993d..4dac647 100644
--- a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerHeaderAdapter.kt
+++ b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerHeaderAdapter.kt
@@ -58,10 +58,10 @@
             R.id.emoji_picker_header_icon
         ).apply {
             setImageDrawable(context.getDrawable(emojiPickerItems.getHeaderIconId(i)))
-            setOnClickListener { onHeaderIconClicked(i) }
             isSelected = isItemSelected
             contentDescription = emojiPickerItems.getHeaderIconDescription(i)
         }
+        viewHolder.itemView.setOnClickListener { onHeaderIconClicked(i) }
         if (isItemSelected) {
             headerIcon.post {
                 headerIcon.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER)
diff --git a/emoji2/emoji2-views-helper/api/1.4.0-beta01.txt b/emoji2/emoji2-views-helper/api/1.4.0-beta01.txt
new file mode 100644
index 0000000..30a6feb
--- /dev/null
+++ b/emoji2/emoji2-views-helper/api/1.4.0-beta01.txt
@@ -0,0 +1,27 @@
+// Signature format: 4.0
+package androidx.emoji2.viewsintegration {
+
+  public final class EmojiEditTextHelper {
+    ctor public EmojiEditTextHelper(android.widget.EditText);
+    ctor public EmojiEditTextHelper(android.widget.EditText, boolean);
+    method public android.text.method.KeyListener? getKeyListener(android.text.method.KeyListener?);
+    method public int getMaxEmojiCount();
+    method public boolean isEnabled();
+    method public android.view.inputmethod.InputConnection? onCreateInputConnection(android.view.inputmethod.InputConnection?, android.view.inputmethod.EditorInfo);
+    method public void setEnabled(boolean);
+    method public void setMaxEmojiCount(@IntRange(from=0) int);
+  }
+
+  public final class EmojiTextViewHelper {
+    ctor public EmojiTextViewHelper(android.widget.TextView);
+    ctor public EmojiTextViewHelper(android.widget.TextView, boolean);
+    method public android.text.InputFilter![] getFilters(android.text.InputFilter![]);
+    method public boolean isEnabled();
+    method public void setAllCaps(boolean);
+    method public void setEnabled(boolean);
+    method public void updateTransformationMethod();
+    method public android.text.method.TransformationMethod? wrapTransformationMethod(android.text.method.TransformationMethod?);
+  }
+
+}
+
diff --git a/emoji2/emoji2-views-helper/api/public_plus_experimental_1.4.0-beta01.txt b/emoji2/emoji2-views-helper/api/public_plus_experimental_1.4.0-beta01.txt
new file mode 100644
index 0000000..30a6feb
--- /dev/null
+++ b/emoji2/emoji2-views-helper/api/public_plus_experimental_1.4.0-beta01.txt
@@ -0,0 +1,27 @@
+// Signature format: 4.0
+package androidx.emoji2.viewsintegration {
+
+  public final class EmojiEditTextHelper {
+    ctor public EmojiEditTextHelper(android.widget.EditText);
+    ctor public EmojiEditTextHelper(android.widget.EditText, boolean);
+    method public android.text.method.KeyListener? getKeyListener(android.text.method.KeyListener?);
+    method public int getMaxEmojiCount();
+    method public boolean isEnabled();
+    method public android.view.inputmethod.InputConnection? onCreateInputConnection(android.view.inputmethod.InputConnection?, android.view.inputmethod.EditorInfo);
+    method public void setEnabled(boolean);
+    method public void setMaxEmojiCount(@IntRange(from=0) int);
+  }
+
+  public final class EmojiTextViewHelper {
+    ctor public EmojiTextViewHelper(android.widget.TextView);
+    ctor public EmojiTextViewHelper(android.widget.TextView, boolean);
+    method public android.text.InputFilter![] getFilters(android.text.InputFilter![]);
+    method public boolean isEnabled();
+    method public void setAllCaps(boolean);
+    method public void setEnabled(boolean);
+    method public void updateTransformationMethod();
+    method public android.text.method.TransformationMethod? wrapTransformationMethod(android.text.method.TransformationMethod?);
+  }
+
+}
+
diff --git a/ads/ads-identifier-provider/api/res-current.txt b/emoji2/emoji2-views-helper/api/res-1.4.0-beta01.txt
similarity index 100%
copy from ads/ads-identifier-provider/api/res-current.txt
copy to emoji2/emoji2-views-helper/api/res-1.4.0-beta01.txt
diff --git a/emoji2/emoji2-views-helper/api/restricted_1.4.0-beta01.txt b/emoji2/emoji2-views-helper/api/restricted_1.4.0-beta01.txt
new file mode 100644
index 0000000..30a6feb
--- /dev/null
+++ b/emoji2/emoji2-views-helper/api/restricted_1.4.0-beta01.txt
@@ -0,0 +1,27 @@
+// Signature format: 4.0
+package androidx.emoji2.viewsintegration {
+
+  public final class EmojiEditTextHelper {
+    ctor public EmojiEditTextHelper(android.widget.EditText);
+    ctor public EmojiEditTextHelper(android.widget.EditText, boolean);
+    method public android.text.method.KeyListener? getKeyListener(android.text.method.KeyListener?);
+    method public int getMaxEmojiCount();
+    method public boolean isEnabled();
+    method public android.view.inputmethod.InputConnection? onCreateInputConnection(android.view.inputmethod.InputConnection?, android.view.inputmethod.EditorInfo);
+    method public void setEnabled(boolean);
+    method public void setMaxEmojiCount(@IntRange(from=0) int);
+  }
+
+  public final class EmojiTextViewHelper {
+    ctor public EmojiTextViewHelper(android.widget.TextView);
+    ctor public EmojiTextViewHelper(android.widget.TextView, boolean);
+    method public android.text.InputFilter![] getFilters(android.text.InputFilter![]);
+    method public boolean isEnabled();
+    method public void setAllCaps(boolean);
+    method public void setEnabled(boolean);
+    method public void updateTransformationMethod();
+    method public android.text.method.TransformationMethod? wrapTransformationMethod(android.text.method.TransformationMethod?);
+  }
+
+}
+
diff --git a/emoji2/emoji2-views/api/1.4.0-beta01.txt b/emoji2/emoji2-views/api/1.4.0-beta01.txt
new file mode 100644
index 0000000..879b30e
--- /dev/null
+++ b/emoji2/emoji2-views/api/1.4.0-beta01.txt
@@ -0,0 +1,34 @@
+// Signature format: 4.0
+package androidx.emoji2.widget {
+
+  public class EmojiButton extends android.widget.Button {
+    ctor public EmojiButton(android.content.Context);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+  public class EmojiEditText extends android.widget.EditText {
+    ctor public EmojiEditText(android.content.Context);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?, int);
+    method public int getMaxEmojiCount();
+    method public void setMaxEmojiCount(@IntRange(from=0) int);
+  }
+
+  public class EmojiExtractTextLayout extends android.widget.LinearLayout {
+    ctor public EmojiExtractTextLayout(android.content.Context);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?, int);
+    method public int getEmojiReplaceStrategy();
+    method public void onUpdateExtractingViews(android.inputmethodservice.InputMethodService, android.view.inputmethod.EditorInfo);
+    method public void setEmojiReplaceStrategy(int);
+  }
+
+  public class EmojiTextView extends android.widget.TextView {
+    ctor public EmojiTextView(android.content.Context);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+}
+
diff --git a/emoji2/emoji2-views/api/public_plus_experimental_1.4.0-beta01.txt b/emoji2/emoji2-views/api/public_plus_experimental_1.4.0-beta01.txt
new file mode 100644
index 0000000..879b30e
--- /dev/null
+++ b/emoji2/emoji2-views/api/public_plus_experimental_1.4.0-beta01.txt
@@ -0,0 +1,34 @@
+// Signature format: 4.0
+package androidx.emoji2.widget {
+
+  public class EmojiButton extends android.widget.Button {
+    ctor public EmojiButton(android.content.Context);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+  public class EmojiEditText extends android.widget.EditText {
+    ctor public EmojiEditText(android.content.Context);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?, int);
+    method public int getMaxEmojiCount();
+    method public void setMaxEmojiCount(@IntRange(from=0) int);
+  }
+
+  public class EmojiExtractTextLayout extends android.widget.LinearLayout {
+    ctor public EmojiExtractTextLayout(android.content.Context);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?, int);
+    method public int getEmojiReplaceStrategy();
+    method public void onUpdateExtractingViews(android.inputmethodservice.InputMethodService, android.view.inputmethod.EditorInfo);
+    method public void setEmojiReplaceStrategy(int);
+  }
+
+  public class EmojiTextView extends android.widget.TextView {
+    ctor public EmojiTextView(android.content.Context);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+}
+
diff --git a/emoji2/emoji2-views/api/res-1.4.0-beta01.txt b/emoji2/emoji2-views/api/res-1.4.0-beta01.txt
new file mode 100644
index 0000000..8bc8423
--- /dev/null
+++ b/emoji2/emoji2-views/api/res-1.4.0-beta01.txt
@@ -0,0 +1,2 @@
+attr emojiReplaceStrategy
+attr maxEmojiCount
diff --git a/emoji2/emoji2-views/api/restricted_1.4.0-beta01.txt b/emoji2/emoji2-views/api/restricted_1.4.0-beta01.txt
new file mode 100644
index 0000000..879b30e
--- /dev/null
+++ b/emoji2/emoji2-views/api/restricted_1.4.0-beta01.txt
@@ -0,0 +1,34 @@
+// Signature format: 4.0
+package androidx.emoji2.widget {
+
+  public class EmojiButton extends android.widget.Button {
+    ctor public EmojiButton(android.content.Context);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiButton(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+  public class EmojiEditText extends android.widget.EditText {
+    ctor public EmojiEditText(android.content.Context);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiEditText(android.content.Context, android.util.AttributeSet?, int);
+    method public int getMaxEmojiCount();
+    method public void setMaxEmojiCount(@IntRange(from=0) int);
+  }
+
+  public class EmojiExtractTextLayout extends android.widget.LinearLayout {
+    ctor public EmojiExtractTextLayout(android.content.Context);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiExtractTextLayout(android.content.Context, android.util.AttributeSet?, int);
+    method public int getEmojiReplaceStrategy();
+    method public void onUpdateExtractingViews(android.inputmethodservice.InputMethodService, android.view.inputmethod.EditorInfo);
+    method public void setEmojiReplaceStrategy(int);
+  }
+
+  public class EmojiTextView extends android.widget.TextView {
+    ctor public EmojiTextView(android.content.Context);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public EmojiTextView(android.content.Context, android.util.AttributeSet?, int);
+  }
+
+}
+
diff --git a/emoji2/emoji2/api/1.4.0-beta01.txt b/emoji2/emoji2/api/1.4.0-beta01.txt
new file mode 100644
index 0000000..11d9335
--- /dev/null
+++ b/emoji2/emoji2/api/1.4.0-beta01.txt
@@ -0,0 +1,131 @@
+// Signature format: 4.0
+package androidx.emoji2.text {
+
+  public final class DefaultEmojiCompatConfig {
+    method public static androidx.emoji2.text.FontRequestEmojiCompatConfig? create(android.content.Context);
+  }
+
+  @AnyThread public class EmojiCompat {
+    method public static androidx.emoji2.text.EmojiCompat get();
+    method public String getAssetSignature();
+    method public int getEmojiEnd(CharSequence, @IntRange(from=0) int);
+    method public int getEmojiMatch(CharSequence, @IntRange(from=0) int);
+    method public int getEmojiStart(CharSequence, @IntRange(from=0) int);
+    method public int getLoadState();
+    method public static boolean handleDeleteSurroundingText(android.view.inputmethod.InputConnection, android.text.Editable, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
+    method public static boolean handleOnKeyDown(android.text.Editable, int, android.view.KeyEvent);
+    method @Deprecated public boolean hasEmojiGlyph(CharSequence);
+    method @Deprecated public boolean hasEmojiGlyph(CharSequence, @IntRange(from=0) int);
+    method public static androidx.emoji2.text.EmojiCompat? init(android.content.Context);
+    method public static androidx.emoji2.text.EmojiCompat init(androidx.emoji2.text.EmojiCompat.Config);
+    method public static boolean isConfigured();
+    method public void load();
+    method @CheckResult public CharSequence? process(CharSequence?);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, int);
+    method public void registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public void unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public void updateEditorInfo(android.view.inputmethod.EditorInfo);
+    field public static final String EDITOR_INFO_METAVERSION_KEY = "android.support.text.emoji.emojiCompat_metadataVersion";
+    field public static final String EDITOR_INFO_REPLACE_ALL_KEY = "android.support.text.emoji.emojiCompat_replaceAll";
+    field public static final int EMOJI_FALLBACK = 2; // 0x2
+    field public static final int EMOJI_SUPPORTED = 1; // 0x1
+    field public static final int EMOJI_UNSUPPORTED = 0; // 0x0
+    field public static final int LOAD_STATE_DEFAULT = 3; // 0x3
+    field public static final int LOAD_STATE_FAILED = 2; // 0x2
+    field public static final int LOAD_STATE_LOADING = 0; // 0x0
+    field public static final int LOAD_STATE_SUCCEEDED = 1; // 0x1
+    field public static final int LOAD_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int LOAD_STRATEGY_MANUAL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_ALL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int REPLACE_STRATEGY_NON_EXISTENT = 2; // 0x2
+  }
+
+  public abstract static class EmojiCompat.Config {
+    ctor protected EmojiCompat.Config(androidx.emoji2.text.EmojiCompat.MetadataRepoLoader);
+    method protected final androidx.emoji2.text.EmojiCompat.MetadataRepoLoader getMetadataRepoLoader();
+    method public androidx.emoji2.text.EmojiCompat.Config registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorColor(@ColorInt int);
+    method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorEnabled(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setGlyphChecker(androidx.emoji2.text.EmojiCompat.GlyphChecker);
+    method public androidx.emoji2.text.EmojiCompat.Config setMetadataLoadStrategy(int);
+    method public androidx.emoji2.text.EmojiCompat.Config setReplaceAll(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setSpanFactory(androidx.emoji2.text.EmojiCompat.SpanFactory);
+    method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean, java.util.List<java.lang.Integer!>?);
+    method public androidx.emoji2.text.EmojiCompat.Config unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+  }
+
+  public static interface EmojiCompat.GlyphChecker {
+    method public boolean hasGlyph(CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+  }
+
+  public abstract static class EmojiCompat.InitCallback {
+    ctor public EmojiCompat.InitCallback();
+    method public void onFailed(Throwable?);
+    method public void onInitialized();
+  }
+
+  public static interface EmojiCompat.MetadataRepoLoader {
+    method public void load(androidx.emoji2.text.EmojiCompat.MetadataRepoLoaderCallback);
+  }
+
+  public abstract static class EmojiCompat.MetadataRepoLoaderCallback {
+    ctor public EmojiCompat.MetadataRepoLoaderCallback();
+    method public abstract void onFailed(Throwable?);
+    method public abstract void onLoaded(androidx.emoji2.text.MetadataRepo);
+  }
+
+  public static interface EmojiCompat.SpanFactory {
+    method @RequiresApi(19) public androidx.emoji2.text.EmojiSpan createSpan(androidx.emoji2.text.TypefaceEmojiRasterizer);
+  }
+
+  public class EmojiCompatInitializer implements androidx.startup.Initializer<java.lang.Boolean> {
+    ctor public EmojiCompatInitializer();
+    method public Boolean create(android.content.Context);
+    method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>!> dependencies();
+  }
+
+  @RequiresApi(19) public abstract class EmojiSpan extends android.text.style.ReplacementSpan {
+    method public int getSize(android.graphics.Paint, CharSequence!, int, int, android.graphics.Paint.FontMetricsInt?);
+    method public final androidx.emoji2.text.TypefaceEmojiRasterizer getTypefaceRasterizer();
+  }
+
+  public class FontRequestEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+    ctor public FontRequestEmojiCompatConfig(android.content.Context, androidx.core.provider.FontRequest);
+    method @Deprecated public androidx.emoji2.text.FontRequestEmojiCompatConfig setHandler(android.os.Handler?);
+    method public androidx.emoji2.text.FontRequestEmojiCompatConfig setLoadingExecutor(java.util.concurrent.Executor);
+    method public androidx.emoji2.text.FontRequestEmojiCompatConfig setRetryPolicy(androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy?);
+  }
+
+  public static class FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy extends androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy(long);
+    method public long getRetryDelay();
+  }
+
+  public abstract static class FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.RetryPolicy();
+    method public abstract long getRetryDelay();
+  }
+
+  @AnyThread @RequiresApi(19) public final class MetadataRepo {
+    method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.io.InputStream) throws java.io.IOException;
+    method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.nio.ByteBuffer) throws java.io.IOException;
+    method public static androidx.emoji2.text.MetadataRepo create(android.content.res.AssetManager, String) throws java.io.IOException;
+  }
+
+  @AnyThread @RequiresApi(19) public class TypefaceEmojiRasterizer {
+    method public void draw(android.graphics.Canvas, float, float, android.graphics.Paint);
+    method public int getCodepointAt(int);
+    method public int getCodepointsLength();
+    method public int getHeight();
+    method public android.graphics.Typeface getTypeface();
+    method public int getWidth();
+    method public boolean isDefaultEmoji();
+    method public boolean isPreferredSystemRender();
+  }
+
+}
+
diff --git a/emoji2/emoji2/api/public_plus_experimental_1.4.0-beta01.txt b/emoji2/emoji2/api/public_plus_experimental_1.4.0-beta01.txt
new file mode 100644
index 0000000..11d9335
--- /dev/null
+++ b/emoji2/emoji2/api/public_plus_experimental_1.4.0-beta01.txt
@@ -0,0 +1,131 @@
+// Signature format: 4.0
+package androidx.emoji2.text {
+
+  public final class DefaultEmojiCompatConfig {
+    method public static androidx.emoji2.text.FontRequestEmojiCompatConfig? create(android.content.Context);
+  }
+
+  @AnyThread public class EmojiCompat {
+    method public static androidx.emoji2.text.EmojiCompat get();
+    method public String getAssetSignature();
+    method public int getEmojiEnd(CharSequence, @IntRange(from=0) int);
+    method public int getEmojiMatch(CharSequence, @IntRange(from=0) int);
+    method public int getEmojiStart(CharSequence, @IntRange(from=0) int);
+    method public int getLoadState();
+    method public static boolean handleDeleteSurroundingText(android.view.inputmethod.InputConnection, android.text.Editable, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
+    method public static boolean handleOnKeyDown(android.text.Editable, int, android.view.KeyEvent);
+    method @Deprecated public boolean hasEmojiGlyph(CharSequence);
+    method @Deprecated public boolean hasEmojiGlyph(CharSequence, @IntRange(from=0) int);
+    method public static androidx.emoji2.text.EmojiCompat? init(android.content.Context);
+    method public static androidx.emoji2.text.EmojiCompat init(androidx.emoji2.text.EmojiCompat.Config);
+    method public static boolean isConfigured();
+    method public void load();
+    method @CheckResult public CharSequence? process(CharSequence?);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, int);
+    method public void registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public void unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public void updateEditorInfo(android.view.inputmethod.EditorInfo);
+    field public static final String EDITOR_INFO_METAVERSION_KEY = "android.support.text.emoji.emojiCompat_metadataVersion";
+    field public static final String EDITOR_INFO_REPLACE_ALL_KEY = "android.support.text.emoji.emojiCompat_replaceAll";
+    field public static final int EMOJI_FALLBACK = 2; // 0x2
+    field public static final int EMOJI_SUPPORTED = 1; // 0x1
+    field public static final int EMOJI_UNSUPPORTED = 0; // 0x0
+    field public static final int LOAD_STATE_DEFAULT = 3; // 0x3
+    field public static final int LOAD_STATE_FAILED = 2; // 0x2
+    field public static final int LOAD_STATE_LOADING = 0; // 0x0
+    field public static final int LOAD_STATE_SUCCEEDED = 1; // 0x1
+    field public static final int LOAD_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int LOAD_STRATEGY_MANUAL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_ALL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int REPLACE_STRATEGY_NON_EXISTENT = 2; // 0x2
+  }
+
+  public abstract static class EmojiCompat.Config {
+    ctor protected EmojiCompat.Config(androidx.emoji2.text.EmojiCompat.MetadataRepoLoader);
+    method protected final androidx.emoji2.text.EmojiCompat.MetadataRepoLoader getMetadataRepoLoader();
+    method public androidx.emoji2.text.EmojiCompat.Config registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorColor(@ColorInt int);
+    method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorEnabled(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setGlyphChecker(androidx.emoji2.text.EmojiCompat.GlyphChecker);
+    method public androidx.emoji2.text.EmojiCompat.Config setMetadataLoadStrategy(int);
+    method public androidx.emoji2.text.EmojiCompat.Config setReplaceAll(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setSpanFactory(androidx.emoji2.text.EmojiCompat.SpanFactory);
+    method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean, java.util.List<java.lang.Integer!>?);
+    method public androidx.emoji2.text.EmojiCompat.Config unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+  }
+
+  public static interface EmojiCompat.GlyphChecker {
+    method public boolean hasGlyph(CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+  }
+
+  public abstract static class EmojiCompat.InitCallback {
+    ctor public EmojiCompat.InitCallback();
+    method public void onFailed(Throwable?);
+    method public void onInitialized();
+  }
+
+  public static interface EmojiCompat.MetadataRepoLoader {
+    method public void load(androidx.emoji2.text.EmojiCompat.MetadataRepoLoaderCallback);
+  }
+
+  public abstract static class EmojiCompat.MetadataRepoLoaderCallback {
+    ctor public EmojiCompat.MetadataRepoLoaderCallback();
+    method public abstract void onFailed(Throwable?);
+    method public abstract void onLoaded(androidx.emoji2.text.MetadataRepo);
+  }
+
+  public static interface EmojiCompat.SpanFactory {
+    method @RequiresApi(19) public androidx.emoji2.text.EmojiSpan createSpan(androidx.emoji2.text.TypefaceEmojiRasterizer);
+  }
+
+  public class EmojiCompatInitializer implements androidx.startup.Initializer<java.lang.Boolean> {
+    ctor public EmojiCompatInitializer();
+    method public Boolean create(android.content.Context);
+    method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>!> dependencies();
+  }
+
+  @RequiresApi(19) public abstract class EmojiSpan extends android.text.style.ReplacementSpan {
+    method public int getSize(android.graphics.Paint, CharSequence!, int, int, android.graphics.Paint.FontMetricsInt?);
+    method public final androidx.emoji2.text.TypefaceEmojiRasterizer getTypefaceRasterizer();
+  }
+
+  public class FontRequestEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+    ctor public FontRequestEmojiCompatConfig(android.content.Context, androidx.core.provider.FontRequest);
+    method @Deprecated public androidx.emoji2.text.FontRequestEmojiCompatConfig setHandler(android.os.Handler?);
+    method public androidx.emoji2.text.FontRequestEmojiCompatConfig setLoadingExecutor(java.util.concurrent.Executor);
+    method public androidx.emoji2.text.FontRequestEmojiCompatConfig setRetryPolicy(androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy?);
+  }
+
+  public static class FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy extends androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy(long);
+    method public long getRetryDelay();
+  }
+
+  public abstract static class FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.RetryPolicy();
+    method public abstract long getRetryDelay();
+  }
+
+  @AnyThread @RequiresApi(19) public final class MetadataRepo {
+    method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.io.InputStream) throws java.io.IOException;
+    method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.nio.ByteBuffer) throws java.io.IOException;
+    method public static androidx.emoji2.text.MetadataRepo create(android.content.res.AssetManager, String) throws java.io.IOException;
+  }
+
+  @AnyThread @RequiresApi(19) public class TypefaceEmojiRasterizer {
+    method public void draw(android.graphics.Canvas, float, float, android.graphics.Paint);
+    method public int getCodepointAt(int);
+    method public int getCodepointsLength();
+    method public int getHeight();
+    method public android.graphics.Typeface getTypeface();
+    method public int getWidth();
+    method public boolean isDefaultEmoji();
+    method public boolean isPreferredSystemRender();
+  }
+
+}
+
diff --git a/ads/ads-identifier/api/res-current.txt b/emoji2/emoji2/api/res-1.4.0-beta01.txt
similarity index 100%
copy from ads/ads-identifier/api/res-current.txt
copy to emoji2/emoji2/api/res-1.4.0-beta01.txt
diff --git a/emoji2/emoji2/api/restricted_1.4.0-beta01.txt b/emoji2/emoji2/api/restricted_1.4.0-beta01.txt
new file mode 100644
index 0000000..11d9335
--- /dev/null
+++ b/emoji2/emoji2/api/restricted_1.4.0-beta01.txt
@@ -0,0 +1,131 @@
+// Signature format: 4.0
+package androidx.emoji2.text {
+
+  public final class DefaultEmojiCompatConfig {
+    method public static androidx.emoji2.text.FontRequestEmojiCompatConfig? create(android.content.Context);
+  }
+
+  @AnyThread public class EmojiCompat {
+    method public static androidx.emoji2.text.EmojiCompat get();
+    method public String getAssetSignature();
+    method public int getEmojiEnd(CharSequence, @IntRange(from=0) int);
+    method public int getEmojiMatch(CharSequence, @IntRange(from=0) int);
+    method public int getEmojiStart(CharSequence, @IntRange(from=0) int);
+    method public int getLoadState();
+    method public static boolean handleDeleteSurroundingText(android.view.inputmethod.InputConnection, android.text.Editable, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
+    method public static boolean handleOnKeyDown(android.text.Editable, int, android.view.KeyEvent);
+    method @Deprecated public boolean hasEmojiGlyph(CharSequence);
+    method @Deprecated public boolean hasEmojiGlyph(CharSequence, @IntRange(from=0) int);
+    method public static androidx.emoji2.text.EmojiCompat? init(android.content.Context);
+    method public static androidx.emoji2.text.EmojiCompat init(androidx.emoji2.text.EmojiCompat.Config);
+    method public static boolean isConfigured();
+    method public void load();
+    method @CheckResult public CharSequence? process(CharSequence?);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @CheckResult public CharSequence? process(CharSequence?, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, int);
+    method public void registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public void unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public void updateEditorInfo(android.view.inputmethod.EditorInfo);
+    field public static final String EDITOR_INFO_METAVERSION_KEY = "android.support.text.emoji.emojiCompat_metadataVersion";
+    field public static final String EDITOR_INFO_REPLACE_ALL_KEY = "android.support.text.emoji.emojiCompat_replaceAll";
+    field public static final int EMOJI_FALLBACK = 2; // 0x2
+    field public static final int EMOJI_SUPPORTED = 1; // 0x1
+    field public static final int EMOJI_UNSUPPORTED = 0; // 0x0
+    field public static final int LOAD_STATE_DEFAULT = 3; // 0x3
+    field public static final int LOAD_STATE_FAILED = 2; // 0x2
+    field public static final int LOAD_STATE_LOADING = 0; // 0x0
+    field public static final int LOAD_STATE_SUCCEEDED = 1; // 0x1
+    field public static final int LOAD_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int LOAD_STRATEGY_MANUAL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_ALL = 1; // 0x1
+    field public static final int REPLACE_STRATEGY_DEFAULT = 0; // 0x0
+    field public static final int REPLACE_STRATEGY_NON_EXISTENT = 2; // 0x2
+  }
+
+  public abstract static class EmojiCompat.Config {
+    ctor protected EmojiCompat.Config(androidx.emoji2.text.EmojiCompat.MetadataRepoLoader);
+    method protected final androidx.emoji2.text.EmojiCompat.MetadataRepoLoader getMetadataRepoLoader();
+    method public androidx.emoji2.text.EmojiCompat.Config registerInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+    method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorColor(@ColorInt int);
+    method public androidx.emoji2.text.EmojiCompat.Config setEmojiSpanIndicatorEnabled(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setGlyphChecker(androidx.emoji2.text.EmojiCompat.GlyphChecker);
+    method public androidx.emoji2.text.EmojiCompat.Config setMetadataLoadStrategy(int);
+    method public androidx.emoji2.text.EmojiCompat.Config setReplaceAll(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setSpanFactory(androidx.emoji2.text.EmojiCompat.SpanFactory);
+    method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean);
+    method public androidx.emoji2.text.EmojiCompat.Config setUseEmojiAsDefaultStyle(boolean, java.util.List<java.lang.Integer!>?);
+    method public androidx.emoji2.text.EmojiCompat.Config unregisterInitCallback(androidx.emoji2.text.EmojiCompat.InitCallback);
+  }
+
+  public static interface EmojiCompat.GlyphChecker {
+    method public boolean hasGlyph(CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+  }
+
+  public abstract static class EmojiCompat.InitCallback {
+    ctor public EmojiCompat.InitCallback();
+    method public void onFailed(Throwable?);
+    method public void onInitialized();
+  }
+
+  public static interface EmojiCompat.MetadataRepoLoader {
+    method public void load(androidx.emoji2.text.EmojiCompat.MetadataRepoLoaderCallback);
+  }
+
+  public abstract static class EmojiCompat.MetadataRepoLoaderCallback {
+    ctor public EmojiCompat.MetadataRepoLoaderCallback();
+    method public abstract void onFailed(Throwable?);
+    method public abstract void onLoaded(androidx.emoji2.text.MetadataRepo);
+  }
+
+  public static interface EmojiCompat.SpanFactory {
+    method @RequiresApi(19) public androidx.emoji2.text.EmojiSpan createSpan(androidx.emoji2.text.TypefaceEmojiRasterizer);
+  }
+
+  public class EmojiCompatInitializer implements androidx.startup.Initializer<java.lang.Boolean> {
+    ctor public EmojiCompatInitializer();
+    method public Boolean create(android.content.Context);
+    method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>!> dependencies();
+  }
+
+  @RequiresApi(19) public abstract class EmojiSpan extends android.text.style.ReplacementSpan {
+    method public int getSize(android.graphics.Paint, CharSequence!, int, int, android.graphics.Paint.FontMetricsInt?);
+    method public final androidx.emoji2.text.TypefaceEmojiRasterizer getTypefaceRasterizer();
+  }
+
+  public class FontRequestEmojiCompatConfig extends androidx.emoji2.text.EmojiCompat.Config {
+    ctor public FontRequestEmojiCompatConfig(android.content.Context, androidx.core.provider.FontRequest);
+    method @Deprecated public androidx.emoji2.text.FontRequestEmojiCompatConfig setHandler(android.os.Handler?);
+    method public androidx.emoji2.text.FontRequestEmojiCompatConfig setLoadingExecutor(java.util.concurrent.Executor);
+    method public androidx.emoji2.text.FontRequestEmojiCompatConfig setRetryPolicy(androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy?);
+  }
+
+  public static class FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy extends androidx.emoji2.text.FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.ExponentialBackoffRetryPolicy(long);
+    method public long getRetryDelay();
+  }
+
+  public abstract static class FontRequestEmojiCompatConfig.RetryPolicy {
+    ctor public FontRequestEmojiCompatConfig.RetryPolicy();
+    method public abstract long getRetryDelay();
+  }
+
+  @AnyThread @RequiresApi(19) public final class MetadataRepo {
+    method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.io.InputStream) throws java.io.IOException;
+    method public static androidx.emoji2.text.MetadataRepo create(android.graphics.Typeface, java.nio.ByteBuffer) throws java.io.IOException;
+    method public static androidx.emoji2.text.MetadataRepo create(android.content.res.AssetManager, String) throws java.io.IOException;
+  }
+
+  @AnyThread @RequiresApi(19) public class TypefaceEmojiRasterizer {
+    method public void draw(android.graphics.Canvas, float, float, android.graphics.Paint);
+    method public int getCodepointAt(int);
+    method public int getCodepointsLength();
+    method public int getHeight();
+    method public android.graphics.Typeface getTypeface();
+    method public int getWidth();
+    method public boolean isDefaultEmoji();
+    method public boolean isPreferredSystemRender();
+  }
+
+}
+
diff --git a/external/paparazzi/paparazzi/src/main/java/app/cash/paparazzi/Paparazzi.kt b/external/paparazzi/paparazzi/src/main/java/app/cash/paparazzi/Paparazzi.kt
index 0a96bcf..40ce39e 100644
--- a/external/paparazzi/paparazzi/src/main/java/app/cash/paparazzi/Paparazzi.kt
+++ b/external/paparazzi/paparazzi/src/main/java/app/cash/paparazzi/Paparazzi.kt
@@ -572,10 +572,19 @@
       val snapshotInvalidations = recomposer.javaClass
         .getDeclaredField("snapshotInvalidations")
         .apply { isAccessible = true }
-        .get(recomposer) as MutableCollection<*>
+        .get(recomposer)
       compositionInvalidations.clear()
-      snapshotInvalidations.clear()
       applyObservers.clear()
+
+      if (snapshotInvalidations is MutableCollection<*>) {
+        snapshotInvalidations.clear()
+      } else {
+        // backed by IdentityArraySet
+        snapshotInvalidations.javaClass
+          .getDeclaredMethod("clear")
+          .apply { isAccessible = true }
+          .invoke(snapshotInvalidations)
+      }
     }
 
     val dispatcher =
diff --git a/fragment/fragment-ktx/api/current.txt b/fragment/fragment-ktx/api/current.txt
index 052abdb..b93e06b 100644
--- a/fragment/fragment-ktx/api/current.txt
+++ b/fragment/fragment-ktx/api/current.txt
@@ -10,7 +10,7 @@
 
   public final class FragmentManagerKt {
     method public static inline void commit(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
-    method public static inline void commitNow(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+    method @MainThread public static inline void commitNow(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
     method @Deprecated public static inline void transaction(androidx.fragment.app.FragmentManager, optional boolean now, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
   }
 
diff --git a/fragment/fragment-ktx/api/public_plus_experimental_current.txt b/fragment/fragment-ktx/api/public_plus_experimental_current.txt
index 052abdb..b93e06b 100644
--- a/fragment/fragment-ktx/api/public_plus_experimental_current.txt
+++ b/fragment/fragment-ktx/api/public_plus_experimental_current.txt
@@ -10,7 +10,7 @@
 
   public final class FragmentManagerKt {
     method public static inline void commit(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
-    method public static inline void commitNow(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+    method @MainThread public static inline void commitNow(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
     method @Deprecated public static inline void transaction(androidx.fragment.app.FragmentManager, optional boolean now, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
   }
 
diff --git a/fragment/fragment-ktx/api/restricted_current.txt b/fragment/fragment-ktx/api/restricted_current.txt
index 052abdb..b93e06b 100644
--- a/fragment/fragment-ktx/api/restricted_current.txt
+++ b/fragment/fragment-ktx/api/restricted_current.txt
@@ -10,7 +10,7 @@
 
   public final class FragmentManagerKt {
     method public static inline void commit(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
-    method public static inline void commitNow(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
+    method @MainThread public static inline void commitNow(androidx.fragment.app.FragmentManager, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
     method @Deprecated public static inline void transaction(androidx.fragment.app.FragmentManager, optional boolean now, optional boolean allowStateLoss, kotlin.jvm.functions.Function1<? super androidx.fragment.app.FragmentTransaction,kotlin.Unit> body);
   }
 
diff --git a/fragment/fragment-ktx/src/main/java/androidx/fragment/app/FragmentManager.kt b/fragment/fragment-ktx/src/main/java/androidx/fragment/app/FragmentManager.kt
index 1d4b602..e9c1c07 100644
--- a/fragment/fragment-ktx/src/main/java/androidx/fragment/app/FragmentManager.kt
+++ b/fragment/fragment-ktx/src/main/java/androidx/fragment/app/FragmentManager.kt
@@ -16,6 +16,8 @@
 
 package androidx.fragment.app
 
+import androidx.annotation.MainThread
+
 /**
  * Run [body] in a [FragmentTransaction] which is automatically committed if it completes without
  * exception.
@@ -44,6 +46,7 @@
  * [allowStateLoss] is set to `true` in which case [FragmentTransaction.commitNowAllowingStateLoss]
  * will be used.
  */
+@MainThread
 public inline fun FragmentManager.commitNow(
     allowStateLoss: Boolean = false,
     body: FragmentTransaction.() -> Unit
diff --git a/fragment/fragment/api/current.txt b/fragment/fragment/api/current.txt
index b44ca72..2f167cf 100644
--- a/fragment/fragment/api/current.txt
+++ b/fragment/fragment/api/current.txt
@@ -14,6 +14,7 @@
     method public void onCancel(android.content.DialogInterface);
     method @MainThread public android.app.Dialog onCreateDialog(android.os.Bundle?);
     method @CallSuper public void onDismiss(android.content.DialogInterface);
+    method public final androidx.activity.ComponentDialog requireComponentDialog();
     method public final android.app.Dialog requireDialog();
     method public void setCancelable(boolean);
     method public void setShowsDialog(boolean);
@@ -277,7 +278,7 @@
     method public final void clearFragmentResultListener(String);
     method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
     method @Deprecated public static void enableDebugLogging(boolean);
-    method public boolean executePendingTransactions();
+    method @MainThread public boolean executePendingTransactions();
     method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
     method public androidx.fragment.app.Fragment? findFragmentById(@IdRes int);
     method public androidx.fragment.app.Fragment? findFragmentByTag(String?);
@@ -293,8 +294,8 @@
     method public void popBackStack();
     method public void popBackStack(String?, int);
     method public void popBackStack(int, int);
-    method public boolean popBackStackImmediate();
-    method public boolean popBackStackImmediate(String?, int);
+    method @MainThread public boolean popBackStackImmediate();
+    method @MainThread public boolean popBackStackImmediate(String?, int);
     method public boolean popBackStackImmediate(int, int);
     method public void putFragment(android.os.Bundle, String, androidx.fragment.app.Fragment);
     method public void registerFragmentLifecycleCallbacks(androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks, boolean);
@@ -403,8 +404,8 @@
     method public androidx.fragment.app.FragmentTransaction attach(androidx.fragment.app.Fragment);
     method public abstract int commit();
     method public abstract int commitAllowingStateLoss();
-    method public abstract void commitNow();
-    method public abstract void commitNowAllowingStateLoss();
+    method @MainThread public abstract void commitNow();
+    method @MainThread public abstract void commitNowAllowingStateLoss();
     method public androidx.fragment.app.FragmentTransaction detach(androidx.fragment.app.Fragment);
     method public androidx.fragment.app.FragmentTransaction disallowAddToBackStack();
     method public androidx.fragment.app.FragmentTransaction hide(androidx.fragment.app.Fragment);
diff --git a/fragment/fragment/api/public_plus_experimental_current.txt b/fragment/fragment/api/public_plus_experimental_current.txt
index b44ca72..2f167cf 100644
--- a/fragment/fragment/api/public_plus_experimental_current.txt
+++ b/fragment/fragment/api/public_plus_experimental_current.txt
@@ -14,6 +14,7 @@
     method public void onCancel(android.content.DialogInterface);
     method @MainThread public android.app.Dialog onCreateDialog(android.os.Bundle?);
     method @CallSuper public void onDismiss(android.content.DialogInterface);
+    method public final androidx.activity.ComponentDialog requireComponentDialog();
     method public final android.app.Dialog requireDialog();
     method public void setCancelable(boolean);
     method public void setShowsDialog(boolean);
@@ -277,7 +278,7 @@
     method public final void clearFragmentResultListener(String);
     method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
     method @Deprecated public static void enableDebugLogging(boolean);
-    method public boolean executePendingTransactions();
+    method @MainThread public boolean executePendingTransactions();
     method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
     method public androidx.fragment.app.Fragment? findFragmentById(@IdRes int);
     method public androidx.fragment.app.Fragment? findFragmentByTag(String?);
@@ -293,8 +294,8 @@
     method public void popBackStack();
     method public void popBackStack(String?, int);
     method public void popBackStack(int, int);
-    method public boolean popBackStackImmediate();
-    method public boolean popBackStackImmediate(String?, int);
+    method @MainThread public boolean popBackStackImmediate();
+    method @MainThread public boolean popBackStackImmediate(String?, int);
     method public boolean popBackStackImmediate(int, int);
     method public void putFragment(android.os.Bundle, String, androidx.fragment.app.Fragment);
     method public void registerFragmentLifecycleCallbacks(androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks, boolean);
@@ -403,8 +404,8 @@
     method public androidx.fragment.app.FragmentTransaction attach(androidx.fragment.app.Fragment);
     method public abstract int commit();
     method public abstract int commitAllowingStateLoss();
-    method public abstract void commitNow();
-    method public abstract void commitNowAllowingStateLoss();
+    method @MainThread public abstract void commitNow();
+    method @MainThread public abstract void commitNowAllowingStateLoss();
     method public androidx.fragment.app.FragmentTransaction detach(androidx.fragment.app.Fragment);
     method public androidx.fragment.app.FragmentTransaction disallowAddToBackStack();
     method public androidx.fragment.app.FragmentTransaction hide(androidx.fragment.app.Fragment);
diff --git a/fragment/fragment/api/restricted_current.txt b/fragment/fragment/api/restricted_current.txt
index cb5b014..83801ad 100644
--- a/fragment/fragment/api/restricted_current.txt
+++ b/fragment/fragment/api/restricted_current.txt
@@ -14,6 +14,7 @@
     method public void onCancel(android.content.DialogInterface);
     method @MainThread public android.app.Dialog onCreateDialog(android.os.Bundle?);
     method @CallSuper public void onDismiss(android.content.DialogInterface);
+    method public final androidx.activity.ComponentDialog requireComponentDialog();
     method public final android.app.Dialog requireDialog();
     method public void setCancelable(boolean);
     method public void setShowsDialog(boolean);
@@ -281,7 +282,7 @@
     method public final void clearFragmentResultListener(String);
     method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
     method @Deprecated public static void enableDebugLogging(boolean);
-    method public boolean executePendingTransactions();
+    method @MainThread public boolean executePendingTransactions();
     method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
     method public androidx.fragment.app.Fragment? findFragmentById(@IdRes int);
     method public androidx.fragment.app.Fragment? findFragmentByTag(String?);
@@ -298,8 +299,8 @@
     method public void popBackStack();
     method public void popBackStack(String?, int);
     method public void popBackStack(int, int);
-    method public boolean popBackStackImmediate();
-    method public boolean popBackStackImmediate(String?, int);
+    method @MainThread public boolean popBackStackImmediate();
+    method @MainThread public boolean popBackStackImmediate(String?, int);
     method public boolean popBackStackImmediate(int, int);
     method public void putFragment(android.os.Bundle, String, androidx.fragment.app.Fragment);
     method public void registerFragmentLifecycleCallbacks(androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks, boolean);
@@ -408,8 +409,8 @@
     method public androidx.fragment.app.FragmentTransaction attach(androidx.fragment.app.Fragment);
     method public abstract int commit();
     method public abstract int commitAllowingStateLoss();
-    method public abstract void commitNow();
-    method public abstract void commitNowAllowingStateLoss();
+    method @MainThread public abstract void commitNow();
+    method @MainThread public abstract void commitNowAllowingStateLoss();
     method public androidx.fragment.app.FragmentTransaction detach(androidx.fragment.app.Fragment);
     method public androidx.fragment.app.FragmentTransaction disallowAddToBackStack();
     method public androidx.fragment.app.FragmentTransaction hide(androidx.fragment.app.Fragment);
diff --git a/fragment/fragment/build.gradle b/fragment/fragment/build.gradle
index 614d3fb..248f510 100644
--- a/fragment/fragment/build.gradle
+++ b/fragment/fragment/build.gradle
@@ -34,7 +34,7 @@
     api("androidx.lifecycle:lifecycle-livedata-core:2.6.1")
     api("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
     api("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1")
-    implementation("androidx.profileinstaller:profileinstaller:1.2.1")
+    implementation("androidx.profileinstaller:profileinstaller:1.3.0")
     api("androidx.savedstate:savedstate:1.2.1")
     api("androidx.annotation:annotation-experimental:1.0.0")
     api(libs.kotlinStdlib)
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/DialogFragmentTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/DialogFragmentTest.kt
index 3e1efd5..b08ff70 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/DialogFragmentTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/DialogFragmentTest.kt
@@ -27,6 +27,7 @@
 import android.widget.EditText
 import android.widget.FrameLayout
 import android.widget.TextView
+import androidx.activity.ComponentDialog
 import androidx.fragment.app.test.EmptyFragmentTestActivity
 import androidx.fragment.test.R
 import androidx.lifecycle.ViewModelStore
@@ -38,6 +39,7 @@
 import androidx.testutils.withUse
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Assert.assertThrows
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -532,6 +534,86 @@
         fc2.shutdown(viewModelStore)
     }
 
+    @Test
+    fun testRequireDialog() {
+        val dialogFragment = TestDialogFragment()
+        val fm = activityTestRule.activity.supportFragmentManager
+
+        activityTestRule.runOnUiThread {
+            fm.beginTransaction()
+                .add(dialogFragment, null)
+                .commitNow()
+        }
+
+        val dialog = dialogFragment.requireDialog()
+        activityTestRule.runOnUiThread {
+            assertWithMessage("requireDialog() should return")
+                .that(dialogFragment.requireDialog())
+                .isNotNull()
+        }
+
+        activityTestRule.runOnUiThread {
+            dialog.cancel()
+            fm.beginTransaction()
+                .remove(dialogFragment)
+                .commitNow()
+            assertThrows(IllegalStateException::class.java) {
+                dialogFragment.requireDialog()
+            }
+        }
+    }
+
+    @Test
+    fun testRequireComponentDialog() {
+        val dialogFragment = DialogFragment()
+        val fm = activityTestRule.activity.supportFragmentManager
+
+        lateinit var componentDialog: ComponentDialog
+        activityTestRule.runOnUiThread {
+            componentDialog = ComponentDialog(activityTestRule.activity)
+            dialogFragment.setupDialog(componentDialog, 1)
+            fm.beginTransaction()
+                .add(dialogFragment, null)
+                .commitNow()
+        }
+
+        activityTestRule.runOnUiThread {
+            assertWithMessage("requireComponentDialog() should return")
+                .that(dialogFragment.requireComponentDialog())
+                .isNotNull()
+        }
+
+        activityTestRule.runOnUiThread {
+            componentDialog.cancel()
+            fm.beginTransaction()
+                .remove(dialogFragment)
+                .commitNow()
+            assertThrows(IllegalStateException::class.java) {
+                dialogFragment.requireComponentDialog()
+            }
+        }
+    }
+
+    @Test
+    fun testRequireComponentDialog_notComponentDialog() {
+        val dialogFragment = TestDialogFragment()
+        val fm = activityTestRule.activity.supportFragmentManager
+
+        activityTestRule.runOnUiThread {
+            val componentDialog = ComponentDialog(activityTestRule.activity)
+            dialogFragment.setupDialog(componentDialog, 1)
+            fm.beginTransaction()
+                .add(dialogFragment, null)
+                .commitNow()
+        }
+
+        activityTestRule.runOnUiThread {
+            assertThrows(IllegalStateException::class.java) {
+                dialogFragment.requireComponentDialog()
+            }
+        }
+    }
+
     class TestDialogFragment(val setShowsDialog: Boolean = false) : DialogFragment() {
         var onCancelCalled = false
 
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentResultTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentResultTest.kt
index a7112a1..4535f2c 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentResultTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentResultTest.kt
@@ -22,6 +22,8 @@
 import androidx.activity.result.ActivityResult
 import androidx.fragment.app.test.FragmentTestActivity
 import androidx.fragment.test.R
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
 import androidx.test.core.app.ActivityScenario
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
@@ -200,6 +202,36 @@
     }
 
     @Test
+    fun testClearResultListenerInCallbackWhenStarted() {
+        withUse(ActivityScenario.launch(FragmentTestActivity::class.java)) {
+            val fm = withActivity {
+                setContentView(R.layout.simple_container)
+                supportFragmentManager
+            }
+
+            val fragment1 = ClearResultFragment(Lifecycle.State.RESUMED)
+
+            // set a result while no listener is available so it is stored in the fragment manager
+            fm.setFragmentResult("requestKey", Bundle())
+
+            // adding the fragment is going to execute and clear its listener.
+            withActivity {
+                fm.beginTransaction()
+                    .add(R.id.fragmentContainer, fragment1)
+                    .commitNow()
+            }
+
+            withActivity {
+                // Send a second result, which should not be received by fragment1
+                fm.setFragmentResult("requestKey", Bundle())
+            }
+
+            assertWithMessage("the listener should only be executed once")
+                .that(fragment1.callbackCount).isEqualTo(1)
+        }
+    }
+
+    @Test
     fun testResetResultListener() {
        withUse(ActivityScenario.launch(FragmentTestActivity::class.java)) {
             val fm = withActivity {
@@ -453,19 +485,24 @@
     }
 }
 
-class ClearResultFragment : StrictFragment() {
+class ClearResultFragment(
+    private val setLifecycleInState: Lifecycle.State = Lifecycle.State.CREATED
+) : StrictFragment() {
     var callbackCount = 0
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-
-        parentFragmentManager.setFragmentResultListener(
-            "requestKey", this,
-            FragmentResultListener { _, _ ->
-                callbackCount++
-                parentFragmentManager.clearFragmentResultListener("requestKey")
+        lifecycle.addObserver(LifecycleEventObserver { _, event ->
+            if (Lifecycle.Event.upTo(setLifecycleInState) == event) {
+                parentFragmentManager.setFragmentResultListener(
+                    "requestKey", this,
+                    FragmentResultListener { _, _ ->
+                        callbackCount++
+                        parentFragmentManager.clearFragmentResultListener("requestKey")
+                    }
+                )
             }
-        )
+        })
     }
 }
 
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentViewLifecycleTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentViewLifecycleTest.kt
index 9f4e213..21413a9 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentViewLifecycleTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentViewLifecycleTest.kt
@@ -36,14 +36,14 @@
 import androidx.test.filters.LargeTest
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
-import org.junit.Assert.fail
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
 import leakcanary.DetectLeaksAfterTestSuccess
+import org.junit.Assert.fail
+import org.junit.Rule
+import org.junit.Test
 import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
 @LargeTest
@@ -83,8 +83,8 @@
         } catch (expected: IllegalStateException) {
             assertThat(expected)
                 .hasMessageThat().contains(
-                    "Can't access the Fragment View's LifecycleOwner when" +
-                        " getView() is null i.e., before onCreateView() or after onDestroyView()"
+                    "Can't access the Fragment View's LifecycleOwner for $fragment when " +
+                        "getView() is null i.e., before onCreateView() or after onDestroyView()"
                 )
         }
     }
@@ -228,8 +228,8 @@
         } catch (expected: IllegalStateException) {
             assertThat(expected)
                 .hasMessageThat().contains(
-                    "Can't access the Fragment View's LifecycleOwner when" +
-                        " getView() is null i.e., before onCreateView() or after onDestroyView()"
+                    "Can't access the Fragment View's LifecycleOwner for $fragment when " +
+                        "getView() is null i.e., before onCreateView() or after onDestroyView()"
                 )
         }
     }
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/LoaderTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/LoaderTest.kt
index 97f5ae8..af1ead3 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/LoaderTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/LoaderTest.kt
@@ -29,10 +29,8 @@
 import androidx.testutils.waitForExecution
 import com.google.common.truth.Truth.assertThat
 import java.lang.ref.WeakReference
-import leakcanary.DetectLeaksAfterTestSuccess
 import org.junit.Rule
 import org.junit.Test
-import org.junit.rules.RuleChain
 import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
@@ -40,12 +38,16 @@
 class LoaderTest {
 
     @Suppress("DEPRECATION")
+    @get:Rule
     var activityRule = androidx.test.rule.ActivityTestRule(LoaderActivity::class.java)
 
+    // TODO(b/272519998): Add back in leak detection rule chain once leak addressed by platform
     // Detect leaks BEFORE and AFTER activity is destroyed
+    /*
     @get:Rule
     val ruleChain: RuleChain = RuleChain.outerRule(DetectLeaksAfterTestSuccess())
         .around(activityRule)
+    */
 
     /**
      * Test to ensure that there is no Activity leak due to Loader
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 8dacb98..ddc3195 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/OnBackStackChangedListenerTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/OnBackStackChangedListenerTest.kt
@@ -19,9 +19,6 @@
 import androidx.fragment.app.FragmentManager.OnBackStackChangedListener
 import androidx.fragment.app.test.FragmentTestActivity
 import androidx.fragment.test.R
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleEventObserver
-import androidx.lifecycle.LifecycleOwner
 import androidx.test.core.app.ActivityScenario
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -314,15 +311,6 @@
                 }
             }
             fragmentManager.addOnBackStackChangedListener(listener)
-            withActivity {
-                fragment.lifecycle.addObserver(object : LifecycleEventObserver {
-                    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
-                        if (event == Lifecycle.Event.ON_START) {
-                            fragmentManager.removeOnBackStackChangedListener(listener)
-                        }
-                    }
-                })
-            }
 
             fragmentManager.beginTransaction()
                 .setReorderingAllowed(true)
@@ -332,6 +320,39 @@
             executePendingTransactions()
 
             assertThat(startedCount).isEqualTo(1)
+            assertThat(committedCount).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun testOnBackChangeNoAddToBackstack() {
+        with(ActivityScenario.launch(FragmentTestActivity::class.java)) {
+            val fragmentManager = withActivity { supportFragmentManager }
+
+            val fragment = StrictFragment()
+            var startedCount = 0
+            var committedCount = 0
+            val listener = object : OnBackStackChangedListener {
+                override fun onBackStackChanged() { /* nothing */ }
+
+                override fun onBackStackChangeStarted(fragment: Fragment, pop: Boolean) {
+                    startedCount++
+                }
+
+                override fun onBackStackChangeCommitted(fragment: Fragment, pop: Boolean) {
+                    committedCount++
+                }
+            }
+            fragmentManager.addOnBackStackChangedListener(listener)
+
+            withActivity {
+                fragmentManager.beginTransaction()
+                    .setReorderingAllowed(true)
+                    .add(R.id.content, fragment)
+                    .commitNow()
+            }
+
+            assertThat(startedCount).isEqualTo(0)
             assertThat(committedCount).isEqualTo(0)
         }
     }
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java b/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java
index 7f942ad..2ff6f45 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java
@@ -649,6 +649,26 @@
         return dialog;
     }
 
+    /**
+     * Return the {@link ComponentDialog} this fragment is currently controlling.
+     *
+     * @throws IllegalStateException if the Dialog found is not a ComponentDialog or
+     * if Dialog has not yet been created (before {@link #onCreateDialog(Bundle)})
+     * or has been destroyed (after {@link #onDestroyView()}.
+     *
+     * @see #requireDialog()
+     */
+    @NonNull
+    public final ComponentDialog requireComponentDialog() {
+        Dialog dialog = requireDialog();
+        if (!(dialog instanceof ComponentDialog)) {
+            throw new IllegalStateException("DialogFragment " + this
+                    + " did not return a ComponentDialog instance from "
+                    + "requireDialog(). The actual Dialog is " + dialog);
+        }
+        return (ComponentDialog) dialog;
+    }
+
     @StyleRes
     public int getTheme() {
         return mTheme;
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 bbc43d0..c4081ff 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
@@ -379,8 +379,9 @@
     @NonNull
     public LifecycleOwner getViewLifecycleOwner() {
         if (mViewLifecycleOwner == null) {
-            throw new IllegalStateException("Can't access the Fragment View's LifecycleOwner when "
-                    + "getView() is null i.e., before onCreateView() or after onDestroyView()");
+            throw new IllegalStateException("Can't access the Fragment View's LifecycleOwner "
+                    + "for " + this + " when getView() is null i.e., before onCreateView() or "
+                    + "after onDestroyView()");
         }
         return mViewLifecycleOwner;
     }
@@ -3108,6 +3109,10 @@
             // Tell the fragment's new view about it before we tell anyone listening
             // to mViewLifecycleOwnerLiveData and before onViewCreated, so that calls to
             // ViewTree get() methods return something meaningful
+            if (FragmentManager.isLoggingEnabled(Log.DEBUG)) {
+                Log.d(FragmentManager.TAG, "Setting ViewLifecycleOwner on View " + mView
+                        + " for Fragment " + this);
+            }
             ViewTreeLifecycleOwner.set(mView, mViewLifecycleOwner);
             ViewTreeViewModelStoreOwner.set(mView, mViewLifecycleOwner);
             ViewTreeSavedStateRegistryOwner.set(mView, mViewLifecycleOwner);
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 5393b86..13c6bc2 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
@@ -635,6 +635,7 @@
      * @return Returns true if there were any pending transactions to be
      * executed.
      */
+    @MainThread
     public boolean executePendingTransactions() {
         boolean updates = execPendingActions(true);
         forcePostponedTransactions();
@@ -790,6 +791,7 @@
      * afterwards without forcing the start of postponed Transactions.
      * @return Returns true if there was something popped, else false.
      */
+    @MainThread
     public boolean popBackStackImmediate() {
         return popBackStackImmediate(null, -1, 0);
     }
@@ -817,6 +819,7 @@
      * afterwards without forcing the start of postponed Transactions.
      * @return Returns true if there was something popped, else false.
      */
+    @MainThread
     public boolean popBackStackImmediate(@Nullable String name, int flags) {
         return popBackStackImmediate(name, -1, flags);
     }
@@ -988,7 +991,6 @@
                 }
             }
         };
-        lifecycle.addObserver(observer);
         LifecycleAwareResultListener storedListener = mResultListeners.put(requestKey,
                 new LifecycleAwareResultListener(lifecycle, listener, observer));
         if (storedListener != null) {
@@ -998,6 +1000,9 @@
             Log.v(FragmentManager.TAG, "Setting FragmentResultListener with key " + requestKey
                     + " lifecycleOwner " + lifecycle + " and listener " + listener);
         }
+        // Only add the observer after we've added the listener to the map
+        // to ensure that re-entrant removals actually have a registered listener to remove
+        lifecycle.addObserver(observer);
     }
 
     @Override
@@ -1892,14 +1897,24 @@
         // such as push, push, pop, push are correctly considered a push
         boolean isPop = isRecordPop.get(endIndex - 1);
 
-        if (mBackStackChangeListeners != null) {
-            // we dispatch callbacks based on each record
+        if (addToBackStack && mBackStackChangeListeners != null
+                && !mBackStackChangeListeners.isEmpty()) {
+            ArrayList<Fragment> fragments = new ArrayList<>();
+            // Build a list of fragments based on the records
             for (BackStackRecord record : records) {
-                for (Fragment fragment : fragmentsFromRecord(record)) {
-                    // We give all fragment the back stack changed started signal first
-                    for (OnBackStackChangedListener listener : mBackStackChangeListeners) {
-                        listener.onBackStackChangeStarted(fragment, isPop);
-                    }
+                fragments.addAll(fragmentsFromRecord(record));
+            }
+            // Dispatch to all of the fragments in the list
+            for (OnBackStackChangedListener listener : mBackStackChangeListeners) {
+                // We give all fragment the back stack changed started signal first
+                for (Fragment fragment: fragments) {
+                    listener.onBackStackChangeStarted(fragment, isPop);
+                }
+            }
+            for (OnBackStackChangedListener listener : mBackStackChangeListeners) {
+                // Then we give them all the committed signal
+                for (Fragment fragment: fragments) {
+                    listener.onBackStackChangeCommitted(fragment, isPop);
                 }
             }
         }
@@ -1950,17 +1965,6 @@
         }
         if (addToBackStack) {
             reportBackStackChanged();
-            if (mBackStackChangeListeners != null) {
-                // we dispatch callbacks based on each record
-                for (BackStackRecord record : records) {
-                    for (Fragment fragment : fragmentsFromRecord(record)) {
-                        // Then we give them all the committed signal
-                        for (OnBackStackChangedListener listener : mBackStackChangeListeners) {
-                            listener.onBackStackChangeCommitted(fragment, isPop);
-                        }
-                    }
-                }
-            }
         }
     }
 
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 859045b..e68aac2 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java
@@ -556,6 +556,9 @@
         mFragment.mContainer = container;
         mFragment.performCreateView(layoutInflater, container, savedInstanceState);
         if (mFragment.mView != null) {
+            if (FragmentManager.isLoggingEnabled(Log.DEBUG)) {
+                Log.d(TAG, "moveto VIEW_CREATED: " + mFragment);
+            }
             mFragment.mView.setSaveFromParentEnabled(false);
             mFragment.mView.setTag(R.id.fragment_container_view_tag, mFragment);
             if (container != null) {
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 c7bca3d..b177fd0 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransaction.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransaction.java
@@ -26,6 +26,7 @@
 import androidx.annotation.AnimatorRes;
 import androidx.annotation.IdRes;
 import androidx.annotation.IntDef;
+import androidx.annotation.MainThread;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
@@ -952,6 +953,7 @@
      * be restored from its state.  See {@link #commitAllowingStateLoss()} for
      * situations where it may be okay to lose the commit.</p>
      */
+    @MainThread
     public abstract void commitNow();
 
     /**
@@ -961,5 +963,6 @@
      * this should only be used for cases where it is okay for the UI state
      * to change unexpectedly on the user.
      */
+    @MainThread
     public abstract void commitNowAllowingStateLoss();
 }
diff --git a/glance/glance-appwidget-preview/src/androidAndroidTest/kotlin/androidx/glance/appwidget/preview/GlanceAppWidgetViewAdapterTest.kt b/glance/glance-appwidget-preview/src/androidAndroidTest/kotlin/androidx/glance/appwidget/preview/GlanceAppWidgetViewAdapterTest.kt
index d851096..92d17b07 100644
--- a/glance/glance-appwidget-preview/src/androidAndroidTest/kotlin/androidx/glance/appwidget/preview/GlanceAppWidgetViewAdapterTest.kt
+++ b/glance/glance-appwidget-preview/src/androidAndroidTest/kotlin/androidx/glance/appwidget/preview/GlanceAppWidgetViewAdapterTest.kt
@@ -21,6 +21,7 @@
 import android.os.Bundle
 import android.view.View
 import android.view.ViewGroup
+import android.widget.Button
 import android.widget.FrameLayout
 import android.widget.LinearLayout
 import android.widget.RelativeLayout
@@ -110,10 +111,11 @@
             assertNotNull(viewNotFoundMsg("TextView", "Text"), textView)
             val linearLayoutRow = linearLayoutColumn.getChildOfType<LinearLayout>()
             assertNotNull(viewNotFoundMsg("LinearLayout", "Row"), linearLayoutRow)
-            // Depending on the API version Button might be wrapped in the RelativeLayout
-            val button1 = linearLayoutRow!!.getChildOfType<FrameLayout>()
+            // Backport button are implemented using FrameLayout and depending on the API version
+            // Button might be wrapped in the RelativeLayout.
+            val button1 = linearLayoutRow!!.getChildOfType<Button>()
                 ?: linearLayoutRow.getChildOfType<RelativeLayout>()!!.getChildOfType<FrameLayout>()
-            val button2 = linearLayoutRow.getChildOfType<FrameLayout>(1)
+            val button2 = linearLayoutRow.getChildOfType<Button>(1)
                 ?: linearLayoutRow.getChildOfType<RelativeLayout>(1)!!.getChildOfType<FrameLayout>()
             assertNotNull(viewNotFoundMsg("FrameLayout", "Button"), button1)
             assertNotNull(viewNotFoundMsg("FrameLayout", "Button"), button2)
diff --git a/glance/glance-appwidget/api/current.txt b/glance/glance-appwidget/api/current.txt
index fbdf3cf..a0ce5f3 100644
--- a/glance/glance-appwidget/api/current.txt
+++ b/glance/glance-appwidget/api/current.txt
@@ -2,8 +2,8 @@
 package androidx.glance.appwidget {
 
   public final class AndroidRemoteViewsKt {
-    method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews);
-    method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, @IdRes int containerViewId, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, optional androidx.glance.GlanceModifier modifier);
+    method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, @IdRes int containerViewId, optional androidx.glance.GlanceModifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
   public final class AppWidgetBackgroundKt {
diff --git a/glance/glance-appwidget/api/public_plus_experimental_current.txt b/glance/glance-appwidget/api/public_plus_experimental_current.txt
index 041c8e4..2d5f4c8 100644
--- a/glance/glance-appwidget/api/public_plus_experimental_current.txt
+++ b/glance/glance-appwidget/api/public_plus_experimental_current.txt
@@ -2,8 +2,8 @@
 package androidx.glance.appwidget {
 
   public final class AndroidRemoteViewsKt {
-    method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews);
-    method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, @IdRes int containerViewId, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, optional androidx.glance.GlanceModifier modifier);
+    method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, @IdRes int containerViewId, optional androidx.glance.GlanceModifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
   public final class AppWidgetBackgroundKt {
diff --git a/glance/glance-appwidget/api/restricted_current.txt b/glance/glance-appwidget/api/restricted_current.txt
index fbdf3cf..a0ce5f3 100644
--- a/glance/glance-appwidget/api/restricted_current.txt
+++ b/glance/glance-appwidget/api/restricted_current.txt
@@ -2,8 +2,8 @@
 package androidx.glance.appwidget {
 
   public final class AndroidRemoteViewsKt {
-    method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews);
-    method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, @IdRes int containerViewId, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, optional androidx.glance.GlanceModifier modifier);
+    method @androidx.compose.runtime.Composable public static void AndroidRemoteViews(android.widget.RemoteViews remoteViews, @IdRes int containerViewId, optional androidx.glance.GlanceModifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
   public final class AppWidgetBackgroundKt {
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/AndroidManifest.xml b/glance/glance-appwidget/integration-tests/demos/src/main/AndroidManifest.xml
index 60f3519..27cc5b3 100644
--- a/glance/glance-appwidget/integration-tests/demos/src/main/AndroidManifest.xml
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/AndroidManifest.xml
@@ -151,6 +151,32 @@
         </receiver>
 
         <receiver
+            android:name="androidx.glance.appwidget.demos.RippleAppWidgetReceiver"
+            android:label="@string/ripple_widget_name"
+            android:enabled="@bool/glance_appwidget_available"
+            android:exported="false">
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+            </intent-filter>
+            <meta-data
+                android:name="android.appwidget.provider"
+                android:resource="@xml/default_app_widget_info" />
+        </receiver>
+
+        <receiver
+            android:name="androidx.glance.appwidget.demos.RemoteViewsWidgetReceiver"
+            android:label="@string/remote_views_widget_name"
+            android:enabled="@bool/glance_appwidget_available"
+            android:exported="false">
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+            </intent-filter>
+            <meta-data
+                android:name="android.appwidget.provider"
+                android:resource="@xml/default_app_widget_info" />
+        </receiver>
+
+        <receiver
             android:name="androidx.glance.appwidget.demos.VerticalGridAppWidgetReceiver"
             android:label="@string/grid_widget_name"
             android:enabled="@bool/glance_appwidget_available"
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/RemoteViewsWidget.kt b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/RemoteViewsWidget.kt
new file mode 100644
index 0000000..5797cff5
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/RemoteViewsWidget.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.glance.appwidget.demos
+
+import android.content.Context
+import android.widget.RemoteViews
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.glance.GlanceId
+import androidx.glance.GlanceModifier
+import androidx.glance.LocalContext
+import androidx.glance.appwidget.AndroidRemoteViews
+import androidx.glance.appwidget.GlanceAppWidget
+import androidx.glance.appwidget.GlanceAppWidgetReceiver
+import androidx.glance.appwidget.SizeMode
+import androidx.glance.appwidget.cornerRadius
+import androidx.glance.appwidget.provideContent
+import androidx.glance.background
+import androidx.glance.layout.Alignment
+import androidx.glance.layout.Box
+import androidx.glance.layout.Column
+import androidx.glance.layout.fillMaxSize
+import androidx.glance.layout.fillMaxWidth
+import androidx.glance.layout.wrapContentHeight
+import androidx.glance.layout.wrapContentSize
+import androidx.glance.text.Text
+
+/**
+ * Sample AppWidget that showcase the [AndroidRemoteViews] fallback composable.
+ */
+class RemoteViewsWidget : GlanceAppWidget() {
+    override val sizeMode: SizeMode = SizeMode.Exact
+
+    override suspend fun provideGlance(
+        context: Context,
+        id: GlanceId
+    ) = provideContent {
+        Column(
+            modifier = GlanceModifier.fillMaxSize().background(Color.White),
+            horizontalAlignment = Alignment.Horizontal.CenterHorizontally
+        ) {
+            // Demonstrates a single item remote view layout
+            val remoteViews =
+                RemoteViews(LocalContext.current.packageName, R.layout.test_remote_views_single)
+            AndroidRemoteViews(
+                remoteViews = remoteViews,
+                modifier = GlanceModifier
+                    .fillMaxWidth()
+                    .wrapContentHeight()
+                    .cornerRadius(16.dp)
+                    .background(Color.Red)
+            )
+
+            // Demonstrates a remote view layout being used as a container for regular glance
+            // composables
+            val flipper =
+                RemoteViews(LocalContext.current.packageName, R.layout.test_remote_views_multiple)
+            Text(text = "Container RemoteViews")
+            Box(
+                modifier = GlanceModifier.fillMaxWidth().defaultWeight(),
+                contentAlignment = Alignment.Center
+            ) {
+                AndroidRemoteViews(
+                    remoteViews = flipper,
+                    containerViewId = R.id.test_flipper_root,
+                    modifier = GlanceModifier.wrapContentSize()
+                ) {
+                    Text(text = "First")
+                    Text(text = "Second")
+                }
+            }
+        }
+    }
+}
+
+class RemoteViewsWidgetReceiver : GlanceAppWidgetReceiver() {
+    override val glanceAppWidget: GlanceAppWidget = RemoteViewsWidget()
+}
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/RippleAppWidget.kt b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/RippleAppWidget.kt
new file mode 100644
index 0000000..c84c97f
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/RippleAppWidget.kt
@@ -0,0 +1,206 @@
+/*
+ * 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.glance.appwidget.demos
+
+import android.content.Context
+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.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.glance.ColorFilter
+import androidx.glance.GlanceId
+import androidx.glance.GlanceModifier
+import androidx.glance.GlanceTheme
+import androidx.glance.Image
+import androidx.glance.ImageProvider
+import androidx.glance.action.clickable
+import androidx.glance.appwidget.GlanceAppWidget
+import androidx.glance.appwidget.GlanceAppWidgetReceiver
+import androidx.glance.appwidget.cornerRadius
+import androidx.glance.appwidget.provideContent
+import androidx.glance.background
+import androidx.glance.color.ColorProvider
+import androidx.glance.layout.Alignment
+import androidx.glance.layout.Box
+import androidx.glance.layout.Column
+import androidx.glance.layout.ContentScale
+import androidx.glance.layout.Row
+import androidx.glance.layout.Spacer
+import androidx.glance.layout.fillMaxSize
+import androidx.glance.layout.fillMaxWidth
+import androidx.glance.layout.height
+import androidx.glance.layout.padding
+import androidx.glance.layout.size
+import androidx.glance.text.FontWeight
+import androidx.glance.text.Text
+import androidx.glance.text.TextAlign
+import androidx.glance.text.TextStyle
+
+/**
+ * Sample AppWidget to showcase the ripples.
+ * Note: Rounded corners are supported in S+
+ */
+class RippleAppWidget : GlanceAppWidget() {
+    private val columnBgColorsA = listOf(Color(0xffA2BDF2), Color(0xff5087EF))
+    private val columnBgColorsB = listOf(Color(0xFFBD789C), Color(0xFF880E4F))
+    private val boxColors = listOf(Color(0xffF7A998), Color(0xffFA5F3D))
+
+    override suspend fun provideGlance(
+        context: Context,
+        id: GlanceId
+    ) = provideContent {
+        RippleDemoContent()
+    }
+
+    @Composable
+    private fun RippleDemoContent() {
+        var count by remember { mutableStateOf(0) }
+        var type by remember { mutableStateOf(ContentScale.Fit) }
+        var columnBgColors by remember { mutableStateOf(columnBgColorsA) }
+
+        Column(
+            horizontalAlignment = Alignment.CenterHorizontally,
+            modifier = GlanceModifier.fillMaxSize().padding(8.dp)
+                .cornerRadius(20.dp)
+                .background(ColorProvider(day = columnBgColors[0], night = columnBgColors[1]))
+                .clickable {
+                    columnBgColors = when (columnBgColors[0]) {
+                        columnBgColorsA[0] -> columnBgColorsB
+                        else -> columnBgColorsA
+                    }
+                }
+        ) {
+            Row(verticalAlignment = Alignment.CenterVertically) {
+                Text(
+                    text = "Content Scale: ${type.asString()}, Image / Box click count: $count",
+                    modifier = GlanceModifier.padding(5.dp).defaultWeight()
+                )
+                // Demonstrates an icon button with circular ripple.
+                Image(
+                    provider = ImageProvider(R.drawable.ic_color_reset),
+                    contentDescription = "Remove background color",
+                    colorFilter = ColorFilter.tint(GlanceTheme.colors.secondary),
+                    modifier = GlanceModifier
+                        .padding(5.dp)
+                        .cornerRadius(24.dp) // To get a rounded ripple
+                        .clickable {
+                            columnBgColors = listOf(Color.Transparent, Color.Transparent)
+                        }
+                )
+            }
+            // A drawable image with rounded corners and a click modifier.
+            OutlinedButtonUsingImage(text = "Toggle content scale", onClick = {
+                type = when (type) {
+                    ContentScale.Crop -> ContentScale.FillBounds
+                    ContentScale.FillBounds -> ContentScale.Fit
+                    else -> ContentScale.Crop
+                }
+            })
+            Spacer(GlanceModifier.size(5.dp))
+            Text(
+                text = "Image in a clickable box with rounded corners",
+                modifier = GlanceModifier.padding(5.dp)
+            )
+            ImageInClickableBoxWithRoundedCorners(contentScale = type, onClick = { count++ })
+            Spacer(GlanceModifier.size(5.dp))
+            Text(
+                text = "Rounded corner image in a clickable box",
+                modifier = GlanceModifier.padding(5.dp)
+            )
+            RoundedImageInClickableBox(contentScale = type, onClick = { count++ })
+        }
+    }
+    @Composable
+    private fun ImageInClickableBoxWithRoundedCorners(
+        contentScale: ContentScale,
+        onClick: () -> Unit
+    ) {
+        Box(
+            modifier = GlanceModifier
+                .height(100.dp)
+                .background(ColorProvider(day = boxColors[0], night = boxColors[1]))
+                .cornerRadius(25.dp)
+                .clickable(onClick)
+        ) {
+            Image(
+                provider = ImageProvider(R.drawable.compose),
+                contentDescription = "Image sample in a box with rounded corners",
+                contentScale = contentScale,
+                modifier = GlanceModifier.fillMaxSize()
+            )
+        }
+    }
+
+    @Composable
+    private fun RoundedImageInClickableBox(contentScale: ContentScale, onClick: () -> Unit) {
+        Box(
+            modifier = GlanceModifier
+                .height(100.dp)
+                .background(ColorProvider(day = boxColors[0], night = boxColors[1]))
+                .clickable(onClick)
+        ) {
+            Image(
+                provider = ImageProvider(R.drawable.compose),
+                contentDescription = "Image sample with rounded corners",
+                contentScale = contentScale,
+                modifier = GlanceModifier.fillMaxSize().cornerRadius(25.dp)
+            )
+        }
+    }
+
+    @Composable
+    fun OutlinedButtonUsingImage(
+        text: String,
+        onClick: () -> Unit,
+    ) {
+        Box(
+            modifier = GlanceModifier.height(40.dp).fillMaxWidth(),
+            contentAlignment = Alignment.Center
+        ) {
+            // Demonstrates a button with rounded outline using a clickable image. Alternatively,
+            // such button can also be created using Box + Text by adding background image, corner
+            // radius and click modifiers to the box.
+            Image(
+                provider = ImageProvider(R.drawable.ic_outlined_button),
+                contentDescription = "Outlined button sample",
+                // Radius value matched with the border in the outline image so that the ripple
+                // matches it (in versions that support cornerRadius modifier).
+                modifier = GlanceModifier.fillMaxSize().cornerRadius(20.dp).clickable(onClick)
+            )
+            Text(
+                text = text,
+                style = TextStyle(fontWeight = FontWeight.Medium, textAlign = TextAlign.Center),
+                modifier = GlanceModifier.background(Color.Transparent)
+            )
+        }
+    }
+
+    private fun ContentScale.asString(): String =
+        when (this) {
+            ContentScale.Fit -> "Fit"
+            ContentScale.FillBounds -> "Fill Bounds"
+            ContentScale.Crop -> "Crop"
+            else -> "Unknown content scale"
+        }
+}
+
+class RippleAppWidgetReceiver : GlanceAppWidgetReceiver() {
+    override val glanceAppWidget: GlanceAppWidget = RippleAppWidget()
+}
\ No newline at end of file
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/res/drawable/ic_color_reset.xml b/glance/glance-appwidget/integration-tests/demos/src/main/res/drawable/ic_color_reset.xml
new file mode 100644
index 0000000..27def3a
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/res/drawable/ic_color_reset.xml
@@ -0,0 +1,21 @@
+<!--
+  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.
+  -->
+
+<vector android:height="24dp" android:tint="#6A8AF5"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M18,14c0,-4 -6,-10.8 -6,-10.8s-1.33,1.51 -2.73,3.52l8.59,8.59c0.09,-0.42 0.14,-0.86 0.14,-1.31zM17.12,17.12L12.5,12.5 5.27,5.27 4,6.55l3.32,3.32C6.55,11.32 6,12.79 6,14c0,3.31 2.69,6 6,6 1.52,0 2.9,-0.57 3.96,-1.5l2.63,2.63 1.27,-1.27 -2.74,-2.74z"/>
+</vector>
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/res/drawable/ic_outlined_button.xml b/glance/glance-appwidget/integration-tests/demos/src/main/res/drawable/ic_outlined_button.xml
new file mode 100644
index 0000000..ee58ce0
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/res/drawable/ic_outlined_button.xml
@@ -0,0 +1,23 @@
+<?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.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@android:color/transparent" />
+    <corners android:radius="20dp"/>
+    <stroke android:width="1dp" android:color="#000000" />
+    <padding android:left="1dp" android:top="1dp" android:right="1dp"
+        android:bottom="1dp" />
+</shape>
\ No newline at end of file
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/res/layout/test_remote_views_multiple.xml b/glance/glance-appwidget/integration-tests/demos/src/main/res/layout/test_remote_views_multiple.xml
new file mode 100644
index 0000000..257a250
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/res/layout/test_remote_views_multiple.xml
@@ -0,0 +1,23 @@
+<?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.
+  -->
+
+<ViewFlipper xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/test_flipper_root"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:autoStart="true"
+    android:flipInterval="2000">
+</ViewFlipper>
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/res/layout/test_remote_views_single.xml b/glance/glance-appwidget/integration-tests/demos/src/main/res/layout/test_remote_views_single.xml
new file mode 100644
index 0000000..e2004b8
--- /dev/null
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/res/layout/test_remote_views_single.xml
@@ -0,0 +1,23 @@
+<?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.
+  -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="48dp"
+    android:gravity="center"
+    android:text="Single RemoteView">
+</TextView>
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/res/values/strings.xml b/glance/glance-appwidget/integration-tests/demos/src/main/res/values/strings.xml
index 3f8a5df..0076752 100644
--- a/glance/glance-appwidget/integration-tests/demos/src/main/res/values/strings.xml
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/res/values/strings.xml
@@ -29,6 +29,8 @@
     <string name="error_widget_name">Error UI Widget</string>
     <string name="scrollable_widget_name">Scrollable Widget</string>
     <string name="image_widget_name">Image Widget</string>
+    <string name="ripple_widget_name">Ripple Widget</string>
+    <string name="remote_views_widget_name">Android Remote Views Widget</string>
     <string name="grid_widget_name">Vertical Grid Widget</string>
     <string name="default_state_widget_name">Default State Widget</string>
     <string name="progress_indicator_widget_name">ProgressBar Widget</string>
diff --git a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverScreenshotTest.kt b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverScreenshotTest.kt
index 3a4ec0e..b0897e3 100644
--- a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverScreenshotTest.kt
+++ b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverScreenshotTest.kt
@@ -38,6 +38,7 @@
 import androidx.glance.layout.Column
 import androidx.glance.layout.ContentScale
 import androidx.glance.layout.Row
+import androidx.glance.layout.Spacer
 import androidx.glance.layout.fillMaxHeight
 import androidx.glance.layout.fillMaxSize
 import androidx.glance.layout.fillMaxWidth
@@ -224,6 +225,25 @@
     }
 
     @Test
+    fun checkButtonRoundedCorners_light() {
+        TestGlanceAppWidget.uiDefinition = { RoundedButtonScreenshotTest() }
+
+        mHostRule.startHost()
+
+        mScreenshotRule.checkScreenshot(mHostRule.mHostView, "roundedButton_light")
+    }
+
+    @Test
+    @WithNightMode
+    fun checkButtonRoundedCorners_dark() {
+        TestGlanceAppWidget.uiDefinition = { RoundedButtonScreenshotTest() }
+
+        mHostRule.startHost()
+
+        mScreenshotRule.checkScreenshot(mHostRule.mHostView, "roundedButton_dark")
+    }
+
+    @Test
     fun checkButtonTextAlignment() {
         TestGlanceAppWidget.uiDefinition = {
             Column(modifier = GlanceModifier.fillMaxSize()) {
@@ -624,6 +644,49 @@
 }
 
 @Composable
+private fun RoundedButtonScreenshotTest() {
+    val columnColors = listOf(Color(0xffffdbcd), Color(0xff7d2d00))
+    val buttonBgColors = listOf(Color(0xffa33e00), Color(0xffffb596))
+    val buttonTextColors = listOf(Color(0xffffffff), Color(0xff581e00))
+
+    Column(
+        modifier = GlanceModifier.padding(10.dp)
+            .background(day = columnColors[0], night = columnColors[1])
+    ) {
+        Button(
+            "Button with textAlign = Start",
+            onClick = actionStartActivity<Activity>(),
+            colors = ButtonColors(
+                backgroundColor = ColorProvider(day = buttonBgColors[0], night = buttonBgColors[1]),
+                contentColor = ColorProvider(day = buttonTextColors[0], night = buttonTextColors[1])
+            ),
+            style = TextStyle(textAlign = TextAlign.Start)
+        )
+        Spacer(modifier = GlanceModifier.height(5.dp).fillMaxWidth())
+        Button(
+            "Button with textAlign = Center and padding (30dp, 30dp)",
+            onClick = actionStartActivity<Activity>(),
+            modifier = GlanceModifier.padding(horizontal = 30.dp, vertical = 30.dp),
+            colors = ButtonColors(
+                backgroundColor = ColorProvider(day = buttonBgColors[0], night = buttonBgColors[1]),
+                contentColor = ColorProvider(day = buttonTextColors[0], night = buttonTextColors[1])
+            ),
+            style = TextStyle(textAlign = TextAlign.Center)
+        )
+        Spacer(modifier = GlanceModifier.height(5.dp).fillMaxWidth())
+        Button(
+            "Button with textAlign = End",
+            onClick = actionStartActivity<Activity>(),
+            colors = ButtonColors(
+                backgroundColor = ColorProvider(day = buttonBgColors[0], night = buttonBgColors[1]),
+                contentColor = ColorProvider(day = buttonTextColors[0], night = buttonTextColors[1])
+            ),
+            style = TextStyle(textAlign = TextAlign.End)
+        )
+    }
+}
+
+@Composable
 private fun CheckBoxScreenshotTest() {
     Column(modifier = GlanceModifier.background(day = Color.White, night = Color.Black)) {
         CheckBox(
diff --git a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverTest.kt b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverTest.kt
index 8f20d20..274f935 100644
--- a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverTest.kt
+++ b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverTest.kt
@@ -29,9 +29,11 @@
 import android.util.Log
 import android.view.View
 import android.view.ViewGroup
+import android.widget.Button
 import android.widget.CompoundButton
 import android.widget.FrameLayout
 import android.widget.ImageView
+import android.widget.ImageView.ScaleType
 import android.widget.LinearLayout
 import android.widget.RadioButton
 import android.widget.TextView
@@ -436,6 +438,7 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = 31)
     fun createButton() {
         TestGlanceAppWidget.uiDefinition = {
             Button(
@@ -451,6 +454,33 @@
 
         mHostRule.startHost()
 
+        mHostRule.onUnboxedHostView<Button> { button ->
+            checkNotNull(button.text.toString() == "Button") {
+                "Couldn't find 'Button'"
+            }
+
+            assertThat(button.isEnabled).isFalse()
+            assertThat(button.hasOnClickListeners()).isFalse()
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 29, maxSdkVersion = 30)
+    fun createButtonBackport() {
+        TestGlanceAppWidget.uiDefinition = {
+            Button(
+                text = "Button",
+                onClick = actionStartActivity<Activity>(),
+                colors = ButtonColors(
+                    backgroundColor = ColorProvider(Color.Transparent),
+                    contentColor = ColorProvider(Color.DarkGray)
+                ),
+                enabled = false
+            )
+        }
+
+        mHostRule.startHost()
+
         mHostRule.onUnboxedHostView<FrameLayout> { button ->
             checkNotNull(button.findChild<TextView> { it.text.toString() == "Button" }) {
                 "Couldn't find TextView 'Button'"
@@ -488,8 +518,14 @@
 
         mHostRule.startHost()
 
-        mHostRule.onUnboxedHostView<TextView> { textView ->
-            assertThat(textView.background).isNotNull()
+        mHostRule.onUnboxedHostView<FrameLayout> { box ->
+            assertThat(box.notGoneChildCount).isEqualTo(2)
+            val (boxedImage, boxedText) = box.notGoneChildren.toList()
+            val image = boxedImage.getTargetView<ImageView>()
+            val text = boxedText.getTargetView<TextView>()
+            assertThat(image.drawable).isNotNull()
+            assertThat(image.scaleType).isEqualTo(ScaleType.FIT_XY)
+            assertThat(text.background).isNull()
         }
     }
 
@@ -511,6 +547,30 @@
             val image = boxedImage.getTargetView<ImageView>()
             val text = boxedText.getTargetView<TextView>()
             assertThat(image.drawable).isNotNull()
+            assertThat(image.scaleType).isEqualTo(ScaleType.FIT_CENTER)
+            assertThat(text.background).isNull()
+        }
+    }
+
+    @Test
+    fun drawableCropBackground() {
+        TestGlanceAppWidget.uiDefinition = {
+            Text(
+                "Some useful text",
+                modifier = GlanceModifier.fillMaxWidth().height(220.dp)
+                    .background(ImageProvider(R.drawable.oval), contentScale = ContentScale.Crop)
+            )
+        }
+
+        mHostRule.startHost()
+
+        mHostRule.onUnboxedHostView<FrameLayout> { box ->
+            assertThat(box.notGoneChildCount).isEqualTo(2)
+            val (boxedImage, boxedText) = box.notGoneChildren.toList()
+            val image = boxedImage.getTargetView<ImageView>()
+            val text = boxedText.getTargetView<TextView>()
+            assertThat(image.drawable).isNotNull()
+            assertThat(image.scaleType).isEqualTo(ScaleType.CENTER_CROP)
             assertThat(text.background).isNull()
         }
     }
@@ -878,6 +938,7 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = 31)
     fun lambdaActionCallback() = runTest {
         TestGlanceAppWidget.uiDefinition = {
             val text = remember { mutableStateOf("initial") }
@@ -891,6 +952,34 @@
 
         mHostRule.startHost()
         var button: View? = null
+        mHostRule.onUnboxedHostView<Button> { buttonView ->
+            assertThat(buttonView.text.toString()).isEqualTo("initial")
+            button = buttonView
+        }
+        mHostRule.runAndWaitForUpdate {
+            button!!.performClick()
+        }
+
+        mHostRule.onUnboxedHostView<Button> { buttonView ->
+            assertThat(buttonView.text.toString()).isEqualTo("clicked")
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 29, maxSdkVersion = 30)
+    fun lambdaActionCallback_backportButton() = runTest {
+        TestGlanceAppWidget.uiDefinition = {
+            val text = remember { mutableStateOf("initial") }
+            Button(
+                text = text.value,
+                onClick = {
+                    text.value = "clicked"
+                }
+            )
+        }
+
+        mHostRule.startHost()
+        var button: View? = null
         mHostRule.onUnboxedHostView<ViewGroup> { root ->
             val text = checkNotNull(root.findChild<TextView> { it.text.toString() == "initial" })
             button = text.parent as View
diff --git a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/LazyColumnTest.kt b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/LazyColumnTest.kt
index 5798d51..ea13a13 100644
--- a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/LazyColumnTest.kt
+++ b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/LazyColumnTest.kt
@@ -20,6 +20,7 @@
 import android.os.Build
 import android.view.Gravity
 import android.view.View
+import android.widget.Button
 import android.widget.FrameLayout
 import android.widget.ListView
 import android.widget.TextView
@@ -281,6 +282,7 @@
         }
     }
 
+    @Ignore("b/273482357")
     @Test
     fun itemContent_multipleViews() {
         TestGlanceAppWidget.uiDefinition = {
@@ -322,6 +324,7 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = 31)
     fun clickable_addsClickHandlers() {
         TestGlanceAppWidget.uiDefinition = {
             LazyColumn {
@@ -343,8 +346,40 @@
         mHostRule.waitForListViewChildren { list ->
             val row = list.getUnboxedListItem<FrameLayout>(0)
             val (rowItem0, rowItem1) = row.notGoneChildren.toList()
-            // All items with actions are wrapped in FrameLayout
+            // Clickable text items are wrapped in a FrameLayout.
             assertIs<FrameLayout>(rowItem0)
+            assertIs<Button>(rowItem1)
+            assertThat(rowItem0.hasOnClickListeners()).isTrue()
+            assertThat(rowItem1.hasOnClickListeners()).isTrue()
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 29, maxSdkVersion = 30)
+    fun clickable_backportButton_addsClickHandlers() {
+        TestGlanceAppWidget.uiDefinition = {
+            LazyColumn {
+                item {
+                    Text(
+                        "Text",
+                        modifier = GlanceModifier.clickable(actionStartActivity<Activity>())
+                    )
+                    Button(
+                        "Button",
+                        onClick = actionStartActivity<Activity>()
+                    )
+                }
+            }
+        }
+
+        mHostRule.startHost()
+
+        mHostRule.waitForListViewChildren { list ->
+            val row = list.getUnboxedListItem<FrameLayout>(0)
+            val (rowItem0, rowItem1) = row.notGoneChildren.toList()
+            // Clickable text items are wrapped in a FrameLayout.
+            assertIs<FrameLayout>(rowItem0)
+            // backport buttons are implemented using FrameLayout.
             assertIs<FrameLayout>(rowItem1)
             assertThat(rowItem0.hasOnClickListeners()).isTrue()
             assertThat(rowItem1.hasOnClickListeners()).isTrue()
@@ -353,6 +388,7 @@
 
     @OptIn(FlowPreview::class)
     @Test
+    @SdkSuppress(minSdkVersion = 31)
     fun clickTriggersOnlyOneLambda() = runBlocking {
         val received = MutableStateFlow(-1)
         TestGlanceAppWidget.uiDefinition = {
@@ -370,6 +406,42 @@
 
         mHostRule.startHost()
 
+        val buttons = arrayOfNulls<Button>(5)
+        mHostRule.waitForListViewChildren { list ->
+            for (it in 0..4) {
+                val button = list.getUnboxedListItem<Button>(it)
+                buttons[it] = button
+            }
+        }
+        (0..4).shuffled().forEach { index ->
+            mHostRule.onHostActivity {
+                buttons[index]!!.performClick()
+            }
+            val lastClicked = received.debounce(500.milliseconds).first()
+            assertThat(lastClicked).isEqualTo(index)
+        }
+    }
+
+    @OptIn(FlowPreview::class)
+    @Test
+    @SdkSuppress(minSdkVersion = 29, maxSdkVersion = 30)
+    fun clickTriggersOnlyOneLambda_backportButton() = runBlocking {
+        val received = MutableStateFlow(-1)
+        TestGlanceAppWidget.uiDefinition = {
+            LazyColumn {
+                items((0..4).toList()) {
+                    Button(
+                        "$it",
+                        onClick = {
+                            launch { received.emit(it) }
+                        }
+                    )
+                }
+            }
+        }
+
+        mHostRule.startHost()
+
         val buttons = arrayOfNulls<FrameLayout>(5)
         mHostRule.waitForListViewChildren { list ->
             for (it in 0..4) {
diff --git a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/StrictModeTest.kt b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/StrictModeTest.kt
index ee9bb39..a4ccfdc 100644
--- a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/StrictModeTest.kt
+++ b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/StrictModeTest.kt
@@ -20,6 +20,7 @@
 import android.os.StrictMode
 import android.util.Log
 import android.view.View
+import android.widget.Button
 import android.widget.FrameLayout
 import android.widget.TextView
 import androidx.annotation.RequiresApi
@@ -156,7 +157,8 @@
         mHostRule.waitForListViewChildren { list ->
             val row = list.getUnboxedListItem<FrameLayout>(0)
             val (_, rowItem1) = row.notGoneChildren.toList()
-            assertIs<FrameLayout>(rowItem1)
+            // S+ buttons are implemented using native buttons.
+            assertIs<Button>(rowItem1)
             Truth.assertThat(rowItem1.hasOnClickListeners()).isTrue()
             allowUnsafeIntentLaunch { rowItem1.performClick() }
         }
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AndroidRemoteViews.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AndroidRemoteViews.kt
index 98e7863..f199042 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AndroidRemoteViews.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/AndroidRemoteViews.kt
@@ -29,10 +29,14 @@
  * Add [RemoteViews] into a glance composition.
  *
  * @param remoteViews the views to add to the composition.
+ * @param modifier modifier used to adjust the layout algorithm or draw decoration content.
  */
 @Composable
-fun AndroidRemoteViews(remoteViews: RemoteViews) {
-    AndroidRemoteViews(remoteViews, View.NO_ID) { }
+fun AndroidRemoteViews(
+    remoteViews: RemoteViews,
+    modifier: GlanceModifier = GlanceModifier
+) {
+    AndroidRemoteViews(remoteViews, View.NO_ID, modifier) { }
 }
 
 /**
@@ -43,11 +47,14 @@
  * pre-existing children of that view will be removed with [RemoteViews.removeAllViews], and
  * any children defined in the [content] block will be added with [RemoteViews.addView] (or
  * [RemoteViews.addStableView] if available on the system).
+ * @param modifier modifier used to adjust the layout algorithm or draw decoration content.
+ * @param content the content that will be added to the provided container.
  */
 @Composable
 fun AndroidRemoteViews(
     remoteViews: RemoteViews,
     @IdRes containerViewId: Int,
+    modifier: GlanceModifier = GlanceModifier,
     content: @Composable () -> Unit,
 ) {
     GlanceNode(
@@ -55,6 +62,7 @@
         update = {
             this.set(remoteViews) { this.remoteViews = it }
             this.set(containerViewId) { this.containerViewId = it }
+            this.set(modifier) { this.modifier = it }
         },
         content = content
     )
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/CoroutineBroadcastReceiver.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/CoroutineBroadcastReceiver.kt
index a5f3dde..7ab9e6a 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/CoroutineBroadcastReceiver.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/CoroutineBroadcastReceiver.kt
@@ -54,7 +54,13 @@
             }
         } finally {
             // This must be the last call, as the process may be killed after calling this.
-            pendingResult.finish()
+            try {
+                pendingResult.finish()
+            } catch (e: IllegalStateException) {
+                // On some OEM devices, this may throw an error about "Broadcast already finished".
+                // See b/257513022.
+                Log.e(GlanceAppWidgetTag, "Error thrown when trying to finish broadcast", e)
+            }
         }
     }
 }
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/NormalizeCompositionTree.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/NormalizeCompositionTree.kt
index 5336388..e4045f7 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/NormalizeCompositionTree.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/NormalizeCompositionTree.kt
@@ -15,9 +15,9 @@
  */
 package androidx.glance.appwidget
 
+import android.os.Build
 import android.util.Log
 import androidx.compose.ui.unit.dp
-import androidx.glance.AndroidResourceImageProvider
 import androidx.glance.BackgroundModifier
 import androidx.glance.Emittable
 import androidx.glance.EmittableButton
@@ -29,13 +29,13 @@
 import androidx.glance.action.LambdaAction
 import androidx.glance.appwidget.action.CompoundButtonAction
 import androidx.glance.appwidget.lazy.EmittableLazyListItem
-import androidx.glance.background
 import androidx.glance.extractModifier
 import androidx.glance.findModifier
 import androidx.glance.layout.Alignment
 import androidx.glance.layout.ContentScale
 import androidx.glance.layout.EmittableBox
 import androidx.glance.layout.HeightModifier
+import androidx.glance.layout.PaddingModifier
 import androidx.glance.layout.WidthModifier
 import androidx.glance.layout.fillMaxHeight
 import androidx.glance.layout.fillMaxSize
@@ -189,72 +189,113 @@
     if (this is EmittableLazyListItem || this is EmittableSizeBox) return this
 
     var target = this
-
-    // We only need to add a background image view if the background is a Bitmap, or a
-    // drawable resource with non-default content scale. Otherwise, we can set the background
-    // directly on the target element in ApplyModifiers.kt.
-    val (bgModifier, notBgModifier) = target.modifier.extractModifier<BackgroundModifier>()
-    val addBackground = bgModifier?.imageProvider != null &&
-        (bgModifier.imageProvider !is AndroidResourceImageProvider ||
-            bgModifier.contentScale != ContentScale.FillBounds)
-
-    // Add a ripple for every element with an action that does not have already have a built in
-    // ripple.
-    notBgModifier.warnIfMultipleClickableActions()
-    val (actionModifier, notBgOrActionModifier) = notBgModifier.extractModifier<ActionModifier>()
-    val addRipple = actionModifier != null && !hasBuiltinRipple()
     val isButton = target is EmittableButton
-    if (!addBackground && !addRipple && !isButton) return target
 
-    // Hoist the size and action modifiers to the wrapping Box, then set the target element to fill
-    // the given space. doNotUnsetAction() prevents the views within the Box from being made
-    // clickable.
-    val (sizeModifiers, nonSizeModifiers) = notBgOrActionModifier.extractSizeModifiers()
-    val boxModifiers = mutableListOf<GlanceModifier?>(sizeModifiers, actionModifier)
-    val targetModifiers = mutableListOf<GlanceModifier?>(
-        nonSizeModifiers.fillMaxSize()
-    )
+    val shouldWrapTargetInABox = target.modifier.any {
+        // Background images (i.e. BitMap or drawable resources) are emulated by placing the image
+        // before the target in the wrapper box. This allows us to support content scale as well as
+        // can help support additional processing on background images. Note: Button's don't support
+        // bg image modifier.
+        (it is BackgroundModifier && it.imageProvider != null) ||
+        // R- buttons are implemented using box, images and text.
+        (isButton && Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) ||
+         // Ripples are implemented by placing a drawable after the target in the wrapper box.
+        (it is ActionModifier && !hasBuiltinRipple())
+    }
+    if (!shouldWrapTargetInABox) return target
 
-    // If we don't need to emulate the background, add the background modifier back to the target.
-    if (!addBackground) {
-        targetModifiers += bgModifier
+    // Hoisted modifiers are subtracted from the target one by one and added to the box and the
+    // remaining modifiers are applied to the target.
+    val boxModifiers = mutableListOf<GlanceModifier?>()
+    val targetModifiers = mutableListOf<GlanceModifier?>()
+    var backgroundImage: EmittableImage? = null
+    var rippleImage: EmittableImage? = null
+
+    val (bgModifier, targetModifiersMinusBg) = target.modifier.extractModifier<BackgroundModifier>()
+    if (bgModifier != null) {
+        if (isButton) {
+            // Emulate rounded corners (fixed radius) using a drawable and apply background colors
+            // to it. Note: Currently, button doesn't support bg image modifier, but only button
+            // colors.
+            backgroundImage = EmittableImage().apply {
+                modifier = GlanceModifier.fillMaxSize()
+                provider = ImageProvider(R.drawable.glance_button_outline)
+                // Without setting alpha, if this drawable's base was transparent, solid color won't
+                // be applied as the default blending mode uses alpha from base. And if this
+                // drawable's base was white/none, applying transparent tint will lead to black
+                // color. This shouldn't be issue for icon type drawables, but in this case we are
+                // emulating colored outline. So, we apply tint as well as alpha.
+                bgModifier.colorProvider?.let {
+                    colorFilterParams = TintAndAlphaColorFilterParams(it)
+                }
+                contentScale = ContentScale.FillBounds
+            }
+        } else {
+            // bgModifier.imageProvider is converted to an actual image but bgModifier.colorProvider
+            // is applied back to the target. Note: We could have hoisted the bg color to box
+            // instead of adding it back to the target, but for buttons, we also add an outline
+            // background to the box.
+            if (bgModifier.imageProvider != null) {
+                backgroundImage = EmittableImage().apply {
+                    modifier = GlanceModifier.fillMaxSize()
+                    provider = bgModifier.imageProvider
+                    contentScale = bgModifier.contentScale
+                }
+            } else { // is a background color modifier
+                targetModifiers += bgModifier
+            }
+        }
     }
 
-    // If this is a button, set the necessary modifiers on the wrapping Box.
+    // Action modifiers are hoisted on the wrapping box and a ripple image is added to the
+    // foreground if the target doesn't have it built-in.
+    targetModifiersMinusBg.warnIfMultipleClickableActions()
+    val (actionModifier, targetModifiersMinusAction) =
+        targetModifiersMinusBg.extractModifier<ActionModifier>()
+    boxModifiers += actionModifier
+    if (actionModifier != null && !hasBuiltinRipple()) {
+        val rippleImageProvider =
+            if (isButton) ImageProvider(R.drawable.glance_button_ripple)
+            else ImageProvider(R.drawable.glance_ripple)
+        rippleImage = EmittableImage().apply {
+            modifier = GlanceModifier.fillMaxSize()
+            provider = rippleImageProvider
+        }
+    }
+
+    // Hoist the size and corner radius modifiers to the wrapping Box, then set the target element
+    // to fill the given space.
+    val (sizeAndCornerModifiers, targetModifiersMinusSizeAndCornerRadius) =
+        targetModifiersMinusAction.extractSizeAndCornerRadiusModifiers()
+    boxModifiers += sizeAndCornerModifiers
+    targetModifiers += targetModifiersMinusSizeAndCornerRadius.fillMaxSize()
+
     if (target is EmittableButton) {
-        boxModifiers += GlanceModifier
-            .clipToOutline(true)
-            .enabled(target.enabled)
-            .background(ImageProvider(R.drawable.glance_button_outline))
+        boxModifiers += GlanceModifier.enabled(target.enabled)
         target = target.toEmittableText()
-        targetModifiers += GlanceModifier.padding(horizontal = 16.dp, vertical = 8.dp)
+        if (target.modifier.findModifier<PaddingModifier>() == null) {
+            targetModifiers += GlanceModifier.padding(horizontal = 16.dp, vertical = 8.dp)
+        }
     }
 
     return EmittableBox().apply {
         modifier = boxModifiers.collect()
         if (isButton) contentAlignment = Alignment.Center
 
-        if (addBackground && bgModifier != null) {
-            children += EmittableImage().apply {
-                modifier = GlanceModifier.fillMaxSize()
-                provider = bgModifier.imageProvider
-                contentScale = bgModifier.contentScale
-            }
-        }
+        backgroundImage?.let { children += it }
         children += target.apply { modifier = targetModifiers.collect() }
-        if (addRipple) {
-            children += EmittableImage().apply {
-                modifier = GlanceModifier.fillMaxSize()
-                provider = ImageProvider(R.drawable.glance_ripple)
-            }
-        }
+        rippleImage?.let { children += it }
     }
 }
 
 private fun Emittable.hasBuiltinRipple() =
     this is EmittableSwitch ||
     this is EmittableRadioButton ||
-    this is EmittableCheckBox
+    this is EmittableCheckBox ||
+     // S+ versions use a native button with fixed rounded corners and matching ripple set in
+     // layout xml. In R- versions, buttons are implemented using a background drawable with
+     // rounded corners and an EmittableText in R- versions.
+    (this is EmittableButton && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
 
 private data class ExtractedSizeModifiers(
     val sizeModifiers: GlanceModifier = GlanceModifier,
@@ -262,13 +303,15 @@
 )
 
 /**
- * Split the [GlanceModifier] into one that contains the [WidthModifier]s and [HeightModifier]s and
- * one that contains the rest.
+ * Split the [GlanceModifier] into one that contains the [WidthModifier]s, [HeightModifier]s and
+ * and [CornerRadiusModifier]s and one that contains the rest.
  */
-private fun GlanceModifier.extractSizeModifiers() =
-    if (any { it is WidthModifier || it is HeightModifier }) {
+private fun GlanceModifier.extractSizeAndCornerRadiusModifiers() =
+    if (any { it is WidthModifier || it is HeightModifier || it is CornerRadiusModifier }) {
         foldIn(ExtractedSizeModifiers()) { acc, modifier ->
-            if (modifier is WidthModifier || modifier is HeightModifier) {
+            if (modifier is WidthModifier ||
+                modifier is HeightModifier ||
+                modifier is CornerRadiusModifier) {
                 acc.copy(sizeModifiers = acc.sizeModifiers.then(modifier))
             } else {
                 acc.copy(nonSizeModifiers = acc.nonSizeModifiers.then(modifier))
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/RemoteViewsTranslator.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/RemoteViewsTranslator.kt
index 405ae563..1b50857 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/RemoteViewsTranslator.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/RemoteViewsTranslator.kt
@@ -34,11 +34,11 @@
 import androidx.glance.Emittable
 import androidx.glance.EmittableButton
 import androidx.glance.EmittableImage
-import androidx.glance.GlanceModifier
 import androidx.glance.appwidget.lazy.EmittableLazyColumn
 import androidx.glance.appwidget.lazy.EmittableLazyListItem
 import androidx.glance.appwidget.lazy.EmittableLazyVerticalGrid
 import androidx.glance.appwidget.lazy.EmittableLazyVerticalGridListItem
+import androidx.glance.appwidget.translators.setText
 import androidx.glance.appwidget.translators.translateEmittableCheckBox
 import androidx.glance.appwidget.translators.translateEmittableCircularProgressIndicator
 import androidx.glance.appwidget.translators.translateEmittableImage
@@ -50,12 +50,13 @@
 import androidx.glance.appwidget.translators.translateEmittableRadioButton
 import androidx.glance.appwidget.translators.translateEmittableSwitch
 import androidx.glance.appwidget.translators.translateEmittableText
+import androidx.glance.findModifier
 import androidx.glance.layout.Alignment
 import androidx.glance.layout.EmittableBox
 import androidx.glance.layout.EmittableColumn
 import androidx.glance.layout.EmittableRow
 import androidx.glance.layout.EmittableSpacer
-import androidx.glance.layout.fillMaxSize
+import androidx.glance.layout.PaddingModifier
 import androidx.glance.layout.padding
 import androidx.glance.text.EmittableText
 import java.util.concurrent.atomic.AtomicBoolean
@@ -405,21 +406,28 @@
     translationContext: TranslationContext,
     element: EmittableButton
 ) {
-    // Separate the button into a wrapper and the text, this allows us to set the color of the text
-    // background, while maintaining the ripple for the click indicator.
-    // TODO: add Image button
-    val content = EmittableText().apply {
-        text = element.text
-        style = element.style
-        maxLines = element.maxLines
-        modifier =
-            GlanceModifier
-                .fillMaxSize()
-                .padding(horizontal = 16.dp, vertical = 8.dp)
+    check(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+        "Buttons in Android R and below are emulated using a EmittableBox containing the text."
     }
-    translateEmittableText(translationContext, content)
-}
+    val viewDef = insertView(translationContext, LayoutType.Button, element.modifier)
+    setText(
+        translationContext,
+        viewDef.mainViewId,
+        element.text,
+        element.style,
+        maxLines = element.maxLines,
+        verticalTextGravity = Gravity.CENTER_VERTICAL,
+    )
 
+    // Adjust appWidget specific modifiers.
+    element.modifier = element.modifier
+        .enabled(element.enabled)
+        .cornerRadius(16.dp)
+    if (element.modifier.findModifier<PaddingModifier>() == null) {
+        element.modifier = element.modifier.padding(horizontal = 16.dp, vertical = 8.dp)
+    }
+    applyModifiers(translationContext, this, element.modifier, viewDef)
+}
 private fun RemoteViews.translateEmittableSpacer(
     translationContext: TranslationContext,
     element: EmittableSpacer
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/TintAndAlphaColorFilterParams.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/TintAndAlphaColorFilterParams.kt
new file mode 100644
index 0000000..03a01af
--- /dev/null
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/TintAndAlphaColorFilterParams.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.glance.appwidget
+
+import androidx.glance.ColorFilterParams
+import androidx.glance.unit.ColorProvider
+
+/**
+ * An internal, AppWidget specific colorFilter that applies alpha as well as tint from the provided
+ * color. Helps with changing color of entire drawable and using it as shaped color background.
+ */
+internal class TintAndAlphaColorFilterParams(val colorProvider: ColorProvider) : ColorFilterParams {
+    override fun toString() =
+        "TintAndAlphaColorFilterParams(colorProvider=$colorProvider))"
+}
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/lazy/LazyList.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/lazy/LazyList.kt
index 5e4d046..adb2d6e 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/lazy/LazyList.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/lazy/LazyList.kt
@@ -33,7 +33,8 @@
  * @param modifier the modifier to apply to this layout
  * @param horizontalAlignment the horizontal alignment applied to the items.
  * @param content a block which describes the content. Inside this block you can use methods like
- * [LazyListScope.item] to add a single item or [LazyListScope.items] to add a list of items.
+ * [LazyListScope.item] to add a single item or [LazyListScope.items] to add a list of items. If the
+ * item has more than one top-level child, they will be automatically wrapped in a Box.
  */
 // TODO(b/198618359): interaction handling
 @Composable
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/lazy/LazyVerticalGrid.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/lazy/LazyVerticalGrid.kt
index c0d1f50..0efa461 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/lazy/LazyVerticalGrid.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/lazy/LazyVerticalGrid.kt
@@ -34,8 +34,9 @@
  * @param modifier the modifier to apply to this layout
  * @param horizontalAlignment the horizontal alignment applied to the items.
  * @param content a block which describes the content. Inside this block you can use methods like
- * [LazyVerticalGridScope.item] to add a single item or
- * [LazyVerticalGridScope.items] to add a list of items.
+ * [LazyVerticalGridScope.item] to add a single item or [LazyVerticalGridScope.items] to add a list
+ * of items. If the item has more than one top-level child, they will be automatically wrapped in a
+ * Box.
  */
 @Composable
 fun LazyVerticalGrid(
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/ImageTranslator.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/ImageTranslator.kt
index f012ed0..dd97752 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/ImageTranslator.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/ImageTranslator.kt
@@ -27,6 +27,7 @@
 import androidx.core.widget.RemoteViewsCompat.setImageViewAdjustViewBounds
 import androidx.core.widget.RemoteViewsCompat.setImageViewColorFilter
 import androidx.core.widget.RemoteViewsCompat.setImageViewColorFilterResource
+import androidx.core.widget.RemoteViewsCompat.setImageViewImageAlpha
 import androidx.glance.AndroidResourceImageProvider
 import androidx.glance.BitmapImageProvider
 import androidx.glance.ColorFilterParams
@@ -36,6 +37,7 @@
 import androidx.glance.appwidget.GlanceAppWidgetTag
 import androidx.glance.appwidget.InsertedViewInfo
 import androidx.glance.appwidget.LayoutType
+import androidx.glance.appwidget.TintAndAlphaColorFilterParams
 import androidx.glance.appwidget.TranslationContext
 import androidx.glance.appwidget.UriImageProvider
 import androidx.glance.appwidget.applyModifiers
@@ -110,6 +112,19 @@
             }
         }
 
+        is TintAndAlphaColorFilterParams -> {
+            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
+                val color =
+                    colorFilterParams.colorProvider.getColor(translationContext.context).toArgb()
+                rv.setImageViewColorFilter(viewDef.mainViewId, color)
+                rv.setImageViewImageAlpha(viewDef.mainViewId, android.graphics.Color.alpha(color))
+            } else {
+                throw IllegalStateException(
+                    "The is no use case yet to support this colorFilter in S+ versions."
+                )
+            }
+        }
+
         else -> throw IllegalArgumentException("An unsupported ColorFilter was used.")
     }
 }
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/TextTranslator.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/TextTranslator.kt
index 2989592003..8a75049 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/TextTranslator.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/translators/TextTranslator.kt
@@ -26,6 +26,7 @@
 import android.text.style.StrikethroughSpan
 import android.text.style.StyleSpan
 import android.text.style.TextAppearanceSpan
+import android.text.style.TypefaceSpan
 import android.text.style.UnderlineSpan
 import android.util.Log
 import android.util.TypedValue
@@ -115,6 +116,9 @@
         }
         spans.add(TextAppearanceSpan(translationContext.context, textAppearance))
     }
+    style.fontFamily?.let { family ->
+        spans.add(TypefaceSpan(family.family))
+    }
     style.textAlign?.let { align ->
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
             TextTranslatorApi31Impl.setTextViewGravity(
@@ -140,6 +144,7 @@
                 setTextColor(resId, colorProvider.getColor(translationContext.context).toArgb())
             }
         }
+
         is DayNightColorProvider -> {
             if (Build.VERSION.SDK_INT >= 31) {
                 setTextViewTextColor(
@@ -151,6 +156,7 @@
                 setTextColor(resId, colorProvider.getColor(translationContext.context).toArgb())
             }
         }
+
         else -> Log.w(GlanceAppWidgetTag, "Unexpected text color: $colorProvider")
     }
 }
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable/glance_button_outline.xml b/glance/glance-appwidget/src/androidMain/res/drawable/glance_button_outline.xml
index 976eaeb..f95a318 100644
--- a/glance/glance-appwidget/src/androidMain/res/drawable/glance_button_outline.xml
+++ b/glance/glance-appwidget/src/androidMain/res/drawable/glance_button_outline.xml
@@ -15,6 +15,5 @@
   -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="@android:color/transparent" />
     <corners android:radius="16dp" />
 </shape>
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable/glance_button_ripple.xml b/glance/glance-appwidget/src/androidMain/res/drawable/glance_button_ripple.xml
new file mode 100644
index 0000000..d974ec2
--- /dev/null
+++ b/glance/glance-appwidget/src/androidMain/res/drawable/glance_button_ripple.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!-- Fixed radius ripple matching the button's outline -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <corners android:radius="16dp"/>
+            <solid android:color="@android:color/white"/>
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/androidMain/res/layout/glance_button.xml b/glance/glance-appwidget/src/androidMain/res/layout/glance_button.xml
index 9489e43..2044ad3 100644
--- a/glance/glance-appwidget/src/androidMain/res/layout/glance_button.xml
+++ b/glance/glance-appwidget/src/androidMain/res/layout/glance_button.xml
@@ -14,9 +14,18 @@
   limitations under the License.
   -->
 
+<!--
+Setting stateListAnimator to null to remove default shadows on the button and make it look
+similar to the custom emulated button in backport implementation. The corner radius of the ripple
+matches the one that is applied to the button later.
+ -->
 <Button xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     app:glance_isTopLevelLayout="true"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    style="@style/Glance.AppWidget.Button"/>
+    android:foreground="@drawable/glance_button_ripple"
+    android:minHeight="0dp"
+    android:minWidth="0dp"
+    android:stateListAnimator="@null"
+    style="@style/Glance.AppWidget.Button"/>
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/AppWidgetSessionTest.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/AppWidgetSessionTest.kt
index 86215ef..411726c 100644
--- a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/AppWidgetSessionTest.kt
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/AppWidgetSessionTest.kt
@@ -24,6 +24,7 @@
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
 import androidx.glance.Emittable
+import androidx.glance.GlanceId
 import androidx.glance.GlanceModifier
 import androidx.glance.action.ActionModifier
 import androidx.glance.action.LambdaAction
@@ -44,7 +45,6 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertThrows
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.robolectric.RobolectricTestRunner
@@ -81,10 +81,14 @@
         assertThat(widget.provideGlanceCalled.get()).isTrue()
     }
 
-    @Ignore // b/266518169
     @Test
     fun provideGlanceEmitsIgnoreResultForNullContent() = runTest {
-        // The session starts out with null content, so we can check that here.
+        // Create a widget that never calls provideContent, which means the session never produces
+        // a valid result.
+        val widget = object : GlanceAppWidget() {
+            override suspend fun provideGlance(context: Context, id: GlanceId) {}
+        }
+        val session = AppWidgetSession(widget, id, defaultOptions, testState)
         val root = runCompositionUntil(
             { state, _ -> state == Recomposer.State.Idle },
             session.provideGlance(context)
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/RemoteViewsTranslatorKtTest.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/RemoteViewsTranslatorKtTest.kt
index a8cec419..0545ef1 100644
--- a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/RemoteViewsTranslatorKtTest.kt
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/RemoteViewsTranslatorKtTest.kt
@@ -26,6 +26,7 @@
 import android.text.style.UnderlineSpan
 import android.util.Log
 import android.view.View
+import android.widget.Button
 import android.widget.FrameLayout
 import android.widget.GridView
 import android.widget.ImageView
@@ -726,7 +727,8 @@
     }
 
     @Test
-    fun canTranslateButton() = fakeCoroutineScope.runTest {
+    @Config(maxSdk = 30)
+    fun canTranslateBackportButton_enabled() = fakeCoroutineScope.runTest {
         val rv = context.runAndTranslate {
             Button(
                 "Button",
@@ -735,7 +737,8 @@
             )
         }
 
-        // All items with actions are wrapped in FrameLayout
+        // Backport button is implemented using a FrameLayout containing an outline image, a text
+        // and a ripple image.
         val frame = assertIs<FrameLayout>(context.applyRemoteViews(rv))
         assertThat(frame.hasOnClickListeners()).isTrue()
         assertThat(frame.isEnabled).isTrue()
@@ -743,6 +746,43 @@
     }
 
     @Test
+    @Config(minSdk = 31)
+    fun canTranslateButton_enabled() = fakeCoroutineScope.runTest {
+        val rv = context.runAndTranslate {
+            Button(
+                "Button",
+                onClick = actionStartActivity<Activity>(),
+                enabled = true
+            )
+        }
+
+        val button = assertIs<Button>(context.applyRemoteViews(rv))
+        assertThat(button.hasOnClickListeners()).isTrue()
+        assertThat(button.isEnabled).isTrue()
+        assertThat(button.text.toString()).isEqualTo("Button")
+    }
+
+    @Test
+    @Config(maxSdk = 30)
+    fun canTranslateBackportButton_disabled() = fakeCoroutineScope.runTest {
+        val rv = context.runAndTranslate {
+            Button(
+                "Button",
+                onClick = actionStartActivity<Activity>(),
+                enabled = false
+            )
+        }
+
+        // Backport button is implemented using a FrameLayout containing an outline image, a text
+        // and a ripple image.
+        val frame = assertIs<FrameLayout>(context.applyRemoteViews(rv))
+        assertThat(frame.hasOnClickListeners()).isFalse()
+        assertThat(frame.isEnabled).isFalse()
+        checkNotNull(frame.findView<TextView> { it.text.toString() == "Button" })
+    }
+
+    @Test
+    @Config(minSdk = 31)
     fun canTranslateButton_disabled() = fakeCoroutineScope.runTest {
         val rv = context.runAndTranslate {
             Button(
@@ -752,10 +792,10 @@
             )
         }
 
-        val frame = assertIs<FrameLayout>(context.applyRemoteViews(rv))
-        assertThat(frame.hasOnClickListeners()).isFalse()
-        assertThat(frame.isEnabled).isFalse()
-        checkNotNull(frame.findView<TextView> { it.text.toString() == "Button" })
+        val button = assertIs<Button>(context.applyRemoteViews(rv))
+        assertThat(button.isEnabled).isFalse()
+        assertThat(button.hasOnClickListeners()).isFalse()
+        assertThat(button.text.toString()).isEqualTo("Button")
     }
 
     @Test
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/TestUtils.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/TestUtils.kt
index 766b3ba..04e1faf 100644
--- a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/TestUtils.kt
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/TestUtils.kt
@@ -111,7 +111,11 @@
     appWidgetId: Int = 0,
     content: @Composable () -> Unit
 ): RemoteViews {
-    val root = runTestingComposition(content)
+    val originalRoot = runTestingComposition(content)
+
+    // Copy makes a deep copy of the emittable tree, so will exercise the copy methods
+    // of all of the emmitables the test checks too.
+    val root = originalRoot.copy() as RemoteViewsRoot
     normalizeCompositionTree(root)
     return translateComposition(
         this,
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/TextTranslatorTest.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/TextTranslatorTest.kt
index b877f04..1b4731c 100644
--- a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/TextTranslatorTest.kt
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/TextTranslatorTest.kt
@@ -26,6 +26,7 @@
 import android.text.style.StrikethroughSpan
 import android.text.style.StyleSpan
 import android.text.style.TextAppearanceSpan
+import android.text.style.TypefaceSpan
 import android.text.style.UnderlineSpan
 import android.view.Gravity
 import android.widget.LinearLayout
@@ -47,6 +48,7 @@
 import androidx.glance.layout.fillMaxWidth
 import androidx.glance.semantics.contentDescription
 import androidx.glance.semantics.semantics
+import androidx.glance.text.FontFamily
 import androidx.glance.text.FontStyle
 import androidx.glance.text.FontWeight
 import androidx.glance.text.Text
@@ -56,6 +58,7 @@
 import androidx.glance.unit.ColorProvider
 import androidx.test.core.app.ApplicationProvider
 import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertIs
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
@@ -64,7 +67,6 @@
 import org.junit.runner.RunWith
 import org.robolectric.RobolectricTestRunner
 import org.robolectric.annotation.Config
-import kotlin.test.assertIs
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(RobolectricTestRunner::class)
@@ -119,6 +121,96 @@
     }
 
     @Test
+    fun canTranslateText_withMonoFontFamily() = fakeCoroutineScope.runTest {
+        val rv = context.runAndTranslate {
+            Text(
+                "test",
+                style = TextStyle(fontFamily = FontFamily.Monospace),
+            )
+        }
+        val view = context.applyRemoteViews(rv)
+
+        assertIs<TextView>(view)
+        val content = view.text as SpannedString
+        assertThat(content.toString()).isEqualTo("test")
+        content.checkSingleSpan<TypefaceSpan> { span ->
+            assertThat(span.family).isEqualTo("monospace")
+        }
+    }
+
+    @Test
+    fun canTranslateText_withMonoSerifFamily() = fakeCoroutineScope.runTest {
+        val rv = context.runAndTranslate {
+            Text(
+                "test",
+                style = TextStyle(fontFamily = FontFamily.Serif),
+            )
+        }
+        val view = context.applyRemoteViews(rv)
+
+        assertIs<TextView>(view)
+        val content = view.text as SpannedString
+        assertThat(content.toString()).isEqualTo("test")
+        content.checkSingleSpan<TypefaceSpan> { span ->
+            assertThat(span.family).isEqualTo("serif")
+        }
+    }
+
+    @Test
+    fun canTranslateText_withSansFontFamily() = fakeCoroutineScope.runTest {
+        val rv = context.runAndTranslate {
+            Text(
+                "test",
+                style = TextStyle(fontFamily = FontFamily.SansSerif),
+            )
+        }
+        val view = context.applyRemoteViews(rv)
+
+        assertIs<TextView>(view)
+        val content = view.text as SpannedString
+        assertThat(content.toString()).isEqualTo("test")
+        content.checkSingleSpan<TypefaceSpan> { span ->
+            assertThat(span.family).isEqualTo("sans-serif")
+        }
+    }
+
+    @Test
+    fun canTranslateText_withCursiveFontFamily() = fakeCoroutineScope.runTest {
+        val rv = context.runAndTranslate {
+            Text(
+                "test",
+                style = TextStyle(fontFamily = FontFamily.Cursive),
+            )
+        }
+        val view = context.applyRemoteViews(rv)
+
+        assertIs<TextView>(view)
+        val content = view.text as SpannedString
+        assertThat(content.toString()).isEqualTo("test")
+        content.checkSingleSpan<TypefaceSpan> { span ->
+            assertThat(span.family).isEqualTo("cursive")
+        }
+    }
+
+    @Test
+    fun canTranslateText_withCustomFontFamily() = fakeCoroutineScope.runTest {
+        val rv = context.runAndTranslate {
+            Text(
+                "test",
+                style = TextStyle(fontFamily = FontFamily("casual")),
+            )
+        }
+        val view = context.applyRemoteViews(rv)
+
+        assertIs<TextView>(view)
+        val content = view.text as SpannedString
+        assertThat(content.toString()).isEqualTo("test")
+        content.checkSingleSpan<TypefaceSpan> { span ->
+            assertThat(span.family).isEqualTo("casual")
+        }
+    }
+
+    @Test
     fun canTranslateText_withStyleStrikeThrough() = fakeCoroutineScope.runTest {
         val rv = context.runAndTranslate {
             Text("test", style = TextStyle(textDecoration = TextDecoration.LineThrough))
diff --git a/glance/glance-wear-tiles/src/test/kotlin/androidx/glance/wear/tiles/GlanceTileServiceTest.kt b/glance/glance-wear-tiles/src/test/kotlin/androidx/glance/wear/tiles/GlanceTileServiceTest.kt
index 6be7404..6959421 100644
--- a/glance/glance-wear-tiles/src/test/kotlin/androidx/glance/wear/tiles/GlanceTileServiceTest.kt
+++ b/glance/glance-wear-tiles/src/test/kotlin/androidx/glance/wear/tiles/GlanceTileServiceTest.kt
@@ -41,6 +41,7 @@
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.guava.await
 import kotlinx.coroutines.runBlocking
@@ -92,10 +93,11 @@
         )
 
         executor = InlineExecutorService()
-        tileServiceWithState = TestGlanceTileServiceWithState()
+        tileServiceWithState = TestGlanceTileServiceWithState(fakeCoroutineScope)
         tileServiceClientWithState = TestTileClient(
             tileServiceWithState,
-            executor
+            fakeCoroutineScope,
+            fakeCoroutineScope.coroutineContext[CoroutineDispatcher]!!
         )
 
         ovalBitmap =
@@ -248,7 +250,9 @@
 
         val tileRequest = RequestBuilders.TileRequest.Builder().build()
         val tileFuture = tileServiceClientWithState.requestTile(tileRequest)
+
         shadowOf(Looper.getMainLooper()).idle()
+
         val tile = tileFuture.await()
 
         assertThat(tile.timeline!!.timelineEntries).hasSize(1)
@@ -319,6 +323,7 @@
                 testTimelineMode.timeIntervals.elementAt(0) -> {
                     Text("No event")
                 }
+
                 testTimelineMode.timeIntervals.elementAt(1) -> {
                     Text("Coffee")
                     Image(
@@ -328,6 +333,7 @@
                         contentScale = ContentScale.FillBounds
                     )
                 }
+
                 testTimelineMode.timeIntervals.elementAt(2) -> {
                     Text("Work")
                     Image(
@@ -336,6 +342,7 @@
                         modifier = GlanceModifier.size(40.dp),
                     )
                 }
+
                 testTimelineMode.timeIntervals.elementAt(3) -> {
                     Text("Dinner")
                 }
@@ -343,8 +350,14 @@
         }
     }
 
-    private inner class TestGlanceTileServiceWithState : GlanceTileService() {
+    private inner class TestGlanceTileServiceWithState(scope: CoroutineScope) :
+        GlanceTileService() {
         override val stateDefinition = PreferencesGlanceStateDefinition
+
+        init {
+            stateDefinition.setCoroutineScope(scope)
+        }
+
         val prefsNameKey = stringPreferencesKey("user_name")
 
         @Composable
diff --git a/glance/glance/api/current.txt b/glance/glance/api/current.txt
index a14ea96..c9b546b 100644
--- a/glance/glance/api/current.txt
+++ b/glance/glance/api/current.txt
@@ -666,6 +666,24 @@
 
 package androidx.glance.text {
 
+  public final class FontFamily {
+    ctor public FontFamily(String family);
+    method public String getFamily();
+    property public final String family;
+    field public static final androidx.glance.text.FontFamily.Companion Companion;
+  }
+
+  public static final class FontFamily.Companion {
+    method public androidx.glance.text.FontFamily getCursive();
+    method public androidx.glance.text.FontFamily getMonospace();
+    method public androidx.glance.text.FontFamily getSansSerif();
+    method public androidx.glance.text.FontFamily getSerif();
+    property public final androidx.glance.text.FontFamily Cursive;
+    property public final androidx.glance.text.FontFamily Monospace;
+    property public final androidx.glance.text.FontFamily SansSerif;
+    property public final androidx.glance.text.FontFamily Serif;
+  }
+
   @kotlin.jvm.JvmInline public final value class FontStyle {
     field public static final androidx.glance.text.FontStyle.Companion Companion;
   }
@@ -739,15 +757,17 @@
   }
 
   @androidx.compose.runtime.Immutable public final class TextStyle {
-    ctor public TextStyle(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration);
-    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);
+    ctor public TextStyle(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
+    method public androidx.glance.text.TextStyle copy(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
     method public androidx.glance.unit.ColorProvider getColor();
+    method public androidx.glance.text.FontFamily? getFontFamily();
     method public androidx.compose.ui.unit.TextUnit? getFontSize();
     method public androidx.glance.text.FontStyle? getFontStyle();
     method public androidx.glance.text.FontWeight? getFontWeight();
     method public androidx.glance.text.TextAlign? getTextAlign();
     method public androidx.glance.text.TextDecoration? getTextDecoration();
     property public final androidx.glance.unit.ColorProvider color;
+    property public final androidx.glance.text.FontFamily? fontFamily;
     property public final androidx.compose.ui.unit.TextUnit? fontSize;
     property public final androidx.glance.text.FontStyle? fontStyle;
     property public final androidx.glance.text.FontWeight? fontWeight;
@@ -765,7 +785,6 @@
 
   public final class ColorProviderKt {
     method public static androidx.glance.unit.ColorProvider ColorProvider(long color);
-    method public static androidx.glance.unit.ColorProvider ColorProvider(@ColorRes int resId);
   }
 
 }
diff --git a/glance/glance/api/public_plus_experimental_current.txt b/glance/glance/api/public_plus_experimental_current.txt
index a14ea96..c9b546b 100644
--- a/glance/glance/api/public_plus_experimental_current.txt
+++ b/glance/glance/api/public_plus_experimental_current.txt
@@ -666,6 +666,24 @@
 
 package androidx.glance.text {
 
+  public final class FontFamily {
+    ctor public FontFamily(String family);
+    method public String getFamily();
+    property public final String family;
+    field public static final androidx.glance.text.FontFamily.Companion Companion;
+  }
+
+  public static final class FontFamily.Companion {
+    method public androidx.glance.text.FontFamily getCursive();
+    method public androidx.glance.text.FontFamily getMonospace();
+    method public androidx.glance.text.FontFamily getSansSerif();
+    method public androidx.glance.text.FontFamily getSerif();
+    property public final androidx.glance.text.FontFamily Cursive;
+    property public final androidx.glance.text.FontFamily Monospace;
+    property public final androidx.glance.text.FontFamily SansSerif;
+    property public final androidx.glance.text.FontFamily Serif;
+  }
+
   @kotlin.jvm.JvmInline public final value class FontStyle {
     field public static final androidx.glance.text.FontStyle.Companion Companion;
   }
@@ -739,15 +757,17 @@
   }
 
   @androidx.compose.runtime.Immutable public final class TextStyle {
-    ctor public TextStyle(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration);
-    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);
+    ctor public TextStyle(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
+    method public androidx.glance.text.TextStyle copy(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
     method public androidx.glance.unit.ColorProvider getColor();
+    method public androidx.glance.text.FontFamily? getFontFamily();
     method public androidx.compose.ui.unit.TextUnit? getFontSize();
     method public androidx.glance.text.FontStyle? getFontStyle();
     method public androidx.glance.text.FontWeight? getFontWeight();
     method public androidx.glance.text.TextAlign? getTextAlign();
     method public androidx.glance.text.TextDecoration? getTextDecoration();
     property public final androidx.glance.unit.ColorProvider color;
+    property public final androidx.glance.text.FontFamily? fontFamily;
     property public final androidx.compose.ui.unit.TextUnit? fontSize;
     property public final androidx.glance.text.FontStyle? fontStyle;
     property public final androidx.glance.text.FontWeight? fontWeight;
@@ -765,7 +785,6 @@
 
   public final class ColorProviderKt {
     method public static androidx.glance.unit.ColorProvider ColorProvider(long color);
-    method public static androidx.glance.unit.ColorProvider ColorProvider(@ColorRes int resId);
   }
 
 }
diff --git a/glance/glance/api/restricted_current.txt b/glance/glance/api/restricted_current.txt
index a14ea96..c9b546b 100644
--- a/glance/glance/api/restricted_current.txt
+++ b/glance/glance/api/restricted_current.txt
@@ -666,6 +666,24 @@
 
 package androidx.glance.text {
 
+  public final class FontFamily {
+    ctor public FontFamily(String family);
+    method public String getFamily();
+    property public final String family;
+    field public static final androidx.glance.text.FontFamily.Companion Companion;
+  }
+
+  public static final class FontFamily.Companion {
+    method public androidx.glance.text.FontFamily getCursive();
+    method public androidx.glance.text.FontFamily getMonospace();
+    method public androidx.glance.text.FontFamily getSansSerif();
+    method public androidx.glance.text.FontFamily getSerif();
+    property public final androidx.glance.text.FontFamily Cursive;
+    property public final androidx.glance.text.FontFamily Monospace;
+    property public final androidx.glance.text.FontFamily SansSerif;
+    property public final androidx.glance.text.FontFamily Serif;
+  }
+
   @kotlin.jvm.JvmInline public final value class FontStyle {
     field public static final androidx.glance.text.FontStyle.Companion Companion;
   }
@@ -739,15 +757,17 @@
   }
 
   @androidx.compose.runtime.Immutable public final class TextStyle {
-    ctor public TextStyle(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration);
-    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);
+    ctor public TextStyle(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
+    method public androidx.glance.text.TextStyle copy(optional androidx.glance.unit.ColorProvider color, optional androidx.compose.ui.unit.TextUnit? fontSize, optional androidx.glance.text.FontWeight? fontWeight, optional androidx.glance.text.FontStyle? fontStyle, optional androidx.glance.text.TextAlign? textAlign, optional androidx.glance.text.TextDecoration? textDecoration, optional androidx.glance.text.FontFamily? fontFamily);
     method public androidx.glance.unit.ColorProvider getColor();
+    method public androidx.glance.text.FontFamily? getFontFamily();
     method public androidx.compose.ui.unit.TextUnit? getFontSize();
     method public androidx.glance.text.FontStyle? getFontStyle();
     method public androidx.glance.text.FontWeight? getFontWeight();
     method public androidx.glance.text.TextAlign? getTextAlign();
     method public androidx.glance.text.TextDecoration? getTextDecoration();
     property public final androidx.glance.unit.ColorProvider color;
+    property public final androidx.glance.text.FontFamily? fontFamily;
     property public final androidx.compose.ui.unit.TextUnit? fontSize;
     property public final androidx.glance.text.FontStyle? fontStyle;
     property public final androidx.glance.text.FontWeight? fontWeight;
@@ -765,7 +785,6 @@
 
   public final class ColorProviderKt {
     method public static androidx.glance.unit.ColorProvider ColorProvider(long color);
-    method public static androidx.glance.unit.ColorProvider ColorProvider(@ColorRes int resId);
   }
 
 }
diff --git a/glance/glance/src/androidMain/kotlin/androidx/glance/state/GlanceStateDefinition.kt b/glance/glance/src/androidMain/kotlin/androidx/glance/state/GlanceStateDefinition.kt
index 9b7d161..59fb9fb 100644
--- a/glance/glance/src/androidMain/kotlin/androidx/glance/state/GlanceStateDefinition.kt
+++ b/glance/glance/src/androidMain/kotlin/androidx/glance/state/GlanceStateDefinition.kt
@@ -23,6 +23,7 @@
 import androidx.datastore.preferences.core.Preferences
 import androidx.datastore.preferences.preferencesDataStoreFile
 import java.io.File
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
@@ -151,12 +152,26 @@
 }
 
 /**
- * Base class helping the creation of a state using DataStore's [Preferences].
+ * Base class helping the creation of a state using DataStore's [Preferences] with an optional
+ * CoroutineScope for [DataStore].
  */
 object PreferencesGlanceStateDefinition : GlanceStateDefinition<Preferences> {
+    private var coroutineScope: CoroutineScope? = null
     override fun getLocation(context: Context, fileKey: String): File =
         context.preferencesDataStoreFile(fileKey)
 
-    override suspend fun getDataStore(context: Context, fileKey: String): DataStore<Preferences> =
-        PreferenceDataStoreFactory.create { context.preferencesDataStoreFile(fileKey) }
+    override suspend fun getDataStore(context: Context, fileKey: String): DataStore<Preferences> {
+        return coroutineScope?.let {
+            PreferenceDataStoreFactory.create(scope = it) {
+                context.preferencesDataStoreFile(
+                    fileKey
+                )
+            }
+        } ?: PreferenceDataStoreFactory.create { context.preferencesDataStoreFile(fileKey) }
+    }
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    fun setCoroutineScope(scope: CoroutineScope) {
+        coroutineScope = scope
+    }
 }
diff --git a/glance/glance/src/androidMain/kotlin/androidx/glance/text/FontFamily.kt b/glance/glance/src/androidMain/kotlin/androidx/glance/text/FontFamily.kt
new file mode 100644
index 0000000..39b8e52
--- /dev/null
+++ b/glance/glance/src/androidMain/kotlin/androidx/glance/text/FontFamily.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.glance.text
+
+/**
+ * Describes the family of the font.
+ * Defaults are provided, but it is also possible to supply a custom family. If this is found on
+ * the system it will be used, otherwise it will fallback to a system default.
+ */
+class FontFamily constructor(val family: String) {
+    companion object {
+        /**
+         * The formal text style for scripts.
+         */
+        val Serif = FontFamily("serif")
+
+        /**
+         * Font family with low contrast and plain stroke endings.
+         */
+        val SansSerif = FontFamily("sans-serif")
+
+        /**
+         * Font family where glyphs have the same fixed width.
+         */
+        val Monospace = FontFamily("monospace")
+
+        /**
+         * Cursive, hand-written like font family.
+         */
+        val Cursive = FontFamily("cursive")
+    }
+
+    override fun toString(): String {
+        return family
+    }
+}
diff --git a/glance/glance/src/androidMain/kotlin/androidx/glance/text/Text.kt b/glance/glance/src/androidMain/kotlin/androidx/glance/text/Text.kt
index 6878e55..b079d59 100644
--- a/glance/glance/src/androidMain/kotlin/androidx/glance/text/Text.kt
+++ b/glance/glance/src/androidMain/kotlin/androidx/glance/text/Text.kt
@@ -70,7 +70,7 @@
         it.modifier = modifier
         it.text = text
         it.style = style
-        it.maxLines = it.maxLines
+        it.maxLines = maxLines
     }
 
     override fun toString(): String =
diff --git a/glance/glance/src/androidMain/kotlin/androidx/glance/text/TextStyle.kt b/glance/glance/src/androidMain/kotlin/androidx/glance/text/TextStyle.kt
index f8c01f9..75fdc05 100644
--- a/glance/glance/src/androidMain/kotlin/androidx/glance/text/TextStyle.kt
+++ b/glance/glance/src/androidMain/kotlin/androidx/glance/text/TextStyle.kt
@@ -22,6 +22,20 @@
 
 /**
  * Description of a text style for the [androidx.glance.text.Text] composable.
+ *
+ * @param color optionally specifies which font family to use for the text, defaults to
+ *         [TextDefaults.defaultTextColor].
+ * @param fontSize optionally specifies the size to use for the text, defaults to system when null.
+ * @param fontWeight optionally specifies the weight to use for the text, defaults to system
+ *         when null.
+ * @param fontStyle optionally specifies style (such as italics) to use for the text, defaults to
+ *         system when null.
+ * @param textAlign optionally specifies the alignment to use for the text, defaults to start when.
+ *         null.
+ * @param textDecoration optionally specifies decorations (e.g. underline) to use for the text,
+ *         defaults to none when null
+ * @param fontFamily optionally specifies which font family to use for the text, defaults to system
+ *         when null.
  */
 @Immutable
 class TextStyle(
@@ -31,6 +45,7 @@
     val fontStyle: FontStyle? = null,
     val textAlign: TextAlign? = null,
     val textDecoration: TextDecoration? = null,
+    val fontFamily: FontFamily? = null,
 ) {
     fun copy(
         color: ColorProvider = this.color,
@@ -38,14 +53,16 @@
         fontWeight: FontWeight? = this.fontWeight,
         fontStyle: FontStyle? = this.fontStyle,
         textAlign: TextAlign? = this.textAlign,
-        textDecoration: TextDecoration? = this.textDecoration
+        textDecoration: TextDecoration? = this.textDecoration,
+        fontFamily: FontFamily? = this.fontFamily,
     ) = TextStyle(
         color = color,
         fontSize = fontSize,
         fontWeight = fontWeight,
         fontStyle = fontStyle,
         textAlign = textAlign,
-        textDecoration = textDecoration
+        textDecoration = textDecoration,
+        fontFamily = fontFamily,
     )
 
     override fun equals(other: Any?): Boolean {
@@ -57,6 +74,7 @@
         if (fontStyle != other.fontStyle) return false
         if (textDecoration != other.textDecoration) return false
         if (textAlign != other.textAlign) return false
+        if (fontFamily != other.fontFamily) return false
         return true
     }
 
@@ -67,10 +85,12 @@
         result = 31 * result + fontStyle.hashCode()
         result = 31 * result + textDecoration.hashCode()
         result = 31 * result + textAlign.hashCode()
+        result = 31 * result + fontFamily.hashCode()
         return result
     }
 
     override fun toString() =
         "TextStyle(color=$color, fontSize=$fontSize, fontWeight=$fontWeight, " +
-            "fontStyle=$fontStyle, textDecoration=$textDecoration, textAlign=$textAlign)"
+            "fontStyle=$fontStyle, textDecoration=$textDecoration, textAlign=$textAlign, " +
+            "fontFamily=$fontFamily)"
 }
diff --git a/glance/glance/src/androidMain/kotlin/androidx/glance/unit/ColorProvider.kt b/glance/glance/src/androidMain/kotlin/androidx/glance/unit/ColorProvider.kt
index 354ed42..c1d5693 100644
--- a/glance/glance/src/androidMain/kotlin/androidx/glance/unit/ColorProvider.kt
+++ b/glance/glance/src/androidMain/kotlin/androidx/glance/unit/ColorProvider.kt
@@ -38,7 +38,11 @@
     return FixedColorProvider(color)
 }
 
-/** Returns a [ColorProvider] that resolves to the color resource. */
+/** Returns a [ColorProvider] that resolves to the color resource. This should
+ * not be used outside of the Glance Libraries due to inconsistencies with regards
+ * to what process (app vs launcher) colors are resolved in
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 fun ColorProvider(@ColorRes resId: Int): ColorProvider {
     return ResourceColorProvider(resId)
 }
diff --git a/gradle.properties b/gradle.properties
index a26afbf..69496b3 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -25,9 +25,12 @@
 android.experimental.lint.missingBaselineIsEmptyBaseline=true
 android.experimental.lint.version = 8.1.0-alpha07
 
-# Don't generate versioned API files
+# Generate versioned API files
 androidx.writeVersionedApiFiles=true
 
+# Run the CheckAarMetadata task
+androidx.checkAarMetadata=true
+
 # Do restrict compileSdkPreview usage
 androidx.allowCustomCompileSdk=false
 
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 2383b5d9..31189e2 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -98,7 +98,7 @@
 checkerframework = { module = "org.checkerframework:checker-qual", version = "2.5.3" }
 checkmark = { module = "net.saff.checkmark:checkmark", version = "0.1.6" }
 constraintLayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.0.1"}
-dackka = { module = "com.google.devsite:dackka", version = "1.3.0" }
+dackka = { module = "com.google.devsite:dackka", version = "1.3.1" }
 dagger = { module = "com.google.dagger:dagger", version.ref = "dagger" }
 daggerCompiler = { module = "com.google.dagger:dagger-compiler", version.ref = "dagger" }
 dexmakerMockito = { module = "com.linkedin.dexmaker:dexmaker-mockito", version.ref = "dexmaker" }
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 048d8fc..26dab29 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -491,6 +491,14 @@
             <sha256 value="100a2c6db1ec4aab9545ead93be094c9728ecc671fba8353648d04ef405f30c8" origin="Generated by Gradle" reason="Artifact is not signed"/>
          </artifact>
       </component>
+      <component group="com.google.android.libraries.identity.googleid" name="googleid" version="1.0.0">
+         <artifact name="googleid-1.0.0.aar">
+            <sha256 value="7c6c910a751e32951afafd79be22f9d5222229ec59a92961515a8b0b6f8ecdcb" origin="Generated by Gradle" reason="Artifact is not signed"/>
+         </artifact>
+         <artifact name="googleid-1.0.0.pom">
+            <sha256 value="663aed69db1623331032fe4dedee4f2b1c9decbdad0ab06a4fc0be14b3e52c7f" origin="Generated by Gradle" reason="Artifact is not signed"/>
+         </artifact>
+      </component>
       <component group="com.google.android.odml" name="image" version="1.0.0-beta1">
          <artifact name="image-1.0.0-beta1.aar">
             <sha256 value="2e71aa31f83a9415277f119de67195726f07d1760e9542c111778c320e3aa1f2" origin="Generated by Gradle" reason="Artifact is not signed"/>
@@ -563,16 +571,6 @@
             <sha256 value="47a89be0fa0fedd476db5fd2c83487654d2a119c391f83a142be876667cf7dab" origin="Generated by Gradle" reason="Artifact is not signed"/>
          </artifact>
       </component>
-      <component group="com.gradle" name="common-custom-user-data-gradle-plugin" version="1.7.2">
-         <artifact name="common-custom-user-data-gradle-plugin-1.7.2.pom">
-            <sha256 value="c70db912c8b127b1b9a6c0cccac1a9353e9fc3b063a3be0114a5208f43c09c31" origin="Generated by Gradle" reason="Artifact is not signed"/>
-         </artifact>
-      </component>
-      <component group="com.gradle" name="gradle-enterprise-gradle-plugin" version="3.10.2">
-         <artifact name="gradle-enterprise-gradle-plugin-3.10.2.pom">
-            <sha256 value="57603c9a75a9ef86ce30b1cb2db728d3cd9caf1be967343f1fc2316c85df5653" origin="Generated by Gradle"/>
-         </artifact>
-      </component>
       <component group="de.undercouch" name="gradle-download-task" version="4.1.1">
          <artifact name="gradle-download-task-4.1.1.jar">
             <ignored-keys>
diff --git a/graphics/graphics-core/api/current.txt b/graphics/graphics-core/api/current.txt
index 92d9c70..e10c65e 100644
--- a/graphics/graphics-core/api/current.txt
+++ b/graphics/graphics-core/api/current.txt
@@ -10,6 +10,24 @@
     property public final int width;
   }
 
+  @RequiresApi(android.os.Build.VERSION_CODES.Q) public final class CanvasFrontBufferedRenderer<T> {
+    ctor public CanvasFrontBufferedRenderer(android.view.SurfaceView surfaceView, androidx.graphics.lowlatency.CanvasFrontBufferedRenderer.Callback<T> callback);
+    method public void cancel();
+    method public void commit();
+    method public boolean isValid();
+    method public void release(boolean cancelPending, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onReleaseComplete);
+    method public void release(boolean cancelPending);
+    method public void renderFrontBufferedLayer(T? param);
+    method public void renderMultiBufferedLayer(java.util.Collection<? extends T> params);
+  }
+
+  @kotlin.jvm.JvmDefaultWithCompatibility public static interface CanvasFrontBufferedRenderer.Callback<T> {
+    method @WorkerThread public void onDrawFrontBufferedLayer(android.graphics.Canvas canvas, int bufferWidth, int bufferHeight, T? param);
+    method @WorkerThread public void onDrawMultiBufferedLayer(android.graphics.Canvas canvas, int bufferWidth, int bufferHeight, java.util.Collection<? extends T> params);
+    method @WorkerThread public default void onFrontBufferedLayerRenderComplete(androidx.graphics.surface.SurfaceControlCompat frontBufferedLayerSurfaceControl, androidx.graphics.surface.SurfaceControlCompat.Transaction transaction);
+    method @WorkerThread public default void onMultiBufferedLayerRenderComplete(androidx.graphics.surface.SurfaceControlCompat frontBufferedLayerSurfaceControl, androidx.graphics.surface.SurfaceControlCompat.Transaction transaction);
+  }
+
   public final class FrontBufferSyncStrategy implements androidx.graphics.opengl.SyncStrategy {
     ctor public FrontBufferSyncStrategy(long usageFlags);
     method @RequiresApi(android.os.Build.VERSION_CODES.KITKAT) public androidx.hardware.SyncFenceCompat? createSyncFence(androidx.graphics.opengl.egl.EGLSpec eglSpec);
@@ -28,19 +46,15 @@
     method public boolean isValid();
     method public void release(boolean cancelPending, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onReleaseComplete);
     method public void release(boolean cancelPending);
-    method public void renderDoubleBufferedLayer(java.util.Collection<? extends T> params);
     method public void renderFrontBufferedLayer(T? param);
-    field public static final androidx.graphics.lowlatency.GLFrontBufferedRenderer.Companion Companion;
+    method public void renderMultiBufferedLayer(java.util.Collection<? extends T> params);
   }
 
   @kotlin.jvm.JvmDefaultWithCompatibility public static interface GLFrontBufferedRenderer.Callback<T> {
-    method @WorkerThread public default void onDoubleBufferedLayerRenderComplete(androidx.graphics.surface.SurfaceControlCompat frontBufferedLayerSurfaceControl, androidx.graphics.surface.SurfaceControlCompat.Transaction transaction);
-    method @WorkerThread public void onDrawDoubleBufferedLayer(androidx.graphics.opengl.egl.EGLManager eglManager, androidx.graphics.lowlatency.BufferInfo bufferInfo, float[] transform, java.util.Collection<? extends T> params);
     method @WorkerThread public void onDrawFrontBufferedLayer(androidx.graphics.opengl.egl.EGLManager eglManager, androidx.graphics.lowlatency.BufferInfo bufferInfo, float[] transform, T? param);
+    method @WorkerThread public void onDrawMultiBufferedLayer(androidx.graphics.opengl.egl.EGLManager eglManager, androidx.graphics.lowlatency.BufferInfo bufferInfo, float[] transform, java.util.Collection<? extends T> params);
     method @WorkerThread public default void onFrontBufferedLayerRenderComplete(androidx.graphics.surface.SurfaceControlCompat frontBufferedLayerSurfaceControl, androidx.graphics.surface.SurfaceControlCompat.Transaction transaction);
-  }
-
-  public static final class GLFrontBufferedRenderer.Companion {
+    method @WorkerThread public default void onMultiBufferedLayerRenderComplete(androidx.graphics.surface.SurfaceControlCompat frontBufferedLayerSurfaceControl, androidx.graphics.surface.SurfaceControlCompat.Transaction transaction);
   }
 
 }
diff --git a/graphics/graphics-core/api/public_plus_experimental_current.txt b/graphics/graphics-core/api/public_plus_experimental_current.txt
index 92d9c70..e10c65e 100644
--- a/graphics/graphics-core/api/public_plus_experimental_current.txt
+++ b/graphics/graphics-core/api/public_plus_experimental_current.txt
@@ -10,6 +10,24 @@
     property public final int width;
   }
 
+  @RequiresApi(android.os.Build.VERSION_CODES.Q) public final class CanvasFrontBufferedRenderer<T> {
+    ctor public CanvasFrontBufferedRenderer(android.view.SurfaceView surfaceView, androidx.graphics.lowlatency.CanvasFrontBufferedRenderer.Callback<T> callback);
+    method public void cancel();
+    method public void commit();
+    method public boolean isValid();
+    method public void release(boolean cancelPending, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onReleaseComplete);
+    method public void release(boolean cancelPending);
+    method public void renderFrontBufferedLayer(T? param);
+    method public void renderMultiBufferedLayer(java.util.Collection<? extends T> params);
+  }
+
+  @kotlin.jvm.JvmDefaultWithCompatibility public static interface CanvasFrontBufferedRenderer.Callback<T> {
+    method @WorkerThread public void onDrawFrontBufferedLayer(android.graphics.Canvas canvas, int bufferWidth, int bufferHeight, T? param);
+    method @WorkerThread public void onDrawMultiBufferedLayer(android.graphics.Canvas canvas, int bufferWidth, int bufferHeight, java.util.Collection<? extends T> params);
+    method @WorkerThread public default void onFrontBufferedLayerRenderComplete(androidx.graphics.surface.SurfaceControlCompat frontBufferedLayerSurfaceControl, androidx.graphics.surface.SurfaceControlCompat.Transaction transaction);
+    method @WorkerThread public default void onMultiBufferedLayerRenderComplete(androidx.graphics.surface.SurfaceControlCompat frontBufferedLayerSurfaceControl, androidx.graphics.surface.SurfaceControlCompat.Transaction transaction);
+  }
+
   public final class FrontBufferSyncStrategy implements androidx.graphics.opengl.SyncStrategy {
     ctor public FrontBufferSyncStrategy(long usageFlags);
     method @RequiresApi(android.os.Build.VERSION_CODES.KITKAT) public androidx.hardware.SyncFenceCompat? createSyncFence(androidx.graphics.opengl.egl.EGLSpec eglSpec);
@@ -28,19 +46,15 @@
     method public boolean isValid();
     method public void release(boolean cancelPending, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onReleaseComplete);
     method public void release(boolean cancelPending);
-    method public void renderDoubleBufferedLayer(java.util.Collection<? extends T> params);
     method public void renderFrontBufferedLayer(T? param);
-    field public static final androidx.graphics.lowlatency.GLFrontBufferedRenderer.Companion Companion;
+    method public void renderMultiBufferedLayer(java.util.Collection<? extends T> params);
   }
 
   @kotlin.jvm.JvmDefaultWithCompatibility public static interface GLFrontBufferedRenderer.Callback<T> {
-    method @WorkerThread public default void onDoubleBufferedLayerRenderComplete(androidx.graphics.surface.SurfaceControlCompat frontBufferedLayerSurfaceControl, androidx.graphics.surface.SurfaceControlCompat.Transaction transaction);
-    method @WorkerThread public void onDrawDoubleBufferedLayer(androidx.graphics.opengl.egl.EGLManager eglManager, androidx.graphics.lowlatency.BufferInfo bufferInfo, float[] transform, java.util.Collection<? extends T> params);
     method @WorkerThread public void onDrawFrontBufferedLayer(androidx.graphics.opengl.egl.EGLManager eglManager, androidx.graphics.lowlatency.BufferInfo bufferInfo, float[] transform, T? param);
+    method @WorkerThread public void onDrawMultiBufferedLayer(androidx.graphics.opengl.egl.EGLManager eglManager, androidx.graphics.lowlatency.BufferInfo bufferInfo, float[] transform, java.util.Collection<? extends T> params);
     method @WorkerThread public default void onFrontBufferedLayerRenderComplete(androidx.graphics.surface.SurfaceControlCompat frontBufferedLayerSurfaceControl, androidx.graphics.surface.SurfaceControlCompat.Transaction transaction);
-  }
-
-  public static final class GLFrontBufferedRenderer.Companion {
+    method @WorkerThread public default void onMultiBufferedLayerRenderComplete(androidx.graphics.surface.SurfaceControlCompat frontBufferedLayerSurfaceControl, androidx.graphics.surface.SurfaceControlCompat.Transaction transaction);
   }
 
 }
diff --git a/graphics/graphics-core/api/restricted_current.txt b/graphics/graphics-core/api/restricted_current.txt
index 47f6f95..bf4aa7c 100644
--- a/graphics/graphics-core/api/restricted_current.txt
+++ b/graphics/graphics-core/api/restricted_current.txt
@@ -10,6 +10,24 @@
     property public final int width;
   }
 
+  @RequiresApi(android.os.Build.VERSION_CODES.Q) public final class CanvasFrontBufferedRenderer<T> {
+    ctor public CanvasFrontBufferedRenderer(android.view.SurfaceView surfaceView, androidx.graphics.lowlatency.CanvasFrontBufferedRenderer.Callback<T> callback);
+    method public void cancel();
+    method public void commit();
+    method public boolean isValid();
+    method public void release(boolean cancelPending, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onReleaseComplete);
+    method public void release(boolean cancelPending);
+    method public void renderFrontBufferedLayer(T? param);
+    method public void renderMultiBufferedLayer(java.util.Collection<? extends T> params);
+  }
+
+  @kotlin.jvm.JvmDefaultWithCompatibility public static interface CanvasFrontBufferedRenderer.Callback<T> {
+    method @WorkerThread public void onDrawFrontBufferedLayer(android.graphics.Canvas canvas, int bufferWidth, int bufferHeight, T? param);
+    method @WorkerThread public void onDrawMultiBufferedLayer(android.graphics.Canvas canvas, int bufferWidth, int bufferHeight, java.util.Collection<? extends T> params);
+    method @WorkerThread public default void onFrontBufferedLayerRenderComplete(androidx.graphics.surface.SurfaceControlCompat frontBufferedLayerSurfaceControl, androidx.graphics.surface.SurfaceControlCompat.Transaction transaction);
+    method @WorkerThread public default void onMultiBufferedLayerRenderComplete(androidx.graphics.surface.SurfaceControlCompat frontBufferedLayerSurfaceControl, androidx.graphics.surface.SurfaceControlCompat.Transaction transaction);
+  }
+
   public final class FrontBufferSyncStrategy implements androidx.graphics.opengl.SyncStrategy {
     ctor public FrontBufferSyncStrategy(long usageFlags);
     method @RequiresApi(android.os.Build.VERSION_CODES.KITKAT) public androidx.hardware.SyncFenceCompat? createSyncFence(androidx.graphics.opengl.egl.EGLSpec eglSpec);
@@ -28,19 +46,15 @@
     method public boolean isValid();
     method public void release(boolean cancelPending, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onReleaseComplete);
     method public void release(boolean cancelPending);
-    method public void renderDoubleBufferedLayer(java.util.Collection<? extends T> params);
     method public void renderFrontBufferedLayer(T? param);
-    field public static final androidx.graphics.lowlatency.GLFrontBufferedRenderer.Companion Companion;
+    method public void renderMultiBufferedLayer(java.util.Collection<? extends T> params);
   }
 
   @kotlin.jvm.JvmDefaultWithCompatibility public static interface GLFrontBufferedRenderer.Callback<T> {
-    method @WorkerThread public default void onDoubleBufferedLayerRenderComplete(androidx.graphics.surface.SurfaceControlCompat frontBufferedLayerSurfaceControl, androidx.graphics.surface.SurfaceControlCompat.Transaction transaction);
-    method @WorkerThread public void onDrawDoubleBufferedLayer(androidx.graphics.opengl.egl.EGLManager eglManager, androidx.graphics.lowlatency.BufferInfo bufferInfo, float[] transform, java.util.Collection<? extends T> params);
     method @WorkerThread public void onDrawFrontBufferedLayer(androidx.graphics.opengl.egl.EGLManager eglManager, androidx.graphics.lowlatency.BufferInfo bufferInfo, float[] transform, T? param);
+    method @WorkerThread public void onDrawMultiBufferedLayer(androidx.graphics.opengl.egl.EGLManager eglManager, androidx.graphics.lowlatency.BufferInfo bufferInfo, float[] transform, java.util.Collection<? extends T> params);
     method @WorkerThread public default void onFrontBufferedLayerRenderComplete(androidx.graphics.surface.SurfaceControlCompat frontBufferedLayerSurfaceControl, androidx.graphics.surface.SurfaceControlCompat.Transaction transaction);
-  }
-
-  public static final class GLFrontBufferedRenderer.Companion {
+    method @WorkerThread public default void onMultiBufferedLayerRenderComplete(androidx.graphics.surface.SurfaceControlCompat frontBufferedLayerSurfaceControl, androidx.graphics.surface.SurfaceControlCompat.Transaction transaction);
   }
 
 }
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/SurfaceTextureRendererTest.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/SurfaceTextureRendererTest.kt
index aae6cde..199c4cd 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/SurfaceTextureRendererTest.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/SurfaceTextureRendererTest.kt
@@ -19,12 +19,15 @@
 import android.graphics.Color
 import android.graphics.RenderNode
 import android.graphics.SurfaceTexture
+import android.opengl.GLES20
 import android.os.Build
 import android.os.Handler
 import android.os.HandlerThread
 import android.os.Looper
 import androidx.annotation.RequiresApi
 import androidx.graphics.SurfaceTextureRendererTest.TestHelpers.Companion.createSurfaceTextureRenderer
+import androidx.graphics.opengl.GLRenderer
+import androidx.graphics.opengl.egl.EGLManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
@@ -85,6 +88,67 @@
         }
     }
 
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+    @Test
+    fun testMultipleRenderFramesRequests() {
+        withHandlerThread { handler ->
+            var renderLatch = CountDownLatch(1)
+            val glRenderer = GLRenderer().apply { start() }
+            val node = RenderNode("node").apply {
+                setPosition(0, 0, TEST_WIDTH, TEST_HEIGHT)
+            }
+            var attached = false
+            var texture: SurfaceTexture? = null
+            val target = glRenderer.createRenderTarget(
+                TEST_WIDTH,
+                TEST_HEIGHT,
+                object : GLRenderer.RenderCallback {
+                    override fun onDrawFrame(eglManager: EGLManager) {
+                        if (!attached) {
+                            val tex = IntArray(1)
+                            GLES20.glGenTextures(1, tex, 0)
+                            texture!!.attachToGLContext(tex[0])
+                            attached = true
+                        }
+
+                        texture!!.updateTexImage()
+                        renderLatch.countDown()
+                    }
+                })
+            val renderer = createSurfaceTextureRenderer(renderNode = node, handler = handler) {
+                    surfaceTexture ->
+                texture = surfaceTexture
+                target.requestRender()
+            }
+
+            var canvas = node.beginRecording()
+            canvas.drawColor(Color.RED)
+            node.endRecording()
+
+            renderer.renderFrame() // 1
+
+            canvas = node.beginRecording()
+            canvas.drawColor(Color.YELLOW)
+            node.endRecording()
+
+            renderer.renderFrame() // 2
+
+            assertTrue(renderLatch.await(3000, TimeUnit.MILLISECONDS))
+
+            canvas = node.beginRecording()
+            canvas.drawColor(Color.BLUE)
+            node.endRecording()
+
+            renderLatch = CountDownLatch(1)
+
+            renderer.renderFrame() // 3
+
+            assertTrue(renderLatch.await(3000, TimeUnit.MILLISECONDS))
+
+            renderer.release()
+        }
+    }
+
     /**
      * Static inner class to enclose helper methods that rely on APIs that were introduced in newer
      * versions of Android. Even though the individual tests are gated with SdkSuppress annotations,
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/CanvasFrontBufferedRendererTest.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/CanvasFrontBufferedRendererTest.kt
new file mode 100644
index 0000000..354e4ee
--- /dev/null
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/CanvasFrontBufferedRendererTest.kt
@@ -0,0 +1,496 @@
+/*
+ * 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.graphics.lowlatency
+
+import android.graphics.Canvas
+import android.graphics.Color
+import android.os.Build
+import android.util.Log
+import android.view.SurfaceView
+import androidx.annotation.RequiresApi
+import androidx.graphics.drawSquares
+import androidx.graphics.surface.SurfaceControlCompat
+import androidx.graphics.surface.SurfaceControlUtils
+import androidx.lifecycle.Lifecycle
+import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executors
+import java.util.concurrent.TimeUnit
+import kotlin.math.roundToInt
+import org.junit.Assert
+import org.junit.Ignore
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class CanvasFrontBufferedRendererTest {
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+    @Test
+    fun testFrontBufferedLayerRender() {
+        val renderLatch = CountDownLatch(1)
+        val callbacks = object : CanvasFrontBufferedRenderer.Callback<Any> {
+            override fun onDrawFrontBufferedLayer(
+                canvas: Canvas,
+                bufferWidth: Int,
+                bufferHeight: Int,
+                param: Any
+            ) {
+                canvas.drawColor(Color.RED)
+            }
+
+            override fun onDrawMultiBufferedLayer(
+                canvas: Canvas,
+                bufferWidth: Int,
+                bufferHeight: Int,
+                params: Collection<Any>
+            ) {
+                canvas.drawColor(Color.BLUE)
+            }
+
+            override fun onFrontBufferedLayerRenderComplete(
+                frontBufferedLayerSurfaceControl: SurfaceControlCompat,
+                transaction: SurfaceControlCompat.Transaction
+            ) {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+                    transaction.addTransactionCommittedListener(
+                        Executors.newSingleThreadExecutor(),
+                        object : SurfaceControlCompat.TransactionCommittedListener {
+                            override fun onTransactionCommitted() {
+                                renderLatch.countDown()
+                            }
+                        }
+                    )
+                } else {
+                    renderLatch.countDown()
+                }
+            }
+        }
+        var renderer: CanvasFrontBufferedRenderer<Any>? = null
+        var surfaceView: SurfaceView? = null
+        try {
+            val scenario = ActivityScenario.launch(FrontBufferedRendererTestActivity::class.java)
+                .moveToState(Lifecycle.State.CREATED)
+                .onActivity {
+                    surfaceView = it.getSurfaceView()
+                    renderer = CanvasFrontBufferedRenderer(surfaceView!!, callbacks)
+                }
+            scenario.moveToState(Lifecycle.State.RESUMED).onActivity {
+                renderer?.renderFrontBufferedLayer(Any())
+            }
+            Assert.assertTrue(renderLatch.await(3000, TimeUnit.MILLISECONDS))
+
+            val coords = IntArray(2)
+            val width: Int
+            val height: Int
+            with(surfaceView!!) {
+                getLocationOnScreen(coords)
+                width = this.width
+                height = this.height
+            }
+
+            SurfaceControlUtils.validateOutput { bitmap ->
+                Color.RED ==
+                    bitmap.getPixel(coords[0] + width / 2, coords[1] + height / 2)
+            }
+        } finally {
+            renderer.blockingRelease()
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+    @Test
+    fun testMultiBufferedLayerRender() {
+        val renderLatch = CountDownLatch(1)
+        val callbacks = object : CanvasFrontBufferedRenderer.Callback<Any> {
+
+            override fun onDrawFrontBufferedLayer(
+                canvas: Canvas,
+                bufferWidth: Int,
+                bufferHeight: Int,
+                param: Any
+            ) {
+                Log.v("MultiBufferTest", "")
+                canvas.drawColor(Color.RED)
+            }
+
+            override fun onDrawMultiBufferedLayer(
+                canvas: Canvas,
+                bufferWidth: Int,
+                bufferHeight: Int,
+                params: Collection<Any>
+            ) {
+                canvas.drawColor(Color.BLUE)
+            }
+
+            override fun onMultiBufferedLayerRenderComplete(
+                frontBufferedLayerSurfaceControl: SurfaceControlCompat,
+                transaction: SurfaceControlCompat.Transaction
+            ) {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+                    transaction.addTransactionCommittedListener(
+                        Executors.newSingleThreadExecutor(),
+                        object : SurfaceControlCompat.TransactionCommittedListener {
+                            override fun onTransactionCommitted() {
+                                renderLatch.countDown()
+                            }
+                        })
+                } else {
+                    renderLatch.countDown()
+                }
+            }
+        }
+        var renderer: CanvasFrontBufferedRenderer<Any>? = null
+        var surfaceView: SurfaceView? = null
+        try {
+            val scenario = ActivityScenario.launch(FrontBufferedRendererTestActivity::class.java)
+                .moveToState(Lifecycle.State.CREATED)
+                .onActivity {
+                    surfaceView = it.getSurfaceView()
+                    renderer = CanvasFrontBufferedRenderer(surfaceView!!, callbacks)
+                }
+
+            scenario.moveToState(Lifecycle.State.RESUMED).onActivity {
+                renderer?.renderFrontBufferedLayer(Any())
+                renderer?.commit()
+            }
+            Assert.assertTrue(renderLatch.await(3000, TimeUnit.MILLISECONDS))
+
+            val coords = IntArray(2)
+            val width: Int
+            val height: Int
+            with(surfaceView!!) {
+                getLocationOnScreen(coords)
+                width = this.width
+                height = this.height
+            }
+
+            SurfaceControlUtils.validateOutput { bitmap ->
+                (Math.abs(
+                    Color.red(Color.BLUE) - Color.red(
+                        bitmap.getPixel(
+                            coords[0] + width / 2,
+                            coords[1] + height / 2
+                        )
+                    )
+                ) < 2) &&
+                    (Math.abs(
+                        Color.green(Color.BLUE) - Color.green(
+                            bitmap.getPixel(
+                                coords[0] + width / 2,
+                                coords[1] + height / 2
+                            )
+                        )
+                    ) < 2) &&
+                    (Math.abs(
+                        Color.blue(Color.BLUE) - Color.blue(
+                            bitmap.getPixel(
+                                coords[0] + width / 2,
+                                coords[1] + height / 2
+                            )
+                        )
+                    ) < 2)
+            }
+        } finally {
+            renderer?.blockingRelease()
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+    @Test
+    fun testRenderMultiBufferLayer() {
+        val squareSize = 100f
+        val renderLatch = CountDownLatch(1)
+        val callbacks = object : CanvasFrontBufferedRenderer.Callback<Int> {
+
+            override fun onDrawFrontBufferedLayer(
+                canvas: Canvas,
+                bufferWidth: Int,
+                bufferHeight: Int,
+                param: Int
+            ) {
+                // NO-OP we do not render to the front buffered layer in this test case
+            }
+
+            override fun onDrawMultiBufferedLayer(
+                canvas: Canvas,
+                bufferWidth: Int,
+                bufferHeight: Int,
+                params: Collection<Int>
+            ) {
+                drawSquares(
+                    canvas,
+                    bufferWidth,
+                    bufferHeight,
+                    Color.RED,
+                    Color.BLACK,
+                    Color.YELLOW,
+                    Color.BLUE
+                )
+            }
+
+            override fun onMultiBufferedLayerRenderComplete(
+                frontBufferedLayerSurfaceControl: SurfaceControlCompat,
+                transaction: SurfaceControlCompat.Transaction
+            ) {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+                    transaction.addTransactionCommittedListener(
+                        Executors.newSingleThreadExecutor(),
+                        object : SurfaceControlCompat.TransactionCommittedListener {
+                            override fun onTransactionCommitted() {
+                                renderLatch.countDown()
+                            }
+                        })
+                } else {
+                    renderLatch.countDown()
+                }
+            }
+        }
+        var renderer: CanvasFrontBufferedRenderer<Int>? = null
+        var surfaceView: SurfaceView? = null
+        try {
+            val scenario = ActivityScenario.launch(FrontBufferedRendererTestActivity::class.java)
+                .moveToState(Lifecycle.State.CREATED)
+                .onActivity {
+                    surfaceView = it.getSurfaceView()
+                    renderer = CanvasFrontBufferedRenderer(surfaceView!!, callbacks)
+                }
+
+            scenario.moveToState(Lifecycle.State.RESUMED).onActivity {
+                val colors = listOf(Color.RED, Color.BLACK, Color.YELLOW, Color.BLUE)
+                renderer?.renderMultiBufferedLayer(colors)
+            }
+            Assert.assertTrue(renderLatch.await(3000, TimeUnit.MILLISECONDS))
+
+            val coords = IntArray(2)
+            with(surfaceView!!) {
+                getLocationOnScreen(coords)
+            }
+
+            SurfaceControlUtils.validateOutput { bitmap ->
+                val topLeft = bitmap.getPixel(
+                    coords[0] + (squareSize / 4).toInt(),
+                    coords[1] + (squareSize / 4).toInt()
+                )
+                val topRight = bitmap.getPixel(
+                    coords[0] + (squareSize * 3f / 4f).roundToInt(),
+                    coords[1] + (squareSize / 4).toInt()
+                )
+                val bottomLeft = bitmap.getPixel(
+                    coords[0] + (squareSize / 4f).toInt(),
+                    coords[1] + (squareSize * 3f / 4f).roundToInt()
+                )
+                val bottomRight = bitmap.getPixel(
+                    coords[0] + (squareSize * 3f / 4f).roundToInt(),
+                    coords[1] + (squareSize * 3f / 4f).roundToInt()
+                )
+                Color.RED == topLeft &&
+                    Color.BLACK == topRight &&
+                    Color.YELLOW == bottomLeft &&
+                    Color.BLUE == bottomRight
+            }
+        } finally {
+            renderer?.blockingRelease()
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+    @Test
+    fun testCancelFrontBufferLayerRender() {
+        val squareSize = 100f
+        val renderLatch = CountDownLatch(1)
+        val callbacks = object : CanvasFrontBufferedRenderer.Callback<Int> {
+
+            override fun onDrawFrontBufferedLayer(
+                canvas: Canvas,
+                bufferWidth: Int,
+                bufferHeight: Int,
+                param: Int
+            ) {
+                canvas.drawColor(param)
+            }
+
+            override fun onDrawMultiBufferedLayer(
+                canvas: Canvas,
+                bufferWidth: Int,
+                bufferHeight: Int,
+                params: Collection<Int>
+            ) {
+                for (p in params) {
+                    canvas.drawColor(p)
+                }
+            }
+
+            override fun onMultiBufferedLayerRenderComplete(
+                frontBufferedLayerSurfaceControl: SurfaceControlCompat,
+                transaction: SurfaceControlCompat.Transaction
+            ) {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+                    transaction.addTransactionCommittedListener(
+                        Executors.newSingleThreadExecutor(),
+                        object : SurfaceControlCompat.TransactionCommittedListener {
+                            override fun onTransactionCommitted() {
+                                renderLatch.countDown()
+                            }
+                        })
+                } else {
+                    renderLatch.countDown()
+                }
+            }
+        }
+        var renderer: CanvasFrontBufferedRenderer<Int>? = null
+        var surfaceView: SurfaceView? = null
+        try {
+            val scenario = ActivityScenario.launch(FrontBufferedRendererTestActivity::class.java)
+                .moveToState(Lifecycle.State.CREATED)
+                .onActivity {
+                    surfaceView = it.getSurfaceView()
+                    renderer = CanvasFrontBufferedRenderer(surfaceView!!, callbacks)
+                }
+
+            scenario.moveToState(Lifecycle.State.RESUMED).onActivity {
+                with(renderer!!) {
+                    renderFrontBufferedLayer(Color.BLUE)
+                    commit()
+                    renderFrontBufferedLayer(Color.RED)
+                    cancel()
+                }
+            }
+            Assert.assertTrue(renderLatch.await(3000, TimeUnit.MILLISECONDS))
+
+            val coords = IntArray(2)
+            with(surfaceView!!) {
+                getLocationOnScreen(coords)
+            }
+
+            SurfaceControlUtils.validateOutput { bitmap ->
+                val pixel = bitmap.getPixel(
+                    coords[0] + (squareSize / 2).toInt(),
+                    coords[1] + (squareSize / 2).toInt()
+                )
+                // After cancel is invoked the front buffered layer should not be visible
+                Color.BLUE == pixel
+            }
+        } finally {
+            renderer?.blockingRelease()
+        }
+    }
+
+    @Ignore("b/266749527")
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+    @Test
+    fun testMultiBufferedContentsNotPersisted() {
+        val screenWidth = FrontBufferedRendererTestActivity.WIDTH
+        val renderLatch = CountDownLatch(1)
+        val firstDrawLatch = CountDownLatch(1)
+        val callbacks = object : CanvasFrontBufferedRenderer.Callback<Any> {
+            override fun onDrawFrontBufferedLayer(
+                canvas: Canvas,
+                bufferWidth: Int,
+                bufferHeight: Int,
+                param: Any
+            ) {
+                canvas.drawColor(Color.RED)
+                firstDrawLatch.countDown()
+            }
+
+            override fun onDrawMultiBufferedLayer(
+                canvas: Canvas,
+                bufferWidth: Int,
+                bufferHeight: Int,
+                params: Collection<Any>
+            ) {
+                for (param in params) {
+                    canvas.drawColor(Color.RED)
+                }
+            }
+
+            override fun onMultiBufferedLayerRenderComplete(
+                frontBufferedLayerSurfaceControl: SurfaceControlCompat,
+                transaction: SurfaceControlCompat.Transaction
+            ) {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+                    transaction.addTransactionCommittedListener(
+                        Executors.newSingleThreadExecutor(),
+                        object : SurfaceControlCompat.TransactionCommittedListener {
+                            override fun onTransactionCommitted() {
+                                renderLatch.countDown()
+                            }
+                        })
+                } else {
+                    renderLatch.countDown()
+                }
+            }
+        }
+        var renderer: CanvasFrontBufferedRenderer<Any>? = null
+        var surfaceView: SurfaceView? = null
+        try {
+            val scenario = ActivityScenario.launch(FrontBufferedRendererTestActivity::class.java)
+                .moveToState(Lifecycle.State.CREATED)
+                .onActivity {
+                    surfaceView = it.getSurfaceView()
+                    renderer = CanvasFrontBufferedRenderer(surfaceView!!, callbacks)
+                }
+            scenario.moveToState(Lifecycle.State.RESUMED).onActivity {
+                renderer?.renderFrontBufferedLayer(0f)
+                renderer?.commit()
+                renderer?.renderFrontBufferedLayer(screenWidth / 2f)
+                renderer?.commit()
+            }
+
+            Assert.assertTrue(renderLatch.await(3000, TimeUnit.MILLISECONDS))
+
+            val coords = IntArray(2)
+            val width: Int
+            val height: Int
+            with(surfaceView!!) {
+                getLocationOnScreen(coords)
+                width = this.width
+                height = this.height
+            }
+
+            SurfaceControlUtils.validateOutput { bitmap ->
+                (bitmap.getPixel(
+                    coords[0] + width / 4, coords[1] + height / 2
+                ) == Color.BLACK) &&
+                    (bitmap.getPixel(
+                        coords[0] + 3 * width / 4 - 1,
+                        coords[1] + height / 2
+                    ) == Color.RED)
+            }
+        } finally {
+            renderer.blockingRelease()
+        }
+    }
+
+    @RequiresApi(Build.VERSION_CODES.Q)
+    private fun CanvasFrontBufferedRenderer<*>?.blockingRelease(timeoutMillis: Long = 3000) {
+        if (this != null) {
+            val destroyLatch = CountDownLatch(1)
+            release(false) {
+                destroyLatch.countDown()
+            }
+            Assert.assertTrue(destroyLatch.await(timeoutMillis, TimeUnit.MILLISECONDS))
+        } else {
+            Assert.fail("CanvasFrontBufferedRenderer is not initialized")
+        }
+    }
+}
\ No newline at end of file
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/GLFrontBufferedRendererTest.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/GLFrontBufferedRendererTest.kt
index b53ff80..15fbf2e 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
@@ -85,7 +85,7 @@
                 Rectangle().draw(mProjectionMatrix, Color.RED, 0f, 0f, 100f, 100f)
             }
 
-            override fun onDrawDoubleBufferedLayer(
+            override fun onDrawMultiBufferedLayer(
                 eglManager: EGLManager,
                 bufferInfo: BufferInfo,
                 transform: FloatArray,
@@ -187,7 +187,7 @@
                 Rectangle().draw(mProjectionMatrix, Color.RED, 0f, 0f, 100f, 100f)
             }
 
-            override fun onDrawDoubleBufferedLayer(
+            override fun onDrawMultiBufferedLayer(
                 eglManager: EGLManager,
                 bufferInfo: BufferInfo,
                 transform: FloatArray,
@@ -208,7 +208,7 @@
                 Rectangle().draw(mProjectionMatrix, Color.BLUE, 0f, 0f, 100f, 100f)
             }
 
-            override fun onDoubleBufferedLayerRenderComplete(
+            override fun onMultiBufferedLayerRenderComplete(
                 frontBufferedLayerSurfaceControl: SurfaceControlCompat,
                 transaction: SurfaceControlCompat.Transaction
             ) {
@@ -300,7 +300,7 @@
                 // NO-OP we do not render to the front buffered layer in this test case
             }
 
-            override fun onDrawDoubleBufferedLayer(
+            override fun onDrawMultiBufferedLayer(
                 eglManager: EGLManager,
                 bufferInfo: BufferInfo,
                 transform: FloatArray,
@@ -331,7 +331,7 @@
                 }
             }
 
-            override fun onDoubleBufferedLayerRenderComplete(
+            override fun onMultiBufferedLayerRenderComplete(
                 frontBufferedLayerSurfaceControl: SurfaceControlCompat,
                 transaction: SurfaceControlCompat.Transaction
             ) {
@@ -360,7 +360,7 @@
 
             scenario.moveToState(Lifecycle.State.RESUMED).onActivity {
                 val colors = listOf(Color.RED, Color.BLACK, Color.YELLOW, Color.BLUE)
-                renderer?.renderDoubleBufferedLayer(colors)
+                renderer?.renderMultiBufferedLayer(colors)
             }
             assertTrue(renderLatch.await(3000, TimeUnit.MILLISECONDS))
 
@@ -450,7 +450,7 @@
                 }
             }
 
-            override fun onDrawDoubleBufferedLayer(
+            override fun onDrawMultiBufferedLayer(
                 eglManager: EGLManager,
                 bufferInfo: BufferInfo,
                 transform: FloatArray,
@@ -510,7 +510,7 @@
                 // NO-OP
             }
 
-            override fun onDrawDoubleBufferedLayer(
+            override fun onDrawMultiBufferedLayer(
                 eglManager: EGLManager,
                 bufferInfo: BufferInfo,
                 transform: FloatArray,
@@ -539,7 +539,7 @@
                 }
             }
 
-            override fun onDoubleBufferedLayerRenderComplete(
+            override fun onMultiBufferedLayerRenderComplete(
                 frontBufferedLayerSurfaceControl: SurfaceControlCompat,
                 transaction: SurfaceControlCompat.Transaction
             ) {
@@ -621,7 +621,7 @@
                 Rectangle().draw(mProjectionMatrix, param, 0f, 0f, squareSize, squareSize)
             }
 
-            override fun onDrawDoubleBufferedLayer(
+            override fun onDrawMultiBufferedLayer(
                 eglManager: EGLManager,
                 bufferInfo: BufferInfo,
                 transform: FloatArray,
@@ -645,7 +645,7 @@
                 }
             }
 
-            override fun onDoubleBufferedLayerRenderComplete(
+            override fun onMultiBufferedLayerRenderComplete(
                 frontBufferedLayerSurfaceControl: SurfaceControlCompat,
                 transaction: SurfaceControlCompat.Transaction
             ) {
@@ -715,7 +715,7 @@
                 // NO-OP
             }
 
-            override fun onDrawDoubleBufferedLayer(
+            override fun onDrawMultiBufferedLayer(
                 eglManager: EGLManager,
                 bufferInfo: BufferInfo,
                 transform: FloatArray,
@@ -824,7 +824,7 @@
                 blue = tmp
             }
 
-            override fun onDrawDoubleBufferedLayer(
+            override fun onDrawMultiBufferedLayer(
                 eglManager: EGLManager,
                 bufferInfo: BufferInfo,
                 transform: FloatArray,
@@ -903,7 +903,7 @@
                 firstDrawLatch.countDown()
             }
 
-            override fun onDrawDoubleBufferedLayer(
+            override fun onDrawMultiBufferedLayer(
                 eglManager: EGLManager,
                 bufferInfo: BufferInfo,
                 transform: FloatArray,
@@ -932,7 +932,7 @@
                 }
             }
 
-            override fun onDoubleBufferedLayerRenderComplete(
+            override fun onMultiBufferedLayerRenderComplete(
                 frontBufferedLayerSurfaceControl: SurfaceControlCompat,
                 transaction: SurfaceControlCompat.Transaction
             ) {
@@ -1022,7 +1022,7 @@
                 Rectangle().draw(mProjectionMatrix, Color.RED, 0f, 0f, 100f, 100f)
             }
 
-            override fun onDrawDoubleBufferedLayer(
+            override fun onDrawMultiBufferedLayer(
                 eglManager: EGLManager,
                 bufferInfo: BufferInfo,
                 transform: FloatArray,
@@ -1151,7 +1151,7 @@
                 getSquare().draw(mProjectionMatrix, Color.RED, 0f, 0f, 100f, 100f)
             }
 
-            override fun onDrawDoubleBufferedLayer(
+            override fun onDrawMultiBufferedLayer(
                 eglManager: EGLManager,
                 bufferInfo: BufferInfo,
                 transform: FloatArray,
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/InkCanvasView.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/InkCanvasView.kt
new file mode 100644
index 0000000..d54945e
--- /dev/null
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/InkCanvasView.kt
@@ -0,0 +1,113 @@
+/*
+ * 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.graphics.lowlatency
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.os.Build
+import android.view.MotionEvent
+import android.view.SurfaceView
+import androidx.annotation.RequiresApi
+
+@RequiresApi(Build.VERSION_CODES.Q)
+class InkCanvasView(context: Context) : SurfaceView(context) {
+
+    private var mCanvasFrontBufferedRenderer: CanvasFrontBufferedRenderer<FloatArray>? = null
+    private val mLinesDrawable = LinesDrawable()
+    private val mCallbacks = object : CanvasFrontBufferedRenderer.Callback<FloatArray> {
+
+        override fun onDrawFrontBufferedLayer(
+            canvas: Canvas,
+            bufferWidth: Int,
+            bufferHeight: Int,
+            param: FloatArray
+        ) {
+            with(mLinesDrawable) {
+                setBounds(0, 0, bufferWidth, bufferHeight)
+                setLines(param)
+                setColor(Color.CYAN)
+                draw(canvas)
+            }
+        }
+
+        override fun onDrawMultiBufferedLayer(
+            canvas: Canvas,
+            bufferWidth: Int,
+            bufferHeight: Int,
+            params: Collection<FloatArray>
+        ) {
+            with(mLinesDrawable) {
+                setBounds(0, 0, bufferWidth, bufferHeight)
+                setColor(Color.MAGENTA)
+                for (param in params) {
+                    setLines(param)
+                    draw(canvas)
+                }
+            }
+        }
+    }
+
+    private var mPreviousX: Float = 0f
+    private var mPreviousY: Float = 0f
+    private var mCurrentX: Float = 0f
+    private var mCurrentY: Float = 0f
+
+    init {
+        setOnTouchListener { _, event ->
+            when (event.action) {
+                MotionEvent.ACTION_DOWN -> {
+                    requestUnbufferedDispatch(event)
+                    mCurrentX = event.x
+                    mCurrentY = event.y
+                }
+                MotionEvent.ACTION_MOVE -> {
+                    mPreviousX = mCurrentX
+                    mPreviousY = mCurrentY
+                    mCurrentX = event.x
+                    mCurrentY = event.y
+
+                    val line = FloatArray(4).apply {
+                        this[0] = mPreviousX
+                        this[1] = mPreviousY
+                        this[2] = mCurrentX
+                        this[3] = mCurrentY
+                    }
+                    mCanvasFrontBufferedRenderer?.renderFrontBufferedLayer(line)
+                }
+                MotionEvent.ACTION_CANCEL -> {
+                    mCanvasFrontBufferedRenderer?.cancel()
+                }
+                MotionEvent.ACTION_UP -> {
+                    mCanvasFrontBufferedRenderer?.commit()
+                }
+            }
+            true
+        }
+    }
+
+    override fun onAttachedToWindow() {
+        super.onAttachedToWindow()
+        mCanvasFrontBufferedRenderer = CanvasFrontBufferedRenderer(this, mCallbacks)
+    }
+
+    override fun onDetachedFromWindow() {
+        super.onDetachedFromWindow()
+        mCanvasFrontBufferedRenderer?.release(true)
+        mCanvasFrontBufferedRenderer = null
+    }
+}
\ No newline at end of file
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/InkSurfaceView.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/InkSurfaceView.kt
index 82dfd0a..650c29c 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/InkSurfaceView.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/InkSurfaceView.kt
@@ -90,7 +90,7 @@
             }
         }
 
-        override fun onDrawDoubleBufferedLayer(
+        override fun onDrawMultiBufferedLayer(
             eglManager: EGLManager,
             bufferInfo: BufferInfo,
             transform: FloatArray,
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/LinesDrawable.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/LinesDrawable.kt
new file mode 100644
index 0000000..8111e7a
--- /dev/null
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/LinesDrawable.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.graphics.lowlatency
+
+import android.graphics.Canvas
+import android.graphics.ColorFilter
+import android.graphics.Paint
+import android.graphics.PixelFormat
+import android.graphics.drawable.Drawable
+
+class LinesDrawable : Drawable() {
+
+    private var mLines: FloatArray? = null
+    private val mPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
+        strokeWidth = 5f
+    }
+
+    var strokeWidth: Float
+        get() = mPaint.strokeWidth
+        set(value) {
+            mPaint.strokeWidth = value
+        }
+
+    override fun draw(canvas: Canvas) {
+        mLines?.let { lines ->
+            for (i in lines.indices step 4) {
+                canvas.drawLine(
+                    lines[i],
+                    lines[i + 1],
+                    lines[i + 2],
+                    lines[i + 3],
+                    mPaint
+                )
+            }
+        }
+    }
+
+    fun setLines(lines: FloatArray) {
+        mLines = lines
+        invalidateSelf()
+    }
+
+    fun setColor(color: Int) {
+        mPaint.color = color
+        invalidateSelf()
+    }
+
+    override fun setAlpha(alpha: Int) {
+        mPaint.alpha = alpha
+        invalidateSelf()
+    }
+
+    override fun setColorFilter(colorFilter: ColorFilter?) {
+        mPaint.colorFilter = colorFilter
+        invalidateSelf()
+    }
+
+    override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
+}
\ No newline at end of file
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/SampleInkViewActivity.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/SampleInkViewActivity.kt
index b2c55e7..56ffdec 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/SampleInkViewActivity.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/SampleInkViewActivity.kt
@@ -19,13 +19,54 @@
 import android.app.Activity
 import android.os.Build
 import android.os.Bundle
+import android.view.Gravity
+import android.view.View
+import android.widget.FrameLayout.LayoutParams.WRAP_CONTENT
+import android.widget.Button
+import android.widget.FrameLayout
+import android.widget.LinearLayout.LayoutParams.MATCH_PARENT
+import androidx.annotation.RequiresApi
 
+@RequiresApi(Build.VERSION_CODES.S)
 class SampleInkViewActivity : Activity() {
 
+    private var inkView: View? = null
+    private var toggle: Button? = null
+    private var container: FrameLayout? = null
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
+        addInkViews()
+    }
+
+    private fun addInkViews() {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
-            setContentView(InkSurfaceView(this))
+            toggle = Button(this).apply {
+                layoutParams = FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
+                    gravity = Gravity.BOTTOM or Gravity.RIGHT
+                }
+                setOnClickListener {
+                    toggleLowLatencyView()
+                }
+            }
+            container = FrameLayout(this).apply {
+                addView(toggle)
+            }
+            toggleLowLatencyView()
+            setContentView(container)
         }
     }
+
+    private fun toggleLowLatencyView() {
+        inkView?.let { view -> container?.removeView(view) }
+        if (inkView == null || inkView is InkSurfaceView) {
+            inkView = InkCanvasView(this)
+            toggle?.text = "Canvas"
+        } else if (inkView is InkCanvasView) {
+            inkView = InkSurfaceView(this)
+            toggle?.text = "OpenGL"
+        }
+        container?.addView(inkView, FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT))
+        container?.bringChildToFront(toggle)
+    }
 }
\ No newline at end of file
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/SingleBufferedCanvasRendererV29Test.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/SingleBufferedCanvasRendererV29Test.kt
index c0d0a6a..5ecc43e 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/SingleBufferedCanvasRendererV29Test.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/SingleBufferedCanvasRendererV29Test.kt
@@ -36,9 +36,12 @@
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.Executors
 import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicInteger
+import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertNotNull
 import org.junit.Assert.assertTrue
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -231,10 +234,6 @@
             renderer.render(Color.RED)
             assertTrue(initialDrawLatch.await(3000, TimeUnit.MILLISECONDS))
 
-            executor.execute {
-                waitForRequestLatch.await()
-            }
-
             drawCancelledRequestLatch = CountDownLatch(2)
             renderer.render(Color.GREEN)
             renderer.render(Color.YELLOW)
@@ -354,6 +353,47 @@
         }
     }
 
+    @Ignore("b/274099885")
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+    @Test
+    fun testBatchedRenders() {
+        val transformer = BufferTransformer()
+        transformer.computeTransform(TEST_WIDTH, TEST_HEIGHT, BUFFER_TRANSFORM_IDENTITY)
+        val executor = Executors.newSingleThreadExecutor()
+        val renderCount = AtomicInteger(0)
+        val renderer = SingleBufferedCanvasRendererV29(
+            TEST_WIDTH,
+            TEST_HEIGHT,
+            transformer,
+            executor,
+            object : SingleBufferedCanvasRenderer.RenderCallbacks<Int> {
+                override fun render(canvas: Canvas, width: Int, height: Int, param: Int) {
+                    canvas.drawColor(param)
+                    renderCount.incrementAndGet()
+                }
+
+                override fun onBufferReady(
+                    hardwareBuffer: HardwareBuffer,
+                    syncFenceCompat: SyncFenceCompat?
+                ) {
+                    // NO-OP
+                }
+            })
+        try {
+            renderer.render(Color.RED)
+            renderer.render(Color.BLUE)
+            renderer.render(Color.YELLOW)
+        } finally {
+            val latch = CountDownLatch(1)
+            renderer.release(false) {
+                executor.shutdownNow()
+                latch.countDown()
+            }
+            assertTrue(latch.await(3000, TimeUnit.MILLISECONDS))
+            assertEquals(3, renderCount.get())
+        }
+    }
+
     @RequiresApi(Build.VERSION_CODES.Q)
     private fun testRenderWithTransform(
         transform: Int,
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/CanvasFrontBufferedRenderer.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/CanvasFrontBufferedRenderer.kt
new file mode 100644
index 0000000..8f01f05
--- /dev/null
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/CanvasFrontBufferedRenderer.kt
@@ -0,0 +1,553 @@
+/*
+ * 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.graphics.lowlatency
+
+import android.annotation.SuppressLint
+import android.graphics.Canvas
+import android.graphics.RenderNode
+import android.hardware.HardwareBuffer
+import android.os.Build
+import android.util.Log
+import android.view.SurfaceHolder
+import android.view.SurfaceView
+import androidx.annotation.RequiresApi
+import androidx.annotation.WorkerThread
+import androidx.graphics.MultiBufferedCanvasRenderer
+import androidx.graphics.surface.SurfaceControlCompat
+import androidx.hardware.SyncFenceCompat
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executors
+
+/**
+ * Class responsible for supporting a "front buffered" rendering system. This allows for lower
+ * latency graphics by leveraging a combination of front buffered and multi buffered content layers.
+ * Active content is rendered first into the front buffered layer which is simultaneously being
+ * presented to the display. Periodically content is rendered into the multi buffered layer which
+ * will have more traditional latency guarantees, however, minimizes the impact of visual artifacts
+ * due to graphical tearing.
+ *
+ * @param surfaceView Target SurfaceView to act as the parent rendering layer for multi buffered
+ *  content
+ * @param callback Callbacks used to render into front and multi buffered layers as well as
+ *  configuring [SurfaceControlCompat.Transaction]s for controlling these layers in addition to
+ *  other [SurfaceControlCompat] instances that must be updated atomically within the user
+ *  interface. These callbacks are invoked on an internal rendering thread. The templated type
+ *  here is consumer defined to represent the data structures to be consumed for rendering within
+ *  [Callback.onDrawFrontBufferedLayer] and [Callback.onDrawMultiBufferedLayer] and are provided
+ *  by the [CanvasFrontBufferedRenderer.renderFrontBufferedLayer] and
+ *  [CanvasFrontBufferedRenderer.renderMultiBufferedLayer] methods.
+ */
+@RequiresApi(Build.VERSION_CODES.Q)
+class CanvasFrontBufferedRenderer<T>(
+    private val surfaceView: SurfaceView,
+    private val callback: Callback<T>,
+) {
+
+    /**
+     * Executor used to deliver callbacks for rendering as well as issuing surface control
+     * transactions
+     */
+    private val mExecutor = Executors.newSingleThreadExecutor()
+
+    /**
+     * RenderNode used to draw the entire multi buffered scene
+     */
+    private var mMultiBufferNode: RenderNode? = null
+
+    /**
+     * Renderer used to draw [mMultiBufferNode] into a [HardwareBuffer] that is used to configure
+     * the parent SurfaceControl that represents the multi-buffered scene
+     */
+    private var mMultiBufferedCanvasRenderer: MultiBufferedCanvasRenderer? = null
+
+    /**
+     * Renderer used to draw the front buffer content into a HardwareBuffer instance that is
+     * preserved across frames
+     */
+    private var mPersistedCanvasRenderer: SingleBufferedCanvasRenderer<T>? = null
+
+    /**
+     * [SurfaceControlCompat] used to configure buffers and visibility of the front buffered layer
+     */
+    private var mFrontBufferSurfaceControl: SurfaceControlCompat? = null
+
+    /**
+     * [SurfaceControlCompat] used to configure buffers and visibility of the multi-buffered layer
+     */
+    private var mParentSurfaceControl: SurfaceControlCompat? = null
+
+    /**
+     * Queue of parameters to be consumed in [Callback.onDrawFrontBufferedLayer] with the parameter
+     * provided in [renderFrontBufferedLayer]. When [commit] is invoked the collection is used
+     * to render the multi-buffered scene and is subsequently cleared
+     */
+    private var mParams = ArrayList<T>()
+
+    /**
+     * Flag to determine if the [CanvasFrontBufferedRenderer] has previously been released. If this
+     * flag is true, then subsequent requests to [renderFrontBufferedLayer],
+     * [renderMultiBufferedLayer], [commit], and [release] are ignored.
+     */
+    private var mIsReleased = false
+
+    /**
+     * Runnable executed on the GLThread to update [FrontBufferSyncStrategy.isVisible] as well
+     * as hide the SurfaceControl associated with the front buffered layer
+     */
+    private val mCancelRunnable = Runnable {
+        mPersistedCanvasRenderer?.isVisible = false
+        mFrontBufferSurfaceControl?.let { frontBufferSurfaceControl ->
+            SurfaceControlCompat.Transaction()
+                .setVisibility(frontBufferSurfaceControl, false)
+                .commit()
+        }
+    }
+
+    init {
+        surfaceView.holder.addCallback(object : SurfaceHolder.Callback2 {
+
+            private var mWidth = -1
+            private var mHeight = -1
+
+            private var transformHint = BufferTransformHintResolver.UNKNOWN_TRANSFORM
+            private var inverse = BufferTransformHintResolver.UNKNOWN_TRANSFORM
+            private val mTransformResolver = BufferTransformHintResolver()
+            private val mBufferTransform = BufferTransformer()
+
+            override fun surfaceCreated(p0: SurfaceHolder) {
+                // NO-OP
+            }
+
+            override fun surfaceChanged(
+                holder: SurfaceHolder,
+                format: Int,
+                width: Int,
+                height: Int
+            ) {
+                mWidth = width
+                mHeight = height
+                releaseInternal(true)
+                transformHint = mTransformResolver.getBufferTransformHint(surfaceView)
+                inverse = mBufferTransform.invertBufferTransform(transformHint)
+                mBufferTransform.computeTransform(width, height, inverse)
+
+                mPersistedCanvasRenderer = SingleBufferedCanvasRenderer.create<T>(
+                    width,
+                    height,
+                    mBufferTransform,
+                    mExecutor,
+                    object : SingleBufferedCanvasRenderer.RenderCallbacks<T> {
+
+                        override fun render(canvas: Canvas, width: Int, height: Int, param: T) {
+                            callback.onDrawFrontBufferedLayer(canvas, width, height, param)
+                        }
+
+                        @SuppressLint("WrongConstant")
+                        override fun onBufferReady(
+                            hardwareBuffer: HardwareBuffer,
+                            syncFenceCompat: SyncFenceCompat?
+                        ) {
+                            mPersistedCanvasRenderer?.isVisible = true
+                            mFrontBufferSurfaceControl?.let { frontBufferSurfaceControl ->
+                                val transaction = SurfaceControlCompat.Transaction()
+                                    .setLayer(frontBufferSurfaceControl, Integer.MAX_VALUE)
+                                    .setBuffer(
+                                        frontBufferSurfaceControl,
+                                        hardwareBuffer,
+                                        syncFenceCompat
+                                    )
+                                    .setVisibility(frontBufferSurfaceControl, true)
+                                    .reparent(frontBufferSurfaceControl, mParentSurfaceControl)
+                                if (inverse != BufferTransformHintResolver.UNKNOWN_TRANSFORM) {
+                                    transaction.setBufferTransform(
+                                        frontBufferSurfaceControl,
+                                        inverse
+                                    )
+                                }
+                                callback.onFrontBufferedLayerRenderComplete(
+                                    frontBufferSurfaceControl, transaction)
+                                transaction.commit()
+                                syncFenceCompat?.close()
+                            }
+                        }
+                    })
+
+                val parentSurfaceControl = SurfaceControlCompat.Builder()
+                    .setParent(surfaceView)
+                    .setName("MultiBufferedLayer")
+                    .build()
+                    .apply {
+                        // SurfaceControl is not visible by default so make it visible right
+                        // after creation
+                        SurfaceControlCompat.Transaction()
+                            .setVisibility(this, true)
+                            .commit()
+                    }
+
+                val multiBufferNode = RenderNode("MultiBufferNode").apply {
+                    setPosition(0, 0, width, height)
+                    mMultiBufferNode = this
+                }
+                mMultiBufferedCanvasRenderer = MultiBufferedCanvasRenderer(
+                    multiBufferNode,
+                    width,
+                    height
+                )
+
+                mFrontBufferSurfaceControl = SurfaceControlCompat.Builder()
+                    .setParent(parentSurfaceControl)
+                    .setName("FrontBufferedLayer")
+                    .build()
+
+                mParentSurfaceControl = parentSurfaceControl
+            }
+
+            override fun surfaceDestroyed(p0: SurfaceHolder) {
+                releaseInternal(true)
+            }
+
+            override fun surfaceRedrawNeeded(holder: SurfaceHolder) {
+                val latch = CountDownLatch(1)
+                surfaceRedrawNeededAsync(holder) {
+                    latch.countDown()
+                }
+                latch.await()
+            }
+
+            override fun surfaceRedrawNeededAsync(
+                holder: SurfaceHolder,
+                drawingFinished: Runnable
+            ) {
+                val renderer = mMultiBufferedCanvasRenderer
+                if (renderer != null) {
+                    renderer.renderFrame(mExecutor) { buffer ->
+                        setParentSurfaceControlBuffer(buffer, drawingFinished)
+                    }
+                } else {
+                    drawingFinished.run()
+                }
+            }
+        })
+    }
+
+    private inline fun RenderNode.record(block: (canvas: Canvas) -> Unit): RenderNode {
+        val canvas = beginRecording()
+        block(canvas)
+        endRecording()
+        return this
+    }
+
+    /**
+     * Render content to the front buffered layer providing optional parameters to be consumed in
+     * [Callback.onDrawFrontBufferedLayer].
+     * Additionally the parameter provided here will also be consumed in
+     * [Callback.onDrawMultiBufferedLayer]
+     * when the corresponding [commit] method is invoked, which will include all [param]s in each
+     * call made to this method up to the corresponding [commit] call.
+     *
+     * If this [CanvasFrontBufferedRenderer] has been released, that is [isValid] returns `false`,
+     * this call is ignored.
+     *
+     * @param param Optional parameter to be consumed when rendering content into the commit layer
+     */
+    fun renderFrontBufferedLayer(param: T) {
+        if (isValid()) {
+            mParams.add(param)
+            mPersistedCanvasRenderer?.render(param)
+        } else {
+            Log.w(TAG, "Attempt to render to front buffered layer when " +
+                    "CanvasFrontBufferedRenderer has been released"
+            )
+        }
+    }
+
+    /**
+     * Requests to render to the multi buffered layer. This schedules a call to
+     * [Callback.onDrawMultiBufferedLayer] with the parameters provided. If the front buffered
+     * layer is visible, this will hide this layer after rendering to the multi buffered layer
+     * is complete. This is equivalent to calling [CanvasFrontBufferedRenderer.renderFrontBufferedLayer]
+     * for each parameter provided in the collection followed by a single call to
+     * [CanvasFrontBufferedRenderer.commit]. This is useful for re-rendering the multi buffered
+     * scene when the corresponding Activity is being resumed from the background in which the
+     * contents should be re-drawn. Additionally this allows for applications to decide to
+     * dynamically render to either front or multi buffered layers.
+     *
+     * If this [CanvasFrontBufferedRenderer] has been released, that is [isValid] returns 'false',
+     * this call is ignored.
+     *
+     * @param params Parameters that to be consumed when rendering to the multi buffered layer.
+     * These parameters will be provided in the corresponding call to
+     * [Callback.onDrawMultiBufferedLayer]
+     */
+    fun renderMultiBufferedLayer(params: Collection<T>) {
+        if (isValid()) {
+            mParams.addAll(params)
+            commit()
+        } else {
+            Log.w(TAG, "Attempt to render to the multi buffered layer when " +
+                    "CanvasFrontBufferedRenderer has been released"
+            )
+        }
+    }
+
+    /**
+     * Determines whether or not the [CanvasFrontBufferedRenderer] is in a valid state. That is the
+     * [release] method has not been called.
+     * If this returns false, then subsequent calls to [renderFrontBufferedLayer],
+     * [renderMultiBufferedLayer], [commit], and [release] are ignored
+     *
+     * @return `true` if this [CanvasFrontBufferedRenderer] has been released, `false` otherwise
+     */
+    fun isValid() = !mIsReleased
+
+    internal fun setParentSurfaceControlBuffer(
+        buffer: HardwareBuffer,
+        block: Runnable? = null
+    ) {
+        val frontBufferSurfaceControl = mFrontBufferSurfaceControl
+        val parentSurfaceControl = mParentSurfaceControl
+        if (frontBufferSurfaceControl != null && parentSurfaceControl != null) {
+            mPersistedCanvasRenderer?.isVisible = false
+            val transaction = SurfaceControlCompat.Transaction()
+                .setVisibility(frontBufferSurfaceControl, false)
+                .setVisibility(parentSurfaceControl, true)
+                .setBuffer(parentSurfaceControl, buffer) {
+                    buffer.close()
+                }
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+                val listener = if (block != null) {
+                    object : SurfaceControlCompat.TransactionCommittedListener {
+                        override fun onTransactionCommitted() {
+                            mCommitListener.onTransactionCommitted()
+                            block.run()
+                        }
+                    }
+                } else {
+                    mCommitListener
+                }
+                transaction.addTransactionCommittedListener(mExecutor, listener)
+            } else {
+                if (block != null) {
+                    mExecutor.execute {
+                        mCommitRunnable.run()
+                        block.run()
+                    }
+                } else {
+                    mExecutor.execute(mCommitRunnable)
+                }
+            }
+            callback.onMultiBufferedLayerRenderComplete(
+                frontBufferSurfaceControl, transaction)
+            transaction.commit()
+        }
+    }
+
+    /**
+     * Requests to render the entire scene to the multi buffered layer and schedules a call to
+     * [Callback.onDrawMultiBufferedLayer]. The parameters provided to
+     * [Callback.onDrawMultiBufferedLayer] will include each argument provided to every
+     * [renderFrontBufferedLayer] call since the last call to [commit] has been made. When rendering
+     * to the multi-buffered layer is complete, this synchronously hides the front buffer and
+     * updates the multi buffered layer.
+     *
+     * If this [CanvasFrontBufferedRenderer] has been released, that is [isValid] returns `false`,
+     * this call is ignored.
+     */
+    fun commit() {
+        if (isValid()) {
+            mPersistedCanvasRenderer?.cancelPending()
+            val params = mParams
+            mParams = ArrayList<T>()
+            val width = surfaceView.width
+            val height = surfaceView.height
+            mExecutor.execute {
+                mMultiBufferNode?.record { canvas ->
+                    callback.onDrawMultiBufferedLayer(canvas, width, height, params)
+                }
+                params.clear()
+                mMultiBufferedCanvasRenderer?.renderFrame(mExecutor) { buffer ->
+                    setParentSurfaceControlBuffer(buffer)
+                }
+            }
+        } else {
+            Log.w(TAG, "Attempt to render to the multi buffered layer when " +
+                    "CanvasFrontBufferedRenderer has been released"
+            )
+        }
+    }
+
+    /**
+     * Requests to cancel rendering and hides the front buffered layer.
+     * Unlike [commit], this does not schedule a call to render into the multi buffered layer. This
+     * is useful in palm rejection use cases, where some initial touch events might be processed
+     * before a corresponding cancel event is received indicating the touch gesture is coming
+     * from a palm rather than intentional user input. In the case where MotionEvent#getAction
+     * returns ACTION_CANCEL, this is to be invoked.
+     *
+     * If this [GLFrontBufferedRenderer] has been released, that is [isValid] returns `false`,
+     * this call is ignored.
+     */
+    fun cancel() {
+        if (isValid()) {
+            mPersistedCanvasRenderer?.cancelPending()
+            mExecutor.execute(mCancelRunnable)
+            mPersistedCanvasRenderer?.clear()
+        } else {
+            Log.w(TAG, "Attempt to cancel rendering to front buffer after " +
+                "CanvasFrontBufferRenderer has been released")
+        }
+    }
+
+    private val mCommitListener = object : SurfaceControlCompat.TransactionCommittedListener {
+        override fun onTransactionCommitted() {
+            mPersistedCanvasRenderer?.clear()
+        }
+    }
+
+    private val mCommitRunnable = Runnable {
+        mPersistedCanvasRenderer?.clear()
+    }
+
+    internal fun releaseInternal(cancelPending: Boolean, releaseCallback: (() -> Unit)? = null) {
+        mPersistedCanvasRenderer?.release(cancelPending) {
+            mMultiBufferNode?.discardDisplayList()
+            mFrontBufferSurfaceControl?.release()
+            mParentSurfaceControl?.release()
+            mMultiBufferedCanvasRenderer?.release()
+
+            mMultiBufferNode = null
+            mFrontBufferSurfaceControl = null
+            mParentSurfaceControl = null
+            mPersistedCanvasRenderer = null
+            mMultiBufferedCanvasRenderer = null
+            releaseCallback?.invoke()
+        }
+    }
+
+    /**
+     * Releases the [CanvasFrontBufferedRenderer]. In process requests are ignored.
+     * If the [CanvasFrontBufferedRenderer] is already released, that is [isValid] returns `false`,
+     * this method does nothing.
+     */
+    @JvmOverloads
+    fun release(cancelPending: Boolean, onReleaseComplete: (() -> Unit)? = null) {
+        if (!mIsReleased) {
+            releaseInternal(cancelPending) {
+                onReleaseComplete?.invoke()
+                mExecutor.shutdown()
+            }
+            mIsReleased = true
+        }
+    }
+
+    /**
+     * Provides callbacks for consumers to draw into the front and multi buffered layers as well as
+     * provide opportunities to synchronize [SurfaceControlCompat.Transaction]s to submit the layers
+     * to the hardware compositor.
+     */
+    @JvmDefaultWithCompatibility
+    interface Callback<T> {
+
+        /**
+         * Callback invoked to render content into the front buffered layer with the specified
+         * parameters.
+         * @param canvas [Canvas] used to issue drawing instructions into the front buffered layer
+         * @param bufferWidth Width of the buffer that is being rendered into.
+         * @param bufferHeight Height of the buffer that is being rendered into.
+         * @param param optional parameter provided the corresponding
+         * [CanvasFrontBufferedRenderer.renderFrontBufferedLayer] method that triggered this request
+         * to render into the front buffered layer
+         */
+        @WorkerThread
+        fun onDrawFrontBufferedLayer(
+            canvas: Canvas,
+            bufferWidth: Int,
+            bufferHeight: Int,
+            param: T
+        )
+
+        /**
+         * Callback invoked to render content into the front buffered layer with the specified
+         * parameters.
+         * @param canvas [Canvas] used to issue drawing instructions into the front buffered layer
+         * @param bufferWidth Width of the buffer that is being rendered into.
+         * @param bufferHeight Height of the buffer that is being rendered into.
+         * @param params optional parameter provided to render the entire scene into the multi
+         * buffered layer.
+         * This is a collection of all parameters provided in consecutive invocations to
+         * [CanvasFrontBufferedRenderer.renderFrontBufferedLayer] since the last call to
+         * [CanvasFrontBufferedRenderer.commit] has been made. After
+         * [CanvasFrontBufferedRenderer.commit] is invoked, this collection is cleared and new
+         * parameters are added on each subsequent call to
+         * [CanvasFrontBufferedRenderer.renderFrontBufferedLayer]
+         */
+        @WorkerThread
+        fun onDrawMultiBufferedLayer(
+            canvas: Canvas,
+            bufferWidth: Int,
+            bufferHeight: Int,
+            params: Collection<T>
+        )
+
+        /**
+         * Optional callback invoked when rendering to the front buffered layer is complete but
+         * before the buffers are submitted to the hardware compositor.
+         * This provides consumers a mechanism for synchronizing the transaction with other
+         * [SurfaceControlCompat] objects that maybe rendered within the scene.
+         *
+         * @param frontBufferedLayerSurfaceControl Handle to the [SurfaceControlCompat] where the
+         * front buffered layer content is drawn. This can be used to configure various properties
+         * of the [SurfaceControlCompat] like z-ordering or visibility with the corresponding
+         * [SurfaceControlCompat.Transaction].
+         * @param transaction Current [SurfaceControlCompat.Transaction] to apply updated buffered
+         * content to the front buffered layer.
+         */
+        @WorkerThread
+        fun onFrontBufferedLayerRenderComplete(
+            frontBufferedLayerSurfaceControl: SurfaceControlCompat,
+            transaction: SurfaceControlCompat.Transaction
+        ) {
+            // Default implementation is a no-op
+        }
+
+        /**
+         * Optional callback invoked when rendering to the multi buffered layer is complete but
+         * before the buffers are submitted to the hardware compositor.
+         * This provides consumers a mechanism for synchronizing the transaction with other
+         * [SurfaceControlCompat] objects that maybe rendered within the scene.
+         *
+         * @param frontBufferedLayerSurfaceControl Handle to the [SurfaceControlCompat] where the
+         * front buffered layer content is drawn. This can be used to configure various properties
+         * of the [SurfaceControlCompat] like z-ordering or visibility with the corresponding
+         * [SurfaceControlCompat.Transaction].
+         * @param transaction Current [SurfaceControlCompat.Transaction] to apply updated buffered
+         * content to the multi buffered layer.
+         */
+        @WorkerThread
+        fun onMultiBufferedLayerRenderComplete(
+            frontBufferedLayerSurfaceControl: SurfaceControlCompat,
+            transaction: SurfaceControlCompat.Transaction
+        ) {
+            // Default implementation is a no-op
+        }
+    }
+
+    internal companion object {
+
+        internal const val TAG = "LowLatencyCanvas"
+    }
+}
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 db7462f..af54b29 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
@@ -40,16 +40,16 @@
 
 /**
  * Class responsible for supporting a "front buffered" rendering system. This allows for lower
- * latency graphics by leveraging a combination of front buffered and double buffered content
+ * latency graphics by leveraging a combination of front buffered and multi buffered content
  * layers.
  * Active content is rendered first into the front buffered layer which is simultaneously being
- * presented to the display. Periodically content is rendered into the double buffered layer which
+ * presented to the display. Periodically content is rendered into the multi buffered layer which
  * will have more traditional latency guarantees, however, minimize the impact of visual artifacts
  * due to graphical tearing.
  *
- * @param surfaceView Target SurfaceView to act as the parent rendering layer for double buffered
+ * @param surfaceView Target SurfaceView to act as the parent rendering layer for multi buffered
  *  content
- * @param callback Callbacks used to render into front and double buffered layers as well as
+ * @param callback Callbacks used to render into front and multi buffered layers as well as
  *  configuring [SurfaceControlCompat.Transaction]s for controlling these layers in addition to
  *  other [SurfaceControlCompat] instances that must be updated atomically within the user
  *  interface. These callbacks are invoked on the backing GL Thread.
@@ -69,19 +69,19 @@
     glRenderer: GLRenderer? = null,
 ) {
     /**
-     * [ParentRenderLayer] used to contain both the front and double buffered layers
+     * [ParentRenderLayer] used to contain both the front and multi buffered layers
      */
     private val mParentRenderLayer: ParentRenderLayer<T> = SurfaceViewRenderLayer(surfaceView)
 
     /**
-     * Callbacks invoked to render into the front and double buffered layers in addition to
+     * Callbacks invoked to render into the front and multi buffered layers in addition to
      * providing consumers an opportunity to specify any potential additional interactions that must
      * be synchronized with the [SurfaceControlCompat.Transaction] to show/hide visibility of the
-     * front buffered layer as well as updating double buffered layers
+     * front buffered layer as well as updating multi buffered layers
      */
     private val mCallback = object : Callback<T> by callback {
         @WorkerThread
-        override fun onDoubleBufferedLayerRenderComplete(
+        override fun onMultiBufferedLayerRenderComplete(
             frontBufferedLayerSurfaceControl: SurfaceControlCompat,
             transaction: SurfaceControlCompat.Transaction
         ) {
@@ -91,7 +91,7 @@
                 clearFrontBuffer()
             }
             mFrontBufferSyncStrategy.isVisible = false
-            callback.onDoubleBufferedLayerRenderComplete(
+            callback.onMultiBufferedLayerRenderComplete(
                 frontBufferedLayerSurfaceControl,
                 transaction
             )
@@ -160,7 +160,7 @@
             detachTargets(true)
         }
 
-        override fun obtainDoubleBufferedLayerParams(): MutableCollection<T>? =
+        override fun obtainMultiBufferedLayerParams(): MutableCollection<T>? =
             mSegments.poll()
 
         override fun getFrontBufferedLayerSurfaceControl(): SurfaceControlCompat? =
@@ -190,9 +190,9 @@
     private val mActiveSegment = ParamQueue<T>()
 
     /**
-     * Collection of parameters to be consumed in [Callback.onDoubleBufferedLayerRenderComplete]
+     * Collection of parameters to be consumed in [Callback.onMultiBufferedLayerRenderComplete]
      * with the parameters defined in consecutive calls to [renderFrontBufferedLayer].
-     * Once the corresponding [Callback.onDoubleBufferedLayerRenderComplete] callback is invoked,
+     * Once the corresponding [Callback.onMultiBufferedLayerRenderComplete] callback is invoked,
      * this collection is cleared and new parameters are added to it with consecutive calls to
      * [renderFrontBufferedLayer].
      */
@@ -243,7 +243,7 @@
     private var mHeight = -1
 
     /**
-     * [GLRenderer] used to issue requests to render into front/double buffered layers
+     * [GLRenderer] used to issue requests to render into front/multi buffered layers
      */
     private val mGLRenderer: GLRenderer
 
@@ -253,7 +253,7 @@
      * If the former, then the [GLFrontBufferedRenderer] is responsible for stopping/releasing this
      * [GLRenderer] in the [release] method. If this is being provided, then we should not be
      * releasing this [GLRenderer] as it maybe used by other consumers.
-     * In this case, only the front/double buffered [GLRenderer.RenderTarget]s are detached.
+     * In this case, only the front/multi buffered [GLRenderer.RenderTarget]s are detached.
      */
     private val mIsManagingGLRenderer: Boolean
 
@@ -263,9 +263,9 @@
     private var mFrontBufferedRenderTarget: GLRenderer.RenderTarget? = null
 
     /**
-     * [GLRenderer.RenderTarget] used to issue requests to render into the double buffered layer
+     * [GLRenderer.RenderTarget] used to issue requests to render into the multi buffered layer
      */
-    private var mDoubleBufferedLayerRenderTarget: GLRenderer.RenderTarget? = null
+    private var mMultiBufferedLayerRenderTarget: GLRenderer.RenderTarget? = null
 
     /**
      * Flag to determine if the [GLFrontBufferedRenderer] has previously been released. If this flag
@@ -302,8 +302,8 @@
     internal fun update(width: Int, height: Int) {
         if (mWidth != width || mHeight != height && isValid()) {
 
-            mDoubleBufferedLayerRenderTarget?.detach(true)
-            val doubleBufferTarget = mParentRenderLayer.createRenderTarget(mGLRenderer, mCallback)
+            mMultiBufferedLayerRenderTarget?.detach(true)
+            val multiBufferTarget = mParentRenderLayer.createRenderTarget(mGLRenderer, mCallback)
 
             mFrontBufferedLayerSurfaceControl?.release()
 
@@ -350,7 +350,7 @@
 
             mFrontBufferedLayerRenderer = frontBufferedLayerRenderer
             mFrontBufferedLayerSurfaceControl = frontBufferedSurfaceControl
-            mDoubleBufferedLayerRenderTarget = doubleBufferTarget
+            mMultiBufferedLayerRenderTarget = multiBufferTarget
             mBufferPool = bufferPool
             mWidth = width
             mHeight = height
@@ -371,7 +371,7 @@
      * Render content to the front buffered layer providing optional parameters to be consumed in
      * [Callback.onDrawFrontBufferedLayer].
      * Additionally the parameter provided here will also be consumed in
-     * [Callback.onDrawDoubleBufferedLayer]
+     * [Callback.onDrawMultiBufferedLayer]
      * when the corresponding [commit] method is invoked, which will include all [param]s in each
      * call made to this method up to the corresponding [commit] call.
      *
@@ -393,24 +393,24 @@
     }
 
     /**
-     * Requests to render to the double buffered layer. This schedules a call to
-     * [Callback.onDrawDoubleBufferedLayer] with the parameters provided. If the front buffered
-     * layer is visible, this will hide this layer after rendering to the double buffered layer
+     * Requests to render to the multi buffered layer. This schedules a call to
+     * [Callback.onDrawMultiBufferedLayer] with the parameters provided. If the front buffered
+     * layer is visible, this will hide this layer after rendering to the multi buffered layer
      * is complete. This is equivalent to calling [GLFrontBufferedRenderer.renderFrontBufferedLayer]
      * for each parameter provided in the collection followed by a single call to
-     * [GLFrontBufferedRenderer.commit]. This is useful for re-rendering the double buffered
+     * [GLFrontBufferedRenderer.commit]. This is useful for re-rendering the multi buffered
      * scene when the corresponding Activity is being resumed from the background in which the
      * contents should be re-drawn. Additionally this allows for applications to decide to
-     * dynamically render to either front or double buffered layers.
+     * dynamically render to either front or multi buffered layers.
      *
      * If this [GLFrontBufferedRenderer] has been released, that is [isValid] returns 'false',
      * this call is ignored.
      *
-     * @param params Parameters that to be consumed when rendering to the double buffered layer.
+     * @param params Parameters that to be consumed when rendering to the multi buffered layer.
      * These parameters will be provided in the corresponding call to
-     * [Callback.onDrawDoubleBufferedLayer]
+     * [Callback.onDrawMultiBufferedLayer]
      */
-    fun renderDoubleBufferedLayer(params: Collection<T>) {
+    fun renderMultiBufferedLayer(params: Collection<T>) {
         if (isValid()) {
             val segment = if (params is MutableCollection<T>) {
                 params
@@ -418,18 +418,18 @@
                 ArrayList<T>().apply { addAll(params) }
             }
             mSegments.add(segment)
-            mDoubleBufferedLayerRenderTarget?.requestRender()
+            mMultiBufferedLayerRenderTarget?.requestRender()
         } else {
             Log.w(
-                TAG, "Attempt to render to the double buffered layer when " +
+                TAG, "Attempt to render to the multi buffered layer when " +
                     "GLFrontBufferedRenderer has been released"
             )
         }
     }
 
     /**
-     * Clears the contents of both the front and double buffered layers. This triggers a call to
-     * [Callback.onDoubleBufferedLayerRenderComplete] and hides the front buffered layer.
+     * Clears the contents of both the front and multi buffered layers. This triggers a call to
+     * [Callback.onMultiBufferedLayerRenderComplete] and hides the front buffered layer.
      */
     fun clear() {
         clearParamQueues()
@@ -438,9 +438,9 @@
     }
 
     /**
-     * Requests to render the entire scene to the double buffered layer and schedules a call to
-     * [Callback.onDrawDoubleBufferedLayer]. The parameters provided to
-     * [Callback.onDrawDoubleBufferedLayer] will include each argument provided to every
+     * Requests to render the entire scene to the multi buffered layer and schedules a call to
+     * [Callback.onDrawMultiBufferedLayer]. The parameters provided to
+     * [Callback.onDrawMultiBufferedLayer] will include each argument provided to every
      * [renderFrontBufferedLayer] call since the last call to [commit] has been made.
      *
      * If this [GLFrontBufferedRenderer] has been released, that is [isValid] returns `false`,
@@ -449,10 +449,10 @@
     fun commit() {
         if (isValid()) {
             mSegments.add(mActiveSegment.release())
-            mDoubleBufferedLayerRenderTarget?.requestRender()
+            mMultiBufferedLayerRenderTarget?.requestRender()
         } else {
             Log.w(
-                TAG, "Attempt to render to the double buffered layer when " +
+                TAG, "Attempt to render to the multi buffered layer when " +
                     "GLFrontBufferedRenderer has been released"
             )
         }
@@ -460,7 +460,7 @@
 
     /**
      * Requests to cancel rendering and hides the front buffered layer.
-     * Unlike [commit], this does not schedule a call to render into the double buffered layer.
+     * Unlike [commit], this does not schedule a call to render into the multi buffered layer.
      *
      * If this [GLFrontBufferedRenderer] has been released, that is [isValid] returns `false`,
      * this call is ignored.
@@ -497,14 +497,14 @@
      */
     internal fun detachTargets(cancelPending: Boolean, onReleaseComplete: (() -> Unit)? = null) {
         // Wrap the callback into a separate lambda to ensure it is invoked only after
-        // both the front and double buffered layer target renderers are detached
+        // both the front and multi buffered layer target renderers are detached
         var callbackCount = 0
         var expectedCount = 0
         if (mFrontBufferedRenderTarget?.isAttached() == true) {
             expectedCount++
         }
 
-        if (mDoubleBufferedLayerRenderTarget?.isAttached() == true) {
+        if (mMultiBufferedLayerRenderTarget?.isAttached() == true) {
             expectedCount++
         }
         val frontBufferedLayerSurfaceControl = mFrontBufferedLayerSurfaceControl
@@ -527,9 +527,9 @@
         }
         mFrontBufferedLayerSurfaceControl = null
         mFrontBufferedRenderTarget?.detach(cancelPending, wrappedCallback)
-        mDoubleBufferedLayerRenderTarget?.detach(cancelPending, wrappedCallback)
+        mMultiBufferedLayerRenderTarget?.detach(cancelPending, wrappedCallback)
         mFrontBufferedRenderTarget = null
-        mDoubleBufferedLayerRenderTarget = null
+        mMultiBufferedLayerRenderTarget = null
         mWidth = -1
         mHeight = -1
     }
@@ -537,7 +537,7 @@
     /**
      * Releases the [GLFrontBufferedRenderer] and provides an optional callback that is invoked when
      * the [GLFrontBufferedRenderer] is fully torn down. If the [cancelPending] flag is true, all
-     * pending requests to render into the front or double buffered layers will be processed before
+     * pending requests to render into the front or multi buffered layers will be processed before
      * the [GLFrontBufferedRenderer] is torn down. Otherwise all in process requests are ignored.
      * If the [GLFrontBufferedRenderer] is already released, that is [isValid] returns `false`, this
      * method does nothing.
@@ -560,7 +560,7 @@
             // If we are managing the GLRenderer that we created ourselves
             // do not cancel pending operations as we will miss callbacks that we are
             // expecting above to properly teardown resources
-            // Instead rely on the cancel pending flags for detaching the front/double buffered
+            // Instead rely on the cancel pending flags for detaching the front/multi buffered
             // render targets instead
             mGLRenderer.stop(false)
         }
@@ -672,14 +672,14 @@
         pool.close()
     }
 
-    companion object {
+    internal companion object {
 
         internal const val TAG = "GLFrontBufferedRenderer"
     }
 
     @JvmDefaultWithCompatibility
     /**
-     * Provides callbacks for consumers to draw into the front and double buffered layers as well as
+     * Provides callbacks for consumers to draw into the front and multi buffered layers as well as
      * provide opportunities to synchronize [SurfaceControlCompat.Transaction]s to submit the layers
      * to the hardware compositor.
      */
@@ -734,10 +734,10 @@
         )
 
         /**
-         * Callback invoked to render content into the doubled buffered layer with the specified
+         * Callback invoked to render content into the multid buffered layer with the specified
          * parameters.
          * @param eglManager [EGLManager] useful in configuring EGL objects to be used when issuing
-         * OpenGL commands to render into the double buffered layer
+         * OpenGL commands to render into the multi buffered layer
          * @param bufferInfo [BufferInfo] about the buffer that is being rendered into. This
          * includes the width and height of the buffer which can be different than the corresponding
          * dimensions of the [SurfaceView] provided to the [GLFrontBufferedRenderer] as pre-rotation
@@ -769,7 +769,7 @@
          * val result = FloatArray(16)
          * Matrix.multiplyMM(result, 0, myMatrix, 0, transform, 0)
          * ```
-         * @param params optional parameter provided to render the entire scene into the double
+         * @param params optional parameter provided to render the entire scene into the multi
          * buffered layer.
          * This is a collection of all parameters provided in consecutive invocations to
          * [GLFrontBufferedRenderer.renderFrontBufferedLayer] since the last call to
@@ -799,7 +799,7 @@
          * is being rendered into taking into account pre-rotation transformations
          */
         @WorkerThread
-        fun onDrawDoubleBufferedLayer(
+        fun onDrawMultiBufferedLayer(
             eglManager: EGLManager,
             bufferInfo: BufferInfo,
             transform: FloatArray,
@@ -828,7 +828,7 @@
         }
 
         /**
-         * Optional callback invoked when rendering to the double buffered layer is complete but
+         * Optional callback invoked when rendering to the multi buffered layer is complete but
          * before the buffers are submitted to the hardware compositor.
          * This provides consumers a mechanism for synchronizing the transaction with other
          * [SurfaceControlCompat] objects that maybe rendered within the scene.
@@ -838,10 +838,10 @@
          * of the [SurfaceControlCompat] like z-ordering or visibility with the corresponding
          * [SurfaceControlCompat.Transaction].
          * @param transaction Current [SurfaceControlCompat.Transaction] to apply updated buffered
-         * content to the double buffered layer.
+         * content to the multi buffered layer.
          */
         @WorkerThread
-        fun onDoubleBufferedLayerRenderComplete(
+        fun onMultiBufferedLayerRenderComplete(
             frontBufferedLayerSurfaceControl: SurfaceControlCompat,
             transaction: SurfaceControlCompat.Transaction
         ) {
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/ParentRenderLayer.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/ParentRenderLayer.kt
index b702a35..d58b32a 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/ParentRenderLayer.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/ParentRenderLayer.kt
@@ -92,7 +92,7 @@
 
     /**
      * Clear the contents of the parent buffer. This triggers a call to
-     * [GLFrontBufferedRenderer.Callback.onDoubleBufferedLayerRenderComplete] to update the
+     * [GLFrontBufferedRenderer.Callback.onMultiBufferedLayerRenderComplete] to update the
      * buffer shown for the dry layer as well as hides the front buffered layer.
      */
     fun clear()
@@ -131,7 +131,7 @@
          * be committed, that is the entire scene is re-rendered into the double buffered layer.
          * This can return null if all the double buffered params have already been queried.
          */
-        fun obtainDoubleBufferedLayerParams(): MutableCollection<T>?
+        fun obtainMultiBufferedLayerParams(): MutableCollection<T>?
 
         /**
          * Obtain a handle to the front buffered layer [SurfaceControlCompat] to be used in
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SingleBufferedCanvasRendererV29.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SingleBufferedCanvasRendererV29.kt
index 042ecb3..2227aa8 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SingleBufferedCanvasRendererV29.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SingleBufferedCanvasRendererV29.kt
@@ -16,18 +16,14 @@
 
 package androidx.graphics.lowlatency
 
-import android.graphics.RenderNode
+import android.graphics.Canvas
 import android.graphics.SurfaceTexture
 import android.hardware.HardwareBuffer
 import android.opengl.GLES20
 import android.opengl.Matrix
 import android.os.Build
-import android.os.Handler
-import android.os.Looper
 import android.util.Log
 import androidx.annotation.RequiresApi
-import androidx.annotation.WorkerThread
-import androidx.graphics.SurfaceTextureRenderer
 import androidx.graphics.lowlatency.FrontBufferUtils.Companion.obtainHardwareBufferUsageFlags
 import androidx.graphics.opengl.FrameBuffer
 import androidx.graphics.opengl.FrameBufferRenderer
@@ -39,7 +35,6 @@
 import java.nio.IntBuffer
 import java.util.concurrent.Executor
 import java.util.concurrent.atomic.AtomicBoolean
-import java.util.concurrent.atomic.AtomicInteger
 
 @RequiresApi(Build.VERSION_CODES.Q)
 internal class SingleBufferedCanvasRendererV29<T>(
@@ -50,74 +45,19 @@
     private val callbacks: SingleBufferedCanvasRenderer.RenderCallbacks<T>,
 ) : SingleBufferedCanvasRenderer<T> {
 
-    private val mMainHandler = Handler(Looper.myLooper() ?: Looper.getMainLooper())
+    private val mProducer = TextureProducer<T>(
+        width,
+        height,
+        object : TextureProducer.Callbacks<T> {
+            override fun onTextureAvailable(texture: SurfaceTexture) {
+                mSurfaceTexture = texture
+                mFrameBufferTarget.requestRender()
+            }
 
-    private val mRenderNode = RenderNode("renderNode").apply {
-        setPosition(
-            0,
-            0,
-            this@SingleBufferedCanvasRendererV29.width,
-            this@SingleBufferedCanvasRendererV29.height
-        )
-    }
-
-    /**
-     * Runnable used to execute the request to render batched parameters
-     */
-    private val mRenderPendingRunnable = Runnable { renderPendingParameters() }
-
-    /**
-     * Runnable used to execute the request to clear buffer content on screen
-     */
-    private val mClearContentsRunnable = Runnable {
-        mFrameBufferRenderer.clear()
-        obtainFrameBufferTarget().requestRender()
-    }
-
-    /**
-     * SurfaceTextureRenderer used to render contents of a RenderNode into a SurfaceTexture
-     * that is then rendered into a HardwareBuffer for consumption
-     */
-    private val mSurfaceTextureRenderer = SurfaceTextureRenderer(
-            mRenderNode,
-            width,
-            height,
-            mMainHandler
-        ) { texture ->
-            mSurfaceTexture = texture
-            obtainFrameBufferTarget().requestRender()
-        }
-
-    /**
-     * Helper method to request the provided RenderNode content to be drawn on the texture
-     * rendering thread
-     */
-    internal fun dispatchRenderTextureRequest() {
-        executor.execute(mRenderPendingRunnable)
-    }
-
-    /**
-     * Helper method to request clearing the contents of the destination HardwareBuffer
-     */
-    private fun dispatchClearRequest() {
-        executor.execute(mClearContentsRunnable)
-    }
-
-    /**
-     * Maximum number of pending renders to the SurfaceTexture before we queue up parameters
-     * and wait for the consumer to catch up. Some devices have very fast input sampling rates
-     * which make the producing side much faster than the consuming side. We batch the pending
-     * parameters and when the consuming side catches up, we batch and render all the pending
-     * parameters into the SurfaceTexture that then gets drawn into the destination HardwareBuffer.
-     * This ensures we don't drop any attempts to render.
-     */
-    private val mMaxPendingBuffers = 2
-
-    /**
-     * Keep track of the number of pending renders of the source SurfaceTexture to the destination
-     * HardwareBuffer
-     */
-    private val mPendingBuffers = AtomicInteger(0)
+            override fun render(canvas: Canvas, width: Int, height: Int, param: T) {
+                callbacks.render(canvas, width, height, param)
+            }
+        })
 
     /**
      * Source SurfaceTexture that the destination of content to be rendered from the provided
@@ -206,6 +146,7 @@
                     // texture.updateTexImage is called within QuadTextureRenderer#draw
                     obtainQuadRenderer().draw(mProjection, width.toFloat(), height.toFloat())
                     texture.releaseTexImage()
+                    mProducer.markTextureConsumed()
                 }
             }
 
@@ -222,21 +163,8 @@
                         IntBuffer.wrap(IntArray(1)))
                 }
 
-                val state = mState.get()
-                if (state != RELEASED) {
-                    executor.execute {
-                        callbacks.onBufferReady(frameBuffer.hardwareBuffer, syncFenceCompat)
-                    }
-                }
-                val pending = mPendingBuffers.decrementAndGet()
-                if (state == PENDING_RELEASE && pending <= 0) {
-                    mMainHandler.post(::tearDown)
-                } else {
-                    // After rendering see if there are additional queued content to render
-                    // If all images within the SurfaceTexture are pending being drawn into the
-                    // destination HardwareBuffer, they are queued up to be batch rendered after
-                    // texture image has been released
-                    dispatchRenderTextureRequest()
+                mProducer.execute {
+                    callbacks.onBufferReady(frameBuffer.hardwareBuffer, syncFenceCompat)
                 }
             }
         },
@@ -248,52 +176,18 @@
      */
     private val mGLRenderer = GLRenderer().apply { start() }
 
-    /**
-     * Thread safe queue of parameters to be consumed in on the texture render thread that are
-     * provided in [SingleBufferedCanvasRenderer.render]
-     */
-    private val mParams = ParamQueue<T>()
-
-    /**
-     * State to determine if [release] has been called on this [SingleBufferedCanvasRendererV29]
-     * instance. If true, all subsequent operations are a no-op
-     */
-    private val mState = AtomicInteger(ACTIVE)
-
-    /**
-     * Pending release callback to be invoked when the renderer is torn down
-     */
-    private var mReleaseComplete: (() -> Unit)? = null
+    private var mFrameBufferTarget: GLRenderer.RenderTarget =
+        mGLRenderer.createRenderTarget(width, height, mFrameBufferRenderer)
 
     /**
      * Flag to maintain visibility state on the main thread
      */
     private var mIsVisible = false
 
-    private fun isReleased(): Boolean {
-        val state = mState.get()
-        return state == RELEASED || state == PENDING_RELEASE
-    }
-
-    @WorkerThread
-    internal fun renderPendingParameters() {
-        val pending = mPendingBuffers.get()
-        // If there are pending requests to draw and we are not waiting on the consuming side
-        // to catch up, then render content in the RenderNode and issue a request to draw into
-        // the SurfaceTexture
-        if (pending < mMaxPendingBuffers) {
-            val params = mParams.release()
-            if (params.isNotEmpty()) {
-                val canvas = mRenderNode.beginRecording()
-                for (p in params) {
-                    callbacks.render(canvas, width, height, p)
-                }
-                mRenderNode.endRecording()
-                mPendingBuffers.incrementAndGet()
-                mSurfaceTextureRenderer.renderFrame()
-            }
-        }
-    }
+    /**
+     * Flag to determine if the SingleBufferedCanvasRenderer instance has been released
+     */
+    private var mIsReleased = false
 
     override var isVisible: Boolean
         set(value) {
@@ -306,77 +200,62 @@
 
     private var mFrontBufferLayer: FrameBuffer? = null
 
-    private fun obtainFrameBufferTarget(): GLRenderer.RenderTarget =
-        mFrameBufferTarget ?: mGLRenderer.createRenderTarget(width, height, mFrameBufferRenderer)
-            .also { mFrameBufferTarget = it }
-
-    private var mFrameBufferTarget: GLRenderer.RenderTarget? = null
-
     override fun render(param: T) {
-        ifNotReleased {
-            mParams.add(param)
-            if (mPendingBuffers.get() < mMaxPendingBuffers) {
-                dispatchRenderTextureRequest()
-            }
+        if (!mIsReleased) {
+            mProducer.requestRender(param)
+        } else {
+            Log.w(TAG, "Attempt to render with CanvasRenderer that has already been released")
         }
     }
 
     override fun release(cancelPending: Boolean, onReleaseComplete: (() -> Unit)?) {
-        ifNotReleased {
-            mReleaseComplete = onReleaseComplete
-            if (cancelPending || !isPendingRendering()) {
-                mState.set(RELEASED)
-                cancelPending()
-                tearDown()
-            } else {
-                mState.set(PENDING_RELEASE)
+        if (!mIsReleased) {
+            if (cancelPending) {
+                mProducer.cancelPending()
             }
+            mProducer.release(cancelPending) {
+                // If the producer is torn down after all pending requests are completed
+                // then there is nothing left for the render target to consume so
+                // detach immediately
+                mFrameBufferTarget.detach(true) {
+                    // GL Thread
+                    mQuadRenderer?.release()
+                    if (mTextureId != -1) {
+                        buffer[0] = mTextureId
+                        GLES20.glDeleteTextures(1, buffer, 0)
+                        mTextureId = -1
+                    }
+                }
+                mGLRenderer.stop(true)
+                onReleaseComplete?.invoke()
+           }
+
+            mIsReleased = true
+        } else {
+            Log.w(TAG, "Attempt to release CanvasRenderer that has already been released")
         }
     }
 
-    private fun isPendingRendering() = mParams.isEmpty() || mPendingBuffers.get() > 0
-
-    internal fun tearDown() {
-        mFrameBufferTarget?.detach(true) {
-            // GL Thread
-            mQuadRenderer?.release()
-            if (mTextureId != -1) {
-                buffer[0] = mTextureId
-                GLES20.glDeleteTextures(1, buffer, 0)
-                mTextureId = -1
-            }
-        }
-        mGLRenderer.stop(false)
-        mSurfaceTexture?.let { texture ->
-            if (!texture.isReleased) {
-                texture.release()
-            }
-        }
-        mRenderNode.discardDisplayList()
-        mSurfaceTextureRenderer.release()
-
-        mReleaseComplete?.let { callback ->
-            mMainHandler.post(callback)
-        }
+    private val mClearRunnable = Runnable {
+        mFrameBufferRenderer.clear()
+        mFrameBufferTarget.requestRender()
     }
 
     override fun cancelPending() {
-        ifNotReleased {
-            mParams.clear()
+        if (!mIsReleased) {
+            mProducer.cancelPending()
+        } else {
+            Log.w(TAG, "Attempt to cancel pending requests when the CanvasRender has " +
+                "already been released")
         }
     }
 
     override fun clear() {
-        ifNotReleased {
-            dispatchClearRequest()
-        }
-    }
-
-    private inline fun ifNotReleased(block: () -> Unit) {
-        if (!isReleased()) {
-            block()
+        if (!mIsReleased) {
+            mProducer.execute(mClearRunnable)
         } else {
-            Log.w(TAG, "Attempt to use already released renderer")
+            Log.w(TAG, "Attempt to clear contents when the CanvasRenderer has already " +
+                "been released")
         }
     }
 
@@ -386,22 +265,6 @@
 
     private companion object {
 
-        /**
-         * Indicates the renderer is an in active state and can render content
-         */
-        const val ACTIVE = 0
-
-        /**
-         * Indicates the renderer is released and is no longer in a valid state to render content
-         */
-        const val RELEASED = 1
-
-        /**
-         * Indicates the renderer is completing rendering of current pending frames but not accepting
-         * new requests to render
-         */
-        const val PENDING_RELEASE = 2
-
         const val TAG = "SingleBufferedCanvasV29"
     }
 }
\ No newline at end of file
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SurfaceViewRenderLayer.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SurfaceViewRenderLayer.kt
index f82da69..b2407a3 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SurfaceViewRenderLayer.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/SurfaceViewRenderLayer.kt
@@ -120,7 +120,7 @@
                         this.width = mBufferTransform.glWidth
                         this.height = mBufferTransform.glHeight
                     }
-                    renderLayerCallback.onDrawDoubleBufferedLayer(
+                    renderLayerCallback.onDrawMultiBufferedLayer(
                         eglManager,
                         bufferInfo,
                         mBufferTransform.transform,
@@ -155,7 +155,7 @@
                             transaction.setBufferTransform(sc, inverse)
                         }
 
-                        renderLayerCallback.onDoubleBufferedLayerRenderComplete(
+                        renderLayerCallback.onMultiBufferedLayerRenderComplete(
                             frontBufferedLayerSurfaceControl,
                             transaction
                         )
@@ -169,7 +169,7 @@
                 }
             })
         val parentFrameBufferRenderer = WrapperFrameBufferRenderer<T>(frameBufferRenderer) {
-            params = mLayerCallback?.obtainDoubleBufferedLayerParams()
+            params = mLayerCallback?.obtainMultiBufferedLayerParams()
             params != null
         }
         val renderTarget = renderer.attach(surfaceView, parentFrameBufferRenderer)
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/TextureProducer.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/TextureProducer.kt
new file mode 100644
index 0000000..ce14f61
--- /dev/null
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/TextureProducer.kt
@@ -0,0 +1,200 @@
+/*
+ * 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.graphics.lowlatency
+
+import android.graphics.Canvas
+import android.graphics.RenderNode
+import android.graphics.SurfaceTexture
+import android.os.Build
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.Message
+import androidx.annotation.AnyThread
+import androidx.annotation.RequiresApi
+import androidx.annotation.WorkerThread
+import androidx.graphics.SurfaceTextureRenderer
+
+/**
+ * Class responsible for the producing side of SurfaceTextures that are rendered with content
+ * provided from a canvas. This class handles proxying all requests to an internal thread
+ * as well as throttles production of frames based on consumption rate.
+ */
+@RequiresApi(Build.VERSION_CODES.Q)
+internal class TextureProducer<T>(
+    val width: Int,
+    val height: Int,
+    val callbacks: Callbacks<T>
+) {
+
+    interface Callbacks<T> {
+        fun onTextureAvailable(texture: SurfaceTexture)
+
+        fun render(canvas: Canvas, width: Int, height: Int, param: T)
+    }
+
+    private var mIsReleasing = false
+    private var mReleaseCallback: (() -> Unit)? = null
+    private val mParams = ArrayList<T>()
+    private var mPendingRenders = 0
+    private val mProducerThread = HandlerThread("producerThread").apply { start() }
+    @Suppress("UNCHECKED_CAST")
+    private val mProducerHandler = Handler(mProducerThread.looper) { message ->
+        when (message.what) {
+            RENDER -> {
+                if (!mIsReleasing) {
+                    val param = message.obj as T
+                    mParams.add(param)
+                    doRender()
+                }
+            }
+            TEXTURE_CONSUMED -> {
+                mPendingRenders--
+                if (mIsReleasing && !isPendingRendering()) {
+                    teardown()
+                } else {
+                    doRender()
+                }
+            }
+            CANCEL_PENDING -> {
+                mParams.clear()
+            }
+            RELEASE -> {
+                mIsReleasing = true
+                mReleaseCallback = message.obj as (() -> Unit)?
+                if (!isPendingRendering()) {
+                    teardown()
+                }
+            }
+        }
+        true
+    }
+
+    @WorkerThread // ProducerThread
+    private fun teardown() {
+        mReleaseCallback?.invoke()
+        mSurfaceTextureRenderer.release()
+        mProducerThread.quit()
+    }
+
+    @WorkerThread // ProducerThread
+    private fun isPendingRendering() = mParams.isNotEmpty() || mPendingRenders > 0
+
+    private val mRenderNode = RenderNode("node").apply {
+        setPosition(
+            0,
+            0,
+            this@TextureProducer.width,
+            this@TextureProducer.height
+        )
+    }
+
+    private inline fun RenderNode.record(block: (Canvas) -> Unit) {
+        val canvas = beginRecording()
+        block(canvas)
+        endRecording()
+    }
+
+    private val mSurfaceTextureRenderer = SurfaceTextureRenderer(
+        mRenderNode,
+        width,
+        height,
+        mProducerHandler
+    ) { texture ->
+        callbacks.onTextureAvailable(texture)
+    }
+
+    @WorkerThread // ProducerThread
+    private fun doRender() {
+        if (mPendingRenders < MAX_PENDING_RENDERS) {
+            if (mParams.isNotEmpty()) {
+                mRenderNode.record { canvas ->
+                    for (p in mParams) {
+                        callbacks.render(canvas, width, height, p)
+                    }
+                }
+                mParams.clear()
+                mPendingRenders++
+                mSurfaceTextureRenderer.renderFrame()
+            }
+        }
+    }
+
+    @AnyThread
+    fun requestRender(param: T) {
+        mProducerHandler.sendMessage(Message.obtain(mProducerHandler, RENDER, param))
+    }
+
+    @AnyThread
+    fun cancelPending() {
+        mProducerHandler.removeMessages(RENDER)
+        mProducerHandler.sendMessage(Message.obtain(mProducerHandler, CANCEL_PENDING))
+    }
+
+    @AnyThread
+    fun markTextureConsumed() {
+        mProducerHandler.sendMessage(Message.obtain(mProducerHandler, TEXTURE_CONSUMED))
+    }
+
+    @AnyThread
+    fun execute(runnable: Runnable) {
+        mProducerHandler.post(runnable)
+    }
+
+    @AnyThread
+    fun remove(runnable: Runnable) {
+        mProducerHandler.removeCallbacks(runnable)
+    }
+
+    @AnyThread
+    fun release(cancelPending: Boolean, onReleaseComplete: (() -> Unit)? = null) {
+        if (cancelPending) {
+            cancelPending()
+        }
+        mProducerHandler.sendMessage(Message.obtain(mProducerHandler, RELEASE, onReleaseComplete))
+    }
+
+    private companion object {
+        /**
+         * Constant to indicate a request to render new content into a SurfaceTexture
+         * for consumption.
+         */
+        const val RENDER = 0
+
+        /**
+         * Constant to indicate that a previously produced frame has been consumed.
+         */
+        const val TEXTURE_CONSUMED = 1
+
+        /**
+         * Cancel all pending requests to render and clear all parameters that are to be consumed
+         * for an upcoming frame
+         */
+        const val CANCEL_PENDING = 2
+
+        /**
+         * Release the resources associated with this [TextureProducer] instance
+         */
+        const val RELEASE = 3
+
+        /**
+         * Maximum number of frames to produce before the producer pauses. Subsequent attempts
+         * to render will batch parameters and continue to produce frames when the consumer
+         * signals that the corresponding textures have been consumed.
+         */
+        const val MAX_PENDING_RENDERS = 2
+    }
+}
\ No newline at end of file
diff --git a/graphics/graphics-shapes/api/current.txt b/graphics/graphics-shapes/api/current.txt
index f6613a7..1c0472b 100644
--- a/graphics/graphics-shapes/api/current.txt
+++ b/graphics/graphics-shapes/api/current.txt
@@ -64,6 +64,22 @@
     method public static void drawCubicShape(android.graphics.Canvas, androidx.graphics.shapes.CubicShape shape, android.graphics.Paint paint);
   }
 
+  public final class Morph {
+    ctor public Morph(androidx.graphics.shapes.Polygon start, androidx.graphics.shapes.Polygon end);
+    method public java.util.List<androidx.graphics.shapes.Cubic> asCubics();
+    method public android.graphics.Path asPath();
+    method public android.graphics.RectF getBounds();
+    method public float getProgress();
+    method public void setProgress(float);
+    method public void transform(android.graphics.Matrix matrix);
+    property public final android.graphics.RectF bounds;
+    property public final float progress;
+  }
+
+  public final class MorphKt {
+    method public static void drawMorph(android.graphics.Canvas, androidx.graphics.shapes.Morph morph, android.graphics.Paint paint);
+  }
+
   public class Polygon {
     ctor public Polygon(java.util.List<? extends android.graphics.PointF> vertices, optional android.graphics.PointF? center);
     ctor public Polygon(int numVertices, optional float radius, optional android.graphics.PointF center);
diff --git a/graphics/graphics-shapes/api/public_plus_experimental_current.txt b/graphics/graphics-shapes/api/public_plus_experimental_current.txt
index f6613a7..1c0472b 100644
--- a/graphics/graphics-shapes/api/public_plus_experimental_current.txt
+++ b/graphics/graphics-shapes/api/public_plus_experimental_current.txt
@@ -64,6 +64,22 @@
     method public static void drawCubicShape(android.graphics.Canvas, androidx.graphics.shapes.CubicShape shape, android.graphics.Paint paint);
   }
 
+  public final class Morph {
+    ctor public Morph(androidx.graphics.shapes.Polygon start, androidx.graphics.shapes.Polygon end);
+    method public java.util.List<androidx.graphics.shapes.Cubic> asCubics();
+    method public android.graphics.Path asPath();
+    method public android.graphics.RectF getBounds();
+    method public float getProgress();
+    method public void setProgress(float);
+    method public void transform(android.graphics.Matrix matrix);
+    property public final android.graphics.RectF bounds;
+    property public final float progress;
+  }
+
+  public final class MorphKt {
+    method public static void drawMorph(android.graphics.Canvas, androidx.graphics.shapes.Morph morph, android.graphics.Paint paint);
+  }
+
   public class Polygon {
     ctor public Polygon(java.util.List<? extends android.graphics.PointF> vertices, optional android.graphics.PointF? center);
     ctor public Polygon(int numVertices, optional float radius, optional android.graphics.PointF center);
diff --git a/graphics/graphics-shapes/api/restricted_current.txt b/graphics/graphics-shapes/api/restricted_current.txt
index f6613a7..1c0472b 100644
--- a/graphics/graphics-shapes/api/restricted_current.txt
+++ b/graphics/graphics-shapes/api/restricted_current.txt
@@ -64,6 +64,22 @@
     method public static void drawCubicShape(android.graphics.Canvas, androidx.graphics.shapes.CubicShape shape, android.graphics.Paint paint);
   }
 
+  public final class Morph {
+    ctor public Morph(androidx.graphics.shapes.Polygon start, androidx.graphics.shapes.Polygon end);
+    method public java.util.List<androidx.graphics.shapes.Cubic> asCubics();
+    method public android.graphics.Path asPath();
+    method public android.graphics.RectF getBounds();
+    method public float getProgress();
+    method public void setProgress(float);
+    method public void transform(android.graphics.Matrix matrix);
+    property public final android.graphics.RectF bounds;
+    property public final float progress;
+  }
+
+  public final class MorphKt {
+    method public static void drawMorph(android.graphics.Canvas, androidx.graphics.shapes.Morph morph, android.graphics.Paint paint);
+  }
+
   public class Polygon {
     ctor public Polygon(java.util.List<? extends android.graphics.PointF> vertices, optional android.graphics.PointF? center);
     ctor public Polygon(int numVertices, optional float radius, optional android.graphics.PointF center);
diff --git a/graphics/graphics-shapes/build.gradle b/graphics/graphics-shapes/build.gradle
index c54a44a..f04e619 100644
--- a/graphics/graphics-shapes/build.gradle
+++ b/graphics/graphics-shapes/build.gradle
@@ -25,7 +25,7 @@
 dependencies {
     api(libs.kotlinStdlib)
     implementation("androidx.annotation:annotation:1.4.0")
-    implementation(project(":core:core-ktx"))
+    implementation("androidx.core:core-ktx:1.10.0-rc01")
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
     androidTestImplementation(libs.testRunner)
diff --git a/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/FloatMappingTest.kt b/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/FloatMappingTest.kt
new file mode 100644
index 0000000..4cb3a8d
--- /dev/null
+++ b/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/FloatMappingTest.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.graphics.shapes
+
+import androidx.test.filters.SmallTest
+import org.junit.Test
+
+@SmallTest
+class FloatMappingTest {
+    @Test
+    fun identityMappingTest() = validateMapping(DoubleMapper.Identity) { it }
+
+    @Test
+    fun simpleMappingTest() = validateMapping(
+        // Map the first half of the start source to the first quarter of the target.
+        mapper = DoubleMapper(
+            0f to 0f,
+            0.5f to 0.25f
+        )
+    ) { x ->
+        if (x < 0.5f) x / 2
+        else (3 * x - 1) / 2
+    }
+
+    @Test
+    fun targetWrapsTest() = validateMapping(
+        // mapping applies a "+ 0.5f"
+        mapper = DoubleMapper(
+            0f to 0.5f,
+            0.1f to 0.6f
+        )
+    ) { x -> (x + 0.5f) % 1f }
+
+    @Test
+    fun sourceWrapsTest() = validateMapping(
+        // Values on the source wrap (this is still the "+ 0.5f" function)
+        mapper = DoubleMapper(
+            0.5f to 0f,
+            0.1f to 0.6f
+        )
+    ) { x -> (x + 0.5f) % 1f }
+
+    @Test
+    fun bothWrapTest() = validateMapping(
+        // Just the identity function
+        mapper = DoubleMapper(
+            0.5f to 0.5f,
+            0.75f to 0.75f,
+            0.1f to 0.1f,
+            0.49f to 0.49f
+        )
+    ) { it }
+
+    @Test
+    fun multiplePointTes() = validateMapping(
+        mapper = DoubleMapper(
+            0.4f to 0.2f,
+            0.5f to 0.22f,
+            0f to 0.8f
+        )
+    ) { x ->
+        if (x < 0.4f) {
+            (0.8f + x) % 1f
+        } else if (x < 0.5f) {
+            0.2f + (x - 0.4f) / 5
+        } else {
+            // maps a change of 0.5 in the source to a change 0.58 in the target, hence the 1.16
+            0.22f + (x - 0.5f) * 1.16f
+        }
+    }
+
+    private fun validateMapping(mapper: DoubleMapper, expectedFunction: (Float) -> Float) {
+        (0..9999).forEach { i ->
+            val source = i / 10000f
+            val target = expectedFunction(source)
+
+            assertEqualish(target, mapper.map(source))
+            assertEqualish(source, mapper.mapBack(target))
+        }
+    }
+}
\ No newline at end of file
diff --git a/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/PolygonMeasureTest.kt b/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/PolygonMeasureTest.kt
new file mode 100644
index 0000000..f574284
--- /dev/null
+++ b/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/PolygonMeasureTest.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.graphics.shapes
+
+import android.graphics.PointF
+import androidx.test.filters.SmallTest
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+@SmallTest
+class PolygonMeasureTest {
+    @Test
+    fun triangleAngleMeasure() = polygonAngleMeasure(3)
+
+    @Test
+    fun pentagonAngleMeasure() = polygonAngleMeasure(5)
+
+    @Test
+    fun dodecagonAngleMeasure() = polygonAngleMeasure(12)
+
+    @Test
+    fun irregularTriangleAngleMeasure() = irregularPolygonAngleMeasure(
+        RoundedPolygon(
+            vertices = listOf(
+                PointF(0f, -1f),
+                PointF(1f, 1f),
+                PointF(0f, 0.5f),
+                PointF(-1f, 1f)
+            ),
+            perVertexRounding = listOf(
+                CornerRounding(0.2f, 0.5f),
+                CornerRounding(0.2f, 0.5f),
+                CornerRounding(0.4f, 0f),
+                CornerRounding(0.2f, 0.5f),
+            )
+        )
+    )
+
+    @Test
+    fun quarterAngleMeasure() = irregularPolygonAngleMeasure(
+        RoundedPolygon(
+            vertices = listOf(
+                PointF(-1f, -1f),
+                PointF(1f, -1f),
+                PointF(1f, 1f),
+                PointF(-1f, 1f)
+            ),
+            perVertexRounding = listOf(
+                CornerRounding.Unrounded,
+                CornerRounding.Unrounded,
+                CornerRounding(0.5f, 0.5f),
+                CornerRounding.Unrounded,
+            )
+        )
+    )
+
+    private fun polygonAngleMeasure(sides: Int) {
+        val polygon = Polygon(sides)
+        val measurer = AngleMeasurer(polygon.center)
+
+        val measuredPolygon = MeasuredPolygon.measurePolygon(measurer, polygon)
+
+        assertEquals(sides, measuredPolygon.size)
+
+        assertEquals(0f, measuredPolygon.first().startOutlineProgress)
+        assertEquals(1f, measuredPolygon.last().endOutlineProgress)
+        measuredPolygon.forEachIndexed { index, measuredCubic ->
+            assertEqualish(index.toFloat() / sides, measuredCubic.startOutlineProgress)
+        }
+    }
+
+    private fun irregularPolygonAngleMeasure(polygon: Polygon) {
+        val measurer = AngleMeasurer(polygon.center)
+
+        val measuredPolygon = MeasuredPolygon.measurePolygon(measurer, polygon)
+
+        assertEquals(0f, measuredPolygon.first().startOutlineProgress)
+        assertEquals(1f, measuredPolygon.last().endOutlineProgress)
+        measuredPolygon.forEachIndexed { index, measuredCubic ->
+            if (index > 0) {
+                assertEquals(
+                    measuredPolygon[index - 1].endOutlineProgress,
+                    measuredCubic.startOutlineProgress
+                )
+            }
+            assertTrue(measuredCubic.endOutlineProgress >= measuredCubic.startOutlineProgress)
+        }
+    }
+}
\ No newline at end of file
diff --git a/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/PolygonTest.kt b/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/PolygonTest.kt
index 69601cf..a58ebcd 100644
--- a/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/PolygonTest.kt
+++ b/graphics/graphics-shapes/src/androidTest/java/androidx/graphics/shapes/PolygonTest.kt
@@ -141,7 +141,7 @@
         val preTransformVertices = mutableListOf<PointF>()
         val preTransformCenters = mutableListOf<PointF>()
         for (feature in features) {
-            if (feature is Corner) {
+            if (feature is Polygon.Corner) {
                 // Copy into new Point objects since the ones in the feature should transform
                 preTransformVertices.add(PointF(feature.vertex.x, feature.vertex.y))
                 preTransformCenters.add(PointF(feature.roundedCenter.x, feature.roundedCenter.y))
@@ -151,7 +151,7 @@
         val postTransformVertices = mutableListOf<PointF>()
         val postTransformCenters = mutableListOf<PointF>()
         for (feature in features) {
-            if (feature is Corner) {
+            if (feature is Polygon.Corner) {
                 postTransformVertices.add(feature.vertex)
                 postTransformCenters.add(feature.roundedCenter)
             }
diff --git a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/FeatureMapping.kt b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/FeatureMapping.kt
new file mode 100644
index 0000000..0e3632e
--- /dev/null
+++ b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/FeatureMapping.kt
@@ -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.graphics.shapes
+
+import androidx.core.graphics.div
+import androidx.core.graphics.minus
+import androidx.core.graphics.plus
+
+/**
+ * MeasuredFeatures contains a list of all features in a polygon along with the [0..1] progress
+ * at that feature
+ */
+internal typealias MeasuredFeatures = List<Pair<Float, Polygon.Feature>>
+
+/**
+ * featureMapper creates a mapping between the "features" (rounded corners) of two shapes
+ */
+internal fun featureMapper(features1: MeasuredFeatures, features2: MeasuredFeatures): DoubleMapper {
+    // We only use corners for this mapping.
+    val filteredFeatures1 = features1.filter { it.second is Polygon.Corner }
+    val filteredFeatures2 = features2.filter { it.second is Polygon.Corner }
+
+    val (m1, m2) = if (filteredFeatures1.size > filteredFeatures2.size) {
+        doMapping(filteredFeatures2, filteredFeatures1) to filteredFeatures2
+    } else {
+        filteredFeatures1 to doMapping(filteredFeatures1, filteredFeatures2)
+    }
+    val mm = m1.zip(m2).map { (f1, f2) -> f1.first to f2.first }
+
+    debugLog(LOG_TAG) { mm.joinToString { "${it.first} -> ${it.second}" } }
+    return DoubleMapper(*mm.toTypedArray()).also { dm ->
+        debugLog(LOG_TAG) {
+            val N = 10
+            "Map: " +
+                (0..N).joinToString { i -> "%.3f".format(dm.map(i.toFloat() / N)) } +
+            "\nMb : " +
+                (0..N).joinToString { i -> "%.3f".format(dm.mapBack(i.toFloat() / N)) }
+        }
+    }
+}
+
+/**
+ * Returns distance along overall shape between two Features on the two different shapes.
+ * This information is used to determine how to map features (and the curves that make up
+ * those features).
+ */
+internal fun featureDistSquared(f1: Polygon.Feature, f2: Polygon.Feature): Float {
+    // TODO: We might want to enable concave-convex matching in some situations. If so, the
+    //  approach below will not work
+    if (f1 is Polygon.Corner && f2 is Polygon.Corner && f1.convex != f2.convex) {
+        // Simple hack to force all features to map only to features of the same concavity, by
+        // returning an infinitely large distance in that case
+        debugLog(LOG_TAG) { "*** Feature distance ∞ for convex-vs-concave corners" }
+        return Float.MAX_VALUE
+    }
+    val (c1, c2) = listOf(f1, f2).map { f ->
+        (f.cubics.first().p0 + f.cubics.last().p3) / 2f
+    }
+    val d = c1 - c2
+    return d.x * d.x + d.y * d.y
+}
+
+/**
+ * Returns a mapping of the features in f2 that best map to the features in f1. The result
+ * will be a list of features in f2 that is the size of f1. This is done to figure out
+ * what the best features are in f2 that map to the existing features in f1. For example, if
+ * f1 has 3 features and f2 has 4, we want to know what the 3 features are in f2 that map to
+ * the features in f1 (then we will create a placeholder feature in the smaller shape for
+ * the morph).
+ */
+internal fun doMapping(f1: MeasuredFeatures, f2: MeasuredFeatures): MeasuredFeatures {
+    // Pick the first mapping in a greedy way.
+    val ix = f2.indices.minBy { featureDistSquared(f1[0].second, f2[it].second) }
+
+    val m = f1.size
+    val n = f2.size
+
+    val ret = mutableListOf(f2[ix])
+    var lastPicked = ix
+    for (i in 1 until m) {
+        // Check the indices we can pick, which one is better.
+        // Leave enough items in f2 to pick matches for the items left in f1.
+        val last = (ix - (m - i)).let { if (it > lastPicked) it else it + n }
+        val best = (lastPicked + 1..last).minBy {
+            featureDistSquared(f1[i].second, f2[it % n].second)
+        }
+        ret.add(f2[best % n])
+        lastPicked = best
+    }
+    return ret
+}
+
+private val LOG_TAG = "FeatureMapping"
\ No newline at end of file
diff --git a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/FloatMapping.kt b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/FloatMapping.kt
new file mode 100644
index 0000000..b22bf9f
--- /dev/null
+++ b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/FloatMapping.kt
@@ -0,0 +1,109 @@
+/*
+ * 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.graphics.shapes
+
+/**
+ * Checks if the given progress is in the given progress range, since progress is in the [0..1)
+ * interval, and wraps, there is a special case when progressTo < progressFrom.
+ * For example, if the progress range is 0.7 to 0.2, both 0.8 and 0.1 are inside and 0.5 is outside.
+ */
+internal fun progressInRange(progress: Float, progressFrom: Float, progressTo: Float) =
+    if (progressTo >= progressFrom) {
+        progress in progressFrom..progressTo
+    } else {
+        progress >= progressFrom || progress <= progressTo
+    }
+
+/**
+ * Maps from one set of progress values to another. This is used by DoubleMapper to retrieve the
+ * value on one shape that maps to the appropriate value on the other.
+ */
+internal fun linearMap(xValues: List<Float>, yValues: List<Float>, x: Float): Float {
+    require(x in 0f..1f) { "Invalid progress: $x" }
+    val segmentStartIndex = xValues.indices.first {
+        progressInRange(x, xValues[it], xValues[(it + 1) % xValues.size])
+    }
+    val segmentEndIndex = (segmentStartIndex + 1) % xValues.size
+    val segmentSizeX = positiveModule(
+        xValues[segmentEndIndex] - xValues[segmentStartIndex],
+        1f
+    )
+    val segmentSizeY = positiveModule(
+        yValues[segmentEndIndex] - yValues[segmentStartIndex],
+        1f
+    )
+    val positionInSegment = segmentSizeX.let {
+        if (it < 0.001f) 0.5f else positiveModule(x - xValues[segmentStartIndex], 1f) / it
+    }
+    return positiveModule(
+        yValues[segmentStartIndex] + segmentSizeY * positionInSegment,
+        1f
+    )
+}
+
+/**
+ * DoubleMapper creates mappings from values in the [0..1) source space to values in the [0..1)
+ * target space, and back.
+ * This mapping is created given a finite list of representative mappings, and this is extended to
+ * the whole interval by linear interpolation, and wrapping around.
+ * For example, if we have mappings 0.2 to 0.5 and 0.4 to 0.6, then 0.3 (which is in the middle of
+ * the source interval) will be mapped to 0.55 (the middle of the targets for the interval), 0.21
+ * will map to 0.505, and so on.
+ * As a more complete example, if we use x to represent a value in the source space and y for the
+ * target space, and given as input the mappings 0 to 0, 0.5 to 0.25, this will create a mapping
+ * that:
+ * { if x in [0 .. 0.5] } y = x / 2
+ * { if x in [0.5 .. 1] } y = 0.25 + (x - 0.5) * 1.5 = x * 1.5 - 0.5
+ * The mapping can also be used the other way around (using the mapBack function), resulting in:
+ * { if y in [0 .. 0.25] } x = y * 2
+ * { if y in [0.25 .. 1] } x = (y + 0.5) / 1.5
+ * This is used to create mappings of progress values between the start and end shape, which is then
+ * used to insert new curves and match curves overall.
+ */
+internal class DoubleMapper(vararg mappings: Pair<Float, Float>) {
+    private val sourceValues = mappings.map { it.first }
+    private val targetValues = mappings.map { it.second }
+
+    init {
+        validateProgress(sourceValues)
+        validateProgress(targetValues)
+    }
+
+    fun map(x: Float) = linearMap(sourceValues, targetValues, x)
+
+    fun mapBack(x: Float) = linearMap(targetValues, sourceValues, x)
+
+    companion object {
+        @JvmField
+        val Identity = DoubleMapper(
+            // We need any 2 points in the (x, x) diagonal, with x in the [0, 1) range,
+            // We spread them as much as possible to minimize float errors.
+            0f to 0f,
+            0.5f to 0.5f
+        )
+    }
+}
+
+internal fun validateProgress(p: List<Float>) {
+    require(p.all { it in 0f..1f }) {
+        "FloatMapping - Progress outside of range: " + p.joinToString()
+    }
+    val wraps = (1 until p.size).count { p[it] < p[it - 1] }
+    require(wraps <= 1) {
+        "FloatMapping - Progress wraps more than once: " + p.joinToString()
+    }
+}
diff --git a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/Morph.kt b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/Morph.kt
new file mode 100644
index 0000000..8fa3f4c
--- /dev/null
+++ b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/Morph.kt
@@ -0,0 +1,367 @@
+/*
+ * 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.graphics.shapes
+
+import android.graphics.Canvas
+import android.graphics.Matrix
+import android.graphics.Paint
+import android.graphics.Path
+import android.graphics.PointF
+import android.graphics.RectF
+import kotlin.math.min
+
+/**
+ * This class is used to animate between start and end polygons objects.
+ *
+ * Morphing between arbitrary objects can be problematic because it can be difficult to
+ * determine how the points of a given shape map to the points of some other shape.
+ * [Morph] simplifies the problem by only operating on [Polygon] objects, which
+ * are known to have similar, contiguous structures. For one thing, the shape of a polygon
+ * is contiguous from start to end (compared to an arbitrary [Path] object, which could have
+ * one or more `moveTo` operations in the shape). Also, all edges of a polygon shape are
+ * represented by [Cubic] objects, thus the start and end shapes use similar operations. Two
+ * Polygon shapes then only differ in the quantity and placement of their curves.
+ * The morph works by determining how to map the curves of the two shapes together (based on
+ * proximity and other information, such as distance to polygon vertices and concavity),
+ * and splitting curves when the shapes do not have the same number of curves or when the
+ * curve placement within the shapes is very different.
+ */
+class Morph(
+    start: Polygon,
+    end: Polygon
+) {
+    // morphMatch is the structure which holds the actual shape being morphed. It contains
+    // all cubics necessary to represent the start and end shapes (the original cubics in the
+    // shapes may be cut to align the start/end shapes)
+    private var morphMatch = match(start, end)
+
+    // path is used to draw the object
+    private val path = Path()
+
+    // These temp anchor/control points are used when progress changes to hold interpolated values
+    // Using these structures avoids allocations during morph animation
+    private val tempA0 = PointF()
+    private val tempC0 = PointF()
+    private val tempC1 = PointF()
+    private val tempA1 = PointF()
+
+    /**
+     * The bounds of the morph object are estimated by control and anchor points of all cubic curves
+     * representing the shape.
+     */
+    val bounds = RectF()
+
+    init {
+        calculateBounds(bounds)
+        updatePath()
+    }
+
+    /**
+     * Rough bounds of the object, based on the min/max bounds of all cubics points in morphMatch
+     */
+    private fun calculateBounds(bounds: RectF) {
+        // TODO: Maybe using just the anchors (p0 and p3) is sufficient and more correct than
+        // also using the control points (p1 and p2)
+        var minX = Float.MAX_VALUE
+        var minY = Float.MAX_VALUE
+        var maxX = Float.MIN_VALUE
+        var maxY = Float.MIN_VALUE
+        for (pair in morphMatch) {
+            with(pair.first.p0) {
+                if (x < minX) minX = x
+                if (y < minY) minY = y
+                if (x > maxX) maxX = x
+                if (y > maxY) maxY = y
+            }
+            with(pair.second.p0) {
+                if (x < minX) minX = x
+                if (y < minY) minY = y
+                if (x > maxX) maxX = x
+                if (y > maxY) maxY = y
+            }
+            with(pair.first.p1) {
+                if (x < minX) minX = x
+                if (y < minY) minY = y
+                if (x > maxX) maxX = x
+                if (y > maxY) maxY = y
+            }
+            with(pair.second.p1) {
+                if (x < minX) minX = x
+                if (y < minY) minY = y
+                if (x > maxX) maxX = x
+                if (y > maxY) maxY = y
+            }
+            with(pair.first.p2) {
+                if (x < minX) minX = x
+                if (y < minY) minY = y
+                if (x > maxX) maxX = x
+                if (y > maxY) maxY = y
+            }
+            with(pair.second.p2) {
+                if (x < minX) minX = x
+                if (y < minY) minY = y
+                if (x > maxX) maxX = x
+                if (y > maxY) maxY = y
+            }
+            // Skip p3 since every p3 is the next curve's p0
+        }
+        bounds.set(minX, minY, maxX, maxY)
+    }
+
+    /**
+     * The progress of a [Morph] object is a value from 0 to 1 that determines its current
+     * shape, between the start and end shapes provided at construction time. A value of 0 results
+     * in the start shape, a value of 1 results in the end shape, and any value in between
+     * results in a shape which is a linear interpolation between those two shapes.
+     *
+     * The range is generally [0..1] and values outside could result in undefined shapes, but
+     * values close to (but outside) the range can be used to get an exaggerated effect
+     * (e.g., for a bounce or overshoot animation).
+     */
+    var progress: Float = 0.0f
+        set(value) {
+            field = value
+            updatePath()
+        }
+
+    /**
+     * This function updates the [path] object which holds the rendering information for the
+     * morph shape, using the current [progress] property for the morph.
+     */
+    private fun updatePath() {
+        // In a future release, Path interpolation may be possible through the Path API
+        // itself. Until then, we have to rewind and repopulate with the new/interpolated
+        // values
+        path.rewind()
+
+        // If the list is not empty, do an initial moveTo using the first element of the match.
+        morphMatch.firstOrNull()?. let { first ->
+            interpolate(first.first.p0, first.second.p0, tempA0, progress)
+            path.moveTo(tempA0.x, tempA0.y)
+        }
+
+        // And one cubicTo for each element, including the first.
+        for (element in morphMatch) {
+            interpolate(element.first.p1, element.second.p1, tempC0, progress)
+            interpolate(element.first.p2, element.second.p2, tempC1, progress)
+            interpolate(element.first.p3, element.second.p3, tempA1, progress)
+            path.cubicTo(tempC0.x, tempC0.y, tempC1.x, tempC1.y, tempA1.x, tempA1.y)
+        }
+    }
+
+    /**
+     * Transforms (scales, rotates, and translates) the shape by the given matrix.
+     * Note that this operation alters the points in the shape directly; the original
+     * points are not retained, nor is the matrix itself. Thus calling this function
+     * twice with the same matrix will composite the effect. For example, a matrix which
+     * scales by 2 will scale the shape by 2. Calling transform twice with that matrix
+     * will have the effect of scaling the original shape by 4.
+     */
+    fun transform(matrix: Matrix) {
+        for (pair in morphMatch) {
+            pair.first.transform(matrix)
+            pair.second.transform(matrix)
+        }
+        calculateBounds(bounds)
+        updatePath()
+    }
+
+    /**
+     * Morph is rendered as a [Path]. A copy of the underlying [Path] object can be
+     * retrieved for use outside of this class. Note that this function returns a copy of
+     * the internal [Path] to maintain immutability, thus there is some overhead in retrieving
+     * the path with this function.
+     */
+    fun asPath(): Path {
+        return Path(path)
+    }
+
+    /**
+     * Returns a view of the current state of this morph as a list of Cubics.
+     * Note that this function causes a new list to be created and populated, so there is some
+     * overhead.
+     */
+    fun asCubics() =
+        mutableListOf<Cubic>().apply {
+            clear()
+            for (pair in morphMatch) {
+                add(Cubic.interpolate(pair.first, pair.second, progress))
+            }
+        }
+
+    internal companion object {
+        /**
+         * [match], called at Morph construction time, creates the structure used to animate between
+         * the start and end shapes. The technique is to match geometry (curves) between the shapes
+         * when and where possible, and to create new/placeholder curves when necessary (when
+         * one of the shapes has more curves than the other). The result is a list of pairs of
+         * Cubic curves. Those curves are the matched pairs: the first of each pair holds the
+         * geometry of the start shape, the second holds the geometry for the end shape.
+         * Changing the progress of a Morph object simply interpolates between all pairs of
+         * curves for the morph shape.
+         *
+         * Curves on both shapes are matched by running the [Measurer] to determine where
+         * the points are in each shape (proportionally, along the outline), and then running
+         * [featureMapper] which decides how to map (match) all of the curves with each other.
+         */
+        @JvmStatic
+        internal fun match(
+            p1: Polygon,
+            p2: Polygon
+        ): List<Pair<Cubic, Cubic>> {
+            if (DEBUG) {
+                repeat(2) { polyIndex ->
+                    debugLog(LOG_TAG) {
+                        listOf("Initial start:\n", "Initial end:\n")[polyIndex] +
+                            listOf(p1, p2)[polyIndex].features.joinToString("\n") { feature ->
+                                "${feature.javaClass.name.split("$").last()} - " +
+                                    ((feature as? Polygon.Corner)?.convex?.let {
+                                        if (it) "Convex - " else "Concave - " } ?: "") +
+                                    feature.cubics.joinToString("|")
+                            }
+                    }
+                }
+            }
+
+            // Measure polygons, returns lists of measured cubics for each polygon, which
+            // we then use to match start/end curves
+            val measuredPolygon1 = MeasuredPolygon.measurePolygon(AngleMeasurer(p1.center), p1)
+            val measuredPolygon2 = MeasuredPolygon.measurePolygon(AngleMeasurer(p2.center), p2)
+
+            // features1 and 2 will contain the list of corners (just the inner circular curve)
+            // along with the progress at the middle of those corners. These measurement values
+            // are then used to compare and match between the two polygons
+            val features1 = measuredPolygon1.features
+            val features2 = measuredPolygon2.features
+
+            // Map features: doubleMapper is the result of mapping the features in each shape to the
+            // closest feature in the other shape.
+            // Given a progress in one of the shapes it can be used to find the corresponding
+            // progress in the other shape (in both directions)
+            val doubleMapper = featureMapper(features1, features2)
+
+            // cut point on poly2 is the mapping of the 0 point on poly1
+            val polygon2CutPoint = doubleMapper.map(0f)
+            debugLog(LOG_TAG) { "polygon2CutPoint = $polygon2CutPoint" }
+
+            // Cut and rotate.
+            // Polygons start at progress 0, and the featureMapper has decided that we want to match
+            // progress 0 in the first polygon to `polygon2CutPoint` on the second polygon.
+            // So we need to cut the second polygon there and "rotate it", so as we walk through
+            // both polygons we can find the matching.
+            // The resulting bs1/2 are MeasuredPolygons, whose MeasuredCubics start from
+            // outlineProgress=0 and increasing until outlineProgress=1
+            val bs1 = measuredPolygon1
+            val bs2 = measuredPolygon2.cutAndShift(polygon2CutPoint)
+
+            if (DEBUG) {
+                (0 until bs1.size).forEach { index ->
+                    debugLog(LOG_TAG) { "start $index: ${bs1.getOrNull(index)}" }
+                }
+                (0 until bs2.size).forEach { index ->
+                    debugLog(LOG_TAG) { "End $index: ${bs2.getOrNull(index)}" }
+                }
+            }
+
+            // Match
+            // Now we can compare the two lists of measured cubics and create a list of pairs
+            // of cubics [ret], which are the start/end curves that represent the Morph object
+            // and the start and end shapes, and which can be interpolated to animate the
+            // between those shapes.
+            val ret = mutableListOf<Pair<Cubic, Cubic>>()
+            // i1/i2 are the indices of the current cubic on the start (1) and end (2) shapes
+            var i1 = 0
+            var i2 = 0
+            // b1, b2 are the current measured cubic for each polygon
+            var b1 = bs1.getOrNull(i1++)
+            var b2 = bs2.getOrNull(i2++)
+            // Iterate until all curves are accounted for and matched
+            while (b1 != null && b2 != null) {
+                // Progresses are in shape1's perspective
+                // b1a, b2a are ending progress values of current measured cubics in [0,1] range
+                val b1a = if (i1 == bs1.size) 1f else b1.endOutlineProgress
+                val b2a = if (i2 == bs2.size) 1f else doubleMapper.mapBack(
+                    positiveModule(b2.endOutlineProgress + polygon2CutPoint, 1f)
+                )
+                val minb = min(b1a, b2a)
+                debugLog(LOG_TAG) { "$b1a $b2a | $minb" }
+                // minb is the progress at which the curve that ends first ends.
+                // If both curves ends roughly there, no cutting is needed, we have a match.
+                // If one curve extends beyond, we need to cut it.
+                val (seg1, newb1) = if (b1a > minb + AngleEpsilon) {
+                    debugLog(LOG_TAG) { "Cut 1" }
+                    b1.cutAtProgress(minb)
+                } else {
+                    b1 to bs1.getOrNull(i1++)
+                }
+                val (seg2, newb2) = if (b2a > minb + AngleEpsilon) {
+                    debugLog(LOG_TAG) { "Cut 2" }
+                    b2.cutAtProgress(positiveModule(doubleMapper.map(minb) - polygon2CutPoint, 1f))
+                } else {
+                    b2 to bs2.getOrNull(i2++)
+                }
+                debugLog(LOG_TAG) { "Match: $seg1 -> $seg2" }
+                ret.add(Cubic(seg1.cubic) to Cubic(seg2.cubic))
+                b1 = newb1
+                b2 = newb2
+            }
+            require(b1 == null && b2 == null)
+
+            if (DEBUG) {
+                // Export as SVG path. Temporary while working with the Koru team.
+                val showPoint: (PointF) -> String = {
+                    "%.3f %.3f".format(it.x * 100, it.y * 100)
+                }
+                repeat(2) { listIx ->
+                    val points = ret.map { if (listIx == 0) it.first else it.second }
+                    debugLog(LOG_TAG) {
+                        "M " + showPoint(points.first().p0) + " " +
+                            points.joinToString(" ") {
+                                "C " + showPoint(it.p1) + ", " +
+                                    showPoint(it.p2) + ", " +
+                                    showPoint(it.p3)
+                            } + " Z"
+                    }
+                }
+            }
+            return ret
+        }
+    }
+
+    /**
+     * Draws the Morph object. This is called by the public extension function
+     * [Canvas.drawMorph]. By default, it simply calls [Canvas.drawPath].
+     */
+    internal fun draw(canvas: Canvas, paint: Paint) {
+        canvas.drawPath(path, paint)
+    }
+}
+
+/**
+ * Extension function which draws the given [Morph] object into this [Canvas]. Rendering
+ * occurs by drawing the underlying path for the object; callers can optionally retrieve the
+ * path and draw it directly via [Morph.asPath] (though that function copies the underlying
+ * path. This extension function avoids that overhead when rendering).
+ *
+ * @param morph The object to be drawn
+ * @param paint The attributes
+ */
+fun Canvas.drawMorph(morph: Morph, paint: Paint) {
+    morph.draw(this, paint)
+}
+
+private val LOG_TAG = "Morph"
\ No newline at end of file
diff --git a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/Polygon.kt b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/Polygon.kt
index 5129044..b78b70d 100644
--- a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/Polygon.kt
+++ b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/Polygon.kt
@@ -130,7 +130,7 @@
         perVertexRounding: List<CornerRounding>? = null,
         center: PointF? = null
     ) {
-        this.center = center ?: calculateCenter(vertices)
+        this.center = center?.copy() ?: calculateCenter(vertices)
         setupPolygon(vertices, rounding, perVertexRounding)
     }
 
@@ -181,9 +181,9 @@
         val tempFeatures = mutableListOf<Feature>()
         for (feature in source.features) {
             if (feature is Edge) {
-                tempFeatures.add(Edge(this, feature))
+                tempFeatures.add(Edge(feature))
             } else {
-                tempFeatures.add(Corner(this, feature as Corner))
+                tempFeatures.add(Corner(feature as Corner))
             }
         }
         features = tempFeatures
@@ -263,9 +263,14 @@
                 cornerIndices.add(cubics.size)
                 cubics.add(cubic)
             }
-            // TODO: determine and pass convexity flag
-            tempFeatures.add(Corner(this, cornerIndices, roundedCorners[i].center, vertices[i]))
-            tempFeatures.add(Edge(this, listOf(cubics.size)))
+            // Determine whether corner at this vertex is concave or convex, based on the
+            // relationship of the prev->curr/curr->next vectors
+            val prevVertex = vertices[(i + vertices.size - 1) % vertices.size]
+            val nextVertex = vertices[(i + 1) % vertices.size]
+            val convex = (vertices[i] - prevVertex).clockwise(nextVertex - vertices[i])
+            tempFeatures.add(Corner(cornerIndices, roundedCorners[i].center, vertices[i],
+                    convex))
+            tempFeatures.add(Edge(listOf(cubics.size)))
             cubics.add(Cubic.straightLine(corners[i].last().p3, corners[(i + 1) % n].first().p0))
         }
         features = tempFeatures
@@ -331,6 +336,60 @@
         return PointF(cumulativeX / vertices.size, cumulativeY / vertices.size)
     }
 
+    /**
+     * This class holds information about a corner (rounded or not) or an edge of a given
+     * polygon. The features of a Polygon can be used to manipulate the shape with more context
+     * of what the shape actually is, rather than simply manipulating the raw curves and lines
+     * which describe it.
+     */
+    internal open inner class Feature(protected val cubicIndices: List<Int>) {
+        val cubics: List<Cubic>
+            get() = cubicIndices.map { toCubicShape().cubics[it] }
+
+        open fun transform(matrix: Matrix) {}
+    }
+    /**
+     * Edges have only a list of the cubic curves which make up the edge. Edges lie between
+     * corners and have no vertex or concavity; the curves are simply straight lines (represented
+     * by Cubic curves).
+     */
+    internal inner class Edge(indices: List<Int>) : Feature(indices) {
+        constructor(source: Edge) : this(source.cubicIndices)
+    }
+
+    /**
+     * Corners contain the list of cubic curves which describe how the corner is rounded (or
+     * not), plus the vertex at the corner (which the cubics may or may not pass through, depending
+     * on whether the corner is rounded) and a flag indicating whether the corner is convex.
+     * A regular polygon has all convex corners, while a star polygon generally (but not
+     * necessarily) has both convex (outer) and concave (inner) corners.
+     */
+    internal inner class Corner(
+        cubicIndices: List<Int>,
+        // TODO: parameters here should be immutable
+        val vertex: PointF,
+        val roundedCenter: PointF,
+        val convex: Boolean = true
+    ) : Feature(cubicIndices) {
+        constructor(source: Corner) : this(
+            source.cubicIndices,
+            source.vertex,
+            source.roundedCenter,
+            source.convex
+        )
+
+        override fun transform(matrix: Matrix) {
+            val tempPoints = floatArrayOf(vertex.x, vertex.y, roundedCenter.x, roundedCenter.y)
+            matrix.mapPoints(tempPoints)
+            vertex.set(tempPoints[0], tempPoints[1])
+            roundedCenter.set(tempPoints[2], tempPoints[3])
+        }
+
+        override fun toString(): String {
+            return "Corner: vtx, center, convex = $vertex, $roundedCenter, $convex"
+        }
+    }
+
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is Polygon) return false
@@ -418,66 +477,6 @@
 }
 
 /**
- * This class holds information about a corner (rounded or not) or an edge of a given
- * polygon. The features of a Polygon can be used to manipulate the shape with more context
- * of what the shape actually is, rather than simply manipulating the raw curves and lines
- * which describe it.
- */
-internal sealed class Feature(private val polygon: Polygon, protected val cubicIndices: List<Int>) {
-    val cubics: MutableList<Cubic>
-        get() {
-            val featureCubics = mutableListOf<Cubic>()
-            val cubics = polygon.toCubicShape().cubics
-            for (index in cubicIndices) {
-                featureCubics.add(cubics[index])
-            }
-            return featureCubics
-        }
-
-    open fun transform(matrix: Matrix) {}
-}
-
-/**
- * Edges have only a list of the cubic curves which make up the edge. Edges lie between
- * corners and have no vertex or concavity; the curves are simply straight lines (represented
- * by Cubic curves).
- */
-internal class Edge(polygon: Polygon, indices: List<Int>) : Feature(polygon, indices) {
-    constructor(polygon: Polygon, source: Edge) : this(polygon, source.cubicIndices)
-}
-
-/**
- * Corners contain the list of cubic curves which describe how the corner is rounded (or
- * not), plus the vertex at the corner (which the cubics may or may not pass through, depending
- * on whether the corner is rounded) and a flag indicating whether the corner is convex. A regular
- * polygon has all convex corners, while a star polygon generally (but not necessarily) has both
- * convex (outer) and concave (inner) corners.
- */
-internal class Corner(
-    polygon: Polygon,
-    cubicIndices: List<Int>,
-    // TODO: parameters here should be immutable
-    val vertex: PointF,
-    val roundedCenter: PointF,
-    val convex: Boolean = true
-) : Feature(polygon, cubicIndices) {
-    constructor(polygon: Polygon, source: Corner) : this(
-        polygon,
-        source.cubicIndices,
-        source.vertex,
-        source.roundedCenter,
-        source.convex
-    )
-
-    override fun transform(matrix: Matrix) {
-        val tempPoints = floatArrayOf(vertex.x, vertex.y, roundedCenter.x, roundedCenter.y)
-        matrix.mapPoints(tempPoints)
-        vertex.set(tempPoints[0], tempPoints[1])
-        roundedCenter.set(tempPoints[2], tempPoints[3])
-    }
-}
-
-/**
  * Private utility class that holds the information about each corner in a polygon. The shape
  * of the corner can be returned by calling the [getCubics] function, which will return a list
  * of curves representing the corner geometry. The shape of the corner depends on the [rounding]
@@ -665,4 +664,6 @@
     polygon.draw(this, paint)
 }
 
-private val scratchTransformPoint = floatArrayOf(0f, 0f)
\ No newline at end of file
+private val scratchTransformPoint = floatArrayOf(0f, 0f)
+
+private val LOG_TAG = "Polygon"
diff --git a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/PolygonMeasure.kt b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/PolygonMeasure.kt
new file mode 100644
index 0000000..70b8ae1f
--- /dev/null
+++ b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/PolygonMeasure.kt
@@ -0,0 +1,324 @@
+/*
+ * 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.graphics.shapes
+
+import android.graphics.PointF
+import androidx.annotation.FloatRange
+import androidx.core.graphics.minus
+import kotlin.math.abs
+
+internal class MeasuredPolygon : AbstractList<MeasuredPolygon.MeasuredCubic> {
+    private val measurer: Measurer
+    private val cubics: List<MeasuredCubic>
+    val features: List<Pair<Float, Polygon.Feature>>
+
+    private constructor(
+        measurer: Measurer,
+        features: List<Pair<Float, Polygon.Feature>>,
+        cubics: List<Cubic>,
+        outlineProgress: List<Float>
+    ) {
+        require(outlineProgress.size == cubics.size + 1)
+        require(outlineProgress.first() == 0f)
+        require(outlineProgress.last() == 1f)
+        this.measurer = measurer
+        this.features = features
+
+        val measuredCubics = mutableListOf<MeasuredCubic>()
+        var startOutlineProgress = 0f
+        cubics.forEachIndexed { index, cubic ->
+            // Filter out "empty" cubics
+            if ((outlineProgress[index + 1] - outlineProgress[index]) > DistanceEpsilon) {
+                measuredCubics.add(MeasuredCubic(
+                    cubic,
+                    startOutlineProgress,
+                    outlineProgress[index + 1]
+                ))
+                // The next measured cubic will start exactly where this one ends.
+                startOutlineProgress = outlineProgress[index + 1]
+            }
+        }
+        // We could have removed empty cubics at the end. Ensure the last measured cubic ends at 1f
+        measuredCubics[measuredCubics.lastIndex].updateProgressRange(endOutlineProgress = 1f)
+        this.cubics = measuredCubics
+    }
+
+    /**
+     * A MeasuredCubic holds information about the cubic itself, the feature (if any) associated
+     * with it, and the outline progress values (start and end) for the cubic. This information is
+     * used to match cubics between shapes that lie at similar outline progress positions along
+     * their respective shapes (after matching features and shifting).
+     *
+     * Outline progress is a value in [0..1) that represents the distance traveled along the overall
+     * outline path of the shape.
+     */
+    internal inner class MeasuredCubic(
+        val cubic: Cubic,
+        @FloatRange(from = 0.0, to = 1.0) startOutlineProgress: Float,
+        @FloatRange(from = 0.0, to = 1.0) endOutlineProgress: Float,
+    ) {
+        init {
+            require(endOutlineProgress >= startOutlineProgress)
+        }
+
+        val measuredSize = measurer.measureCubic(cubic)
+
+        var startOutlineProgress = startOutlineProgress
+            private set
+
+        var endOutlineProgress = endOutlineProgress
+            private set
+
+        fun updateProgressRange(
+            startOutlineProgress: Float = this.startOutlineProgress,
+            endOutlineProgress: Float = this.endOutlineProgress
+        ) {
+            require(endOutlineProgress >= startOutlineProgress)
+            this.startOutlineProgress = startOutlineProgress
+            this.endOutlineProgress = endOutlineProgress
+        }
+
+        // Modify both startProgress and endProgress by the given delta.
+        // Is the caller's responsibility to keep it in the [0..1] range
+        internal fun shift(delta: Float) {
+            startOutlineProgress += delta
+            endOutlineProgress += delta
+        }
+
+        /**
+         * Cut this MeasuredCubic into two MeasuredCubics at the given outline progress value.
+         */
+        fun cutAtProgress(cutOutlineProgress: Float): Pair<MeasuredCubic, MeasuredCubic> {
+            val outlineProgressSize = endOutlineProgress - startOutlineProgress
+            val progressFromStart = positiveModule(cutOutlineProgress - startOutlineProgress, 1f)
+            // progressFromStart should be in the [0 .. outlineProgressSize] range.
+            // If it's not, cap to that range.
+            val mid = if (progressFromStart > (1 + outlineProgressSize) / 2)
+                0f
+            else
+                progressFromStart.coerceAtMost(outlineProgressSize)
+
+            // Note that in earlier parts of the computation, we have empty MeasuredCubics (cubics
+            // with progressSize == 0f), but those cubics are filtered out before this method is
+            // called.
+            val relativeMidProgress = mid / outlineProgressSize
+            val t = measurer.findCubicCutPoint(cubic, relativeMidProgress * measuredSize)
+            require(t in 0f..1f)
+
+            debugLog(LOG_TAG) {
+                "cutAtProgress: progress = $cutOutlineProgress / " +
+                    "this = [$startOutlineProgress .. $endOutlineProgress] / " +
+                    "pp = $mid / rp = $relativeMidProgress / t = $t"
+            }
+
+            // c1/c2 are the two new cubics, then we return MeasuredCubics created from them
+            val (c1, c2) = cubic.split(t)
+            return MeasuredCubic(c1, startOutlineProgress, cutOutlineProgress) to
+                MeasuredCubic(c2, cutOutlineProgress, endOutlineProgress)
+        }
+
+        fun isNotEmpty() = measuredSize > DistanceEpsilon
+
+        override fun toString(): String {
+            return "MeasuredCubic(outlineProgress=" +
+                "[$startOutlineProgress .. $endOutlineProgress], " +
+                "size=$measuredSize, cubic=$cubic)"
+        }
+    }
+
+    /**
+     * Finds the point in the input list of measured cubics that pass the given outline progress,
+     * and generates a new MeasuredPolygon (equivalent to this), that starts at that
+     * point.
+     * This usually means cutting the cubic that crosses the outline progress (unless the cut is
+     * at one of its ends)
+     * For example, given outline progress 0.4f and measured cubics on these outline progress
+     * ranges:
+     *
+     * c1 [0 -> 0.2]
+     * c2 [0.2 -> 0.5]
+     * c3 [0.5 -> 1.0]
+     *
+     * c2 will be cut in two, at the given outline progress, we can name these c2a [0.2 -> 0.4] and
+     * c2b [0.4 -> 0.5]
+     *
+     * The return then will have measured cubics [c2b, c3, c1, c2a], and they will have their
+     * outline progress ranges adjusted so the new list starts at 0.
+     * c2b [0 -> 0.1]
+     * c3 [0.1 -> 0.6]
+     * c1 [0.6 -> 0.8]
+     * c2a [0.8 -> 1.0]
+     */
+    fun cutAndShift(
+        cuttingPoint: Float
+    ): MeasuredPolygon {
+        require(cuttingPoint in 0f..1f)
+        val n = cubics.size
+        // Find the index of cubic we want to cut
+        val targetIndex = cubics.indexOfFirst {
+            cuttingPoint in it.startOutlineProgress..it.endOutlineProgress
+        }
+        val target = cubics[targetIndex]
+        if (DEBUG) {
+            cubics.forEachIndexed { index, cubic ->
+                debugLog(LOG_TAG) { "cut&Shift | cubic #$index : $cubic " }
+            }
+            debugLog(LOG_TAG) {
+                "cut&Shift, cuttingPoint = $cuttingPoint, target = ($targetIndex) $target"
+            }
+        }
+        // Cut the target cubic.
+        // b1, b2 are two resulting cubics after cut
+        val (b1, b2) = target.cutAtProgress(cuttingPoint)
+        debugLog(LOG_TAG) { "Split | $target -> $b1 & $b2" }
+
+        // Construct the list of the cubics we need:
+        // * The second part of the target cubic (after the cut)
+        // * All cubics after the target, until the end + All cubics from the start, before the
+        //   target cubic
+        // * The first part of the target cubic (before the cut)
+        val retCubics = mutableListOf(b2.cubic)
+        for (i in 1 until n) {
+            retCubics.add(cubics[(i + targetIndex) % cubics.size].cubic)
+        }
+        retCubics.add(b1.cubic)
+
+        // Construct the array of outline progress.
+        // For example, if we have 3 cubics with outline progress [0 .. 0.3], [0.3 .. 0.8] &
+        // [0.8 .. 1.0], and we cut + shift at 0.6:
+        // 0.  0123456789
+        //     |--|--/-|-|
+        // The outline progresses will start at 0 (the cutting point, that shifs to 0.0),
+        // then 0.8 - 0.6 = 0.2, then 1 - 0.6 = 0.4, then 0.3 - 0.6 + 1 = 0.7,
+        // then 1 (the cutting point again),
+        // all together: (0.0, 0.2, 0.4, 0.7, 1.0)
+        val retOutlineProgress = Array(cubics.size + 2) { index ->
+            when (index) {
+                0 -> 0f
+                cubics.size + 1 -> 1f
+                else -> {
+                    val cubicIndex = (targetIndex + index - 1) % cubics.size
+                    positiveModule(cubics[cubicIndex].endOutlineProgress - cuttingPoint, 1f)
+                }
+            }
+        }.asList()
+
+        // Shift the feature's outline progress too.
+        val newFeatures = features.map { (outlineProgress, feature) ->
+            positiveModule(outlineProgress - cuttingPoint, 1f) to feature
+        }
+
+        // Filter out all empty cubics (i.e. start and end anchor are (almost) the same point.)
+        return MeasuredPolygon(measurer, newFeatures, retCubics, retOutlineProgress)
+    }
+
+    // Implementation of AbstractList.
+    override val size
+        get() = cubics.size
+
+    override fun get(index: Int) = cubics[index]
+
+    companion object {
+        internal fun measurePolygon(measurer: Measurer, polygon: Polygon): MeasuredPolygon {
+            val cubics = mutableListOf<Cubic>()
+            val featureToCubic = mutableListOf<Pair<Polygon.Feature, Int>>()
+
+            // Get the cubics from the polygon, at the same time, extract the features and keep a
+            // reference to the representative cubic we will use.
+            polygon.features.forEach { feature ->
+                feature.cubics.forEachIndexed { index, cubic ->
+                    if (feature is Polygon.Corner &&
+                        index == feature.cubics.size / 2) {
+                        featureToCubic.add(feature to cubics.size)
+                    }
+                    cubics.add(cubic)
+                }
+            }
+            val measures = cubics.scan(0f) { measure, cubic ->
+                measure + measurer.measureCubic(cubic).also { require(it >= 0f) }
+            }
+            val totalMeasure = measures.last()
+            val outlineProgress = measures.map { it / totalMeasure }
+
+            debugLog(LOG_TAG) { "Total size: $totalMeasure" }
+
+            val features = featureToCubic.map { featureAndIndex ->
+                val ix = featureAndIndex.second
+                (outlineProgress[ix] + outlineProgress[ix + 1]) / 2 to
+                    featureAndIndex.first
+            }
+
+            return MeasuredPolygon(measurer, features, cubics, outlineProgress)
+        }
+    }
+}
+
+// TODO: make this and the measurers public.
+/**
+ * Interface for measuring a cubic. Implementations can use whatever algorithm desired to produce
+ * these measurement values.
+ */
+internal interface Measurer {
+
+    /**
+     * Returns size of given cubic, according to however the implementation wants to measure
+     * the size (angle, length, etc). It has to be greater or equal to 0.
+     */
+    fun measureCubic(c: Cubic): Float
+
+    /**
+     * Given a cubic and a measure that should be between 0 and the value returned by measureCubic
+     * (If not, it will be capped), finds the parameter t of the cubic at which that measure is
+     * reached.
+     */
+    fun findCubicCutPoint(c: Cubic, m: Float): Float
+}
+
+/**
+ * This measurer uses the angle of each cubic around the shape. This works well for current
+ * Polygon shapes, but there are important assumptions which will break down for more general
+ * shapes:
+ * 1) Curves along the shape outline proceed in order; there is no reverse or self-intersecting
+ * allowed. This guarantees that angle measurements are unique for every curve.
+ * 2) There is a given 'center' for a shape. If the geometry is more arbitrary, there may be
+ * no concept of a center, or the angles computed for an arbitrary center point might not be
+ * consistent enough across the curves to work for general measurement.
+ */
+internal class AngleMeasurer(val center: PointF) : Measurer {
+    /**
+     * The measurement for a given cubic is the difference in angles between the start
+     * and end points (first and last anchors) of the cubic.
+     */
+    override fun measureCubic(c: Cubic) =
+        positiveModule(
+            (c.p3 - center).angle() - (c.p0 - center).angle(),
+            TwoPi
+        ).let {
+            // Avoid an empty cubic to measure almost TwoPi
+            if (it > TwoPi - DistanceEpsilon) 0f else it
+        }
+
+    override fun findCubicCutPoint(c: Cubic, m: Float): Float {
+        val angle0 = (c.p0 - center).angle()
+        // TODO: use binary search.
+        return findMinimum(0f, 1f, tolerance = 1e-5f) { t ->
+            abs(positiveModule((c.pointOnCurve(t) - center).angle() - angle0, TwoPi) - m)
+        }
+    }
+}
+
+private val LOG_TAG = "PolygonMeasure"
\ No newline at end of file
diff --git a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/Utils.kt b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/Utils.kt
index 5e0215a..a486fc0 100644
--- a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/Utils.kt
+++ b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/Utils.kt
@@ -19,6 +19,7 @@
 package androidx.graphics.shapes
 
 import android.graphics.PointF
+import android.util.Log
 import androidx.core.graphics.div
 import androidx.core.graphics.plus
 import androidx.core.graphics.times
@@ -55,6 +56,13 @@
 internal fun PointF.dotProduct(other: PointF) = x * other.x + y * other.y
 
 /**
+ * Compute the Z coordinate of the cross product of two vectors, to check if the second vector is
+ * going clockwise ( > 0 ) or counterclockwise (< 0) compared with the first one.
+ * It could also be 0, if the vectors are co-linear.
+ */
+internal fun PointF.clockwise(other: PointF) = x * other.y - y * other.x > 0
+
+/**
  * Returns unit vector representing the direction to this point from (0, 0)
  */
 internal fun PointF.getDirection() = run {
@@ -63,9 +71,11 @@
     this / d
 }
 
-// These epsilon values are used internally to determine when two points are the same, within
-// some reasonable roundoff error. The distance episilon is smaller, with the intention that the
-// roundoff should not be larger than a pixel on any reasonable sized display.
+/**
+ * These epsilon values are used internally to determine when two points are the same, within
+ * some reasonable roundoff error. The distance epsilon is smaller, with the intention that the
+ * roundoff should not be larger than a pixel on any reasonable sized display.
+ */
 internal const val DistanceEpsilon = 1e-4f
 internal const val AngleEpsilon = 1e-6f
 
@@ -92,3 +102,41 @@
 
 internal fun radialToCartesian(radius: Float, angleRadians: Float, center: PointF = Zero) =
     directionVector(angleRadians) * radius + center
+
+internal fun <T> Iterable<T>.sumOf(f: (T) -> Float) = map(f).sum()
+
+internal fun positiveModule(num: Float, mod: Float) = (num % mod + mod) % mod
+
+/*
+ * Does a ternary search in [v0..v1] to find the parameter that minimizes the given function.
+ * Stops when the search space size is reduced below the given tolerance.
+ *
+ * NTS: Does it make sense to split the function f in 2, one to generate a candidate, of a custom
+ * type T (i.e. (Float) -> T), and one to evaluate it ( (T) -> Float )?
+ */
+internal fun findMinimum(
+    v0: Float,
+    v1: Float,
+    tolerance: Float = 1e-3f,
+    f: (Float) -> Float
+): Float {
+    var a = v0
+    var b = v1
+    while (b - a > tolerance) {
+        val c1 = (2 * a + b) / 3
+        val c2 = (2 * b + a) / 3
+        if (f(c1) < f(c2)) {
+            b = c2
+        } else {
+            a = c1
+        }
+    }
+    return (a + b) / 2
+}
+
+// Used to enable debug logging in the library
+internal val DEBUG = true
+
+internal inline fun debugLog(tag: String, messageFactory: () -> String) {
+    if (DEBUG) messageFactory().split("\n").forEach { Log.d(tag, it) }
+}
\ No newline at end of file
diff --git a/graphics/integration-tests/testapp-compose/build.gradle b/graphics/integration-tests/testapp-compose/build.gradle
new file mode 100644
index 0000000..97fce58
--- /dev/null
+++ b/graphics/integration-tests/testapp-compose/build.gradle
@@ -0,0 +1,47 @@
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+    id("AndroidXPlugin")
+    id("AndroidXComposePlugin")
+    id("com.android.application")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    api(libs.kotlinStdlib)
+
+    implementation(project(":graphics:graphics-shapes"))
+
+    implementation("androidx.activity:activity-compose:1.3.1")
+    implementation("androidx.appcompat:appcompat:1.5.1")
+    implementation("androidx.compose.foundation:foundation:1.3.1")
+    implementation("androidx.compose.foundation:foundation-layout:1.3.1")
+    implementation("androidx.compose.material:material:1.3.1")
+    implementation("androidx.compose.runtime:runtime:1.3.3")
+    implementation("androidx.compose.ui:ui:1.3.3")
+    implementation("androidx.core:core-ktx:1.9.0")
+}
+
+android {
+    namespace "androidx.graphics.shapes.testcompose"
+
+    defaultConfig {
+        minSdkVersion 21
+    }
+}
diff --git a/graphics/integration-tests/testapp-compose/src/main/AndroidManifest.xml b/graphics/integration-tests/testapp-compose/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..91ec203
--- /dev/null
+++ b/graphics/integration-tests/testapp-compose/src/main/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <application>
+        <activity android:name=".MainActivity"
+            android:label="Graphics Shapes Test"
+            android:exported="true"
+            android:theme="@android:style/Theme.Material.Light.NoActionBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+    </application>
+
+</manifest>
diff --git a/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/DebugDraw.kt b/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/DebugDraw.kt
new file mode 100644
index 0000000..95bd121
--- /dev/null
+++ b/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/DebugDraw.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.graphics.shapes.testcompose
+
+import android.graphics.Path
+import android.graphics.PointF
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.asComposePath
+import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.graphics.shapes.Cubic
+import androidx.graphics.shapes.CubicShape
+import androidx.graphics.shapes.Morph
+
+internal fun DrawScope.debugDraw(morph: Morph) = debugDraw(morph.asCubics(), morph.asPath())
+
+internal fun DrawScope.debugDraw(cubicShape: CubicShape) =
+    debugDraw(cubicShape.cubics, cubicShape.toPath())
+
+internal fun DrawScope.debugDraw(cubics: List<Cubic>, path: Path) {
+    drawPath(path.asComposePath(), Color.Green, style = Stroke(2f))
+
+    for (bezier in cubics) {
+        // Draw red circles for start and end.
+        drawCircle(bezier.p0, 6f, Color.Red, strokeWidth = 2f)
+        drawCircle(bezier.p3, 8f, Color.Magenta, strokeWidth = 2f)
+        // Draw a circle for the first control point, and a line from start to it.
+        // The curve will start in this direction
+
+        drawLine(bezier.p0, bezier.p1, Color.Yellow, strokeWidth = 0f)
+        drawCircle(bezier.p1, 4f, Color.Yellow, strokeWidth = 2f)
+        // Draw a circle for the second control point, and a line from it to the end.
+        // The curve will end in this direction
+        drawLine(bezier.p2, bezier.p3, Color.Yellow, strokeWidth = 0f)
+        drawCircle(bezier.p2, 4f, Color.Yellow, strokeWidth = 2f)
+    }
+}
+
+/**
+ * Utility extension functions to bridge OffsetF as points to Compose's Offsets.
+ */
+private fun PointF.asOffset() = Offset(x, y)
+
+private fun DrawScope.drawCircle(
+    center: PointF,
+    radius: Float,
+    color: Color,
+    strokeWidth: Float = 2f
+) {
+    drawCircle(color, radius, center.asOffset(), style = Stroke(strokeWidth))
+}
+private fun DrawScope.drawLine(start: PointF, end: PointF, color: Color, strokeWidth: Float = 2f) {
+    drawLine(color, start.asOffset(), end.asOffset(), strokeWidth = strokeWidth)
+}
diff --git a/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/MainActivity.kt b/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/MainActivity.kt
new file mode 100644
index 0000000..be34136
--- /dev/null
+++ b/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/MainActivity.kt
@@ -0,0 +1,402 @@
+/*
+ * 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.graphics.shapes.testcompose
+
+import android.graphics.Matrix
+import android.graphics.PointF
+import android.graphics.RectF
+import android.os.Bundle
+import androidx.activity.compose.setContent
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.Button
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Slider
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.asComposePath
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+import androidx.compose.ui.unit.dp
+import androidx.fragment.app.FragmentActivity
+import androidx.graphics.shapes.Morph
+import androidx.graphics.shapes.Polygon
+import kotlin.math.abs
+import kotlin.math.min
+import kotlinx.coroutines.launch
+
+@Composable
+fun PolygonComposable(polygon: Polygon, modifier: Modifier = Modifier) =
+    PolygonComposableImpl(polygon, modifier)
+
+@Composable
+private fun MorphComposable(
+    sizedMorph: SizedMorph,
+    progress: () -> Float,
+    modifier: Modifier = Modifier,
+    isDebug: Boolean = false
+) =
+    MorphComposableImpl(sizedMorph, modifier, isDebug) {
+        sizedMorph.morph.progress = progress()
+    }
+
+internal fun calculateMatrix(bounds: RectF, width: Float, height: Float): Matrix {
+    val originalWidth = bounds.right - bounds.left
+    val originalHeight = bounds.bottom - bounds.top
+    val scale = min(width / originalWidth, height / originalHeight)
+    val newLeft = bounds.left - (width / scale - originalWidth) / 2
+    val newTop = bounds.top - (height / scale - originalHeight) / 2
+    val matrix = Matrix()
+    matrix.setTranslate(-newLeft, -newTop)
+    matrix.postScale(scale, scale)
+    return matrix
+}
+
+internal fun PointF.transform(
+    matrix: Matrix,
+    dst: PointF = PointF(),
+    floatArray: FloatArray = FloatArray(2)
+): PointF {
+    floatArray[0] = x
+    floatArray[1] = y
+    matrix.mapPoints(floatArray)
+    dst.x = floatArray[0]
+    dst.y = floatArray[1]
+    return dst
+}
+
+private val TheBounds = RectF(0f, 0f, 1f, 1f)
+
+private class SizedMorph(val morph: Morph) {
+    var width = 1f
+    var height = 1f
+
+    fun resizeMaybe(newWidth: Float, newHeight: Float) {
+        if (abs(width - newWidth) > 1e-4 || abs(height - newHeight) > 1e-4) {
+            val matrix = calculateMatrix(RectF(0f, 0f, width, height), newWidth, newHeight)
+            morph.transform(matrix)
+            width = newWidth
+            height = newHeight
+        }
+    }
+}
+
+@Composable
+private fun MorphComposableImpl(
+    sizedMorph: SizedMorph,
+    modifier: Modifier = Modifier,
+    isDebug: Boolean = false,
+    prep: ContentDrawScope.() -> Unit
+) {
+    Box(
+        modifier
+            .fillMaxSize()
+            .drawWithContent {
+                prep()
+                drawContent()
+                sizedMorph.resizeMaybe(size.width, size.height)
+                if (isDebug) {
+                    debugDraw(sizedMorph.morph)
+                } else {
+                    drawPath(sizedMorph.morph.asPath().asComposePath(), Color.White)
+                }
+            })
+}
+
+@Composable
+internal fun PolygonComposableImpl(
+    shape: Polygon,
+    modifier: Modifier = Modifier,
+    debug: Boolean = false
+) {
+    val sizedPolygonCache = remember(shape) {
+        mutableMapOf<Size, Polygon>()
+    }
+    Box(
+        modifier
+            .fillMaxSize()
+            .drawWithContent {
+                drawContent()
+                val sizedPolygon = sizedPolygonCache.getOrPut(size) {
+                    val matrix = calculateMatrix(TheBounds, size.width, size.height)
+                    Polygon(shape).apply { transform(matrix) }
+                }
+                if (debug) {
+                    debugDraw(sizedPolygon.toCubicShape())
+                } else {
+                    drawPath(sizedPolygon.toPath().asComposePath(), Color.White)
+                }
+            })
+}
+
+@Composable
+fun MainScreen() {
+    var editing by remember { mutableStateOf<ShapeParameters?>(null) }
+    var selectedShape = remember { mutableStateOf(0) }
+    val shapes = remember {
+        listOf(
+            // LINE 1
+            // Circle
+            ShapeParameters(
+                sides = 4,
+                roundness = 1f,
+                shapeId = ShapeParameters.ShapeId.Polygon
+            ),
+            //
+            ShapeParameters(
+                sides = 12,
+                innerRadiusRatio = .928f,
+                roundness = .1f,
+                shapeId = ShapeParameters.ShapeId.Star
+            ),
+            // Clover
+            ShapeParameters(
+                sides = 4,
+                innerRadiusRatio = .352f,
+                roundness = .32f,
+                rotation = 45f,
+                shapeId = ShapeParameters.ShapeId.Star
+            ),
+            // Alice
+            ShapeParameters(
+                innerRadiusRatio = 0.1f,
+                roundness = 0.22f,
+                shapeId = ShapeParameters.ShapeId.Triangle
+            ),
+            // Wiggle Star
+            ShapeParameters(
+                sides = 8,
+                innerRadiusRatio = .784f,
+                roundness = .16f,
+                shapeId = ShapeParameters.ShapeId.Star
+            ),
+
+            // LINE 2
+            // Wovel
+            ShapeParameters(
+                sides = 15,
+                innerRadiusRatio = .892f,
+                roundness = 1f,
+                shapeId = ShapeParameters.ShapeId.Star
+            ),
+            // BlobR
+            ShapeParameters(
+                innerRadiusRatio = .19f,
+                roundness = 0.86f,
+                rotation = -45f,
+                shapeId = ShapeParameters.ShapeId.Blob
+            ),
+            // BlobL
+            ShapeParameters(
+                innerRadiusRatio = .19f,
+                roundness = 0.86f,
+                rotation = 45f,
+                shapeId = ShapeParameters.ShapeId.Blob
+            ),
+            // Scalop
+            ShapeParameters(
+                sides = 12,
+                innerRadiusRatio = .928f,
+                roundness = .928f,
+                shapeId = ShapeParameters.ShapeId.Star
+            ),
+            // More
+            ShapeParameters(
+                sides = 3,
+                roundness = .2f,
+                rotation = 30f,
+                shapeId = ShapeParameters.ShapeId.Polygon
+            ),
+
+            // LINE 3
+            // CornerSE
+            ShapeParameters(roundness = .4f, shapeId = ShapeParameters.ShapeId.CornerSE),
+
+            // Non - material shapes:
+            // Square
+            ShapeParameters(
+                sides = 4,
+                rotation = 45f,
+                shapeId = ShapeParameters.ShapeId.Polygon
+            ),
+
+            // Pentagon
+            ShapeParameters(
+                sides = 5,
+                rotation = -360f / 20f,
+                shapeId = ShapeParameters.ShapeId.Polygon
+            ),
+
+            // 5-Sided Star
+            ShapeParameters(
+                sides = 5,
+                rotation = -360f / 20,
+                innerRadiusRatio = .3f,
+                shapeId = ShapeParameters.ShapeId.Star
+            ),
+
+            // 8-Sided Star
+            ShapeParameters(
+                sides = 8,
+                innerRadiusRatio = .6f,
+                shapeId = ShapeParameters.ShapeId.Star
+            )
+        )
+    }
+    editing?.let {
+        ShapeEditor(it) { editing = null }
+    } ?: MorphScreen(shapes, selectedShape) { editing = shapes[selectedShape.value] }
+}
+
+@Composable
+fun MorphScreen(
+    shapeParams: List<ShapeParameters>,
+    selectedShape: MutableState<Int>,
+    onEditClicked: () -> Unit
+) {
+    val shapes = remember {
+        shapeParams.map { sp ->
+            sp.genShape().also { poly ->
+                val matrix = calculateMatrix(poly.bounds, 1f, 1f)
+                poly.transform(matrix)
+            }
+        }
+    }
+
+    var currShape by remember { mutableStateOf(selectedShape.value) }
+    val progress = remember { Animatable(0f) }
+
+    var debug by remember { mutableStateOf(false) }
+
+    val morphed by remember {
+        derivedStateOf {
+            // NOTE: We need to access this variable to ensure we recalculate the morph !
+            debugLog("Re-computing morph / $debug")
+            SizedMorph(
+                Morph(
+                    shapes[currShape],
+                    shapes[selectedShape.value]
+                )
+            )
+        }
+    }
+
+    val scope = rememberCoroutineScope()
+    val clickFn: (Int) -> Unit = remember {
+            { shapeIx ->
+            scope.launch {
+                currShape = selectedShape.value
+                selectedShape.value = shapeIx
+                doAnimation(progress)
+            }
+        }
+    }
+    Column(
+        Modifier
+            .fillMaxSize()
+            .background(Color.Black)
+    ) {
+        repeat(3) { rowIx ->
+            Row(Modifier.fillMaxWidth()) {
+                repeat(5) { columnIx ->
+                    val shapeIx = rowIx * 5 + columnIx
+                    val borderAlpha = (
+                        (if (shapeIx == selectedShape.value) progress.value else 0f) +
+                        (if (shapeIx == currShape) 1 - progress.value else 0f)
+                    ).coerceIn(0f, 1f)
+                    Box(
+                        Modifier
+                            .weight(1f)
+                            .aspectRatio(1f)
+                            .padding(horizontal = 5.dp)
+                            .border(
+                                3.dp,
+                                Color.Red.copy(alpha = borderAlpha)
+                            )
+                    ) {
+                        // draw shape
+                        val shape = shapes[shapeIx]
+                        PolygonComposable(shape, Modifier.clickable { clickFn(shapeIx) })
+                    }
+                }
+            }
+        }
+        Row(horizontalArrangement = Arrangement.spacedBy(5.dp)) {
+            Button(onClick = { debug = !debug }) {
+                Text(if (debug) "Debug" else "Shape")
+            }
+            Button(onClick = onEditClicked) {
+                Text("Edit")
+            }
+        }
+        Slider(value = progress.value.coerceIn(0f, 1f), onValueChange = {
+            scope.launch { progress.snapTo(it) }
+        })
+        MorphComposable(morphed, { progress.value },
+            Modifier
+                .fillMaxSize()
+                .clickable(
+                    indication = null, // Eliminate the ripple effect.
+                    interactionSource = remember { MutableInteractionSource() }
+                ) {
+                    scope.launch { doAnimation(progress) }
+                }, debug)
+    }
+}
+
+private suspend fun doAnimation(progress: Animatable<Float, AnimationVector1D>) {
+    progress.snapTo(0f)
+    progress.animateTo(
+        1f,
+        animationSpec =
+             spring(0.6f, 50f)
+    )
+}
+
+class MainActivity : FragmentActivity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContent(parent = null) {
+            MaterialTheme {
+                MainScreen()
+            }
+        }
+    }
+}
diff --git a/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/PanZoomRotate.kt b/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/PanZoomRotate.kt
new file mode 100644
index 0000000..5b8e256
--- /dev/null
+++ b/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/PanZoomRotate.kt
@@ -0,0 +1,111 @@
+/*
+ * 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.graphics.shapes.testcompose
+
+import androidx.compose.foundation.gestures.detectTransformGestures
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.material.Button
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.TransformOrigin
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.input.pointer.pointerInput
+import kotlin.math.PI
+import kotlin.math.cos
+import kotlin.math.sin
+
+data class PanZoomRotateModel(
+    var zoom: MutableState<Float> = mutableStateOf(1f),
+    var offset: MutableState<Offset> = mutableStateOf(Offset.Zero),
+    var angle: MutableState<Float> = mutableStateOf(0f)
+) {
+    fun reset() {
+        zoom.value = 1f
+        offset.value = Offset.Zero
+        angle.value = 0f
+    }
+}
+
+// Wrap the content in a box that adds a gesture detector and a transform layer.
+// This lets the content be scaled/rotated/panned.
+// Small note that AFAIK, the center of the gestures on the emulator are at the center of the
+// screen, so for this to work on emulator the center of the screen needs to be inside this
+// component.
+@Composable
+fun PanZoomRotateBox(
+    modifier: Modifier = Modifier,
+    model: PanZoomRotateModel = remember { PanZoomRotateModel() },
+    allowRotation: Boolean = true,
+    allowZoom: Boolean = true,
+    allowPan: Boolean = true,
+    content: @Composable BoxScope.() -> Unit
+) {
+    with(model) {
+        Box(modifier = modifier) {
+            Box(
+                Modifier.pointerInput(Unit) {
+                    detectTransformGestures(
+                        onGesture = { centroid, pan, gestureZoom, gestureRotate ->
+                            val actualRotation = if (allowRotation) gestureRotate else 0f
+                            val oldScale = zoom.value
+                            val newScale = zoom.value * if (allowZoom) gestureZoom else 1f
+
+                            // For natural zooming and rotating, the centroid of the gesture should
+                            // be the fixed point where zooming and rotating occurs.
+                            // We compute where the centroid was (in the pre-transformed coordinate
+                            // space), and then compute where it will be after this delta.
+                            // We then compute what the new offset should be to keep the centroid
+                            // visually stationary for rotating and zooming, and also apply the pan.
+                            offset.value =
+                                (offset.value + centroid / oldScale)
+                                    .rotate(actualRotation.toRadians()) -
+                                    (centroid / newScale +
+                                        (if (allowPan) pan else Offset.Zero) / oldScale)
+                            zoom.value = newScale
+                            angle.value += actualRotation
+                        }
+                    )
+                }
+                .graphicsLayer {
+                    translationX = -offset.value.x * zoom.value
+                    translationY = -offset.value.y * zoom.value
+                    scaleX = zoom.value
+                    scaleY = zoom.value
+                    rotationZ = angle.value
+                    transformOrigin = TransformOrigin(0f, 0f)
+                },
+            content = content)
+            Button(onClick = { model.reset() }) {
+                Text("Reset View")
+            }
+        }
+    }
+}
+
+internal fun Float.toRadians() = this * PI.toFloat() / 180f
+private fun Offset.rotate90() = Offset(-y, x)
+internal fun directionVector(angleRadians: Float) = Offset(cos(angleRadians), sin(angleRadians))
+private fun Offset.rotate(angleRadians: Float): Offset {
+    val vec = directionVector(angleRadians)
+    return vec * x + vec.rotate90() * y
+}
diff --git a/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/ShapeEditor.kt b/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/ShapeEditor.kt
new file mode 100644
index 0000000..e8a85ac
--- /dev/null
+++ b/graphics/integration-tests/testapp-compose/src/main/java/androidx/graphics/shapes/testcompose/ShapeEditor.kt
@@ -0,0 +1,396 @@
+/*
+ * 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.graphics.shapes.testcompose
+
+import android.graphics.Matrix
+import android.graphics.PointF
+import android.util.Log
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+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.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.Button
+import androidx.compose.material.Slider
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.derivedStateOf
+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.draw.clipToBounds
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.core.graphics.plus
+import androidx.graphics.shapes.CornerRounding
+import androidx.graphics.shapes.Polygon
+import androidx.graphics.shapes.RoundedPolygon
+import androidx.graphics.shapes.Star
+import kotlin.math.cos
+import kotlin.math.max
+import kotlin.math.roundToInt
+import kotlin.math.sin
+
+private val LOG_TAG = "ShapeEditor"
+private val DEBUG = false
+
+internal fun debugLog(message: String) {
+    if (DEBUG) Log.d(LOG_TAG, message)
+}
+
+data class ShapeItem(
+    val name: String,
+    val shapegen: () -> Polygon,
+    val debugDump: () -> Unit,
+    val usesSides: Boolean = true,
+    val usesInnerRatio: Boolean = true,
+    val usesRoundness: Boolean = true,
+    val usesInnerParameters: Boolean = true
+)
+
+private val PointZero = PointF(0f, 0f)
+
+class ShapeParameters(
+    sides: Int = 5,
+    innerRadiusRatio: Float = 0.5f,
+    roundness: Float = 0f,
+    smooth: Float = 0f,
+    innerRoundness: Float = roundness,
+    innerSmooth: Float = smooth,
+    rotation: Float = 0f,
+    shapeId: ShapeId = ShapeId.Polygon
+) {
+    internal val sides = mutableStateOf(sides.toFloat())
+    internal val innerRadiusRatio = mutableStateOf(innerRadiusRatio)
+    internal val roundness = mutableStateOf(roundness)
+    internal val smooth = mutableStateOf(smooth)
+    internal val innerRoundness = mutableStateOf(innerRoundness)
+    internal val innerSmooth = mutableStateOf(innerSmooth)
+    internal val rotation = mutableStateOf(rotation)
+
+    internal var shapeIx by mutableStateOf(shapeId.ordinal)
+
+    fun copy() = ShapeParameters(
+        this.sides.value.roundToInt(),
+        this.innerRadiusRatio.value,
+        this.roundness.value,
+        this.smooth.value,
+        this.innerRoundness.value,
+        this.innerSmooth.value,
+        this.rotation.value,
+        ShapeId.values()[this.shapeIx]
+    )
+
+    enum class ShapeId {
+        Star, Polygon, Triangle, Blob, CornerSE
+    }
+
+    private fun radialToCartesian(
+        radius: Float,
+        angleRadians: Float,
+        center: PointF = PointZero
+    ) = directionVectorPointF(angleRadians) * radius + center
+
+    private fun rotationAsString() =
+        if (this.rotation.value != 0f)
+            "rotation = ${this.rotation.value}f, "
+        else
+            ""
+
+    // Primitive shapes we can draw (so far)
+    internal val shapes = listOf(
+        ShapeItem("Star", shapegen = {
+                Star(
+                    numOuterVertices = this.sides.value.roundToInt(),
+                    innerRadiusRatio = this.innerRadiusRatio.value,
+                    rounding = CornerRounding(this.roundness.value, this.smooth.value),
+                    innerRounding = CornerRounding(
+                        this.innerRoundness.value,
+                        this.innerSmooth.value
+                    )
+                )
+            },
+            debugDump = {
+                debugLog(
+                    "ShapeParameters(sides = ${this.sides.value.roundToInt()}, " +
+                        "innerRadiusRatio = ${this.innerRadiusRatio.value}f, " +
+                        "roundness = ${this.roundness.value}f, " +
+                        "smooth = ${this.smooth.value}f, " +
+                        "innerRoundness = ${this.innerRoundness.value}f, " +
+                        "innerSmooth = ${this.innerSmooth.value}f, " +
+                        rotationAsString() +
+                        "shapeId = ShapeParameters.ShapeId.Star)"
+                )
+            }
+        ),
+        ShapeItem("Polygon", shapegen = {
+                RoundedPolygon(
+                    numVertices = this.sides.value.roundToInt(),
+                    rounding = CornerRounding(this.roundness.value, this.smooth.value),
+                )
+            },
+            debugDump = {
+                debugLog(
+                    "ShapeParameters(sides = ${this.sides.value.roundToInt()}, " +
+                        "roundness = ${this.roundness.value}f, " +
+                        "smooth = ${this.smooth.value}f, " +
+                        rotationAsString() +
+                        ")"
+                )
+            }, usesInnerRatio = false, usesInnerParameters = false
+        ),
+        ShapeItem(
+            "Triangle", shapegen = {
+                val points = listOf(
+                    radialToCartesian(1f, 270f.toRadians()),
+                    radialToCartesian(1f, 30f.toRadians()),
+                    radialToCartesian(this.innerRadiusRatio.value, 90f.toRadians()),
+                    radialToCartesian(1f, 150f.toRadians()),
+                )
+                RoundedPolygon(
+                    points,
+                    CornerRounding(this.roundness.value, this.smooth.value),
+                    center = PointZero
+                )
+            },
+            debugDump = {
+                debugLog(
+                    "ShapeParameters(innerRadiusRatio = ${this.innerRadiusRatio.value}f, " +
+                        "smooth = ${this.smooth.value}f, " +
+                        rotationAsString() +
+                        "shapeId = ShapeParameters.ShapeId.Triangle)"
+                )
+            },
+            usesSides = false, usesInnerParameters = false
+        ),
+        ShapeItem(
+            "Blob", shapegen = {
+                val sx = this.innerRadiusRatio.value.coerceAtLeast(0.1f)
+                val sy = this.roundness.value.coerceAtLeast(0.1f)
+                RoundedPolygon(
+                    listOf(
+                        PointF(-sx, -sy),
+                        PointF(sx, -sy),
+                        PointF(sx, sy),
+                        PointF(-sx, sy),
+                    ),
+                    rounding = CornerRounding(this.roundness.value, this.smooth.value),
+                    center = PointZero
+                )
+            },
+            debugDump = {
+                debugLog(
+                    "ShapeParameters(roundness = ${this.roundness.value}f, " +
+                        "smooth = ${this.smooth.value}f, " +
+                        rotationAsString() +
+                        "shapeId = ShapeParameters.ShapeId.Blob)"
+                )
+            },
+            usesSides = false, usesInnerParameters = false
+        ),
+        ShapeItem(
+            "CornerSE", shapegen = {
+                RoundedPolygon(
+                    SquarePoints(),
+                    perVertexRounding = listOf(
+                        CornerRounding(this.roundness.value, this.smooth.value),
+                        CornerRounding(1f),
+                        CornerRounding(1f),
+                        CornerRounding(1f)
+                    ),
+                    center = PointZero
+                )
+            },
+            debugDump = {
+                debugLog(
+                    "ShapeParameters(roundness = ${this.roundness.value}f, " +
+                        "smooth = ${this.smooth.value}f, " +
+                        rotationAsString() +
+                        "shapeId = ShapeParameters.ShapeId.CornerSE)"
+                )
+            },
+            usesSides = false,
+            usesInnerRatio = false,
+            usesInnerParameters = false
+        )
+
+        /*
+        TODO: Add quarty. Needs to be able to specify a rounding radius of up to 2f
+        ShapeItem("Quarty", { DefaultShapes.quarty(roundness.value, smooth.value) },
+        usesSides = false, usesInnerRatio = false),
+        */
+    )
+
+    fun selectedShape() = derivedStateOf { shapes[shapeIx] }
+
+    fun genShape(autoSize: Boolean = true) = selectedShape().value.shapegen().apply {
+        transform(Matrix().apply {
+            if (autoSize) {
+                // Move the center to the origin.
+                center
+                postTranslate(-(bounds.left + bounds.right) / 2, -(bounds.top + bounds.bottom) / 2)
+
+                // Scale to the [-1, 1] range
+                val scale = 2f / max(bounds.width(), bounds.height())
+                postScale(scale, scale)
+            }
+            // Apply the needed rotation
+            postRotate(rotation.value)
+        })
+    }
+}
+
+@Composable
+fun ShapeEditor(params: ShapeParameters, onClose: () -> Unit) {
+    val shapeParams = params.selectedShape().value
+    var debug by remember { mutableStateOf(false) }
+    var autoSize by remember { mutableStateOf(true) }
+
+    Column(
+        Modifier
+            .fillMaxSize()
+            .background(Color.Black)
+    ) {
+        Row(verticalAlignment = Alignment.CenterVertically) {
+            Text("Base Shape:", color = Color.White)
+            Spacer(Modifier.width(10.dp))
+            Button(onClick = { params.shapeIx = (params.shapeIx + 1) % params.shapes.size }) {
+                Text(params.selectedShape().value.name)
+            }
+        }
+        MySlider("Sides", 3f, 20f, 1f, params.sides, shapeParams.usesSides)
+        MySlider(
+            "InnerRadiusRatio",
+            0.1f,
+            0.999f,
+            0f,
+            params.innerRadiusRatio,
+            shapeParams.usesInnerRatio
+        )
+        MySlider("RoundRadius", 0f, 1f, 0f, params.roundness, shapeParams.usesRoundness)
+        MySlider("Smoothing", 0f, 1f, 0f, params.smooth)
+        MySlider(
+            "InnerRoundRadius",
+            0f,
+            1f,
+            0f,
+            params.innerRoundness,
+            shapeParams.usesInnerParameters
+        )
+        MySlider("InnerSmoothing", 0f, 1f, 0f, params.innerSmooth, shapeParams.usesInnerParameters)
+        MySlider("Rotation", 0f, 360f, 45f, params.rotation)
+
+        PanZoomRotateBox(
+            Modifier
+                .clipToBounds()
+                .weight(1f)
+                .border(1.dp, Color.White)
+                .padding(2.dp)
+        ) {
+            PolygonComposableImpl(params.genShape(autoSize = autoSize).also { poly ->
+                if (autoSize) {
+                    val matrix = calculateMatrix(poly.bounds, 1f, 1f)
+                    poly.transform(matrix)
+                }
+            }, debug = debug)
+        }
+        Row {
+            MyTextButton(
+                onClick = onClose,
+                text = "Accept"
+            )
+            // TODO: add cancel!?
+            Spacer(Modifier.weight(1f))
+            MyTextButton(
+                onClick = { debug = !debug },
+                text = if (debug) "Beziers" else "Shape"
+            )
+            Spacer(Modifier.weight(1f))
+            MyTextButton(
+                onClick = { autoSize = !autoSize },
+                text = if (autoSize) "AutoSize" else "NoSizing"
+            )
+            Spacer(Modifier.weight(1f))
+            MyTextButton(
+                onClick = { params.selectedShape().value.debugDump() },
+                text = "Dump to Logcat"
+            )
+        }
+    }
+}
+
+@Composable
+fun MyTextButton(
+    onClick: () -> Unit,
+    text: String,
+    modifier: Modifier = Modifier,
+    // Material defaults are 16 & 8
+    contentPadding: PaddingValues = PaddingValues(horizontal = 10.dp, vertical = 5.dp),
+) = Button(onClick = onClick, modifier = modifier, contentPadding = contentPadding) {
+    Text(text)
+}
+
+@Composable
+fun MySlider(
+    name: String,
+    minValue: Float,
+    maxValue: Float,
+    step: Float,
+    valueHolder: MutableState<Float>,
+    enabled: Boolean = true
+) {
+    Row(Modifier.fillMaxWidth().height(40.dp), verticalAlignment = Alignment.CenterVertically) {
+        Text(name, color = Color.White)
+        Spacer(Modifier.width(10.dp))
+        Slider(
+            value = valueHolder.value,
+            onValueChange = { valueHolder.value = it },
+            valueRange = minValue..maxValue,
+            steps = if (step > maxValue - minValue)
+                ((maxValue - minValue) / step).roundToInt() - 1
+            else
+                0,
+            enabled = enabled
+        )
+    }
+}
+
+// TODO: remove this when it is integrated into Ktx
+operator fun PointF.times(factor: Float): PointF {
+    return PointF(this.x * factor, this.y * factor)
+}
+
+// Create a new list every time, because mutability is complicated.
+private fun SquarePoints() = listOf(
+    PointF(1f, 1f),
+    PointF(-1f, 1f),
+    PointF(-1f, -1f),
+    PointF(1f, -1f)
+)
+
+internal fun directionVectorPointF(angleRadians: Float) =
+    PointF(cos(angleRadians), sin(angleRadians))
diff --git a/health/connect/connect-client-proto/src/main/proto/data.proto b/health/connect/connect-client-proto/src/main/proto/data.proto
index b5e10a0..8dac6c6 100644
--- a/health/connect/connect-client-proto/src/main/proto/data.proto
+++ b/health/connect/connect-client-proto/src/main/proto/data.proto
@@ -56,7 +56,7 @@
   optional int64 end_time_millis = 3;
 }
 
-// Next Id: 22
+// Next Id: 23
 message DataPoint {
   optional DataType data_type = 1;
   map<string, Value> values = 2;
@@ -90,7 +90,9 @@
   optional int32 start_zone_offset_seconds = 19;
   optional int32 end_zone_offset_seconds = 20;
 
-  optional SubTypeDataValue sub_type_data_values = 21;
+  reserved 21; // sub_type_data_values
+
+  map<string, SubTypeDataList> sub_type_data_lists = 22;
 
   message SubTypeDataList {
     repeated SubTypeDataValue values = 1;
diff --git a/health/health-services-client/api/1.0.0-beta03.txt b/health/health-services-client/api/1.0.0-beta03.txt
index 9d0902d..d32c831 100644
--- a/health/health-services-client/api/1.0.0-beta03.txt
+++ b/health/health-services-client/api/1.0.0-beta03.txt
@@ -30,6 +30,7 @@
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? getCurrentExerciseInfo(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.ExerciseInfo>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? markLap(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? overrideAutoPauseAndResumeForActiveExercise(androidx.health.services.client.ExerciseClient, boolean enabled, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
+    method public static suspend Object? overrideBatchingModesForActiveExercise(androidx.health.services.client.ExerciseClient, java.util.Set<androidx.health.services.client.data.BatchingMode> batchingModes, kotlin.coroutines.Continuation<? super java.lang.Void>);
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? pauseExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? prepareExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.WarmUpConfig configuration, kotlin.coroutines.Continuation<? super kotlin.Unit>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? removeGoalFromActiveExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
diff --git a/health/health-services-client/api/current.txt b/health/health-services-client/api/current.txt
index 9d0902d..d32c831 100644
--- a/health/health-services-client/api/current.txt
+++ b/health/health-services-client/api/current.txt
@@ -30,6 +30,7 @@
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? getCurrentExerciseInfo(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.ExerciseInfo>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? markLap(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? overrideAutoPauseAndResumeForActiveExercise(androidx.health.services.client.ExerciseClient, boolean enabled, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
+    method public static suspend Object? overrideBatchingModesForActiveExercise(androidx.health.services.client.ExerciseClient, java.util.Set<androidx.health.services.client.data.BatchingMode> batchingModes, kotlin.coroutines.Continuation<? super java.lang.Void>);
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? pauseExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? prepareExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.WarmUpConfig configuration, kotlin.coroutines.Continuation<? super kotlin.Unit>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? removeGoalFromActiveExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
diff --git a/health/health-services-client/api/public_plus_experimental_1.0.0-beta03.txt b/health/health-services-client/api/public_plus_experimental_1.0.0-beta03.txt
index 9d0902d..d32c831 100644
--- a/health/health-services-client/api/public_plus_experimental_1.0.0-beta03.txt
+++ b/health/health-services-client/api/public_plus_experimental_1.0.0-beta03.txt
@@ -30,6 +30,7 @@
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? getCurrentExerciseInfo(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.ExerciseInfo>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? markLap(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? overrideAutoPauseAndResumeForActiveExercise(androidx.health.services.client.ExerciseClient, boolean enabled, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
+    method public static suspend Object? overrideBatchingModesForActiveExercise(androidx.health.services.client.ExerciseClient, java.util.Set<androidx.health.services.client.data.BatchingMode> batchingModes, kotlin.coroutines.Continuation<? super java.lang.Void>);
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? pauseExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? prepareExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.WarmUpConfig configuration, kotlin.coroutines.Continuation<? super kotlin.Unit>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? removeGoalFromActiveExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
diff --git a/health/health-services-client/api/public_plus_experimental_current.txt b/health/health-services-client/api/public_plus_experimental_current.txt
index 9d0902d..d32c831 100644
--- a/health/health-services-client/api/public_plus_experimental_current.txt
+++ b/health/health-services-client/api/public_plus_experimental_current.txt
@@ -30,6 +30,7 @@
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? getCurrentExerciseInfo(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.ExerciseInfo>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? markLap(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? overrideAutoPauseAndResumeForActiveExercise(androidx.health.services.client.ExerciseClient, boolean enabled, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
+    method public static suspend Object? overrideBatchingModesForActiveExercise(androidx.health.services.client.ExerciseClient, java.util.Set<androidx.health.services.client.data.BatchingMode> batchingModes, kotlin.coroutines.Continuation<? super java.lang.Void>);
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? pauseExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? prepareExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.WarmUpConfig configuration, kotlin.coroutines.Continuation<? super kotlin.Unit>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? removeGoalFromActiveExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
diff --git a/health/health-services-client/api/restricted_1.0.0-beta03.txt b/health/health-services-client/api/restricted_1.0.0-beta03.txt
index 9d0902d..d32c831 100644
--- a/health/health-services-client/api/restricted_1.0.0-beta03.txt
+++ b/health/health-services-client/api/restricted_1.0.0-beta03.txt
@@ -30,6 +30,7 @@
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? getCurrentExerciseInfo(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.ExerciseInfo>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? markLap(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? overrideAutoPauseAndResumeForActiveExercise(androidx.health.services.client.ExerciseClient, boolean enabled, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
+    method public static suspend Object? overrideBatchingModesForActiveExercise(androidx.health.services.client.ExerciseClient, java.util.Set<androidx.health.services.client.data.BatchingMode> batchingModes, kotlin.coroutines.Continuation<? super java.lang.Void>);
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? pauseExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? prepareExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.WarmUpConfig configuration, kotlin.coroutines.Continuation<? super kotlin.Unit>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? removeGoalFromActiveExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
diff --git a/health/health-services-client/api/restricted_current.txt b/health/health-services-client/api/restricted_current.txt
index 9d0902d..d32c831 100644
--- a/health/health-services-client/api/restricted_current.txt
+++ b/health/health-services-client/api/restricted_current.txt
@@ -30,6 +30,7 @@
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? getCurrentExerciseInfo(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super androidx.health.services.client.data.ExerciseInfo>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? markLap(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? overrideAutoPauseAndResumeForActiveExercise(androidx.health.services.client.ExerciseClient, boolean enabled, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
+    method public static suspend Object? overrideBatchingModesForActiveExercise(androidx.health.services.client.ExerciseClient, java.util.Set<androidx.health.services.client.data.BatchingMode> batchingModes, kotlin.coroutines.Continuation<? super java.lang.Void>);
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? pauseExercise(androidx.health.services.client.ExerciseClient, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? prepareExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.WarmUpConfig configuration, kotlin.coroutines.Continuation<? super kotlin.Unit>) throws androidx.health.services.client.HealthServicesException;
     method @kotlin.jvm.Throws(exceptionClasses=HealthServicesException::class) public static suspend Object? removeGoalFromActiveExercise(androidx.health.services.client.ExerciseClient, androidx.health.services.client.data.ExerciseGoal<?> exerciseGoal, kotlin.coroutines.Continuation<? super java.lang.Void>) throws androidx.health.services.client.HealthServicesException;
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseClientExtension.kt b/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseClientExtension.kt
index a11595b..cafecba 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseClientExtension.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseClientExtension.kt
@@ -15,6 +15,7 @@
  */
 package androidx.health.services.client
 
+import androidx.health.services.client.data.BatchingMode
 import androidx.health.services.client.data.DataPoint
 import androidx.health.services.client.data.DataType
 import androidx.health.services.client.data.ExerciseCapabilities
@@ -239,6 +240,18 @@
 ) = overrideAutoPauseAndResumeForActiveExerciseAsync(enabled).awaitWithException()
 
 /**
+ * Sets the batching mode for the current exercise synchronously.
+ *
+ * @param batchingModes [BatchingMode] overrides for exercise updates. Passing an empty set will
+ * clear all existing overrides.
+ * @throws HealthServicesException if an exercise is not active for this app or Health Service fails
+ * to process the call
+ */
+public suspend fun ExerciseClient.overrideBatchingModesForActiveExercise(
+    batchingModes: Set<BatchingMode>
+) = overrideBatchingModesForActiveExerciseAsync(batchingModes).awaitWithException()
+
+/**
  * Returns the [ExerciseCapabilities] of this client for the device.
  *
  * This can be used to determine what [ExerciseType]s and [DataType]s this device supports.
@@ -264,4 +277,4 @@
 @kotlin.jvm.Throws(HealthServicesException::class)
 public suspend fun ExerciseClient.updateExerciseTypeConfig(
     exerciseTypeConfig: ExerciseTypeConfig
-) = updateExerciseTypeConfigAsync(exerciseTypeConfig).awaitWithException()
\ No newline at end of file
+) = updateExerciseTypeConfigAsync(exerciseTypeConfig).awaitWithException()
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/ExerciseClientTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/ExerciseClientTest.kt
index fb50f7c..f463bc8 100644
--- a/health/health-services-client/src/test/java/androidx/health/services/client/ExerciseClientTest.kt
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/ExerciseClientTest.kt
@@ -22,6 +22,7 @@
 import android.os.Looper
 import android.os.RemoteException
 import androidx.health.services.client.data.Availability
+import androidx.health.services.client.data.BatchingMode
 import androidx.health.services.client.data.ComparisonType
 import androidx.health.services.client.data.DataType
 import androidx.health.services.client.data.DataTypeAvailability
@@ -62,7 +63,6 @@
 import com.google.common.collect.ImmutableSet
 import com.google.common.truth.Truth
 import java.util.concurrent.CancellationException
-import kotlin.test.assertFailsWith
 import kotlinx.coroutines.async
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.TestScope
@@ -741,31 +741,65 @@
 
     @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
     @Test
-    fun updateExerciseTypeConfigForActiveExercise() = runTest {
-        service.exerciseConfig = ExerciseConfig.builder(ExerciseType.GOLF).build()
+    fun updateExerciseTypeConfigForActiveExerciseSynchronously() = runTest {
+        val exerciseConfig = ExerciseConfig.builder(ExerciseType.GOLF).build()
         val exerciseTypeConfig =
-            GolfExerciseTypeConfig(GolfExerciseTypeConfig
-                .GolfShotTrackingPlaceInfo.GOLF_SHOT_TRACKING_PLACE_INFO_FAIRWAY)
-        val request =
-            UpdateExerciseTypeConfigRequest(
-                CLIENT_CONFIGURATION.servicePackageName, exerciseTypeConfig
+            GolfExerciseTypeConfig(
+                GolfExerciseTypeConfig
+                    .GolfShotTrackingPlaceInfo.GOLF_SHOT_TRACKING_PLACE_INFO_FAIRWAY
             )
-        val statusCallback = IStatusCallback.Default()
 
-        service.updateExerciseTypeConfigForActiveExercise(request, statusCallback)
+        client.setUpdateCallback(callback)
+        var deferred = async { client.startExercise(exerciseConfig) }
+        advanceMainLooperIdle()
+        deferred.await()
+        deferred = async {
+            client.updateExerciseTypeConfig(exerciseTypeConfig)
+        }
+        advanceMainLooperIdle()
+        deferred.await()
 
         Truth.assertThat(service.exerciseConfig?.exerciseTypeConfig).isEqualTo(exerciseTypeConfig)
     }
 
     @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
     @Test
-    fun overrideBatchingModesForActiveExercise_notImplementedError() = runTest {
-        var request: BatchingModeConfigRequest?
-        request = null
-        assertFailsWith(
-            exceptionClass = NotImplementedError::class,
-            block = { service.overrideBatchingModesForActiveExercise(request, null) }
-        )
+    fun overrideBatchingModesForActiveExerciseSynchronously() = runTest {
+        val batchingMode = HashSet<BatchingMode>()
+        batchingMode.add(BatchingMode.HEART_RATE_5_SECONDS)
+        client.setUpdateCallback(callback)
+
+        var deferred = async {
+            client.overrideBatchingModesForActiveExercise(batchingMode)
+        }
+        advanceMainLooperIdle()
+        deferred.await()
+
+        Truth.assertThat(service.batchingModeOverrides?.size).isEqualTo(1)
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    @Test
+    fun clearBatchingModesForActiveExerciseSynchronously() = runTest {
+        val batchingMode = HashSet<BatchingMode>()
+        batchingMode.add(BatchingMode.HEART_RATE_5_SECONDS)
+        val emptyBatchingMode = HashSet<BatchingMode>()
+        client.setUpdateCallback(callback)
+        var deferred = async {
+            // override batching mode with HEART_RATE_5_SECONDS
+            client.overrideBatchingModesForActiveExercise(batchingMode)
+        }
+        advanceMainLooperIdle()
+        deferred.await()
+
+        deferred = async {
+            // Clear existing batching modes with empty set
+            client.overrideBatchingModesForActiveExercise(emptyBatchingMode)
+        }
+        advanceMainLooperIdle()
+        deferred.await()
+
+        Truth.assertThat(service.batchingModeOverrides?.size).isEqualTo(0)
     }
 
     class FakeExerciseUpdateCallback : ExerciseUpdateCallback {
@@ -808,6 +842,7 @@
         var throwException = false
         var callingAppHasPermissions = true
         val registerGetCapabilitiesRequests = mutableListOf<CapabilitiesRequest>()
+        var batchingModeOverrides: Set<BatchingMode>? = null
 
         override fun prepareExercise(
             prepareExerciseRequest: PrepareExerciseRequest?,
@@ -934,7 +969,8 @@
             batchingModeConfigRequest: BatchingModeConfigRequest?,
             statuscallback: IStatusCallback?
         ) {
-            throw NotImplementedError()
+            batchingModeOverrides = batchingModeConfigRequest?.batchingModeOverrides
+            statusCallbackAction.invoke(statuscallback)
         }
 
         override fun getCapabilities(request: CapabilitiesRequest): ExerciseCapabilitiesResponse {
@@ -995,7 +1031,7 @@
 
         override fun updateExerciseTypeConfigForActiveExercise(
             updateExerciseTypeConfigRequest: UpdateExerciseTypeConfigRequest,
-            statuscallback: IStatusCallback
+            statusCallback: IStatusCallback,
         ) {
             val newExerciseTypeConfig = updateExerciseTypeConfigRequest.exerciseTypeConfig
             val newExerciseConfig =
@@ -1003,6 +1039,7 @@
                     exerciseConfig!!.exerciseType
                 ).setExerciseTypeConfig(newExerciseTypeConfig).build()
             this.exerciseConfig = newExerciseConfig
+            statusCallbackAction.invoke(statusCallback)
         }
 
         fun setException() {
@@ -1028,4 +1065,4 @@
                 IpcConstants.EXERCISE_API_BIND_ACTION
             )
     }
-}
\ No newline at end of file
+}
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedExerciseClientTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedExerciseClientTest.kt
index a0c580b..1164cbf 100644
--- a/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedExerciseClientTest.kt
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedExerciseClientTest.kt
@@ -22,6 +22,7 @@
 import android.os.Looper.getMainLooper
 import androidx.health.services.client.ExerciseUpdateCallback
 import androidx.health.services.client.data.Availability
+import androidx.health.services.client.data.BatchingMode
 import androidx.health.services.client.data.DataType
 import androidx.health.services.client.data.DataType.Companion.HEART_RATE_BPM
 import androidx.health.services.client.data.DataType.Companion.HEART_RATE_BPM_STATS
@@ -48,6 +49,7 @@
 import androidx.health.services.client.impl.response.ExerciseCapabilitiesResponse
 import androidx.test.core.app.ApplicationProvider
 import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertFailsWith
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
@@ -221,6 +223,38 @@
         assertThat(callback.availabilities).containsEntry(HEART_RATE_BPM, ACQUIRING)
     }
 
+    @Test
+    fun updateExerciseTypeConfigForActiveExercise() {
+        val exerciseConfig = ExerciseConfig.builder(ExerciseType.GOLF).build()
+        val exerciseTypeConfig =
+            GolfExerciseTypeConfig(
+                GolfExerciseTypeConfig
+                    .GolfShotTrackingPlaceInfo.GOLF_SHOT_TRACKING_PLACE_INFO_FAIRWAY
+            )
+        client.setUpdateCallback(callback)
+        client.startExerciseAsync(exerciseConfig)
+        shadowOf(getMainLooper()).idle()
+
+        client.updateExerciseTypeConfigAsync(exerciseTypeConfig)
+        shadowOf(getMainLooper()).idle()
+
+        assertThat(fakeService.exerciseConfig?.exerciseTypeConfig).isEqualTo(exerciseTypeConfig)
+    }
+
+    @Test
+    fun overrideBatchingModesForActiveExercise_notImplementedError() {
+        val batchingMode = HashSet<BatchingMode>()
+        client.setUpdateCallback(callback)
+
+        assertFailsWith(
+            exceptionClass = NotImplementedError::class,
+            block = {
+                client.overrideBatchingModesForActiveExerciseAsync(batchingMode)
+                shadowOf(getMainLooper()).idle()
+            }
+        )
+    }
+
     class FakeExerciseUpdateCallback : ExerciseUpdateCallback {
         val availabilities = mutableMapOf<DataType<*, *>, Availability>()
         val registrationFailureThrowables = mutableListOf<Throwable>()
@@ -249,6 +283,7 @@
 
         var listener: IExerciseUpdateListener? = null
         var statusCallbackAction: (IStatusCallback?) -> Unit = { it!!.onSuccess() }
+        var exerciseConfig: ExerciseConfig? = null
 
         override fun getApiVersion(): Int = 12
 
@@ -263,6 +298,7 @@
             startExerciseRequest: StartExerciseRequest?,
             statusCallback: IStatusCallback?
         ) {
+            exerciseConfig = startExerciseRequest?.exerciseConfig
             statusCallbackAction.invoke(statusCallback)
         }
 
@@ -343,10 +379,16 @@
         }
 
         override fun updateExerciseTypeConfigForActiveExercise(
-            updateExerciseTypeConfigRequest: UpdateExerciseTypeConfigRequest?,
-            statuscallback: IStatusCallback?
+            updateExerciseTypeConfigRequest: UpdateExerciseTypeConfigRequest,
+            statuscallback: IStatusCallback
         ) {
-            throw NotImplementedError()
+            val newExerciseTypeConfig = updateExerciseTypeConfigRequest.exerciseTypeConfig
+            val newExerciseConfig =
+                ExerciseConfig.builder(
+                    exerciseConfig!!.exerciseType
+                ).setExerciseTypeConfig(newExerciseTypeConfig).build()
+            this.exerciseConfig = newExerciseConfig
+            this.statusCallbackAction.invoke(statuscallback)
         }
     }
 }
diff --git a/inspection/OWNERS b/inspection/OWNERS
index 85c92a7..d238af3 100644
--- a/inspection/OWNERS
+++ b/inspection/OWNERS
@@ -1,3 +1,3 @@
+# Bug component: 1333602
 sergeyv@google.com
-davidherman@google.com
 yboyar@google.com
\ No newline at end of file
diff --git a/javascriptengine/javascriptengine/api/current.txt b/javascriptengine/javascriptengine/api/current.txt
index 9b3ff45..26399e2 100644
--- a/javascriptengine/javascriptengine/api/current.txt
+++ b/javascriptengine/javascriptengine/api/current.txt
@@ -5,11 +5,19 @@
     ctor public EvaluationFailedException(String);
   }
 
+  public final class EvaluationResultSizeLimitExceededException extends androidx.javascriptengine.JavaScriptException {
+    ctor public EvaluationResultSizeLimitExceededException(String);
+    ctor public EvaluationResultSizeLimitExceededException();
+  }
+
   public final class IsolateStartupParameters {
     ctor public IsolateStartupParameters();
+    method @IntRange(from=0) public int getMaxEvaluationReturnSizeBytes();
     method @IntRange(from=0) public long getMaxHeapSizeBytes();
+    method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public void setMaxEvaluationReturnSizeBytes(@IntRange(from=0) int);
     method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_ISOLATE_MAX_HEAP_SIZE, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public void setMaxHeapSizeBytes(@IntRange(from=0) long);
     field public static final long DEFAULT_ISOLATE_HEAP_SIZE = 0L; // 0x0L
+    field public static final int DEFAULT_MAX_EVALUATION_RETURN_SIZE_BYTES = 20971520; // 0x1400000
   }
 
   public final class IsolateTerminatedException extends androidx.javascriptengine.JavaScriptException {
@@ -24,6 +32,7 @@
   public final class JavaScriptIsolate implements java.lang.AutoCloseable {
     method public void close();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.String!> evaluateJavaScriptAsync(String);
+    method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public com.google.common.util.concurrent.ListenableFuture<java.lang.String!> evaluateJavaScriptAsync(byte[]);
     method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_PROVIDE_CONSUME_ARRAY_BUFFER, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public boolean provideNamedData(String, byte[]);
   }
 
@@ -34,6 +43,7 @@
     method public androidx.javascriptengine.JavaScriptIsolate createIsolate(androidx.javascriptengine.IsolateStartupParameters);
     method public boolean isFeatureSupported(String);
     method public static boolean isSupported();
+    field public static final String JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT = "JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT";
     field public static final String JS_FEATURE_ISOLATE_MAX_HEAP_SIZE = "JS_FEATURE_ISOLATE_MAX_HEAP_SIZE";
     field public static final String JS_FEATURE_ISOLATE_TERMINATION = "JS_FEATURE_ISOLATE_TERMINATION";
     field public static final String JS_FEATURE_PROMISE_RETURN = "JS_FEATURE_PROMISE_RETURN";
diff --git a/javascriptengine/javascriptengine/api/public_plus_experimental_current.txt b/javascriptengine/javascriptengine/api/public_plus_experimental_current.txt
index 9b3ff45..26399e2 100644
--- a/javascriptengine/javascriptengine/api/public_plus_experimental_current.txt
+++ b/javascriptengine/javascriptengine/api/public_plus_experimental_current.txt
@@ -5,11 +5,19 @@
     ctor public EvaluationFailedException(String);
   }
 
+  public final class EvaluationResultSizeLimitExceededException extends androidx.javascriptengine.JavaScriptException {
+    ctor public EvaluationResultSizeLimitExceededException(String);
+    ctor public EvaluationResultSizeLimitExceededException();
+  }
+
   public final class IsolateStartupParameters {
     ctor public IsolateStartupParameters();
+    method @IntRange(from=0) public int getMaxEvaluationReturnSizeBytes();
     method @IntRange(from=0) public long getMaxHeapSizeBytes();
+    method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public void setMaxEvaluationReturnSizeBytes(@IntRange(from=0) int);
     method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_ISOLATE_MAX_HEAP_SIZE, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public void setMaxHeapSizeBytes(@IntRange(from=0) long);
     field public static final long DEFAULT_ISOLATE_HEAP_SIZE = 0L; // 0x0L
+    field public static final int DEFAULT_MAX_EVALUATION_RETURN_SIZE_BYTES = 20971520; // 0x1400000
   }
 
   public final class IsolateTerminatedException extends androidx.javascriptengine.JavaScriptException {
@@ -24,6 +32,7 @@
   public final class JavaScriptIsolate implements java.lang.AutoCloseable {
     method public void close();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.String!> evaluateJavaScriptAsync(String);
+    method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public com.google.common.util.concurrent.ListenableFuture<java.lang.String!> evaluateJavaScriptAsync(byte[]);
     method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_PROVIDE_CONSUME_ARRAY_BUFFER, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public boolean provideNamedData(String, byte[]);
   }
 
@@ -34,6 +43,7 @@
     method public androidx.javascriptengine.JavaScriptIsolate createIsolate(androidx.javascriptengine.IsolateStartupParameters);
     method public boolean isFeatureSupported(String);
     method public static boolean isSupported();
+    field public static final String JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT = "JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT";
     field public static final String JS_FEATURE_ISOLATE_MAX_HEAP_SIZE = "JS_FEATURE_ISOLATE_MAX_HEAP_SIZE";
     field public static final String JS_FEATURE_ISOLATE_TERMINATION = "JS_FEATURE_ISOLATE_TERMINATION";
     field public static final String JS_FEATURE_PROMISE_RETURN = "JS_FEATURE_PROMISE_RETURN";
diff --git a/javascriptengine/javascriptengine/api/restricted_current.txt b/javascriptengine/javascriptengine/api/restricted_current.txt
index 9b3ff45..26399e2 100644
--- a/javascriptengine/javascriptengine/api/restricted_current.txt
+++ b/javascriptengine/javascriptengine/api/restricted_current.txt
@@ -5,11 +5,19 @@
     ctor public EvaluationFailedException(String);
   }
 
+  public final class EvaluationResultSizeLimitExceededException extends androidx.javascriptengine.JavaScriptException {
+    ctor public EvaluationResultSizeLimitExceededException(String);
+    ctor public EvaluationResultSizeLimitExceededException();
+  }
+
   public final class IsolateStartupParameters {
     ctor public IsolateStartupParameters();
+    method @IntRange(from=0) public int getMaxEvaluationReturnSizeBytes();
     method @IntRange(from=0) public long getMaxHeapSizeBytes();
+    method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public void setMaxEvaluationReturnSizeBytes(@IntRange(from=0) int);
     method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_ISOLATE_MAX_HEAP_SIZE, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public void setMaxHeapSizeBytes(@IntRange(from=0) long);
     field public static final long DEFAULT_ISOLATE_HEAP_SIZE = 0L; // 0x0L
+    field public static final int DEFAULT_MAX_EVALUATION_RETURN_SIZE_BYTES = 20971520; // 0x1400000
   }
 
   public final class IsolateTerminatedException extends androidx.javascriptengine.JavaScriptException {
@@ -24,6 +32,7 @@
   public final class JavaScriptIsolate implements java.lang.AutoCloseable {
     method public void close();
     method public com.google.common.util.concurrent.ListenableFuture<java.lang.String!> evaluateJavaScriptAsync(String);
+    method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public com.google.common.util.concurrent.ListenableFuture<java.lang.String!> evaluateJavaScriptAsync(byte[]);
     method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_PROVIDE_CONSUME_ARRAY_BUFFER, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public boolean provideNamedData(String, byte[]);
   }
 
@@ -34,6 +43,7 @@
     method public androidx.javascriptengine.JavaScriptIsolate createIsolate(androidx.javascriptengine.IsolateStartupParameters);
     method public boolean isFeatureSupported(String);
     method public static boolean isSupported();
+    field public static final String JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT = "JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT";
     field public static final String JS_FEATURE_ISOLATE_MAX_HEAP_SIZE = "JS_FEATURE_ISOLATE_MAX_HEAP_SIZE";
     field public static final String JS_FEATURE_ISOLATE_TERMINATION = "JS_FEATURE_ISOLATE_TERMINATION";
     field public static final String JS_FEATURE_PROMISE_RETURN = "JS_FEATURE_PROMISE_RETURN";
diff --git a/javascriptengine/javascriptengine/src/androidTest/java/androidx/javascriptengine/UtilsTest.java b/javascriptengine/javascriptengine/src/androidTest/java/androidx/javascriptengine/UtilsTest.java
new file mode 100644
index 0000000..8bb5531
--- /dev/null
+++ b/javascriptengine/javascriptengine/src/androidTest/java/androidx/javascriptengine/UtilsTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.javascriptengine;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import androidx.javascriptengine.common.Utils;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.MediumTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class UtilsTest {
+
+    //These are prefix bytes of valid UTF8.
+    static final byte[] TRUNCATED_TWO_BYTES_UTF8 = new byte[]{(byte) 0xc3};
+    static final byte[] TRUNCATED_THREE_BYTES_UTF8 = new byte[]{(byte) 0xe0, (byte) 0xa0};
+    static final byte[] TRUNCATED_FOUR_BYTES_UTF8 =
+            new byte[]{(byte) 0xf0, (byte) 0x92, (byte) 0x80};
+
+    static final byte[] VALID_UTF8 = "valid byte".getBytes(StandardCharsets.UTF_8);
+    static final byte[] VALID_FOUR_BYTE_UTF8 =
+            new byte[]{(byte) 0xf0, (byte) 0x92, (byte) 0x80, (byte) 0x80};
+
+    @Test
+    public void testIsValidUTF8FirstByte() {
+        assertTrue(Utils.isUTF8ContinuationByte(TRUNCATED_THREE_BYTES_UTF8[1]));
+        assertTrue(Utils.isUTF8ContinuationByte(TRUNCATED_FOUR_BYTES_UTF8[1]));
+        assertTrue(Utils.isUTF8ContinuationByte(TRUNCATED_FOUR_BYTES_UTF8[2]));
+
+        assertFalse(Utils.isUTF8ContinuationByte("0".getBytes(StandardCharsets.UTF_8)[0]));
+        assertFalse(Utils.isUTF8ContinuationByte((byte) 0xff));
+        assertFalse(Utils.isUTF8ContinuationByte(VALID_UTF8[0]));
+        assertFalse(Utils.isUTF8ContinuationByte(VALID_FOUR_BYTE_UTF8[0]));
+        assertFalse(Utils.isUTF8ContinuationByte(TRUNCATED_TWO_BYTES_UTF8[0]));
+        assertFalse(Utils.isUTF8ContinuationByte(TRUNCATED_THREE_BYTES_UTF8[0]));
+        assertFalse(Utils.isUTF8ContinuationByte(TRUNCATED_FOUR_BYTES_UTF8[0]));
+
+    }
+
+    @Test
+    public void testGetValidUTF8PrefixLength() {
+        byte[] validPrefixBytes = "valid byte".getBytes(StandardCharsets.UTF_8);
+        assertEquals(-1, Utils.getLastUTF8StartingByteIndex("".getBytes(StandardCharsets.UTF_8)));
+        assertEquals(0, Utils.getLastUTF8StartingByteIndex("a".getBytes(StandardCharsets.UTF_8)));
+        assertEquals(1, Utils.getLastUTF8StartingByteIndex("ab".getBytes(StandardCharsets.UTF_8)));
+        assertEquals(2, Utils.getLastUTF8StartingByteIndex("abc".getBytes(StandardCharsets.UTF_8)));
+        assertEquals(3,
+                Utils.getLastUTF8StartingByteIndex("abcd".getBytes(StandardCharsets.UTF_8)));
+        assertEquals(validPrefixBytes.length - 1,
+                Utils.getLastUTF8StartingByteIndex(validPrefixBytes));
+        assertEquals(validPrefixBytes.length,
+                Utils.getLastUTF8StartingByteIndex(
+                        concat(validPrefixBytes, TRUNCATED_TWO_BYTES_UTF8)));
+        assertEquals(validPrefixBytes.length, Utils.getLastUTF8StartingByteIndex(
+                concat(validPrefixBytes, TRUNCATED_THREE_BYTES_UTF8)));
+        assertEquals(validPrefixBytes.length, Utils.getLastUTF8StartingByteIndex(
+                concat(validPrefixBytes, TRUNCATED_FOUR_BYTES_UTF8)));
+        assertEquals(validPrefixBytes.length,
+                Utils.getLastUTF8StartingByteIndex(concat(validPrefixBytes, VALID_FOUR_BYTE_UTF8)));
+    }
+
+    private static byte[] concat(byte[] a, byte[] b) {
+        int lenA = a.length;
+        int lenB = b.length;
+        byte[] c = Arrays.copyOf(a, lenA + lenB);
+        System.arraycopy(b, 0, c, lenA, lenB);
+        return c;
+    }
+}
diff --git a/javascriptengine/javascriptengine/src/androidTest/java/androidx/javascriptengine/WebViewJavaScriptSandboxTest.java b/javascriptengine/javascriptengine/src/androidTest/java/androidx/javascriptengine/WebViewJavaScriptSandboxTest.java
index 847aacf..553e919 100644
--- a/javascriptengine/javascriptengine/src/androidTest/java/androidx/javascriptengine/WebViewJavaScriptSandboxTest.java
+++ b/javascriptengine/javascriptengine/src/androidTest/java/androidx/javascriptengine/WebViewJavaScriptSandboxTest.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 
+import androidx.annotation.GuardedBy;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
@@ -33,6 +34,7 @@
 
 import java.nio.charset.StandardCharsets;
 import java.util.Vector;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 
@@ -817,7 +819,7 @@
     @Test
     @LargeTest
     public void testLargeScriptByteArrayJsEvaluation() throws Throwable {
-        String longString = "a".repeat(2000000);
+        final String longString = "a".repeat(2000000);
         final String codeString = ""
                 + "let " + longString + " = 0;"
                 + "\"PASS\"";
@@ -839,4 +841,302 @@
         }
     }
 
-}
\ No newline at end of file
+    @Test
+    @LargeTest
+    public void testLargeReturn() throws Throwable {
+        final String longString = "a".repeat(2000000);
+        final String code = "'a'.repeat(2000000);";
+        final String expected = longString;
+        Context context = ApplicationProvider.getApplicationContext();
+
+        ListenableFuture<JavaScriptSandbox> jsSandboxFuture =
+                JavaScriptSandbox.createConnectedInstanceAsync(context);
+        try (JavaScriptSandbox jsSandbox = jsSandboxFuture.get(5, TimeUnit.SECONDS)) {
+            Assume.assumeTrue(jsSandbox.isFeatureSupported(
+                    JavaScriptSandbox.JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT));
+            try (JavaScriptIsolate jsIsolate = jsSandbox.createIsolate()) {
+                ListenableFuture<String> resultFuture = jsIsolate.evaluateJavaScriptAsync(code);
+                String result = resultFuture.get(60, TimeUnit.SECONDS);
+
+                Assert.assertEquals(expected, result);
+            }
+        }
+    }
+
+    @Test
+    @LargeTest
+    public void testLargeError() throws Throwable {
+        final String longString = "a".repeat(2000000);
+        final String code = "throw \"" + longString + "\");";
+        Context context = ApplicationProvider.getApplicationContext();
+
+        ListenableFuture<JavaScriptSandbox> jsSandboxFuture =
+                JavaScriptSandbox.createConnectedInstanceAsync(context);
+        try (JavaScriptSandbox jsSandbox = jsSandboxFuture.get(5, TimeUnit.SECONDS)) {
+            Assume.assumeTrue(jsSandbox.isFeatureSupported(
+                    JavaScriptSandbox.JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT));
+            try (JavaScriptIsolate jsIsolate = jsSandbox.createIsolate()) {
+                ListenableFuture<String> resultFuture = jsIsolate.evaluateJavaScriptAsync(code);
+                try {
+                    resultFuture.get(5, TimeUnit.SECONDS);
+                    Assert.fail("Should have thrown.");
+                } catch (ExecutionException e) {
+                    Assert.assertTrue(e.getCause().getClass().equals(
+                            EvaluationFailedException.class));
+                    Assert.assertTrue(e.getCause().getMessage().contains(longString));
+                }
+            }
+        }
+    }
+
+    @Test
+    @MediumTest
+    public void testResultSizeEnforced() throws Throwable {
+        final int maxSize = 100;
+        Context context = ApplicationProvider.getApplicationContext();
+
+        ListenableFuture<JavaScriptSandbox> jsSandboxFuture =
+                JavaScriptSandbox.createConnectedInstanceAsync(context);
+        try (JavaScriptSandbox jsSandbox = jsSandboxFuture.get(5, TimeUnit.SECONDS)) {
+            Assume.assumeTrue(jsSandbox.isFeatureSupported(
+                    JavaScriptSandbox.JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT));
+            IsolateStartupParameters settings = new IsolateStartupParameters();
+            settings.setMaxEvaluationReturnSizeBytes(maxSize);
+            try (JavaScriptIsolate jsIsolate = jsSandbox.createIsolate(settings)) {
+                // Running code that returns greater than `maxSize` number of bytes should throw.
+                final String greaterThanMaxSizeCode = ""
+                        + "'a'.repeat(" + (maxSize + 1) + ");";
+                ListenableFuture<String> greaterThanMaxSizeResultFuture =
+                        jsIsolate.evaluateJavaScriptAsync(greaterThanMaxSizeCode);
+                try {
+                    greaterThanMaxSizeResultFuture.get(5, TimeUnit.SECONDS);
+                    Assert.fail("Should have thrown.");
+                } catch (ExecutionException e) {
+                    if (!(e.getCause() instanceof EvaluationResultSizeLimitExceededException)) {
+                        throw e;
+                    }
+                }
+
+                // Running code that returns `maxSize` number of bytes should not throw.
+                final String maxSizeCode = ""
+                        + "'a'.repeat(" + maxSize + ");";
+                final String maxSizeExpected = "a".repeat(maxSize);
+                ListenableFuture<String> maxSizeResultFuture =
+                        jsIsolate.evaluateJavaScriptAsync(maxSizeCode);
+                String maxSizeResult = maxSizeResultFuture.get(5, TimeUnit.SECONDS);
+                Assert.assertEquals(maxSizeExpected, maxSizeResult);
+
+                // Running code that returns less than `maxSize` number of bytes should not throw.
+                final String lessThanMaxSizeCode = ""
+                        + "'a'.repeat(" + (maxSize - 1) + ");";
+                final String lessThanMaxSizeExpected = "a".repeat(maxSize - 1);
+                ListenableFuture<String> lessThanMaxSizeResultFuture =
+                        jsIsolate.evaluateJavaScriptAsync(lessThanMaxSizeCode);
+                String lessThanMaxSizeResult = lessThanMaxSizeResultFuture.get(5,
+                        TimeUnit.SECONDS);
+                Assert.assertEquals(lessThanMaxSizeExpected, lessThanMaxSizeResult);
+            }
+        }
+    }
+
+    @Test
+    @LargeTest
+    public void testConsoleLogging() throws Throwable {
+        final class LoggingJavaScriptConsoleCallback implements JavaScriptConsoleCallback {
+            private final Object mLock = new Object();
+            @GuardedBy("mLock")
+            private final StringBuilder mMessages = new StringBuilder();
+
+            public static final String CLEAR_MARKER = "(console.clear() called)\n";
+            // This is required for synchronization between the instrumentation thread and the UI
+            // thread.
+            public CountDownLatch latch;
+
+            LoggingJavaScriptConsoleCallback(int numberOfCalls) {
+                latch = new CountDownLatch(numberOfCalls);
+            }
+
+            @Override
+            public void onConsoleMessage(JavaScriptConsoleCallback.ConsoleMessage message) {
+                synchronized (mLock) {
+                    mMessages.append(message.toString()).append("\n");
+                }
+                latch.countDown();
+            }
+
+            @Override
+            public void onConsoleClear() {
+                synchronized (mLock) {
+                    mMessages.append(CLEAR_MARKER);
+                }
+                latch.countDown();
+            }
+
+            public String messages() {
+                synchronized (mLock) {
+                    return mMessages.toString();
+                }
+            }
+
+            public void resetLatch(int count) {
+                latch = new CountDownLatch(count);
+            }
+        }
+
+        final String code = ""
+                + "function a() {b();}\n"
+                + "function b() {c();}\n"
+                + "function c() {console.trace('I am a trace message!');}\n"
+                + "console.log('I am a log message!');\n"
+                + "console.debug('I am a debug message!');\n"
+                + "console.info('I am an info message!');\n"
+                + "console.error('I am an error message!');\n"
+                + "console.warn('I am a warn message!');\n"
+                + "console.assert(false, 'I am an assert message!');\n"
+                + "console.log({'I am': [1, 'complex', {}]});\n"
+                + "a();\n"
+                + "console.count('I am counting');\n"
+                + "console.count('I am counting');\n"
+                + "console.clear();\n"
+                + "\"PASS\"";
+        final String expected = "PASS";
+        final String expectedLog = ""
+                + "L <expression>:4:9: I am a log message!\n"
+                + "D <expression>:5:9: I am a debug message!\n"
+                + "I <expression>:6:9: I am an info message!\n"
+                + "E <expression>:7:9: I am an error message!\n"
+                + "W <expression>:8:9: I am a warn message!\n"
+                + "E <expression>:9:9: I am an assert message!\n"
+                + "L <expression>:10:9: [object Object]\n"
+                + "I <expression>:3:23: I am a trace message!\n"
+                + "D <expression>:12:9: I am counting: 1\n"
+                + "D <expression>:13:9: I am counting: 2\n"
+                + LoggingJavaScriptConsoleCallback.CLEAR_MARKER;
+        final int numOfLogs = 11;
+        final Context context = ApplicationProvider.getApplicationContext();
+
+        final ListenableFuture<JavaScriptSandbox> jsSandboxFuture =
+                JavaScriptSandbox.createConnectedInstanceAsync(context);
+        try (JavaScriptSandbox jsSandbox = jsSandboxFuture.get(5, TimeUnit.SECONDS);
+             JavaScriptIsolate jsIsolate = jsSandbox.createIsolate()) {
+            Assume.assumeTrue(jsSandbox.isFeatureSupported(
+                    JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING));
+            final LoggingJavaScriptConsoleCallback client1 =
+                    new LoggingJavaScriptConsoleCallback(numOfLogs);
+            final LoggingJavaScriptConsoleCallback client2 =
+                    new LoggingJavaScriptConsoleCallback(numOfLogs);
+            // Test logging does not crash when no client attached
+            // (There may be no inspector)
+            {
+                final ListenableFuture<String> resultFuture =
+                        jsIsolate.evaluateJavaScriptAsync(code);
+                final String result = resultFuture.get(5, TimeUnit.SECONDS);
+
+                Assert.assertEquals(expected, result);
+            }
+            // Test logging works when client attached
+            // (This may spin up an inspector)
+            {
+                jsIsolate.setConsoleCallback(client1);
+                final ListenableFuture<String> resultFuture =
+                        jsIsolate.evaluateJavaScriptAsync(code);
+                final String result = resultFuture.get(5, TimeUnit.SECONDS);
+
+                Assert.assertEquals(expected, result);
+                Assert.assertTrue(client1.latch.await(2, TimeUnit.SECONDS));
+                Assert.assertEquals(expectedLog, client1.messages());
+            }
+            // Test client can be replaced
+            // (This may retain the same inspector)
+            {
+                jsIsolate.setConsoleCallback(client2);
+                final ListenableFuture<String> resultFuture =
+                        jsIsolate.evaluateJavaScriptAsync(code);
+                final String result = resultFuture.get(5, TimeUnit.SECONDS);
+
+                Assert.assertEquals(expected, result);
+                Assert.assertTrue(client2.latch.await(2, TimeUnit.SECONDS));
+                Assert.assertEquals(expectedLog, client2.messages());
+            }
+            // Test client can be nullified/disabled
+            // (This may tear down the inspector)
+            {
+                jsIsolate.clearConsoleCallback();
+                final ListenableFuture<String> resultFuture =
+                        jsIsolate.evaluateJavaScriptAsync(code);
+                final String result = resultFuture.get(5, TimeUnit.SECONDS);
+
+                Assert.assertEquals(expected, result);
+            }
+            // Ensure console messaging can be re-enabled (on an existing client)
+            // (This may spin up a new inspector)
+            {
+                client1.resetLatch(numOfLogs);
+                jsIsolate.setConsoleCallback(client1);
+                final ListenableFuture<String> resultFuture =
+                        jsIsolate.evaluateJavaScriptAsync(code);
+                final String result = resultFuture.get(5, TimeUnit.SECONDS);
+
+                Assert.assertEquals(expected, result);
+                Assert.assertTrue(client1.latch.await(2, TimeUnit.SECONDS));
+                Assert.assertEquals(expectedLog + expectedLog, client1.messages());
+            }
+            // Ensure client1 and client2 hasn't received anything additional
+            {
+                Thread.sleep(1000);
+                Assert.assertEquals(expectedLog + expectedLog, client1.messages());
+                Assert.assertEquals(expectedLog, client2.messages());
+            }
+        }
+    }
+
+    @Test
+    @MediumTest
+    public void testConsoleCallbackCanCallService() throws Throwable {
+        // This checks that there is nothing intrinsically wrong with calling service APIs from a
+        // console client. Note that, in theory, Binder will reuse the same threads if code recurses
+        // back between processes, which may make subtle (dead)locking bugs harder to detect.
+        //
+        // The console client handler for console.clear will trigger the main evaluation to resolve
+        // via a secondary evaluation.
+        final String code = ""
+                + "var checkIn = null;\n"
+                + "console.clear();\n"
+                + "new Promise((resolve) => {\n"
+                + "  checkIn = resolve;\n"
+                + "})\n";
+        final String callbackCode = ""
+                + "checkIn('PASS');\n"
+                + "'(this callback result is ignored)'\n";
+        final String expected = "PASS";
+        final Context context = ApplicationProvider.getApplicationContext();
+
+        final ListenableFuture<JavaScriptSandbox> jsSandboxFuture =
+                JavaScriptSandbox.createConnectedInstanceAsync(context);
+        try (JavaScriptSandbox jsSandbox = jsSandboxFuture.get(5, TimeUnit.SECONDS);
+             JavaScriptIsolate jsIsolate = jsSandbox.createIsolate()) {
+            Assume.assumeTrue(jsSandbox.isFeatureSupported(
+                    JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING));
+            Assume.assumeTrue(
+                    jsSandbox.isFeatureSupported(JavaScriptSandbox.JS_FEATURE_PROMISE_RETURN));
+
+            CountDownLatch latch = new CountDownLatch(1);
+            jsIsolate.setConsoleCallback(new JavaScriptConsoleCallback() {
+                @Override
+                public void onConsoleMessage(ConsoleMessage message) {}
+
+                @Override
+                public void onConsoleClear() {
+                    jsIsolate.evaluateJavaScriptAsync(callbackCode);
+                    latch.countDown();
+                }
+            });
+            final ListenableFuture<String> resultFuture = jsIsolate.evaluateJavaScriptAsync(code);
+            // Note: the main executor is on a different thread to the instrumentation thread, so
+            // blocking here will not block the console callback.
+            final String result = resultFuture.get(5, TimeUnit.SECONDS);
+            Assert.assertTrue(latch.await(2, TimeUnit.SECONDS));
+            Assert.assertEquals(expected, result);
+        }
+    }
+}
diff --git a/javascriptengine/javascriptengine/src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxConsoleCallback.aidl b/javascriptengine/javascriptengine/src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxConsoleCallback.aidl
new file mode 100644
index 0000000..d18086c
--- /dev/null
+++ b/javascriptengine/javascriptengine/src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxConsoleCallback.aidl
@@ -0,0 +1,49 @@
+/*
+ * 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 org.chromium.android_webview.js_sandbox.common;
+
+/**
+ * Used to relay console messages to the embedding app.
+ * @hide
+ */
+interface IJsSandboxConsoleCallback {
+    // These must be individual bits so that they can be trivially filtered using a bitmask.
+    const int CONSOLE_MESSAGE_LEVEL_LOG = 1 << 0;
+    const int CONSOLE_MESSAGE_LEVEL_DEBUG = 1 << 1;
+    const int CONSOLE_MESSAGE_LEVEL_INFO = 1 << 2;
+    const int CONSOLE_MESSAGE_LEVEL_ERROR = 1 << 3;
+    const int CONSOLE_MESSAGE_LEVEL_WARNING = 1 << 4;
+
+    /**
+     * Notification of a console message.
+     * @param contextGroupId Context group ID.
+     * @param level The message (error/verbosity) level.
+     * @param message The message body.
+     * @param source The source file/expression where the message was generated.
+     * @param line Line number of where the message was generated.
+     * @param column Column number of where the message was generated.
+     * @param trace Stack trace of where the message was generated, which may be null.
+     */
+    void consoleMessage(int contextGroupId, int level, String message, String source, int line,
+            int column, String trace) = 0;
+
+    /**
+     * Notification of a console.clear()
+     * @param contextGroupId context group ID.
+     */
+    void consoleClear(int contextGroupId) = 1;
+}
diff --git a/javascriptengine/javascriptengine/src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxIsolate.aidl b/javascriptengine/javascriptengine/src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxIsolate.aidl
index 3a90693..1905d4f 100644
--- a/javascriptengine/javascriptengine/src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxIsolate.aidl
+++ b/javascriptengine/javascriptengine/src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxIsolate.aidl
@@ -17,7 +17,9 @@
 package org.chromium.android_webview.js_sandbox.common;
 
 import android.content.res.AssetFileDescriptor;
+import org.chromium.android_webview.js_sandbox.common.IJsSandboxConsoleCallback;
 import org.chromium.android_webview.js_sandbox.common.IJsSandboxIsolateCallback;
+import org.chromium.android_webview.js_sandbox.common.IJsSandboxIsolateSyncCallback;
 
 /**
  * Used by the embedding app to execute JavaScript in a sandboxed environment.
@@ -52,6 +54,12 @@
      * @param callback used to pass the information back to the embedding app
      *                 from the sandbox.
      */
-    void evaluateJavascriptWithFd(in AssetFileDescriptor afd, in IJsSandboxIsolateCallback callback)
+    void evaluateJavascriptWithFd(in AssetFileDescriptor afd, in IJsSandboxIsolateSyncCallback callback)
         = 3;
+
+    /**
+     * Set or unset a console callback to receive console messages from the isolate.
+     * @param callback The callback to receive messages, or null to unset.
+     */
+    void setConsoleCallback(IJsSandboxConsoleCallback callback) = 4;
 }
diff --git a/javascriptengine/javascriptengine/src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxIsolateCallback.aidl b/javascriptengine/javascriptengine/src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxIsolateCallback.aidl
index 29c93f6..4b045e1 100644
--- a/javascriptengine/javascriptengine/src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxIsolateCallback.aidl
+++ b/javascriptengine/javascriptengine/src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxIsolateCallback.aidl
@@ -19,6 +19,7 @@
 /**
  * Used to communicate the result of the JavaScript evaluation from the
  * sandbox to the embedding app.
+ * DEPRECATED INTERFACE! Do not add methods or constants into this file.
  * @hide
  */
 oneway interface IJsSandboxIsolateCallback {
diff --git a/javascriptengine/javascriptengine/src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxIsolateSyncCallback.aidl b/javascriptengine/javascriptengine/src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxIsolateSyncCallback.aidl
new file mode 100644
index 0000000..531d767
--- /dev/null
+++ b/javascriptengine/javascriptengine/src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxIsolateSyncCallback.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.chromium.android_webview.js_sandbox.common;
+
+/**
+ * Used to communicate the result of the JavaScript evaluation from the
+ * sandbox to the embedding app.
+ * This interface is not marked 'oneway' like IJsSandboxIsolateCallback and should be preferred for
+ * ordering correctness.
+ * @hide
+ */
+interface IJsSandboxIsolateSyncCallback {
+    // An exception was thrown during the JS evaluation.
+    const int JS_EVALUATION_ERROR = 0;
+    // The evaluation failed and the isolate crashed due to running out of heap memory.
+    const int MEMORY_LIMIT_EXCEEDED = 1;
+
+    /**
+     * @param afd      input AssetFileDescriptor containing the return value of JS evaluation
+     */
+    void reportResultWithFd(in AssetFileDescriptor afd) = 2;
+
+    /**
+     * @param errorType denotes the type of error. Should be one of the constants in this file
+     * @param afd       input AssetFileDescriptor containing the returned error of the JS evaluation
+     */
+    void reportErrorWithFd(int errorType, in AssetFileDescriptor afd) = 3;
+}
diff --git a/javascriptengine/javascriptengine/src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxService.aidl b/javascriptengine/javascriptengine/src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxService.aidl
index 5563e1f..2d65770 100644
--- a/javascriptengine/javascriptengine/src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxService.aidl
+++ b/javascriptengine/javascriptengine/src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxService.aidl
@@ -53,7 +53,13 @@
      * by the Binder transaction limit size.
      */
     const String EVALUATE_WITHOUT_TRANSACTION_LIMIT =
-      "EVALUATE_WITHOUT_TRANSACTION_LIMIT:DEV";
+      "EVALUATE_WITHOUT_TRANSACTION_LIMIT";
+
+    /**
+     * Feature flag indicating that an embedder can subscribe to console messages generated from the
+     * isolate.
+     */
+    const String CONSOLE_MESSAGING = "CONSOLE_MESSAGING";
 
     /**
      * @return A list of feature names supported by this implementation.
@@ -61,5 +67,4 @@
     List<String> getSupportedFeatures() = 1;
 
     IJsSandboxIsolate createIsolateWithMaxHeapSizeBytes(long maxHeapSize) = 2;
-
 }
diff --git a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/EvaluationResultSizeLimitExceededException.java b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/EvaluationResultSizeLimitExceededException.java
new file mode 100644
index 0000000..df56049
--- /dev/null
+++ b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/EvaluationResultSizeLimitExceededException.java
@@ -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.javascriptengine;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Indicates that a JavaScriptIsolate's evaluation failed due to it returning a oversized result.
+ *
+ * This exception is thrown when exceeding the size limit configured for the isolate via
+ * {@link IsolateStartupParameters}, or the default limit.
+ * <p>
+ * The isolate may continue to be used after this exception has been thrown.
+ */
+public final class EvaluationResultSizeLimitExceededException extends JavaScriptException {
+    public EvaluationResultSizeLimitExceededException(@NonNull String error) {
+        super(error);
+    }
+    public EvaluationResultSizeLimitExceededException() {
+        super();
+    }
+}
diff --git a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/IsolateStartupParameters.java b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/IsolateStartupParameters.java
index d32ea7c..df70280 100644
--- a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/IsolateStartupParameters.java
+++ b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/IsolateStartupParameters.java
@@ -24,8 +24,15 @@
  */
 public final class IsolateStartupParameters {
     private long mMaxHeapSizeBytes;
+    private int mMaxEvalutationReturnSizeBytes = DEFAULT_MAX_EVALUATION_RETURN_SIZE_BYTES;
     public static final long DEFAULT_ISOLATE_HEAP_SIZE = 0;
-    public IsolateStartupParameters(){};
+    /**
+     * Default maximum size in bytes for evaluation returns/errors.
+     */
+    public static final int DEFAULT_MAX_EVALUATION_RETURN_SIZE_BYTES = 20 * 1024 * 1024;
+
+    public IsolateStartupParameters() {
+    }
 
     /**
      * Sets the max heap size used by the {@link JavaScriptIsolate}.
@@ -52,15 +59,48 @@
     }
 
     /**
+     * Sets the max size for evaluation return values and errors in the {@link JavaScriptIsolate}.
+     *
+     * The default value is
+     * {@link IsolateStartupParameters#DEFAULT_MAX_EVALUATION_RETURN_SIZE_BYTES}.
+     *
+     * If an evaluation exceeds this limit, {@link EvaluationResultSizeLimitExceededException}
+     * is thrown. Errors will be truncated to adhere to this limit.
+     *
+     * @param size max size in bytes
+     */
+    @RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT,
+            enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
+    public void setMaxEvaluationReturnSizeBytes(
+            @IntRange(from = 0) int size) {
+        if (size < 0) {
+            throw new IllegalArgumentException("maxEvaluationReturnSizeBytes must be >= 0");
+        }
+        mMaxEvalutationReturnSizeBytes = size;
+    }
+
+    /**
      * Gets the max heap size used by the {@link JavaScriptIsolate}.
      *
      * If not set using {@link IsolateStartupParameters#setMaxHeapSizeBytes(long)}, the default
-     * value is {@link IsolateStartupParameters#DEFAULT_ISOLATE_HEAP_SIZE} which indicates no heap
-     * size limit.
+     * value is {@link IsolateStartupParameters#DEFAULT_ISOLATE_HEAP_SIZE} which indicates no
+     * heap size limit.
      *
      * @return heap size in bytes
      */
     public @IntRange(from = 0) long getMaxHeapSizeBytes() {
         return mMaxHeapSizeBytes;
     }
+
+    /**
+     * Gets the max size for evaluation return values and errors in the {@link JavaScriptIsolate}.
+     *
+     * If not set using {@link IsolateStartupParameters#setMaxEvaluationReturnSizeBytes(int)}, the
+     * default value is {@link IsolateStartupParameters#DEFAULT_MAX_EVALUATION_RETURN_SIZE_BYTES}.
+     *
+     * @return max size in bytes
+     */
+    public @IntRange(from = 0) int getMaxEvaluationReturnSizeBytes() {
+        return mMaxEvalutationReturnSizeBytes;
+    }
 }
diff --git a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptConsoleCallback.java b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptConsoleCallback.java
new file mode 100644
index 0000000..0907841
--- /dev/null
+++ b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptConsoleCallback.java
@@ -0,0 +1,170 @@
+/*
+ * 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.javascriptengine;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+
+import org.chromium.android_webview.js_sandbox.common.IJsSandboxConsoleCallback;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A JavaScriptConsoleCallback can be used to receive and process console messages and events which
+ * originate from the isolate.
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public interface JavaScriptConsoleCallback {
+    /**
+     * Representation of a console message, such as produced by console.log.
+     */
+    final class ConsoleMessage {
+        /**
+         * Console message (error) level
+         */
+        @IntDef({LEVEL_LOG, LEVEL_DEBUG, LEVEL_INFO, LEVEL_ERROR, LEVEL_WARNING})
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface Level {}
+        public static final int LEVEL_LOG = IJsSandboxConsoleCallback.CONSOLE_MESSAGE_LEVEL_LOG;
+        public static final int LEVEL_DEBUG = IJsSandboxConsoleCallback.CONSOLE_MESSAGE_LEVEL_DEBUG;
+        public static final int LEVEL_INFO = IJsSandboxConsoleCallback.CONSOLE_MESSAGE_LEVEL_INFO;
+        public static final int LEVEL_ERROR = IJsSandboxConsoleCallback.CONSOLE_MESSAGE_LEVEL_ERROR;
+        public static final int LEVEL_WARNING =
+                IJsSandboxConsoleCallback.CONSOLE_MESSAGE_LEVEL_WARNING;
+        public static final int LEVEL_ALL =
+                LEVEL_LOG | LEVEL_DEBUG | LEVEL_INFO | LEVEL_ERROR | LEVEL_WARNING;
+
+        @Level
+        private final int mLevel;
+        @NonNull
+        private final String mMessage;
+        @NonNull
+        private final String mSource;
+        private final int mLine;
+        private final int mColumn;
+        @Nullable
+        private final String mTrace;
+
+        /**
+         * Construct a new ConsoleMessage
+         * @param level The message (error/verbosity) level.
+         * @param message The message body.
+         * @param source The source file/expression where the message was generated.
+         * @param line Line number of where the message was generated.
+         * @param column Column number of where the message was generated.
+         * @param trace Stack trace of where the message was generated.
+         */
+        public ConsoleMessage(@Level int level, @NonNull String message, @NonNull String source,
+                int line, int column, @Nullable String trace) {
+            mLevel = level;
+            mMessage = message;
+            mSource = source;
+            mLine = line;
+            mColumn = column;
+            mTrace = trace;
+        }
+
+        // Returns a single-character representation of the log level.
+        @NonNull
+        private String levelInitial() {
+            switch (mLevel) {
+                case LEVEL_LOG:
+                    return "L";
+                case LEVEL_DEBUG:
+                    return "D";
+                case LEVEL_INFO:
+                    return "I";
+                case LEVEL_ERROR:
+                    return "E";
+                case LEVEL_WARNING:
+                    return "W";
+                default:
+                    return "?";
+            }
+        };
+
+        /** Return the log level */
+        @Level
+        public int level() {
+            return mLevel;
+        }
+
+        /** Return the message body */
+        @NonNull
+        public String message() {
+            return mMessage;
+        }
+
+        /** Return the source file/expression name */
+        @NonNull
+        public String source() {
+            return mSource;
+        }
+
+        /** Return the line number producing the message */
+        public int line() {
+            return mLine;
+        }
+
+        /** Return the column number producing the message */
+        public int column() {
+            return mColumn;
+        }
+
+        /** Return a stringified stack trace */
+        @Nullable
+        public String trace() {
+            return mTrace;
+        }
+
+        @Override
+        public String toString() {
+            return new StringBuilder()
+                    .append(levelInitial())
+                    .append(" ")
+                    .append(mSource)
+                    .append(":")
+                    .append(mLine)
+                    .append(":")
+                    .append(mColumn)
+                    .append(": ")
+                    .append(mMessage)
+                    .toString();
+        }
+    }
+
+    /**
+     * Called when a console message is produced by the isolate, such as through console.log().
+     * <p>
+     * Do not rely on console messages for the transfer of large volumes of data. Overly large
+     * messages, stack traces, or source identifiers may be truncated.
+     * <p>
+     * The default implementation does nothing.
+     */
+    void onConsoleMessage(@NonNull ConsoleMessage message);
+
+    /**
+     * Called when the console should notionally be cleared, such as through console.clear().
+     * <p>
+     * The default implementation does nothing.
+     */
+    default void onConsoleClear() {}
+};
diff --git a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptIsolate.java b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptIsolate.java
index 29018eb..0b38e04 100644
--- a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptIsolate.java
+++ b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptIsolate.java
@@ -17,7 +17,7 @@
 package androidx.javascriptengine;
 
 import android.content.res.AssetFileDescriptor;
-import android.os.ParcelFileDescriptor;
+import android.os.Binder;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -26,22 +26,21 @@
 import androidx.annotation.RequiresFeature;
 import androidx.annotation.RestrictTo;
 import androidx.concurrent.futures.CallbackToFutureAdapter;
+import androidx.javascriptengine.common.Utils;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
+import org.chromium.android_webview.js_sandbox.common.IJsSandboxConsoleCallback;
 import org.chromium.android_webview.js_sandbox.common.IJsSandboxIsolate;
 import org.chromium.android_webview.js_sandbox.common.IJsSandboxIsolateCallback;
+import org.chromium.android_webview.js_sandbox.common.IJsSandboxIsolateSyncCallback;
 
-import java.io.Closeable;
 import java.io.IOException;
-import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
 import java.util.HashSet;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.annotation.concurrent.GuardedBy;
 
@@ -73,16 +72,7 @@
     @Nullable
     private IJsSandboxIsolate mJsIsolateStub;
     private CloseGuardHelper mGuard = CloseGuardHelper.create();
-    private final ExecutorService mThreadPoolTaskExecutor =
-            Executors.newCachedThreadPool(new ThreadFactory() {
-                private final AtomicInteger mCount = new AtomicInteger(1);
-
-                @Override
-                public Thread newThread(Runnable r) {
-                    return new Thread(r, "JavaScriptIsolate Thread #" + mCount.getAndIncrement());
-                }
-            });
-    private final JavaScriptSandbox mJsSandbox;
+    final JavaScriptSandbox mJsSandbox;
 
     @Nullable
     @GuardedBy("mSetLock")
@@ -96,6 +86,66 @@
     @Nullable
     private Exception mExceptionForNewEvaluations;
     private AtomicBoolean mSandboxClosed = new AtomicBoolean(false);
+    IsolateStartupParameters mStartupParameters;
+
+    private class IJsSandboxIsolateSyncCallbackStubWrapper extends
+            IJsSandboxIsolateSyncCallback.Stub {
+        private CallbackToFutureAdapter.Completer<String> mCompleter;
+
+        IJsSandboxIsolateSyncCallbackStubWrapper(
+                CallbackToFutureAdapter.Completer<String> completer) {
+            mCompleter = completer;
+        }
+
+        @Override
+        public void reportResultWithFd(AssetFileDescriptor afd) {
+            mJsSandbox.mThreadPoolTaskExecutor.execute(
+                    () -> {
+                        String result;
+                        try {
+                            result = Utils.readToString(afd,
+                                    mStartupParameters.getMaxEvaluationReturnSizeBytes(), false);
+                        } catch (IOException | UnsupportedOperationException ex) {
+                            mCompleter.setException(
+                                    new JavaScriptException(
+                                            "Retrieving result failed: " + ex.getMessage()));
+                            removePending(mCompleter);
+                            return;
+                        } catch (IllegalArgumentException ex) {
+                            if (ex.getMessage() != null) {
+                                mCompleter.setException(
+                                        new EvaluationResultSizeLimitExceededException(
+                                                ex.getMessage()));
+                            } else {
+                                mCompleter.setException(
+                                        new EvaluationResultSizeLimitExceededException());
+                            }
+                            removePending(mCompleter);
+                            return;
+                        }
+                        handleEvaluationResult(mCompleter, result);
+                    });
+        }
+
+        @Override
+        public void reportErrorWithFd(@ExecutionErrorTypes int type, AssetFileDescriptor afd) {
+            mJsSandbox.mThreadPoolTaskExecutor.execute(
+                    () -> {
+                        String error;
+                        try {
+                            error = Utils.readToString(afd,
+                                    mStartupParameters.getMaxEvaluationReturnSizeBytes(), true);
+                        } catch (IOException | UnsupportedOperationException ex) {
+                            mCompleter.setException(
+                                    new JavaScriptException(
+                                            "Retrieving error failed: " + ex.getMessage()));
+                            removePending(mCompleter);
+                            return;
+                        }
+                        handleEvaluationError(mCompleter, type, error);
+                    });
+        }
+    }
 
     private class IJsSandboxIsolateCallbackStubWrapper extends IJsSandboxIsolateCallback.Stub {
         private CallbackToFutureAdapter.Completer<String> mCompleter;
@@ -106,37 +156,71 @@
 
         @Override
         public void reportResult(String result) {
-            mCompleter.set(result);
-            removePending(mCompleter);
+            handleEvaluationResult(mCompleter, result);
         }
 
         @Override
         public void reportError(@ExecutionErrorTypes int type, String error) {
-            boolean crashing = false;
-            switch (type) {
-                case IJsSandboxIsolateCallback.JS_EVALUATION_ERROR:
-                    mCompleter.setException(new EvaluationFailedException(error));
-                    break;
-                case IJsSandboxIsolateCallback.MEMORY_LIMIT_EXCEEDED:
-                    mCompleter.setException(new MemoryLimitExceededException(error));
-                    crashing = true;
-                    break;
-                default:
-                    mCompleter.setException(new JavaScriptException(
-                            "Crashing due to unknown JavaScriptException: " + error));
-                    // Assume the worst
-                    crashing = true;
+            handleEvaluationError(mCompleter, type, error);
+        }
+    }
+
+    private static final class JsSandboxConsoleCallbackRelay
+            extends IJsSandboxConsoleCallback.Stub {
+        private final Executor mExecutor;
+        private final JavaScriptConsoleCallback mCallback;
+
+        JsSandboxConsoleCallbackRelay(Executor executor, JavaScriptConsoleCallback callback) {
+            mExecutor = executor;
+            mCallback = callback;
+        }
+
+        @Override
+        public void consoleMessage(final int contextGroupId, final int level, final String message,
+                final String source, final int line, final int column, final String trace) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> {
+                    if ((level & JavaScriptConsoleCallback.ConsoleMessage.LEVEL_ALL) == 0
+                            || ((level - 1) & level) != 0) {
+                        throw new IllegalArgumentException(
+                                "invalid console level " + level + " provided by isolate");
+                    }
+                    if (message == null) {
+                        throw new IllegalArgumentException("null message provided by isolate");
+                    }
+                    if (source == null) {
+                        throw new IllegalArgumentException("null source provided by isolate");
+                    }
+                    mCallback.onConsoleMessage(
+                            new JavaScriptConsoleCallback.ConsoleMessage(
+                                level, message, source, line, column, trace));
+                });
+            } catch (RejectedExecutionException e) {
+                Log.e(TAG, "Console message dropped", e);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
-            removePending(mCompleter);
-            if (crashing) {
-                handleCrash();
+        }
+
+        @Override
+        public void consoleClear(int contextGroupId) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onConsoleClear());
+            } catch (RejectedExecutionException e) {
+                Log.e(TAG, "Console clear dropped", e);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
         }
     }
 
-    JavaScriptIsolate(IJsSandboxIsolate jsIsolateStub, JavaScriptSandbox sandbox) {
+    JavaScriptIsolate(IJsSandboxIsolate jsIsolateStub, JavaScriptSandbox sandbox,
+            IsolateStartupParameters settings) {
         mJsSandbox = sandbox;
         mJsIsolateStub = jsIsolateStub;
+        mStartupParameters = settings;
         mGuard.open("close");
         // This should be at the end of the constructor.
     }
@@ -167,11 +251,12 @@
      * If {@link JavaScriptSandbox#isFeatureSupported(String)} for
      * {@link JavaScriptSandbox#JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT} returns {@code
      * false},
-     * the size of the expression to be evaluated is limited by the binder
-     * transaction limit. If it returns {@code true}, it is not limited by the binder
-     * transaction limit. The result of the evaluation is always bound by the binder
-     * transaction limit. Refer to
-     * {@link android.os.TransactionTooLargeException} for more details.
+     * the size of the expression to be evaluated and the return/error value is limited by the
+     * binder transaction limit ({@link android.os.TransactionTooLargeException}). If it returns
+     * {@code true}, they are not limited by the binder
+     * transaction limit but are bound by
+     * {@link IsolateStartupParameters#setMaxEvaluationReturnSizeBytes(int)} with a default size
+     * of {@link IsolateStartupParameters#DEFAULT_MAX_EVALUATION_RETURN_SIZE_BYTES}.
      *
      * @param code JavaScript code that is evaluated, it should return a JavaScript String or a
      *             Promise of a String in case {@link JavaScriptSandbox#JS_FEATURE_PROMISE_RETURN}
@@ -234,9 +319,7 @@
      *             {@link JavaScriptSandbox#JS_FEATURE_PROMISE_RETURN} is supported
      * @return Future that evaluates to the result String of the evaluation or exceptions (see
      * {@link JavaScriptException} and subclasses) if there is an error
-     * @hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
     @SuppressWarnings("NullAway")
     @NonNull
     @RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT,
@@ -248,7 +331,7 @@
         }
         return CallbackToFutureAdapter.getFuture(completer -> {
             final String futureDebugMessage = "evaluateJavascript Future";
-            IJsSandboxIsolateCallbackStubWrapper callbackStub;
+            IJsSandboxIsolateSyncCallbackStubWrapper callbackStub;
             synchronized (mSetLock) {
                 if (mPendingCompleterSet == null) {
                     completer.setException(mExceptionForNewEvaluations);
@@ -256,17 +339,16 @@
                 }
                 mPendingCompleterSet.add(completer);
             }
-            callbackStub = new IJsSandboxIsolateCallbackStubWrapper(completer);
+            callbackStub = new IJsSandboxIsolateSyncCallbackStubWrapper(completer);
             try {
-                ParcelFileDescriptor readSide = writeBytesIntoPipeAsync(code);
+                AssetFileDescriptor codeAfd = Utils.writeBytesIntoPipeAsync(code,
+                        mJsSandbox.mThreadPoolTaskExecutor);
                 try {
-                    AssetFileDescriptor asd = new AssetFileDescriptor(readSide, /*offset=*/ 0,
-                            /*length=*/ code.length);
-                    mJsIsolateStub.evaluateJavascriptWithFd(asd, callbackStub);
+                    mJsIsolateStub.evaluateJavascriptWithFd(codeAfd, callbackStub);
                 } finally {
-                    // We pass the readSide to the separate sandbox process but we still need to
+                    // We pass the codeAfd to the separate sandbox process but we still need to
                     // close it on our end to avoid file descriptor leaks.
-                    readSide.close();
+                    codeAfd.close();
                 }
             } catch (RemoteException | IOException e) {
                 completer.setException(new RuntimeException(e));
@@ -367,15 +449,14 @@
             throw new NullPointerException("name parameter cannot be null");
         }
         try {
-            ParcelFileDescriptor readSide = writeBytesIntoPipeAsync(inputBytes);
+            AssetFileDescriptor codeAfd = Utils.writeBytesIntoPipeAsync(inputBytes,
+                    mJsSandbox.mThreadPoolTaskExecutor);
             try {
-                AssetFileDescriptor asd = new AssetFileDescriptor(readSide, /*offset=*/ 0,
-                        /*length=*/ inputBytes.length);
-                return mJsIsolateStub.provideNamedData(name, asd);
+                return mJsIsolateStub.provideNamedData(name, codeAfd);
             } finally {
-                // We pass the readSide to the separate sandbox process but we still need to close
+                // We pass the codeAfd to the separate sandbox process but we still need to close
                 // it on our end to avoid file descriptor leaks.
-                readSide.close();
+                codeAfd.close();
             }
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException was thrown during provideNamedData()", e);
@@ -385,18 +466,33 @@
         return false;
     }
 
-    // Creates a pipe, writes the given bytes into one end and returns the other end.
-    ParcelFileDescriptor writeBytesIntoPipeAsync(byte[] inputBytes) throws IOException {
-        ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
-        ParcelFileDescriptor readSide = pipe[0];
-        ParcelFileDescriptor writeSide = pipe[1];
-        OutputStream outputStream =
-                new ParcelFileDescriptor.AutoCloseOutputStream(writeSide);
-        mThreadPoolTaskExecutor.execute(
-                () -> {
-                    writeByteArrayToStream(inputBytes, outputStream);
-                });
-        return readSide;
+    void handleEvaluationError(CallbackToFutureAdapter.Completer<String> completer,
+            int type, String error) {
+        boolean crashing = false;
+        switch (type) {
+            case IJsSandboxIsolateSyncCallback.JS_EVALUATION_ERROR:
+                completer.setException(new EvaluationFailedException(error));
+                break;
+            case IJsSandboxIsolateSyncCallback.MEMORY_LIMIT_EXCEEDED:
+                completer.setException(new MemoryLimitExceededException(error));
+                crashing = true;
+                break;
+            default:
+                completer.setException(new JavaScriptException(
+                        "Crashing due to unknown JavaScriptException: " + error));
+                // Assume the worst
+                crashing = true;
+        }
+        removePending(completer);
+        if (crashing) {
+            handleCrash();
+        }
+    }
+
+    void handleEvaluationResult(CallbackToFutureAdapter.Completer<String> completer,
+            String result) {
+        completer.set(result);
+        removePending(completer);
     }
 
     void notifySandboxClosed() {
@@ -417,11 +513,6 @@
         for (CallbackToFutureAdapter.Completer<String> ele : pendingSet) {
             ele.setException(e);
         }
-        // This is the closest thing to a .close() method for ExecutorServices. This doesn't force
-        // the threads or their Runnables to immediately terminate, but will ensure that once the
-        // worker threads finish their current runnable (if any) that the thread pool terminates
-        // them, preventing a leak of threads.
-        mThreadPoolTaskExecutor.shutdownNow();
     }
 
     void removePending(CallbackToFutureAdapter.Completer<String> completer) {
@@ -436,26 +527,6 @@
         cancelAllPendingEvaluations(new IsolateTerminatedException());
     }
 
-    static void writeByteArrayToStream(byte[] inputBytes, OutputStream outputStream) {
-        try {
-            outputStream.write(inputBytes);
-            outputStream.flush();
-        } catch (IOException e) {
-            Log.e(TAG, "Writing to outputStream failed", e);
-        } finally {
-            closeQuietly(outputStream);
-        }
-    }
-
-    private static void closeQuietly(Closeable closeable) {
-        if (closeable == null) return;
-        try {
-            closeable.close();
-        } catch (IOException ex) {
-            // Ignore the exception on close.
-        }
-    }
-
     @Override
     @SuppressWarnings("GenericException") // super.finalize() throws Throwable
     protected void finalize() throws Throwable {
@@ -470,4 +541,88 @@
             super.finalize();
         }
     }
+
+    /**
+     * Set a custom JavaScriptConsoleCallback to process console messages from the isolate.
+     * <p>
+     * Scripts always have access to console APIs, regardless of whether a ConsoleCallback is
+     * set. By default, no ConsoleCallback is set and calling a console API from JavaScript will do
+     * nothing, and will be relatively cheap. Setting a ConsoleCallback allows console messages to
+     * be forwarded to the embedding application, but may negatively impact performance.
+     * <p>
+     * Note that console APIs may expose messages generated by both JavaScript code and
+     * V8/JavaScriptEngine internals.
+     * <p>
+     * Use caution if using this in production code as it may result in the exposure of debugging
+     * information or secrets through logs.
+     * <p>
+     * When setting a ConsoleCallback, this method should be called before requesting any
+     * evaluations. Calling setConsoleCallback after requesting evaluations may result in those
+     * pending evaluations' console messages being dropped or logged to a previous console callback.
+     * <p>
+     * Note that delayed console messages may continue to be delivered after the isolate has been
+     * closed (or has crashed).
+     * @param executor Executor for running callback methods.
+     * @param callback Callback implementing console logging behaviour.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING,
+            enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
+    public void setConsoleCallback(@NonNull Executor executor,
+            @NonNull JavaScriptConsoleCallback callback) {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor cannot be null");
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("callback cannot be null");
+        }
+        if (mJsIsolateStub == null) {
+            throw new IllegalStateException(
+                    "Calling setConsoleCallback() after closing the Isolate");
+        }
+        try {
+            mJsIsolateStub.setConsoleCallback(
+                    new JsSandboxConsoleCallbackRelay(executor, callback));
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Set a custom JavaScriptConsoleCallback to process console messages from the isolate.
+     * <p>
+     * This is the same as calling {@link #setConsoleCallback(Executor, JavaScriptConsoleCallback}
+     * using the main executor ({@link Context.getMainExecutor()}) of the context used to create the
+     * {@link JavaScriptSandbox} object.
+     * @param callback Callback implementing console logging behaviour.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING,
+            enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
+    public void setConsoleCallback(@NonNull JavaScriptConsoleCallback callback) {
+        setConsoleCallback(mJsSandbox.getMainExecutor(), callback);
+    }
+
+    /**
+     * Clear any JavaScriptConsoleCallback set via {@link #setConsoleCallback}.
+     * <p>
+     * Clearing a callback is not guaranteed to take effect for any already pending evaluations.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @RequiresFeature(name = JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING,
+            enforcement = "androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported")
+    public void clearConsoleCallback() {
+        if (mJsIsolateStub == null) {
+            throw new IllegalStateException(
+                    "Calling clearConsoleCallback() after closing the Isolate");
+        }
+        try {
+            mJsIsolateStub.setConsoleCallback(null);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
diff --git a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptSandbox.java b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptSandbox.java
index f9b536e..6a09bf4 100644
--- a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptSandbox.java
+++ b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptSandbox.java
@@ -46,7 +46,11 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.annotation.concurrent.GuardedBy;
 
@@ -79,6 +83,7 @@
     private static final String TAG = "JavaScriptSandbox";
     private static final String JS_SANDBOX_SERVICE_NAME =
             "org.chromium.android_webview.js_sandbox.service.JsSandboxService0";
+
     static AtomicBoolean sIsReadyToConnect = new AtomicBoolean(true);
     private final Object mLock = new Object();
     private CloseGuardHelper mGuard = CloseGuardHelper.create();
@@ -93,7 +98,18 @@
     @GuardedBy("mLock")
     private HashSet<JavaScriptIsolate> mActiveIsolateSet = new HashSet<JavaScriptIsolate>();
 
+    final ExecutorService mThreadPoolTaskExecutor =
+            Executors.newCachedThreadPool(new ThreadFactory() {
+                private final AtomicInteger mCount = new AtomicInteger(1);
+
+                @Override
+                public Thread newThread(Runnable r) {
+                    return new Thread(r, "JavaScriptSandbox Thread #" + mCount.getAndIncrement());
+                }
+            });
+
     /**
+     *
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY)
     @StringDef(value =
@@ -102,7 +118,9 @@
                     JS_FEATURE_PROMISE_RETURN,
                     JS_FEATURE_PROVIDE_CONSUME_ARRAY_BUFFER,
                     JS_FEATURE_WASM_COMPILATION,
+                    JS_FEATURE_ISOLATE_MAX_HEAP_SIZE,
                     JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT,
+                    JS_FEATURE_CONSOLE_MESSAGING,
             })
     @Retention(RetentionPolicy.SOURCE)
     @Target({ElementType.PARAMETER, ElementType.METHOD})
@@ -166,16 +184,22 @@
      * Feature for {@link #isFeatureSupported(String)}.
      * <p>
      * When this feature is present, the script passed into
-     * {@link JavaScriptIsolate#evaluateJavaScriptAsync(String)} is not limited by the Binder
-     * transaction buffer size.
-     *
-     * @hide
+     * {@link JavaScriptIsolate#evaluateJavaScriptAsync(String)} as well as the result/error is
+     * not limited by the Binder transaction buffer size.
      */
     @SuppressWarnings("IntentName")
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
     public static final String JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT =
             "JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT";
 
+    /**
+     * Feature for {@link #isFeatureSupported(String)}.
+     * <p>
+     * When this feature is present, {@link JavaScriptIsolateClient} can be used.
+     * @hide
+     */
+    public static final String JS_FEATURE_CONSOLE_MESSAGING = "JS_FEATURE_CONSOLE_MESSAGING";
+
+    @GuardedBy("mLock")
     @Nullable
     private HashSet<String> mClientSideFeatureSet;
 
@@ -238,7 +262,7 @@
         }
 
         ConnectionSetup(Context context,
-                        @NonNull CallbackToFutureAdapter.Completer<JavaScriptSandbox> completer) {
+                @NonNull CallbackToFutureAdapter.Completer<JavaScriptSandbox> completer) {
             mContext = context;
             mCompleter = completer;
         }
@@ -359,7 +383,9 @@
     // Use JavaScriptSandbox.createConnectedInstance().
     JavaScriptSandbox(ConnectionSetup connectionSetup, IJsSandboxService jsSandboxService) {
         mConnection = connectionSetup;
-        mJsSandboxService = jsSandboxService;
+        synchronized (mLock) {
+            mJsSandboxService = jsSandboxService;
+        }
         mGuard.open("close");
         // This should be at the end of the constructor.
     }
@@ -370,19 +396,7 @@
      */
     @NonNull
     public JavaScriptIsolate createIsolate() {
-        synchronized (mLock) {
-            if (mJsSandboxService == null) {
-                throw new IllegalStateException(
-                        "Attempting to createIsolate on a service that isn't connected");
-            }
-            IJsSandboxIsolate isolateStub;
-            try {
-                isolateStub = mJsSandboxService.createIsolate();
-            } catch (RemoteException e) {
-                throw new RuntimeException(e);
-            }
-            return createJsIsolateLocked(isolateStub);
-        }
+        return createIsolate(new IsolateStartupParameters());
     }
 
     /**
@@ -414,7 +428,7 @@
             } catch (RemoteException e) {
                 throw new RuntimeException(e);
             }
-            return createJsIsolateLocked(isolateStub);
+            return createJsIsolateLocked(isolateStub, settings);
         }
     }
 
@@ -442,12 +456,16 @@
         if (features.contains(IJsSandboxService.EVALUATE_WITHOUT_TRANSACTION_LIMIT)) {
             mClientSideFeatureSet.add(JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT);
         }
+        if (features.contains(IJsSandboxService.CONSOLE_MESSAGING)) {
+            mClientSideFeatureSet.add(JS_FEATURE_CONSOLE_MESSAGING);
+        }
     }
 
     @GuardedBy("mLock")
     @SuppressWarnings("NullAway")
-    private JavaScriptIsolate createJsIsolateLocked(IJsSandboxIsolate isolateStub) {
-        JavaScriptIsolate isolate = new JavaScriptIsolate(isolateStub, this);
+    private JavaScriptIsolate createJsIsolateLocked(IJsSandboxIsolate isolateStub,
+            IsolateStartupParameters settings) {
+        JavaScriptIsolate isolate = new JavaScriptIsolate(isolateStub, this, settings);
         mActiveIsolateSet.add(isolate);
         return isolate;
     }
@@ -503,6 +521,12 @@
             if (mJsSandboxService == null) {
                 return;
             }
+            // This is the closest thing to a .close() method for ExecutorServices. This doesn't
+            // force the threads or their Runnables to immediately terminate, but will ensure
+            // that once the
+            // worker threads finish their current runnable (if any) that the thread pool terminates
+            // them, preventing a leak of threads.
+            mThreadPoolTaskExecutor.shutdownNow();
             notifyIsolatesAboutClosureLocked();
             mConnection.mContext.unbindService(mConnection);
             // Currently we consider that we are ready for a new connection once we unbind. This
@@ -537,4 +561,8 @@
             super.finalize();
         }
     }
+
+    Executor getMainExecutor() {
+        return ContextCompat.getMainExecutor(mConnection.mContext);
+    }
 }
diff --git a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/common/Utils.java b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/common/Utils.java
new file mode 100644
index 0000000..4d59cf6
--- /dev/null
+++ b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/common/Utils.java
@@ -0,0 +1,197 @@
+/*
+ * 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.javascriptengine.common;
+
+import android.content.res.AssetFileDescriptor;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.io.Closeable;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * Utility methods for use in both service and client side of JavaScriptEngine
+ */
+public class Utils {
+    private static final String TAG = "JavaScriptEngineUtils";
+
+    /**
+     * Utility method to write a byte array into a stream
+     */
+    public static void writeByteArrayToStream(@NonNull byte[] inputBytes,
+            @NonNull OutputStream outputStream) {
+        try {
+            outputStream.write(inputBytes);
+            outputStream.flush();
+        } catch (IOException e) {
+            Log.e(TAG, "Writing to outputStream failed", e);
+        } finally {
+            closeQuietly(outputStream);
+        }
+    }
+
+    /**
+     * Close ignoring exception
+     */
+    public static void closeQuietly(@Nullable Closeable closeable) {
+        if (closeable == null) return;
+        try {
+            closeable.close();
+        } catch (IOException ex) {
+            // Ignore the exception on close.
+        }
+    }
+
+    /**
+     * Creates a pipe, writes the given bytes into one end and returns the other end.
+     */
+    @NonNull
+    public static AssetFileDescriptor writeBytesIntoPipeAsync(@NonNull byte[] inputBytes,
+            @NonNull ExecutorService executorService) throws IOException {
+        ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+        ParcelFileDescriptor readSide = pipe[0];
+        ParcelFileDescriptor writeSide = pipe[1];
+        OutputStream outputStream =
+                new ParcelFileDescriptor.AutoCloseOutputStream(writeSide);
+        executorService.execute(
+                () -> {
+                    Utils.writeByteArrayToStream(inputBytes, outputStream);
+                });
+        return new AssetFileDescriptor(readSide, 0, inputBytes.length);
+    }
+
+    /**
+     * Checks if the given AssetFileDescriptor passes certain conditions
+     */
+    public static void checkAssetFileDescriptor(@NonNull AssetFileDescriptor afd,
+            int maxLength) {
+        if (afd.getStartOffset() != 0) {
+            throw new UnsupportedOperationException(
+                    "AssetFileDescriptor.getStartOffset() != 0");
+        }
+        if (afd.getLength() < 0) {
+            throw new UnsupportedOperationException(
+                    "AssetFileDescriptor.getLength() should be >=0");
+        }
+        if (afd.getLength() > maxLength) {
+            throw new IllegalArgumentException(
+                    "AssetFileDescriptor.getLength() should be <= " + Integer.toString(maxLength));
+        }
+    }
+
+    /**
+     * Read a given number of bytes from a given stream into a byte array
+     *
+     * This allows us to use
+     * <a href=https://developer.android.com/reference/java/io/InputStream#readNBytes(byte[],%20int,%20int)">
+     * this </a>
+     * functionality added in API 33.
+     */
+    public static int readNBytes(@NonNull InputStream inputStream, @NonNull byte[] b, int off,
+            int len)
+            throws IOException {
+        int n = 0;
+        while (n < len) {
+            int count = inputStream.read(b, off + n, len - n);
+            if (count < 0) {
+                break;
+            }
+            n += count;
+        }
+        return n;
+    }
+
+    /**
+     * Checks whether a given byte is a UTF8 continuation byte. If a byte can be part of valid
+     * UTF-8 and is not a continuation byte, it must be a starting byte.
+     */
+    public static boolean isUTF8ContinuationByte(byte b) {
+        final byte maskContinuationByte = (byte) 0b11000000;
+        final byte targetContinuationByte = (byte) 0b10000000;
+        // Checks whether it looks like "0b10xxxxxx"
+        return (b & maskContinuationByte) == targetContinuationByte;
+    }
+
+    /**
+     * Returns the index of right-most UTF-8 starting byte.
+     *
+     * The input must be valid (or truncated) UTF-8 encoded bytes.
+     * Returns -1 if there is no starting byte.
+     */
+    public static int getLastUTF8StartingByteIndex(@NonNull byte[] bytes) {
+        for (int index = bytes.length - 1; index >= 0; index--) {
+            if (!isUTF8ContinuationByte(bytes[index])) {
+                return index;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Read from a AssetFileDescriptor into a String and closes it in case of both success and
+     * failure.
+     */
+    @NonNull
+    public static String readToString(@NonNull AssetFileDescriptor afd, int maxLength,
+            boolean truncate)
+            throws IOException {
+        try {
+            int lengthToRead;
+            try {
+                Utils.checkAssetFileDescriptor(afd, maxLength);
+                lengthToRead = (int) afd.getLength();
+            } catch (IllegalArgumentException ex) {
+                if (!truncate) {
+                    throw ex;
+                } else {
+                    // If truncate is true, read how much ever you are allowed to read.
+                    lengthToRead = maxLength;
+                }
+            }
+            byte[] bytes = new byte[lengthToRead];
+            // We can use AssetFileDescriptor.createInputStream() to get the InputStream directly
+            // but this API is currently broken while fixing another issue regarding multiple
+            // AssetFileDescriptor pointing to the same file. (b/263325931)
+            // Using ParcelFileDescriptor to read the file is correct as long as the offset is 0.
+            try (ParcelFileDescriptor pfd = afd.getParcelFileDescriptor()) {
+                InputStream inputStream = new FileInputStream(pfd.getFileDescriptor());
+                if (Utils.readNBytes(inputStream, bytes, 0, lengthToRead) != lengthToRead) {
+                    throw new IOException("Couldn't read " + lengthToRead + " bytes from the "
+                            + "AssetFileDescriptor");
+                }
+            }
+            int validUtf8PrefixLength = lengthToRead;
+            if (truncate) {
+                // Ignoring the last partial/complete codepoint.
+                validUtf8PrefixLength = getLastUTF8StartingByteIndex(bytes);
+            }
+            // This process can be made more memory efficient by converting the UTF-8 encoded
+            // bytes to String by reading from the pipe in chunks.
+            return new String(bytes, 0, validUtf8PrefixLength, StandardCharsets.UTF_8);
+        } finally {
+            afd.close();
+        }
+    }
+}
diff --git a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/common/package-info.java b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/common/package-info.java
new file mode 100644
index 0000000..f10a922
--- /dev/null
+++ b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/common/package-info.java
@@ -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.
+ */
+
+/**
+ *
+ */
+@RestrictTo(LIBRARY)
+package androidx.javascriptengine.common;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import androidx.annotation.RestrictTo;
diff --git a/libraryversions.toml b/libraryversions.toml
index e2f8957..0b35a8c 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -1,30 +1,26 @@
 [versions]
 ACTIVITY = "1.8.0-alpha02"
-ADS_IDENTIFIER = "1.0.0-alpha06"
-ANNOTATION = "1.7.0-alpha01"
+ANNOTATION = "1.7.0-alpha02"
 ANNOTATION_EXPERIMENTAL = "1.4.0-alpha01"
-ANNOTATION_EXPERIMENTAL_KMP = "1.4.0-dev01"
-ANNOTATION_KMP = "1.6.0-dev01"
 APPACTIONS_INTERACTION = "1.0.0-alpha01"
 APPCOMPAT = "1.7.0-alpha03"
 APPSEARCH = "1.1.0-alpha03"
 ARCH_CORE = "2.3.0-alpha01"
 ASYNCLAYOUTINFLATER = "1.1.0-alpha02"
-AUTOFILL = "1.2.0-beta02"
+AUTOFILL = "1.3.0-alpha01"
 BENCHMARK = "1.2.0-alpha12"
 BIOMETRIC = "1.2.0-alpha06"
 BLUETOOTH = "1.0.0-alpha01"
 BROWSER = "1.6.0-alpha01"
 BUILDSRC_TESTS = "1.0.0-alpha01"
-CAMERA = "1.3.0-alpha04"
+CAMERA = "1.3.0-alpha05"
 CAMERA_PIPE = "1.0.0-alpha01"
 CARDVIEW = "1.1.0-alpha01"
 CAR_APP = "1.4.0-alpha01"
-COLLECTION = "1.3.0-alpha03"
-COLLECTION_KMP = "1.3.0-dev01"
+COLLECTION = "1.3.0-alpha04"
 COMPOSE = "1.5.0-alpha01"
-COMPOSE_COMPILER = "1.4.3"
-COMPOSE_MATERIAL3 = "1.1.0-alpha09"
+COMPOSE_COMPILER = "1.4.4"
+COMPOSE_MATERIAL3 = "1.1.0-beta01"
 COMPOSE_RUNTIME_TRACING = "1.0.0-alpha03"
 CONSTRAINTLAYOUT = "2.2.0-alpha09"
 CONSTRAINTLAYOUT_COMPOSE = "1.1.0-alpha09"
@@ -47,16 +43,14 @@
 CURSORADAPTER = "1.1.0-alpha01"
 CUSTOMVIEW = "1.2.0-alpha03"
 CUSTOMVIEW_POOLINGCONTAINER = "1.1.0-alpha01"
-DATASTORE = "1.1.0-alpha02"
-DATASTORE_KMP = "1.1.0-dev01"
+DATASTORE = "1.1.0-alpha03"
 DOCUMENTFILE = "1.1.0-alpha02"
 DRAGANDDROP = "1.1.0-alpha01"
 DRAWERLAYOUT = "1.3.0-alpha01"
 DYNAMICANIMATION = "1.1.0-alpha04"
 DYNAMICANIMATION_KTX = "1.0.0-alpha04"
 EMOJI = "1.2.0-alpha03"
-EMOJI2 = "1.4.0-alpha01"
-EMOJI2_QUARANTINE = "1.0.0-alpha03"
+EMOJI2 = "1.4.0-beta01"
 ENTERPRISE = "1.1.0-rc01"
 EXIFINTERFACE = "1.4.0-alpha01"
 FRAGMENT = "1.6.0-alpha08"
@@ -88,7 +82,7 @@
 LOADER = "1.2.0-alpha01"
 MEDIA = "1.7.0-alpha02"
 MEDIA2 = "1.3.0-alpha01"
-MEDIAROUTER = "1.4.0-beta02"
+MEDIAROUTER = "1.4.0-rc01"
 METRICS = "1.0.0-alpha04"
 NAVIGATION = "2.6.0-alpha08"
 PAGING = "3.2.0-alpha05"
@@ -97,9 +91,9 @@
 PERCENTLAYOUT = "1.1.0-alpha01"
 PREFERENCE = "1.3.0-alpha01"
 PRINT = "1.1.0-beta01"
-PRIVACYSANDBOX_ADS = "1.0.0-beta01"
+PRIVACYSANDBOX_ADS = "1.0.0-beta02"
 PRIVACYSANDBOX_PLUGINS = "1.0.0-alpha01"
-PRIVACYSANDBOX_SDKRUNTIME = "1.0.0-alpha01"
+PRIVACYSANDBOX_SDKRUNTIME = "1.0.0-alpha03"
 PRIVACYSANDBOX_TOOLS = "1.0.0-alpha04"
 PRIVACYSANDBOX_UI = "1.0.0-alpha02"
 PROFILEINSTALLER = "1.4.0-alpha01"
@@ -126,14 +120,14 @@
 STABLE_AIDL = "1.0.0-alpha01"
 STARTUP = "1.2.0-alpha03"
 SWIPEREFRESHLAYOUT = "1.2.0-alpha01"
-TESTEXT = "1.0.0-alpha01"
+TESTEXT = "1.0.0-alpha02"
 TESTSCREENSHOT = "1.0.0-alpha01"
 TEST_UIAUTOMATOR = "2.3.0-alpha03"
 TEXT = "1.0.0-alpha01"
 TRACING = "1.2.0-beta02"
 TRACING_PERFETTO = "1.0.0-alpha13"
 TRANSITION = "1.5.0-alpha01"
-TV = "1.0.0-alpha04"
+TV = "1.0.0-alpha05"
 TVPROVIDER = "1.1.0-alpha02"
 VECTORDRAWABLE = "1.2.0-beta02"
 VECTORDRAWABLE_ANIMATED = "1.2.0-beta01"
@@ -153,15 +147,14 @@
 WEAR_TILES = "1.2.0-alpha02"
 WEAR_WATCHFACE = "1.2.0-alpha07"
 WEBKIT = "1.7.0-beta01"
-WINDOW = "1.1.0-beta01"
-WINDOW_EXTENSIONS = "1.1.0-beta01"
-WINDOW_EXTENSIONS_CORE = "1.0.0-beta01"
+WINDOW = "1.1.0-beta02"
+WINDOW_EXTENSIONS = "1.1.0-beta02"
+WINDOW_EXTENSIONS_CORE = "1.0.0-beta02"
 WINDOW_SIDECAR = "1.0.0-rc01"
 WORK = "2.9.0-alpha01"
 
 [groups]
 ACTIVITY = { group = "androidx.activity", atomicGroupVersion = "versions.ACTIVITY" }
-ADS = { group = "androidx.ads" }
 ANNOTATION = { group = "androidx.annotation" }
 APPACTIONS_INTERACTION = { group = "androidx.appactions.interaction", atomicGroupVersion = "versions.APPACTIONS_INTERACTION" }
 APPCOMPAT = { group = "androidx.appcompat", atomicGroupVersion = "versions.APPCOMPAT" }
@@ -178,7 +171,7 @@
 CAMERA = { group = "androidx.camera", atomicGroupVersion = "versions.CAMERA" }
 CARDVIEW = { group = "androidx.cardview", atomicGroupVersion = "versions.CARDVIEW" }
 CAR_APP = { group = "androidx.car.app", atomicGroupVersion = "versions.CAR_APP" }
-COLLECTION = { group = "androidx.collection", atomicGroupVersion = "versions.COLLECTION", multiplatformGroupVersion = "versions.COLLECTION_KMP" }
+COLLECTION = { group = "androidx.collection", atomicGroupVersion = "versions.COLLECTION" }
 COMPOSE_ANIMATION = { group = "androidx.compose.animation", atomicGroupVersion = "versions.COMPOSE" }
 COMPOSE_COMPILER = { group = "androidx.compose.compiler", atomicGroupVersion = "versions.COMPOSE_COMPILER" }
 COMPOSE_DESKTOP = { group = "androidx.compose.desktop", atomicGroupVersion = "versions.COMPOSE" }
@@ -196,7 +189,7 @@
 CREDENTIALS = { group = "androidx.credentials", atomicGroupVersion = "versions.CREDENTIALS" }
 CURSORADAPTER = { group = "androidx.cursoradapter", atomicGroupVersion = "versions.CURSORADAPTER" }
 CUSTOMVIEW = { group = "androidx.customview" }
-DATASTORE = { group = "androidx.datastore", atomicGroupVersion = "versions.DATASTORE", multiplatformGroupVersion = "versions.DATASTORE_KMP" }
+DATASTORE = { group = "androidx.datastore", atomicGroupVersion = "versions.DATASTORE" }
 DOCUMENTFILE = { group = "androidx.documentfile", atomicGroupVersion = "versions.DOCUMENTFILE" }
 DRAGANDDROP = { group = "androidx.draganddrop", atomicGroupVersion = "versions.DRAGANDDROP" }
 DRAWERLAYOUT = { group = "androidx.drawerlayout", atomicGroupVersion = "versions.DRAWERLAYOUT" }
diff --git a/lifecycle/lifecycle-common/api/current.txt b/lifecycle/lifecycle-common/api/current.txt
index 263aec8..c68d98d 100644
--- a/lifecycle/lifecycle-common/api/current.txt
+++ b/lifecycle/lifecycle-common/api/current.txt
@@ -14,8 +14,10 @@
     ctor public Lifecycle();
     method @MainThread public abstract void addObserver(androidx.lifecycle.LifecycleObserver observer);
     method @MainThread public abstract androidx.lifecycle.Lifecycle.State getCurrentState();
+    method public kotlinx.coroutines.flow.StateFlow<androidx.lifecycle.Lifecycle.State> getCurrentStateFlow();
     method @MainThread public abstract void removeObserver(androidx.lifecycle.LifecycleObserver observer);
     property @MainThread public abstract androidx.lifecycle.Lifecycle.State currentState;
+    property public kotlinx.coroutines.flow.StateFlow<androidx.lifecycle.Lifecycle.State> currentStateFlow;
   }
 
   public enum Lifecycle.Event {
diff --git a/lifecycle/lifecycle-common/api/public_plus_experimental_current.txt b/lifecycle/lifecycle-common/api/public_plus_experimental_current.txt
index 263aec8..c68d98d 100644
--- a/lifecycle/lifecycle-common/api/public_plus_experimental_current.txt
+++ b/lifecycle/lifecycle-common/api/public_plus_experimental_current.txt
@@ -14,8 +14,10 @@
     ctor public Lifecycle();
     method @MainThread public abstract void addObserver(androidx.lifecycle.LifecycleObserver observer);
     method @MainThread public abstract androidx.lifecycle.Lifecycle.State getCurrentState();
+    method public kotlinx.coroutines.flow.StateFlow<androidx.lifecycle.Lifecycle.State> getCurrentStateFlow();
     method @MainThread public abstract void removeObserver(androidx.lifecycle.LifecycleObserver observer);
     property @MainThread public abstract androidx.lifecycle.Lifecycle.State currentState;
+    property public kotlinx.coroutines.flow.StateFlow<androidx.lifecycle.Lifecycle.State> currentStateFlow;
   }
 
   public enum Lifecycle.Event {
diff --git a/lifecycle/lifecycle-common/api/restricted_current.txt b/lifecycle/lifecycle-common/api/restricted_current.txt
index 440f8b9..634b76f 100644
--- a/lifecycle/lifecycle-common/api/restricted_current.txt
+++ b/lifecycle/lifecycle-common/api/restricted_current.txt
@@ -21,8 +21,10 @@
     ctor public Lifecycle();
     method @MainThread public abstract void addObserver(androidx.lifecycle.LifecycleObserver observer);
     method @MainThread public abstract androidx.lifecycle.Lifecycle.State getCurrentState();
+    method public kotlinx.coroutines.flow.StateFlow<androidx.lifecycle.Lifecycle.State> getCurrentStateFlow();
     method @MainThread public abstract void removeObserver(androidx.lifecycle.LifecycleObserver observer);
     property @MainThread public abstract androidx.lifecycle.Lifecycle.State currentState;
+    property public kotlinx.coroutines.flow.StateFlow<androidx.lifecycle.Lifecycle.State> currentStateFlow;
   }
 
   public enum Lifecycle.Event {
diff --git a/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/Lifecycle.kt b/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/Lifecycle.kt
index b52e8f5..ac586d8 100644
--- a/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/Lifecycle.kt
+++ b/lifecycle/lifecycle-common/src/main/java/androidx/lifecycle/Lifecycle.kt
@@ -28,6 +28,9 @@
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.callbackFlow
 import kotlinx.coroutines.flow.flowOn
 
@@ -95,6 +98,22 @@
     @get:MainThread
     public abstract val currentState: State
 
+    /**
+     * Returns a [StateFlow] where the [StateFlow.value] represents
+     * the current [State] of this Lifecycle.
+     *
+     * @return [StateFlow] where the [StateFlow.value] represents
+     * the current [State] of this Lifecycle.
+     */
+    public open val currentStateFlow: StateFlow<Lifecycle.State>
+        get() {
+            val mutableStateFlow = MutableStateFlow(currentState)
+            LifecycleEventObserver { _, event ->
+                mutableStateFlow.value = event.targetState
+            }.also { addObserver(it) }
+            return mutableStateFlow.asStateFlow()
+        }
+
     public enum class Event {
         /**
          * Constant for onCreate event of the [LifecycleOwner].
diff --git a/lifecycle/lifecycle-runtime-compose/api/current.txt b/lifecycle/lifecycle-runtime-compose/api/current.txt
index c80fa83..95c6977 100644
--- a/lifecycle/lifecycle-runtime-compose/api/current.txt
+++ b/lifecycle/lifecycle-runtime-compose/api/current.txt
@@ -8,5 +8,9 @@
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.Flow<? extends T>, T? initialValue, androidx.lifecycle.Lifecycle lifecycle, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
   }
 
+  public final class LifecycleExtKt {
+    method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.lifecycle.Lifecycle.State> currentStateAsState(androidx.lifecycle.Lifecycle);
+  }
+
 }
 
diff --git a/lifecycle/lifecycle-runtime-compose/api/public_plus_experimental_current.txt b/lifecycle/lifecycle-runtime-compose/api/public_plus_experimental_current.txt
index c80fa83..95c6977 100644
--- a/lifecycle/lifecycle-runtime-compose/api/public_plus_experimental_current.txt
+++ b/lifecycle/lifecycle-runtime-compose/api/public_plus_experimental_current.txt
@@ -8,5 +8,9 @@
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.Flow<? extends T>, T? initialValue, androidx.lifecycle.Lifecycle lifecycle, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
   }
 
+  public final class LifecycleExtKt {
+    method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.lifecycle.Lifecycle.State> currentStateAsState(androidx.lifecycle.Lifecycle);
+  }
+
 }
 
diff --git a/lifecycle/lifecycle-runtime-compose/api/restricted_current.txt b/lifecycle/lifecycle-runtime-compose/api/restricted_current.txt
index c80fa83..95c6977 100644
--- a/lifecycle/lifecycle-runtime-compose/api/restricted_current.txt
+++ b/lifecycle/lifecycle-runtime-compose/api/restricted_current.txt
@@ -8,5 +8,9 @@
     method @androidx.compose.runtime.Composable public static <T> androidx.compose.runtime.State<T> collectAsStateWithLifecycle(kotlinx.coroutines.flow.Flow<? extends T>, T? initialValue, androidx.lifecycle.Lifecycle lifecycle, optional androidx.lifecycle.Lifecycle.State minActiveState, optional kotlin.coroutines.CoroutineContext context);
   }
 
+  public final class LifecycleExtKt {
+    method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<androidx.lifecycle.Lifecycle.State> currentStateAsState(androidx.lifecycle.Lifecycle);
+  }
+
 }
 
diff --git a/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleExtTest.kt b/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleExtTest.kt
new file mode 100644
index 0000000..255d06a
--- /dev/null
+++ b/lifecycle/lifecycle-runtime-compose/src/androidTest/java/androidx/lifecycle/compose/LifecycleExtTest.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.lifecycle.compose
+
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.testing.TestLifecycleOwner
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class LifecycleExtTest {
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    private val lifecycleOwner = TestLifecycleOwner(
+        Lifecycle.State.INITIALIZED,
+        UnconfinedTestDispatcher()
+    )
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun lifecycleCollectAsState() {
+        val lifecycle = lifecycleOwner.lifecycle
+        assertThat(lifecycle.currentStateFlow.value).isEqualTo(Lifecycle.State.INITIALIZED)
+
+        var realStateValue: Lifecycle.State? = null
+        rule.setContent {
+            realStateValue = lifecycle.currentStateAsState().value
+        }
+
+        rule.runOnIdle {
+            assertThat(realStateValue).isEqualTo(Lifecycle.State.INITIALIZED)
+        }
+
+        lifecycleOwner.currentState = Lifecycle.State.RESUMED
+        rule.runOnIdle {
+            assertThat(realStateValue).isEqualTo(Lifecycle.State.RESUMED)
+        }
+    }
+}
\ No newline at end of file
diff --git a/lifecycle/lifecycle-runtime-compose/src/main/java/androidx/lifecycle/compose/LifecycleExt.kt b/lifecycle/lifecycle-runtime-compose/src/main/java/androidx/lifecycle/compose/LifecycleExt.kt
new file mode 100644
index 0000000..27ed591
--- /dev/null
+++ b/lifecycle/lifecycle-runtime-compose/src/main/java/androidx/lifecycle/compose/LifecycleExt.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.lifecycle.compose
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.collectAsState
+import androidx.lifecycle.Lifecycle
+import kotlinx.coroutines.flow.StateFlow
+
+/**
+ * Collects values from the [Lifecycle.currentStateFlow] and represents its latest value via [State].
+ * The [StateFlow.value] is used as an initial value. Every time there would be new value posted
+ * into the [StateFlow] the returned [State] will be updated causing recomposition of every
+ * [State.value] usage.
+ */
+@Composable
+fun Lifecycle.currentStateAsState(): State<Lifecycle.State> = currentStateFlow.collectAsState()
\ No newline at end of file
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
index 00e71a8..6f0c1ec 100644
--- 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
@@ -18,19 +18,34 @@
 
 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 {
-    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+
+    private val dispatcher = UnconfinedTestDispatcher()
     private val lifecycleOwner = TestLifecycleOwner(
         Lifecycle.State.INITIALIZED,
-        UnconfinedTestDispatcher()
+        dispatcher
     )
+    private val testScope = TestScope(dispatcher)
+
+    @Before
+    fun setMainDispatcher() {
+        Dispatchers.setMain(dispatcher)
+    }
 
     @Test
     fun getCurrentState() {
@@ -42,6 +57,79 @@
     }
 
     @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)
diff --git a/lifecycle/lifecycle-runtime/api/current.txt b/lifecycle/lifecycle-runtime/api/current.txt
index e72bd60..b3a4ef0 100644
--- a/lifecycle/lifecycle-runtime/api/current.txt
+++ b/lifecycle/lifecycle-runtime/api/current.txt
@@ -12,6 +12,7 @@
     method public void removeObserver(androidx.lifecycle.LifecycleObserver observer);
     method public void setCurrentState(androidx.lifecycle.Lifecycle.State);
     property public androidx.lifecycle.Lifecycle.State currentState;
+    property public kotlinx.coroutines.flow.StateFlow<androidx.lifecycle.Lifecycle.State> currentStateFlow;
     property public int observerCount;
     field public static final androidx.lifecycle.LifecycleRegistry.Companion Companion;
   }
diff --git a/lifecycle/lifecycle-runtime/api/public_plus_experimental_current.txt b/lifecycle/lifecycle-runtime/api/public_plus_experimental_current.txt
index e72bd60..b3a4ef0 100644
--- a/lifecycle/lifecycle-runtime/api/public_plus_experimental_current.txt
+++ b/lifecycle/lifecycle-runtime/api/public_plus_experimental_current.txt
@@ -12,6 +12,7 @@
     method public void removeObserver(androidx.lifecycle.LifecycleObserver observer);
     method public void setCurrentState(androidx.lifecycle.Lifecycle.State);
     property public androidx.lifecycle.Lifecycle.State currentState;
+    property public kotlinx.coroutines.flow.StateFlow<androidx.lifecycle.Lifecycle.State> currentStateFlow;
     property public int observerCount;
     field public static final androidx.lifecycle.LifecycleRegistry.Companion Companion;
   }
diff --git a/lifecycle/lifecycle-runtime/api/restricted_current.txt b/lifecycle/lifecycle-runtime/api/restricted_current.txt
index 704cdb4..8cfc8dc 100644
--- a/lifecycle/lifecycle-runtime/api/restricted_current.txt
+++ b/lifecycle/lifecycle-runtime/api/restricted_current.txt
@@ -12,6 +12,7 @@
     method public void removeObserver(androidx.lifecycle.LifecycleObserver observer);
     method public void setCurrentState(androidx.lifecycle.Lifecycle.State);
     property public androidx.lifecycle.Lifecycle.State currentState;
+    property public kotlinx.coroutines.flow.StateFlow<androidx.lifecycle.Lifecycle.State> currentStateFlow;
     property public int observerCount;
     field public static final androidx.lifecycle.LifecycleRegistry.Companion Companion;
   }
diff --git a/lifecycle/lifecycle-runtime/build.gradle b/lifecycle/lifecycle-runtime/build.gradle
index fbbd6f1..5199df9 100644
--- a/lifecycle/lifecycle-runtime/build.gradle
+++ b/lifecycle/lifecycle-runtime/build.gradle
@@ -21,7 +21,7 @@
     // necessary for IJ to resolve dependencies.
     api("androidx.annotation:annotation:1.1.0")
     implementation("androidx.arch.core:core-runtime:2.2.0")
-    implementation("androidx.profileinstaller:profileinstaller:1.2.1")
+    implementation("androidx.profileinstaller:profileinstaller:1.3.0")
 
     testImplementation(libs.junit)
     testImplementation(libs.mockitoCore4)
diff --git a/lifecycle/lifecycle-runtime/src/main/java/androidx/lifecycle/LifecycleRegistry.kt b/lifecycle/lifecycle-runtime/src/main/java/androidx/lifecycle/LifecycleRegistry.kt
index 8da7e1e..88f3480 100644
--- a/lifecycle/lifecycle-runtime/src/main/java/androidx/lifecycle/LifecycleRegistry.kt
+++ b/lifecycle/lifecycle-runtime/src/main/java/androidx/lifecycle/LifecycleRegistry.kt
@@ -21,6 +21,9 @@
 import androidx.arch.core.executor.ArchTaskExecutor
 import androidx.arch.core.internal.FastSafeIterableMap
 import java.lang.ref.WeakReference
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
 
 /**
  * An implementation of [Lifecycle] that can handle multiple observers.
@@ -106,6 +109,10 @@
             moveToState(state)
         }
 
+    private val _currentStateFlow: MutableStateFlow<State> = MutableStateFlow(State.INITIALIZED)
+    override val currentStateFlow: StateFlow<State>
+        get() = _currentStateFlow.asStateFlow()
+
     /**
      * Sets the current state and notifies the observers.
      *
@@ -288,6 +295,7 @@
             }
         }
         newEventOccurred = false
+        _currentStateFlow.value = currentState
     }
 
     @SuppressLint("RestrictedApi")
diff --git a/mediarouter/mediarouter/api/current.txt b/mediarouter/mediarouter/api/current.txt
index b1cf094..b832be0 100644
--- a/mediarouter/mediarouter/api/current.txt
+++ b/mediarouter/mediarouter/api/current.txt
@@ -23,7 +23,7 @@
     method public androidx.mediarouter.media.MediaRouteSelector getRouteSelector();
     method public void onAttachedToWindow();
     method public void onDetachedFromWindow();
-    method public void setAlwaysVisible(boolean);
+    method @Deprecated public void setAlwaysVisible(boolean);
     method public void setDialogFactory(androidx.mediarouter.app.MediaRouteDialogFactory);
     method public void setRemoteIndicatorDrawable(android.graphics.drawable.Drawable?);
     method public void setRouteSelector(androidx.mediarouter.media.MediaRouteSelector);
diff --git a/mediarouter/mediarouter/api/public_plus_experimental_current.txt b/mediarouter/mediarouter/api/public_plus_experimental_current.txt
index b1cf094..b832be0 100644
--- a/mediarouter/mediarouter/api/public_plus_experimental_current.txt
+++ b/mediarouter/mediarouter/api/public_plus_experimental_current.txt
@@ -23,7 +23,7 @@
     method public androidx.mediarouter.media.MediaRouteSelector getRouteSelector();
     method public void onAttachedToWindow();
     method public void onDetachedFromWindow();
-    method public void setAlwaysVisible(boolean);
+    method @Deprecated public void setAlwaysVisible(boolean);
     method public void setDialogFactory(androidx.mediarouter.app.MediaRouteDialogFactory);
     method public void setRemoteIndicatorDrawable(android.graphics.drawable.Drawable?);
     method public void setRouteSelector(androidx.mediarouter.media.MediaRouteSelector);
diff --git a/mediarouter/mediarouter/api/restricted_current.txt b/mediarouter/mediarouter/api/restricted_current.txt
index b1cf094..b832be0 100644
--- a/mediarouter/mediarouter/api/restricted_current.txt
+++ b/mediarouter/mediarouter/api/restricted_current.txt
@@ -23,7 +23,7 @@
     method public androidx.mediarouter.media.MediaRouteSelector getRouteSelector();
     method public void onAttachedToWindow();
     method public void onDetachedFromWindow();
-    method public void setAlwaysVisible(boolean);
+    method @Deprecated public void setAlwaysVisible(boolean);
     method public void setDialogFactory(androidx.mediarouter.app.MediaRouteDialogFactory);
     method public void setRemoteIndicatorDrawable(android.graphics.drawable.Drawable?);
     method public void setRouteSelector(androidx.mediarouter.media.MediaRouteSelector);
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteButton.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteButton.java
index 9c8a0f3..db8b8dc 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteButton.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteButton.java
@@ -17,18 +17,13 @@
 package androidx.mediarouter.app;
 
 import android.app.Activity;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.ContextWrapper;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.Drawable;
-import android.net.ConnectivityManager;
-import android.os.Build;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -36,10 +31,8 @@
 import android.view.SoundEffectConstants;
 import android.view.View;
 
-import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
 import androidx.appcompat.content.res.AppCompatResources;
 import androidx.appcompat.widget.TooltipCompat;
 import androidx.core.graphics.drawable.DrawableCompat;
@@ -52,9 +45,6 @@
 import androidx.mediarouter.media.MediaRouter;
 import androidx.mediarouter.media.MediaRouterParams;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * The media route button allows the user to select routes and to control the currently selected
  * route.
@@ -66,7 +56,7 @@
  * that the application is not connected to a route. Clicking on the button opens a
  * {@link MediaRouteChooserDialog} to allow the user to select a route. If no non-default routes
  * match the selector and it is not possible for an active scan to discover any matching routes,
- * then the button is disabled and cannot be clicked unless {@link #setAlwaysVisible} is called.
+ * then the button is disabled.
  *
  * <p>When a non-default route is selected, the button will appear in an active state indicating
  * that the application is connected to a route of the kind that it wants to use. The button may
@@ -89,8 +79,6 @@
             "android.support.v7.mediarouter:MediaRouteChooserDialogFragment";
     private static final String CONTROLLER_FRAGMENT_TAG =
             "android.support.v7.mediarouter:MediaRouteControllerDialogFragment";
-    // Used to check connectivity and hide the button
-    private static ConnectivityReceiver sConnectivityReceiver;
 
     private final MediaRouter mRouter;
     private final MediaRouterCallback mCallback;
@@ -100,8 +88,6 @@
 
     private boolean mAttachedToWindow;
 
-    private int mVisibility = VISIBLE;
-
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     boolean mIsFixedIcon;
 
@@ -126,7 +112,6 @@
     private int mMinWidth;
     private int mMinHeight;
 
-    private boolean mAlwaysVisible;
     private boolean mCheatSheetEnabled;
 
     // The checked state is used when connected to a remote route.
@@ -171,10 +156,6 @@
         mLastConnectionState = mConnectionState =
                 (isRemote ? selectedRoute.getConnectionState() : CONNECTION_STATE_DISCONNECTED);
 
-        if (sConnectivityReceiver == null) {
-            sConnectivityReceiver = new ConnectivityReceiver(context.getApplicationContext());
-        }
-
         mButtonTint = a.getColorStateList(R.styleable.MediaRouteButton_mediaRouteButtonTint);
         mMinWidth = a.getDimensionPixelSize(
                 R.styleable.MediaRouteButton_android_minWidth, 0);
@@ -494,21 +475,14 @@
     }
 
     /**
-     * Sets whether the button is visible when no routes are available.
-     * When true, the button is visible even when there are no routes to connect.
-     * You may want to override {@link View#performClick()} to change the behavior
-     * when the button is clicked.
-     * The default is false.
-     * It doesn't overrides the {@link View#getVisibility visibility} status of the button.
+     * Does nothing. You should not call this method.
      *
-     * @param alwaysVisible true to show the button even when no routes are available.
+     * @deprecated The visibility of the button no longer depends on the availability of routes.
+     * You can still use {@link View#setVisibility(int)} to control the visibility of the button.
      */
+    @Deprecated
     public void setAlwaysVisible(boolean alwaysVisible) {
-        if (alwaysVisible != mAlwaysVisible) {
-            mAlwaysVisible = alwaysVisible;
-            refreshVisibility();
-            refreshRoute();
-        }
+        // no-op
     }
 
     @Override
@@ -528,8 +502,10 @@
 
     @Override
     public void setVisibility(int visibility) {
-        mVisibility = visibility;
-        refreshVisibility();
+        super.setVisibility(visibility);
+        if (mRemoteIndicator != null) {
+            mRemoteIndicator.setVisible(visibility == VISIBLE, false);
+        }
     }
 
     @Override
@@ -545,8 +521,6 @@
             mRouter.addCallback(mSelector, mCallback);
         }
         refreshRoute();
-
-        sConnectivityReceiver.registerReceiver(this);
     }
 
     @Override
@@ -556,8 +530,6 @@
             if (!mSelector.isEmpty()) {
                 mRouter.removeCallback(mCallback);
             }
-
-            sConnectivityReceiver.unregisterReceiver(this);
         }
 
         super.onDetachedFromWindow();
@@ -662,15 +634,6 @@
         refreshDrawableState();
     }
 
-    void refreshVisibility() {
-        super.setVisibility(mVisibility == VISIBLE
-                && !(mAlwaysVisible || sConnectivityReceiver.isConnected())
-                ? INVISIBLE : mVisibility);
-        if (mRemoteIndicator != null) {
-            mRemoteIndicator.setVisible(getVisibility() == VISIBLE, false);
-        }
-    }
-
     void refreshRoute() {
         final MediaRouter.RouteInfo route = mRouter.getSelectedRoute();
         final boolean isRemote = !route.isDefaultOrBluetooth();
@@ -688,7 +651,7 @@
         }
 
         if (mAttachedToWindow) {
-            setEnabled(mAlwaysVisible || isRemote || mRouter.isRouteAvailable(mSelector,
+            setEnabled(isRemote || mRouter.isRouteAvailable(mSelector,
                     MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE));
         }
     }
@@ -825,66 +788,4 @@
             mRemoteIndicatorLoader = null;
         }
     }
-
-    private static final class ConnectivityReceiver extends BroadcastReceiver {
-        private final Context mContext;
-        // If we have no information, assume that the device is connected
-        private boolean mIsConnected = true;
-        private List<MediaRouteButton> mButtons;
-
-        ConnectivityReceiver(Context context) {
-            mContext = context;
-            mButtons = new ArrayList<MediaRouteButton>();
-        }
-
-        public void registerReceiver(MediaRouteButton button) {
-            if (mButtons.size() == 0) {
-                IntentFilter intentFilter = new IntentFilter();
-                intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
-                if (Build.VERSION.SDK_INT < 33) {
-                    mContext.registerReceiver(this, intentFilter);
-                } else {
-                    Api33.registerReceiver(mContext, this, intentFilter,
-                            Context.RECEIVER_NOT_EXPORTED);
-                }
-            }
-            mButtons.add(button);
-        }
-
-        public void unregisterReceiver(MediaRouteButton button) {
-            mButtons.remove(button);
-
-            if (mButtons.size() == 0) {
-                mContext.unregisterReceiver(this);
-            }
-        }
-
-        public boolean isConnected() {
-            return mIsConnected;
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
-                boolean isConnected = !intent.getBooleanExtra(
-                        ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
-                if (mIsConnected != isConnected) {
-                    mIsConnected = isConnected;
-                    for (MediaRouteButton button : mButtons) {
-                        button.refreshVisibility();
-                    }
-                }
-            }
-        }
-    }
-
-    @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/navigation/navigation-common/build.gradle b/navigation/navigation-common/build.gradle
index eff9ca8..72b827e 100644
--- a/navigation/navigation-common/build.gradle
+++ b/navigation/navigation-common/build.gradle
@@ -41,7 +41,7 @@
     api("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1")
     implementation("androidx.core:core-ktx:1.1.0")
     implementation("androidx.collection:collection-ktx:1.1.0")
-    implementation("androidx.profileinstaller:profileinstaller:1.2.1")
+    implementation("androidx.profileinstaller:profileinstaller:1.3.0")
 
     api(libs.kotlinStdlib)
     testImplementation(project(":navigation:navigation-testing"))
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavArgument.kt b/navigation/navigation-common/src/main/java/androidx/navigation/NavArgument.kt
index 3b81db4..285294d 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/NavArgument.kt
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/NavArgument.kt
@@ -182,4 +182,22 @@
         this.defaultValue = defaultValue
         isDefaultValuePresent = defaultValuePresent
     }
+}
+
+/**
+ * Returns a list of NavArgument keys where required NavArguments with that key
+ * returns false for the predicate `isArgumentMissing`.
+ *
+ * @param [isArgumentMissing] predicate that returns true if the key of a required NavArgument
+ * is missing from a Bundle that is expected to contain it.
+ */
+internal fun Map<String, NavArgument?>.missingRequiredArguments(
+    isArgumentMissing: (key: String) -> Boolean
+): List<String> {
+    val requiredArgumentKeys = filterValues {
+        if (it != null) {
+            !it.isNullable && !it.isDefaultValuePresent
+        } else false
+    }.keys
+    return requiredArgumentKeys.filter { key -> isArgumentMissing(key) }
 }
\ No newline at end of file
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavDeepLink.kt b/navigation/navigation-common/src/main/java/androidx/navigation/NavDeepLink.kt
index 7280d87..e589268 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/NavDeepLink.kt
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/NavDeepLink.kt
@@ -188,15 +188,33 @@
         getMatchingUriFragment(deepLink.fragment, bundle, arguments)
 
         // Check that all required arguments are present in bundle
-        for ((argName, argument) in arguments.entries) {
-            val argumentIsRequired = argument != null && !argument.isNullable &&
-                !argument.isDefaultValuePresent
-            if (argumentIsRequired && !bundle.containsKey(argName)) return null
+        val missingRequiredArguments = arguments.missingRequiredArguments { argName ->
+            !bundle.containsKey(argName)
         }
+        if (missingRequiredArguments.isNotEmpty()) return null
 
         return bundle
     }
 
+    /**
+     * Returns a bundle containing matching path and query arguments with the requested uri.
+     * It returns empty bundle if this Deeplink's path pattern does not match with the uri.
+     */
+    internal fun getMatchingPathAndQueryArgs(
+        deepLink: Uri?,
+        arguments: Map<String, NavArgument?>
+    ): Bundle {
+        val bundle = Bundle()
+        if (deepLink == null) return bundle
+        val matcher = pathPattern?.matcher(deepLink.toString()) ?: return bundle
+        if (!matcher.matches()) {
+            return bundle
+        }
+        getMatchingPathArguments(matcher, bundle, arguments)
+        if (isParameterizedQuery) getMatchingQueryArguments(deepLink, bundle, arguments)
+        return bundle
+    }
+
     private fun getMatchingUriFragment(
         fragment: String?,
         bundle: Bundle,
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavDestination.kt b/navigation/navigation-common/src/main/java/androidx/navigation/NavDestination.kt
index 5496d2d..99e410b 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/NavDestination.kt
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/NavDestination.kt
@@ -351,10 +351,9 @@
      * @see NavController.navigate
      */
     public fun addDeepLink(navDeepLink: NavDeepLink) {
-        val missingRequiredArguments =
-            arguments.filterValues { !it.isNullable && !it.isDefaultValuePresent }
-                .keys
-                .filter { it !in navDeepLink.argumentsNames }
+        val missingRequiredArguments = arguments.missingRequiredArguments { key ->
+            key !in navDeepLink.argumentsNames
+        }
         require(missingRequiredArguments.isEmpty()) {
             "Deep link ${navDeepLink.uriPattern} can't be used to open destination $this.\n" +
                 "Following required arguments are missing: $missingRequiredArguments"
@@ -406,7 +405,9 @@
             val mimeType = navDeepLinkRequest.mimeType
             val mimeTypeMatchLevel =
                 if (mimeType != null) deepLink.getMimeTypeMatchRating(mimeType) else -1
-            if (matchingArguments != null || matchingAction || mimeTypeMatchLevel > -1) {
+            if (matchingArguments != null || ((matchingAction || mimeTypeMatchLevel > -1) &&
+                    hasRequiredArguments(deepLink, uri, arguments))
+            ) {
                 val newMatch = DeepLinkMatch(
                     this, matchingArguments,
                     deepLink.isExactDeepLink, matchingPathSegments, matchingAction,
@@ -420,6 +421,18 @@
         return bestMatch
     }
 
+    private fun hasRequiredArguments(
+        deepLink: NavDeepLink,
+        uri: Uri?,
+        arguments: Map<String, NavArgument>
+    ): Boolean {
+        val matchingArgs = deepLink.getMatchingPathAndQueryArgs(uri, arguments)
+        val missingRequiredArguments = arguments.missingRequiredArguments { key ->
+            !matchingArgs.containsKey(key)
+        }
+        return missingRequiredArguments.isEmpty()
+    }
+
     /**
      * Build an array containing the hierarchy from the root down to this destination.
      *
diff --git a/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/FragmentNavigatorTest.kt b/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/FragmentNavigatorTest.kt
index 6404e3a..6aee23d7 100644
--- a/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/FragmentNavigatorTest.kt
+++ b/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/FragmentNavigatorTest.kt
@@ -547,6 +547,37 @@
             .containsExactly(entry)
     }
 
+    @UiThreadTest
+    @Test
+    fun testNavigateAndAddIndependentFragment() {
+        val entry = createBackStackEntry()
+
+        fragmentNavigator.navigate(listOf(entry), null, null)
+        assertThat(navigatorState.backStack.value)
+            .containsExactly(entry)
+        fragmentManager.executePendingTransactions()
+        val fragment = fragmentManager.findFragmentById(R.id.container)
+        assertWithMessage("Fragment should be added")
+            .that(fragment)
+            .isNotNull()
+        assertWithMessage("Fragment should be the correct type")
+            .that(fragment)
+            .isInstanceOf(EmptyFragment::class.java)
+        assertWithMessage("Fragment should be the primary navigation Fragment")
+            .that(fragmentManager.primaryNavigationFragment)
+            .isSameInstanceAs(fragment)
+
+        val independentFragment = EmptyFragment()
+        fragmentManager.beginTransaction()
+            .replace(R.id.container, independentFragment)
+            .commit()
+        fragmentManager.executePendingTransactions()
+
+        assertWithMessage("Independent fragment should be added")
+            .that(fragmentManager.findFragmentById(R.id.container))
+            .isEqualTo(independentFragment)
+    }
+
     @LargeTest
     @UiThreadTest
     @Test
diff --git a/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavControllerWithFragmentTest.kt b/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavControllerWithFragmentTest.kt
index c9eda60..603e078 100644
--- a/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavControllerWithFragmentTest.kt
+++ b/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavControllerWithFragmentTest.kt
@@ -20,6 +20,7 @@
 import android.os.Bundle
 import androidx.fragment.app.DialogFragment
 import androidx.fragment.app.Fragment
+import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
 import androidx.navigation.NavOptions
@@ -48,6 +49,8 @@
         fm?.executePendingTransactions()
         val fragment = fm?.findFragmentById(R.id.nav_host)
 
+        val oldEntry = navController.currentBackStackEntry
+
         navController.navigate(
             R.id.empty_fragment,
             null,
@@ -61,6 +64,13 @@
         assertWithMessage("Replacement should be a new instance")
             .that(replacementFragment)
             .isNotSameInstanceAs(fragment)
+
+        assertWithMessage("Old Entry should have been DESTROYED")
+            .that(oldEntry!!.lifecycle.currentState)
+            .isEqualTo(Lifecycle.State.DESTROYED)
+        assertWithMessage("New Entry should be RESUMED")
+            .that(navController.currentBackStackEntry!!.lifecycle.currentState)
+            .isEqualTo(Lifecycle.State.RESUMED)
     }
 
     @Test
diff --git a/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/OnBackPressedTest.kt b/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/OnBackPressedTest.kt
index b55be87..79013ab 100644
--- a/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/OnBackPressedTest.kt
+++ b/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/OnBackPressedTest.kt
@@ -23,14 +23,15 @@
 import androidx.navigation.fragment.test.NavigationActivityWithFragmentTag
 import androidx.navigation.fragment.test.NavigationBaseActivity
 import androidx.navigation.fragment.test.R
+import androidx.navigation.navOptions
 import androidx.test.core.app.ActivityScenario
 import androidx.test.filters.MediumTest
 import androidx.testutils.withActivity
 import com.google.common.truth.Truth.assertWithMessage
+import java.util.concurrent.TimeUnit
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
-import java.util.concurrent.TimeUnit
 
 @Suppress("DEPRECATION")
 @MediumTest
@@ -78,6 +79,58 @@
     }
 
     @Test
+    fun testOnBackPressedAfterNavigateWithAnimators() {
+        with(ActivityScenario.launch(activityClass)) {
+            withActivity {
+                navController.setGraph(R.navigation.nav_simple)
+                navController.navigate(R.id.empty_fragment, null, navOptions {
+                    anim {
+                        enter = R.animator.fade_enter
+                        exit = R.animator.fade_exit
+                        popEnter = R.animator.fade_enter
+                        popExit = R.animator.fade_exit
+                    }
+                })
+                onBackPressed()
+                assertWithMessage("onBackPressed() should trigger NavController.popBackStack()")
+                    .that(navController.currentDestination?.id)
+                    .isEqualTo(R.id.start_fragment)
+            }
+        }
+    }
+
+    @Test
+    fun testOnBackPressedAfterNavigatePopUpTo() {
+        with(ActivityScenario.launch(activityClass)) {
+            withActivity {
+                navController.setGraph(R.navigation.nav_simple)
+
+                val navigator = navController.navigatorProvider.getNavigator(
+                    FragmentNavigator::class.java
+                )
+                val fragment = supportFragmentManager.findFragmentById(R.id.nav_host)
+                navController.navigate(R.id.empty_fragment)
+                fragment?.childFragmentManager?.executePendingTransactions()
+
+                navController.navigate(R.id.empty_fragment_2, null,
+                    navOptions { popUpTo(R.id.empty_fragment) { inclusive = true } }
+                )
+                fragment?.childFragmentManager?.executePendingTransactions()
+
+                onBackPressed()
+                fragment?.childFragmentManager?.executePendingTransactions()
+
+                assertWithMessage("onBackPressed() should trigger NavController.popBackStack()")
+                    .that(navController.currentDestination?.id)
+                    .isEqualTo(R.id.start_fragment)
+                assertWithMessage("navigator back stack should contain 1 entry")
+                    .that(navigator.backStack.value.size)
+                    .isEqualTo(1)
+            }
+        }
+    }
+
+    @Test
     fun testOnBackPressedAfterNavigate_notDefaultNavHost() {
         with(ActivityScenario.launch(activityClass)) {
             val countDownLatch = withActivity {
diff --git a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/FragmentNavigator.kt b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/FragmentNavigator.kt
index 2c9cbe7..48378f5 100644
--- a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/FragmentNavigator.kt
+++ b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/FragmentNavigator.kt
@@ -72,11 +72,6 @@
      */
     internal val backStack get() = state.backStack
 
-    /**
-     * Temporarily stores entries that need to attach observer on its fragment
-     */
-    private val toAttachObserver = mutableListOf<String>()
-
     private val fragmentObserver = object : LifecycleEventObserver {
         override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
             if (event == Lifecycle.Event.ON_DESTROY) {
@@ -97,22 +92,22 @@
         super.onAttach(state)
 
         fragmentManager.addFragmentOnAttachListener { _, fragment ->
-            fragment.viewLifecycleOwnerLiveData.observe(fragment) {
-                val needToAttach = toAttachObserver.remove(fragment.tag)
-                // attach observer unless it was already popped at this point
-                if (needToAttach && !entriesToPop.contains(fragment.tag)) {
-                    val entry = state.backStack.value.last { it.id == fragment.tag }
-                    attachObserver(entry, fragment)
-                }
-            }
-            fragment.lifecycle.addObserver(fragmentObserver)
-            // We need to ensure that if the fragment has its state saved and then that state
-            // later cleared without the restoring the fragment that we also clear the state
-            // of the associated entry.
-            val viewModel = ViewModelProvider(fragment)[ClearEntryStateViewModel::class.java]
             val entry = state.backStack.value.lastOrNull { it.id == fragment.tag }
-            viewModel.completeTransition =
-                WeakReference { entry?.let { state.markTransitionComplete(it) } }
+            if (entry != null) {
+                fragment.viewLifecycleOwnerLiveData.observe(fragment) { owner ->
+                    // attach observer unless it was already popped at this point
+                    if (owner != null && !entriesToPop.contains(fragment.tag)) {
+                        attachObserver(entry, fragment)
+                    }
+                }
+                fragment.lifecycle.addObserver(fragmentObserver)
+                // We need to ensure that if the fragment has its state saved and then that state
+                // later cleared without the restoring the fragment that we also clear the state
+                // of the associated entry.
+                val viewModel = ViewModelProvider(fragment)[ClearEntryStateViewModel::class.java]
+                viewModel.completeTransition =
+                    WeakReference { entry.let { state.markTransitionComplete(it) } }
+            }
         }
 
         fragmentManager.addOnBackStackChangedListener(object : OnBackStackChangedListener {
@@ -121,13 +116,9 @@
             override fun onBackStackChangeStarted(fragment: Fragment, pop: Boolean) {
                 // We only care about the pop case here since in the navigate case by the time
                 // we get here the fragment will have already been moved to STARTED.
-                // In the case of a pop, we move the top most entry from RESUMED to STARTED by
-                // calling prepareForTransition.
+                // In the case of a pop, we move the entries to STARTED
                 if (pop) {
-                    val entry = state.backStack.value.lastOrNull {
-                        it.id == fragment.tag &&
-                            it.lifecycle.currentState == Lifecycle.State.RESUMED
-                    }
+                    val entry = state.backStack.value.lastOrNull { it.id == fragment.tag }
                     entry?.let { state.prepareForTransition(it) }
                 }
             }
@@ -136,36 +127,25 @@
                 val entry = (state.backStack.value + state.transitionsInProgress.value).lastOrNull {
                     it.id == fragment.tag
                 }
-                if (entry != null && fragmentWasPopped(fragment, entry)) {
-                    entriesToPop.remove(entry.id)
-                } else if (entry != null && fragmentWasAdded(fragment)) {
-                    attachObserver(entry, fragment)
-                } else if (fragmentShouldBePopped(fragment, pop)) {
+                if (pop && entry != null) {
+                    // The entry has already been removed from the back stack so just remove it
+                    // from the list
+                    if (!state.backStack.value.contains(entry)) {
+                        // remove it so we don't falsely identify a direct call to popBackStack
+                        entriesToPop.remove(entry.id)
+                    }
                     // This is the case of system back where we will need to make the call to
                     // popBackStack. Otherwise, popBackStack was called directly and this should
                     // end up being a no-op.
-                    var entryToPop = state.backStack.value.last()
-                    popBackStack(entryToPop, false)
-                    // remove it so we don't falsely identify a direct call to popBackStack
-                    entriesToPop.remove(entryToPop.id)
+                    else if (entriesToPop.isEmpty() && fragment.isRemoving) {
+                        state.popWithTransition(entry, false)
+                    }
                 }
             }
-
-            fun fragmentWasPopped(fragment: Fragment, entry: NavBackStackEntry): Boolean {
-                return fragment.view != null && entriesToPop.contains(entry.id)
-            }
-
-            fun fragmentWasAdded(fragment: Fragment): Boolean {
-                return fragment.view != null && fragment.isAdded
-            }
-
-            fun fragmentShouldBePopped(fragment: Fragment, pop: Boolean): Boolean {
-                return pop && entriesToPop.isEmpty() && !fragment.isAdded
-            }
         })
     }
 
-    internal fun attachObserver(entry: NavBackStackEntry, fragment: Fragment) {
+    private fun attachObserver(entry: NavBackStackEntry, fragment: Fragment) {
         val viewLifecycle = fragment.viewLifecycleOwner.lifecycle
         val currentState = viewLifecycle.currentState
         // We only need to add observers while the viewLifecycle has not reached a final
@@ -184,6 +164,7 @@
                     // Once the lifecycle reaches DESTROYED, we can mark the transition
                     // complete and remove the observer.
                     if (event == Lifecycle.Event.ON_DESTROY) {
+                        entriesToPop.remove(entry.id)
                         state.markTransitionComplete(entry)
                         viewLifecycle.removeObserver(this)
                     }
@@ -328,9 +309,6 @@
 
         if (!initialNavigation) {
             ft.addToBackStack(entry.id)
-        } else {
-            // not added to backstack so we need to make sure we attach fragment observer later
-            toAttachObserver.add(entry.id)
         }
 
         if (navigatorExtras is Extras) {
diff --git a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.kt b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.kt
index 12a5047..b44887e 100644
--- a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.kt
+++ b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.kt
@@ -25,6 +25,7 @@
 import androidx.annotation.NavigationRes
 import androidx.annotation.RestrictTo
 import androidx.core.content.res.use
+import androidx.core.os.bundleOf
 import androidx.fragment.app.DialogFragment
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.FragmentContainerView
@@ -74,8 +75,44 @@
  * coupling to the navigation host.
  */
 public open class NavHostFragment : Fragment(), NavHost {
-    private var navHostController: NavHostController? = null
-    private var isPrimaryBeforeOnCreate: Boolean? = null
+    internal val navHostController: NavHostController by lazy {
+        val context = checkNotNull(context) {
+            "NavController cannot be created before the fragment is attached"
+        }
+        NavHostController(context).apply {
+            setLifecycleOwner(this@NavHostFragment)
+            setViewModelStore(viewModelStore)
+            onCreateNavHostController(this)
+            savedStateRegistry.consumeRestoredStateForKey(KEY_NAV_CONTROLLER_STATE)?.let {
+                restoreState(it)
+            }
+            savedStateRegistry.registerSavedStateProvider(KEY_NAV_CONTROLLER_STATE) {
+                saveState() ?: Bundle.EMPTY
+            }
+            savedStateRegistry.consumeRestoredStateForKey(KEY_GRAPH_ID)?.let { bundle ->
+                graphId = bundle.getInt(KEY_GRAPH_ID)
+            }
+            savedStateRegistry.registerSavedStateProvider(KEY_GRAPH_ID) {
+                if (graphId != 0) {
+                    bundleOf(KEY_GRAPH_ID to graphId)
+                } else {
+                    Bundle.EMPTY
+                }
+            }
+            if (graphId != 0) {
+                // Set from onInflate()
+                setGraph(graphId)
+            } else {
+                // See if it was set by NavHostFragment.create()
+                val args = arguments
+                val graphId = args?.getInt(KEY_GRAPH_ID) ?: 0
+                val startDestinationArgs = args?.getBundle(KEY_START_DESTINATION_ARGS)
+                if (graphId != 0) {
+                    setGraph(graphId, startDestinationArgs)
+                }
+            }
+        }
+    }
     private var viewParent: View? = null
 
     // State that will be saved and restored
@@ -91,14 +128,12 @@
      * @throws IllegalStateException if called before [onCreate]
      */
     final override val navController: NavController
-        get() {
-            checkNotNull(navHostController) { "NavController is not available before onCreate()" }
-            return navHostController as NavHostController
-        }
+        get() = navHostController
 
     @CallSuper
     public override fun onAttach(context: Context) {
         super.onAttach(context)
+
         // TODO This feature should probably be a first-class feature of the Fragment system,
         // but it can stay here until we can add the necessary attr resources to
         // the fragment lib.
@@ -111,43 +146,15 @@
 
     @CallSuper
     public override fun onCreate(savedInstanceState: Bundle?) {
-        val context = requireContext()
-        navHostController = NavHostController(context)
-        navHostController!!.setLifecycleOwner(this)
-        // Set the default state - this will be updated whenever
-        // onPrimaryNavigationFragmentChanged() is called
-        navHostController!!.enableOnBackPressed(
-            isPrimaryBeforeOnCreate != null && isPrimaryBeforeOnCreate as Boolean
-        )
-        isPrimaryBeforeOnCreate = null
-        navHostController!!.setViewModelStore(viewModelStore)
-        onCreateNavHostController(navHostController!!)
-        var navState: Bundle? = null
+        // We are accessing the NavController here to ensure that it is always created by this point
+        navHostController
         if (savedInstanceState != null) {
-            navState = savedInstanceState.getBundle(KEY_NAV_CONTROLLER_STATE)
             if (savedInstanceState.getBoolean(KEY_DEFAULT_NAV_HOST, false)) {
                 defaultNavHost = true
                 parentFragmentManager.beginTransaction()
                     .setPrimaryNavigationFragment(this)
                     .commit()
             }
-            graphId = savedInstanceState.getInt(KEY_GRAPH_ID)
-        }
-        if (navState != null) {
-            // Navigation controller state overrides arguments
-            navHostController!!.restoreState(navState)
-        }
-        if (graphId != 0) {
-            // Set from onInflate()
-            navHostController!!.setGraph(graphId)
-        } else {
-            // See if it was set by NavHostFragment.create()
-            val args = arguments
-            val graphId = args?.getInt(KEY_GRAPH_ID) ?: 0
-            val startDestinationArgs = args?.getBundle(KEY_START_DESTINATION_ARGS)
-            if (graphId != 0) {
-                navHostController!!.setGraph(graphId, startDestinationArgs)
-            }
         }
 
         // We purposefully run this last as this will trigger the onCreate() of
@@ -167,7 +174,8 @@
      *
      * By default, this adds a [DialogFragmentNavigator] and [FragmentNavigator].
      *
-     * This is only called once in [onCreate] and should not be called directly by
+     * This is only called once when the navController is called. This will be called in [onCreate]
+     * if the navController has not yet been called. This should not be called directly by
      * subclasses.
      *
      * @param navHostController The newly created [NavHostController] that will be
@@ -186,7 +194,8 @@
      *
      * By default, this adds a [DialogFragmentNavigator] and [FragmentNavigator].
      *
-     * This is only called once in [onCreate] and should not be called directly by
+     * This is only called once when the navController is called. This will be called in [onCreate]
+     * if the navController has not yet been called. This should not be called directly by
      * subclasses.
      *
      * @param navController The newly created [NavController].
@@ -203,15 +212,6 @@
         navController.navigatorProvider.addNavigator(createFragmentNavigator())
     }
 
-    @CallSuper
-    public override fun onPrimaryNavigationFragmentChanged(isPrimaryNavigationFragment: Boolean) {
-        if (navHostController != null) {
-            navHostController?.enableOnBackPressed(isPrimaryNavigationFragment)
-        } else {
-            isPrimaryBeforeOnCreate = isPrimaryNavigationFragment
-        }
-    }
-
     /**
      * Create the FragmentNavigator that this NavHostFragment will use. By default, this uses
      * [FragmentNavigator], which replaces the entire contents of the NavHostFragment.
@@ -299,16 +299,9 @@
     @CallSuper
     public override fun onSaveInstanceState(outState: Bundle) {
         super.onSaveInstanceState(outState)
-        val navState = navHostController!!.saveState()
-        if (navState != null) {
-            outState.putBundle(KEY_NAV_CONTROLLER_STATE, navState)
-        }
         if (defaultNavHost) {
             outState.putBoolean(KEY_DEFAULT_NAV_HOST, true)
         }
-        if (graphId != 0) {
-            outState.putInt(KEY_GRAPH_ID, graphId)
-        }
     }
 
     public override fun onDestroyView() {
@@ -355,12 +348,12 @@
             var findFragment: Fragment? = fragment
             while (findFragment != null) {
                 if (findFragment is NavHostFragment) {
-                    return findFragment.navHostController as NavController
+                    return findFragment.navHostController
                 }
                 val primaryNavFragment = findFragment.parentFragmentManager
                     .primaryNavigationFragment
                 if (primaryNavFragment is NavHostFragment) {
-                    return primaryNavFragment.navHostController as NavController
+                    return primaryNavFragment.navHostController
                 }
                 findFragment = findFragment.parentFragment
             }
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 12a08c4..86e9852 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
@@ -68,7 +68,10 @@
                 argument("defaultArg") { defaultValue = true }
             }
             test("second_test/{arg2}") {
-                argument("arg2") { type = NavType.StringType }
+                argument("arg2") {
+                    type = NavType.StringType
+                    nullable = true
+                }
                 argument("defaultArg") {
                     type = NavType.StringType
                     defaultValue = "defaultValue"
@@ -82,6 +85,17 @@
                     uriPattern = "android-app://androidx.navigation.test/test/{arg1}/{arg2}"
                 }
             }
+            test("nonNullableArg_test/{arg3}") {
+                argument("arg3") {
+                    type = NavType.StringType
+                    nullable = false
+                }
+                deepLink {
+                    uriPattern = "android-app://androidx.navigation.test/test/test{arg3}"
+                    action = "test.action2"
+                    mimeType = "type/test2"
+                }
+            }
         }
 
     val nav_start_destination_route_graph =
@@ -1308,6 +1322,24 @@
 
     @UiThreadTest
     @Test
+    fun testNavigateViaDeepLinkActionDifferentURI_nonNullableArg() {
+        val navController = createNavController()
+        navController.graph = nav_simple_route_graph
+        val deepLink = NavDeepLinkRequest(Uri.parse("invalidDeepLink.com"), "test.action2", null)
+
+        val expected = assertFailsWith<IllegalArgumentException> {
+            navController.navigate(deepLink)
+        }
+        assertThat(expected.message).isEqualTo(
+            "Navigation destination that matches request " +
+                "NavDeepLinkRequest{ uri=invalidDeepLink.com action=test.action2 } cannot be " +
+                "found in the navigation graph NavGraph(0xc017d10b) route=nav_root " +
+                "startDestination={Destination(0x4a13399c) route=start_test}"
+        )
+    }
+
+    @UiThreadTest
+    @Test
     fun testNavigateViaDeepLinkMimeTypeDifferentUri() {
         val navController = createNavController()
         navController.graph = nav_simple_route_graph
@@ -1321,6 +1353,24 @@
 
     @UiThreadTest
     @Test
+    fun testNavigateViaDeepLinkMimeTypeDifferentUri_nonNullableArg() {
+        val navController = createNavController()
+        navController.graph = nav_simple_route_graph
+        val deepLink = NavDeepLinkRequest(Uri.parse("invalidDeepLink.com"), null, "type/test2")
+
+        val expected = assertFailsWith<IllegalArgumentException> {
+            navController.navigate(deepLink)
+        }
+        assertThat(expected.message).isEqualTo(
+            "Navigation destination that matches request " +
+                "NavDeepLinkRequest{ uri=invalidDeepLink.com mimetype=type/test2 } cannot be " +
+                "found in the navigation graph NavGraph(0xc017d10b) route=nav_root " +
+                "startDestination={Destination(0x4a13399c) route=start_test}"
+        )
+    }
+
+    @UiThreadTest
+    @Test
     @Suppress("DEPRECATION")
     fun testNavigateViaDeepLinkMimeType() {
         val navController = createNavController()
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
index abb391c..14de13f 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
@@ -58,6 +58,7 @@
 import androidx.testutils.test
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
+import kotlin.test.assertFailsWith
 import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.take
@@ -104,6 +105,22 @@
             }
     }
 
+    private val NESTED_NAV_GRAPH_2 =
+        navController.createGraph(route = "graph", startDestination = "dest1") {
+            test("dest1")
+            navigation(route = "nested", startDestination = "dest2") {
+                argument("longArg") {
+                    type = NavType.LongType
+                }
+                test("dest2") {
+                    argument("longArg") {
+                        type = NavType.LongType
+                    }
+                }
+                test("dest3")
+            }
+        }
+
     @UiThreadTest
     @Test
     fun testGetCurrentBackStackEntry() {
@@ -526,6 +543,26 @@
 
     @UiThreadTest
     @Test
+    fun testNavigateViaDeepLinkAction_nonNullableArg() {
+        val navController = createNavController()
+        navController.setGraph(R.navigation.nav_simple)
+        val action = "test.action2"
+        val deepLink = NavDeepLinkRequest(null, action, null)
+
+        val expected = assertFailsWith<IllegalArgumentException> {
+            navController.navigate(deepLink)
+        }
+        assertThat(expected.message).isEqualTo(
+            "Navigation destination that matches request " +
+                "NavDeepLinkRequest{ action=test.action2 } cannot be " +
+                "found in the navigation graph NavGraph(androidx.navigation.test:id/nav_root) " +
+                "label= startDestination={Destination(androidx.navigation.test:id/start_test)}"
+        )
+        assertThat(navController.currentDestination?.route).isEqualTo(null)
+    }
+
+    @UiThreadTest
+    @Test
     @Suppress("DEPRECATION")
     fun testNavigateViaDeepLinkActionUnusedUri() {
         val navController = createNavController()
@@ -545,6 +582,26 @@
 
     @UiThreadTest
     @Test
+    fun testNavigateViaDeepLinkActionUnusedUri_nonNullableArg() {
+        val navController = createNavController()
+        navController.setGraph(R.navigation.nav_simple)
+        val action = "test.action2"
+        val deepLink = NavDeepLinkRequest("http://www.example.com".toUri(), action, null)
+
+        val expected = assertFailsWith<IllegalArgumentException> {
+            navController.navigate(deepLink)
+        }
+        assertThat(expected.message).isEqualTo(
+            "Navigation destination that matches request " +
+                "NavDeepLinkRequest{ uri=http://www.example.com action=test.action2 } cannot be " +
+                "found in the navigation graph NavGraph(androidx.navigation.test:id/nav_root) " +
+                "label= startDestination={Destination(androidx.navigation.test:id/start_test)}"
+        )
+        assertThat(navController.currentDestination?.route).isEqualTo(null)
+    }
+
+    @UiThreadTest
+    @Test
     fun testNavigateViaDeepLinkActionDifferentURI() {
         val navController = createNavController()
         navController.setGraph(R.navigation.nav_simple)
@@ -558,6 +615,24 @@
 
     @UiThreadTest
     @Test
+    fun testNavigateViaDeepLinkActionDifferentURI_nonNullableArg() {
+        val navController = createNavController()
+        navController.setGraph(R.navigation.nav_simple)
+        val deepLink = NavDeepLinkRequest(Uri.parse("invalidDeepLink.com"), "test.action2", null)
+
+        val expected = assertFailsWith<IllegalArgumentException> {
+            navController.navigate(deepLink)
+        }
+        assertThat(expected.message).isEqualTo(
+            "Navigation destination that matches request " +
+                "NavDeepLinkRequest{ uri=invalidDeepLink.com action=test.action2 } cannot be " +
+                "found in the navigation graph NavGraph(androidx.navigation.test:id/nav_root) " +
+                "label= startDestination={Destination(androidx.navigation.test:id/start_test)}"
+        )
+    }
+
+    @UiThreadTest
+    @Test
     fun testNavigateViaDeepLinkMimeTypeDifferentUri() {
         val navController = createNavController()
         navController.setGraph(R.navigation.nav_simple)
@@ -571,6 +646,45 @@
 
     @UiThreadTest
     @Test
+    fun testNavigateViaDeepLinkMimeTypeDifferentUri_nonNullableArg() {
+        val navController = createNavController()
+        navController.setGraph(R.navigation.nav_simple)
+        val deepLink = NavDeepLinkRequest(Uri.parse("invalidDeepLink.com"), null, "type/test2")
+
+        val expected = assertFailsWith<IllegalArgumentException> {
+            navController.navigate(deepLink)
+        }
+        assertThat(expected.message).isEqualTo(
+            "Navigation destination that matches request " +
+                "NavDeepLinkRequest{ uri=invalidDeepLink.com mimetype=type/test2 } cannot be " +
+                "found in the navigation graph NavGraph(androidx.navigation.test:id/nav_root) " +
+                "label= startDestination={Destination(androidx.navigation.test:id/start_test)}"
+        )
+        assertThat(navController.currentDestination?.route).isEqualTo(null)
+    }
+
+    @UiThreadTest
+    @Test
+    fun testNavigateViaDeepLinkMimeTypeMissingQueryArg_nonNullableArg() {
+        val navController = createNavController()
+        navController.setGraph(R.navigation.nav_simple)
+        // deeplink with this mime type has a required Query arg
+        val deepLink = NavDeepLinkRequest(null, null, "type/test3")
+
+        val expected = assertFailsWith<IllegalArgumentException> {
+            navController.navigate(deepLink)
+        }
+        assertThat(expected.message).isEqualTo(
+            "Navigation destination that matches request " +
+                "NavDeepLinkRequest{ mimetype=type/test3 } cannot be " +
+                "found in the navigation graph NavGraph(androidx.navigation.test:id/nav_root) " +
+                "label= startDestination={Destination(androidx.navigation.test:id/start_test)}"
+        )
+        assertThat(navController.currentDestination?.route).isEqualTo(null)
+    }
+
+    @UiThreadTest
+    @Test
     @Suppress("DEPRECATION")
     fun testNavigateViaDeepLinkMimeType() {
         val navController = createNavController()
@@ -1653,6 +1767,23 @@
 
     @UiThreadTest
     @Test
+    fun testNavigateWithMissingNonNullableArg() {
+        val navController = createNavController()
+        navController.graph = NESTED_NAV_GRAPH_2
+        assertThat(navController.currentDestination?.route).isEqualTo("dest1")
+
+        val nestedId = ("android-app://androidx.navigation/nested").hashCode()
+
+        val expected = assertFailsWith<NullPointerException> {
+            navController.navigate(nestedId)
+        }
+        assertThat(expected.message).isEqualTo(
+            "null cannot be cast to non-null type kotlin.Long"
+        )
+    }
+
+    @UiThreadTest
+    @Test
     fun testNavigateMultipleParentsOnHierarchy() {
         val navController = createNavController()
         navController.setGraph(R.navigation.nav_root)
@@ -1670,6 +1801,34 @@
 
     @UiThreadTest
     @Test
+    fun testRebuildParentWithMissingNonNullableArg() {
+        val navController = createNavController()
+        navController.graph = NESTED_NAV_GRAPH_2
+        assertThat(navController.currentDestination?.route).isEqualTo("dest1")
+
+        val nestedId1 = ("android-app://androidx.navigation/nested").hashCode()
+
+        // navigate to nested graph first destination, provide non-nullable arg
+        navController.navigate(
+            nestedId1,
+            bundleOf("longArg" to 123L)
+        )
+        assertThat(navController.currentDestination?.route).isEqualTo("dest2")
+
+        val nestedId2 = ("android-app://androidx.navigation/dest3").hashCode()
+        // navigate to nested graph second destination after popping up to graph (inclusive)
+        // empty bundle to imitate navigating with NavDirections
+        navController.navigate(
+            nestedId2,
+            Bundle(),
+            NavOptions.Builder().setPopUpTo("nested", inclusive = true).build()
+        )
+        // [graph, dest1, nested, dest3]
+        assertThat(navController.currentBackStack.value.size).isEqualTo(4)
+    }
+
+    @UiThreadTest
+    @Test
     fun testNavigateWithOverriddenDefaultArgs() {
         val args = Bundle()
         args.putString(TEST_OVERRIDDEN_VALUE_ARG, TEST_OVERRIDDEN_VALUE_ARG_VALUE)
@@ -2870,6 +3029,97 @@
 
     @UiThreadTest
     @Test
+    fun testHandleDeepLinkActionMissingURI_nonNullableArg() {
+        val navController = createNavController()
+        navController.setGraph(R.navigation.nav_simple)
+        val collectedDestinationIds = mutableListOf<Int>()
+        navController.addOnDestinationChangedListener { _, destination, _ ->
+            collectedDestinationIds.add(destination.id)
+        }
+
+        val intent = Intent("test.action2").apply {
+            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+        }
+        assertThat(intent).isNotNull()
+        assertWithMessage("NavController should not match with any deeplink due to missing arg")
+            .that(navController.handleDeepLink(intent))
+            .isFalse()
+        // Verify that we never navigated further than the startDestination
+        assertThat(collectedDestinationIds).containsExactly(R.id.start_test)
+    }
+
+    @UiThreadTest
+    @Test
+    fun testHandleDeepLinkActionDifferentURI_nonNullableArg() {
+        val navController = createNavController()
+        navController.setGraph(R.navigation.nav_simple)
+        val collectedDestinationIds = mutableListOf<Int>()
+        navController.addOnDestinationChangedListener { _, destination, _ ->
+            collectedDestinationIds.add(destination.id)
+        }
+
+        val intent = Intent(
+            "test.action2",
+            "invalidDeepLink.com".toUri(),
+            ApplicationProvider.getApplicationContext() as Context,
+            TestActivity::class.java
+        )
+        assertThat(intent).isNotNull()
+        assertWithMessage("NavController should not match with any deeplink due to missing arg")
+            .that(navController.handleDeepLink(intent))
+            .isFalse()
+        // Verify that we never navigated further than the startDestination
+        assertThat(collectedDestinationIds).containsExactly(R.id.start_test)
+    }
+
+    @UiThreadTest
+    @Test
+    fun testHandleDeepLinkActionWrongArgType_nonNullableArg() {
+        val navController = createNavController()
+        navController.setGraph(R.navigation.nav_simple)
+        val collectedDestinationIds = mutableListOf<Int>()
+        navController.addOnDestinationChangedListener { _, destination, _ ->
+            collectedDestinationIds.add(destination.id)
+        }
+
+        val intent = Intent(
+            "test.action2",
+            // deeplink with matching action has Int NavType
+            "test-app://test/abc".toUri(),
+            ApplicationProvider.getApplicationContext() as Context,
+            TestActivity::class.java
+        )
+        assertThat(intent).isNotNull()
+        assertWithMessage("NavController should not match with any deeplink due to wrong arg type")
+            .that(navController.handleDeepLink(intent))
+            .isFalse()
+    }
+
+    @UiThreadTest
+    @Test
+    fun testHandleDeepLinkActionMissingQueryArg_nonNullableArg() {
+        val navController = createNavController()
+        navController.setGraph(R.navigation.nav_simple)
+        val collectedDestinationIds = mutableListOf<Int>()
+        navController.addOnDestinationChangedListener { _, destination, _ ->
+            collectedDestinationIds.add(destination.id)
+        }
+
+        val intent = Intent(
+            "test.action3",
+            // deeplink with this action type has a required Query arg
+            "test-app://test".toUri(),
+            ApplicationProvider.getApplicationContext() as Context,
+            TestActivity::class.java
+        )
+        assertThat(intent).isNotNull()
+        assertWithMessage("NavController should not match with any deeplink due to wrong arg type")
+            .that(navController.handleDeepLink(intent))
+            .isFalse()
+    }
+
+    @UiThreadTest
+    @Test
     fun testHandleDeepLinkNestedStartDestination() {
         val navController = createNavController()
         navController.setGraph(R.navigation.nav_nested_start_destination)
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavInflaterTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavInflaterTest.kt
index b9bc6fd..46db9a0 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavInflaterTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavInflaterTest.kt
@@ -114,6 +114,19 @@
     }
 
     @Test
+    fun testInflateDeepLinkWithApplicationIdAction_nonNullableArg() {
+        val context = ApplicationProvider.getApplicationContext() as Context
+        val navInflater = NavInflater(context, TestNavigatorProvider())
+        val graph = navInflater.inflate(R.navigation.nav_simple)
+
+        assertThat(graph).isNotNull()
+        val expectedDeepLinkRequest = NavDeepLinkRequest.Builder.fromAction("test.action2").build()
+        val result = graph.matchDeepLink(expectedDeepLinkRequest)
+        assertThat(result)
+            .isNull()
+    }
+
+    @Test
     fun testInflateDeepLinkWithApplicationIdEmptyAction() {
         val context = ApplicationProvider.getApplicationContext() as Context
         val navInflater = NavInflater(context, TestNavigatorProvider())
@@ -152,6 +165,19 @@
     }
 
     @Test
+    fun testInflateDeepLinkWithApplicationIdMimeType_nonNullableArg() {
+        val context = ApplicationProvider.getApplicationContext() as Context
+        val navInflater = NavInflater(context, TestNavigatorProvider())
+        val graph = navInflater.inflate(R.navigation.nav_simple)
+
+        assertThat(graph).isNotNull()
+        val expectedDeepLinkRequest = NavDeepLinkRequest.Builder.fromMimeType("type/test2").build()
+        val result = graph.matchDeepLink(expectedDeepLinkRequest)
+        assertThat(result)
+            .isNull()
+    }
+
+    @Test
     fun testInflateWithDataPatternApplicationId() {
         val context = ApplicationProvider.getApplicationContext() as Context
         val activityNavigator = ActivityNavigator(context)
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_simple.xml b/navigation/navigation-runtime/src/androidTest/res/navigation/nav_simple.xml
index 13b846a..838be93 100644
--- a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_simple.xml
+++ b/navigation/navigation-runtime/src/androidTest/res/navigation/nav_simple.xml
@@ -30,7 +30,7 @@
     </test>
 
     <test android:id="@+id/second_test">
-        <argument android:name="arg2" app:argType="string" />
+        <argument android:name="arg2" app:argType="string" app:nullable="true" />
         <argument android:name="defaultArg" app:argType="string"
             android:defaultValue="defaultValue" />
         <action android:id="@+id/self" app:destination="@+id/second_test"
@@ -43,6 +43,13 @@
         <deepLink app:uri="android-app://androidx.navigation.test/test/{arg1}/{arg2}"
             app:action="" />
     </test>
+    <test android:id="@+id/nonNullableArg_test">
+        <argument android:name="arg3" app:argType="integer" app:nullable="false" />
+        <deepLink app:uri="test-app://test/{arg3}"
+            app:action="test.action2" app:mimeType="type/test2"/>
+        <deepLink app:uri="test-app://test?arg={arg3}"
+            app:action="test.action3" app:mimeType="type/test3"/>
+    </test>
 
     <test android:id="@+id/nullArg_test">
         <!-- Deeplink must have path param {arg} for the test setup in order for
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
index 9c54f99..302dc96 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
@@ -2045,10 +2045,11 @@
         while (destination != null && findDestination(destination.id) !== destination) {
             val parent = destination.parent
             if (parent != null) {
+                val args = if (finalArgs?.isEmpty == true) null else finalArgs
                 val entry = restoredEntries.lastOrNull { restoredEntry ->
                     restoredEntry.destination == parent
                 } ?: NavBackStackEntry.create(
-                    context, parent, parent.addInDefaultArgs(finalArgs), hostLifecycleState,
+                    context, parent, parent.addInDefaultArgs(args), hostLifecycleState,
                     viewModel
                 )
                 hierarchy.addFirst(entry)
diff --git a/paging/paging-compose/build.gradle b/paging/paging-compose/build.gradle
index f1b7b79..80ba12e 100644
--- a/paging/paging-compose/build.gradle
+++ b/paging/paging-compose/build.gradle
@@ -26,14 +26,6 @@
 }
 
 dependencies {
-    constraints {
-        // this syntax mirrors the temp workaround in paging-common dependencies constraint
-        // which allows paging-common to have project constraint on compose even though
-        // compose is not within the `MAIN` project-set.
-        // update syntax when b/239979823 is fixed
-        implementation("androidx.paging:paging-common:${androidx.LibraryVersions.PAGING}")
-    }
-
     implementation(libs.kotlinStdlib)
     api("androidx.compose.foundation:foundation:1.2.1")
     api(project(":paging:paging-common"))
diff --git a/paging/paging-runtime/build.gradle b/paging/paging-runtime/build.gradle
index e39b1bf..013bbd8 100644
--- a/paging/paging-runtime/build.gradle
+++ b/paging/paging-runtime/build.gradle
@@ -31,11 +31,6 @@
 }
 
 dependencies {
-    //Atomic Group
-    constraints {
-        implementation(project(":paging:paging-common"))
-    }
-
     api(project(":paging:paging-common"))
     // Ensure that the -ktx dependency graph mirrors the Java dependency graph
     api(project(":paging:paging-common-ktx"))
diff --git a/paging/paging-rxjava2/api/current.ignore b/paging/paging-rxjava2/api/current.ignore
index 4c496cc..0e1e678 100644
--- a/paging/paging-rxjava2/api/current.ignore
+++ b/paging/paging-rxjava2/api/current.ignore
@@ -1,3 +1,7 @@
 // Baseline format: 1.0
 ParameterNameChange: androidx.paging.rxjava2.RxPagingSource#load(androidx.paging.PagingSource.LoadParams<Key>, kotlin.coroutines.Continuation<? super androidx.paging.PagingSource.LoadResult<Key,Value>>) parameter #1:
     Attempted to remove parameter name from parameter arg2 in androidx.paging.rxjava2.RxPagingSource.load
+
+
+RemovedClass: androidx.paging.rxjava2.PagingRx:
+    Removed class androidx.paging.rxjava2.PagingRx
diff --git a/paging/paging-rxjava2/api/current.txt b/paging/paging-rxjava2/api/current.txt
index 0b95aeb..9a9398b 100644
--- a/paging/paging-rxjava2/api/current.txt
+++ b/paging/paging-rxjava2/api/current.txt
@@ -38,15 +38,6 @@
     method @CheckResult public static <T, R> androidx.paging.PagingData<R> map(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.Single<R>> transform);
   }
 
-  public final class PagingRx {
-    method @CheckResult public static <T> androidx.paging.PagingData<T> filter(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.Single<java.lang.Boolean>> predicate);
-    method @CheckResult public static <T, R> androidx.paging.PagingData<R> flatMap(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.Single<java.lang.Iterable<R>>> transform);
-    method public static <Key, Value> io.reactivex.Flowable<androidx.paging.PagingData<Value>> getFlowable(androidx.paging.Pager<Key,Value>);
-    method public static <Key, Value> io.reactivex.Observable<androidx.paging.PagingData<Value>> getObservable(androidx.paging.Pager<Key,Value>);
-    method @CheckResult public static <T extends R, R> androidx.paging.PagingData<R> insertSeparators(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function2<? super T,? super T,? extends io.reactivex.Maybe<R>> generator);
-    method @CheckResult public static <T, R> androidx.paging.PagingData<R> map(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.Single<R>> transform);
-  }
-
   public abstract class RxPagingSource<Key, Value> extends androidx.paging.PagingSource<Key,Value> {
     ctor public RxPagingSource();
     method public final suspend Object? load(androidx.paging.PagingSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagingSource.LoadResult<Key,Value>>);
diff --git a/paging/paging-rxjava2/api/public_plus_experimental_current.txt b/paging/paging-rxjava2/api/public_plus_experimental_current.txt
index 7179ce2..acadd7c 100644
--- a/paging/paging-rxjava2/api/public_plus_experimental_current.txt
+++ b/paging/paging-rxjava2/api/public_plus_experimental_current.txt
@@ -40,17 +40,6 @@
     method @CheckResult public static <T, R> androidx.paging.PagingData<R> map(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.Single<R>> transform);
   }
 
-  public final class PagingRx {
-    method @kotlinx.coroutines.ExperimentalCoroutinesApi public static <T> io.reactivex.Observable<androidx.paging.PagingData<T>> cachedIn(io.reactivex.Observable<androidx.paging.PagingData<T>>, kotlinx.coroutines.CoroutineScope scope);
-    method @kotlinx.coroutines.ExperimentalCoroutinesApi public static <T> io.reactivex.Flowable<androidx.paging.PagingData<T>> cachedIn(io.reactivex.Flowable<androidx.paging.PagingData<T>>, kotlinx.coroutines.CoroutineScope scope);
-    method @CheckResult public static <T> androidx.paging.PagingData<T> filter(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.Single<java.lang.Boolean>> predicate);
-    method @CheckResult public static <T, R> androidx.paging.PagingData<R> flatMap(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.Single<java.lang.Iterable<R>>> transform);
-    method public static <Key, Value> io.reactivex.Flowable<androidx.paging.PagingData<Value>> getFlowable(androidx.paging.Pager<Key,Value>);
-    method public static <Key, Value> io.reactivex.Observable<androidx.paging.PagingData<Value>> getObservable(androidx.paging.Pager<Key,Value>);
-    method @CheckResult public static <T extends R, R> androidx.paging.PagingData<R> insertSeparators(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function2<? super T,? super T,? extends io.reactivex.Maybe<R>> generator);
-    method @CheckResult public static <T, R> androidx.paging.PagingData<R> map(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.Single<R>> transform);
-  }
-
   public abstract class RxPagingSource<Key, Value> extends androidx.paging.PagingSource<Key,Value> {
     ctor public RxPagingSource();
     method public final suspend Object? load(androidx.paging.PagingSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagingSource.LoadResult<Key,Value>>);
diff --git a/paging/paging-rxjava2/api/restricted_current.ignore b/paging/paging-rxjava2/api/restricted_current.ignore
index 4c496cc..0e1e678 100644
--- a/paging/paging-rxjava2/api/restricted_current.ignore
+++ b/paging/paging-rxjava2/api/restricted_current.ignore
@@ -1,3 +1,7 @@
 // Baseline format: 1.0
 ParameterNameChange: androidx.paging.rxjava2.RxPagingSource#load(androidx.paging.PagingSource.LoadParams<Key>, kotlin.coroutines.Continuation<? super androidx.paging.PagingSource.LoadResult<Key,Value>>) parameter #1:
     Attempted to remove parameter name from parameter arg2 in androidx.paging.rxjava2.RxPagingSource.load
+
+
+RemovedClass: androidx.paging.rxjava2.PagingRx:
+    Removed class androidx.paging.rxjava2.PagingRx
diff --git a/paging/paging-rxjava2/api/restricted_current.txt b/paging/paging-rxjava2/api/restricted_current.txt
index 0b95aeb..9a9398b 100644
--- a/paging/paging-rxjava2/api/restricted_current.txt
+++ b/paging/paging-rxjava2/api/restricted_current.txt
@@ -38,15 +38,6 @@
     method @CheckResult public static <T, R> androidx.paging.PagingData<R> map(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.Single<R>> transform);
   }
 
-  public final class PagingRx {
-    method @CheckResult public static <T> androidx.paging.PagingData<T> filter(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.Single<java.lang.Boolean>> predicate);
-    method @CheckResult public static <T, R> androidx.paging.PagingData<R> flatMap(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.Single<java.lang.Iterable<R>>> transform);
-    method public static <Key, Value> io.reactivex.Flowable<androidx.paging.PagingData<Value>> getFlowable(androidx.paging.Pager<Key,Value>);
-    method public static <Key, Value> io.reactivex.Observable<androidx.paging.PagingData<Value>> getObservable(androidx.paging.Pager<Key,Value>);
-    method @CheckResult public static <T extends R, R> androidx.paging.PagingData<R> insertSeparators(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function2<? super T,? super T,? extends io.reactivex.Maybe<R>> generator);
-    method @CheckResult public static <T, R> androidx.paging.PagingData<R> map(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.Single<R>> transform);
-  }
-
   public abstract class RxPagingSource<Key, Value> extends androidx.paging.PagingSource<Key,Value> {
     ctor public RxPagingSource();
     method public final suspend Object? load(androidx.paging.PagingSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagingSource.LoadResult<Key,Value>>);
diff --git a/paging/paging-rxjava3/api/current.ignore b/paging/paging-rxjava3/api/current.ignore
index 6159e34..451c35d 100644
--- a/paging/paging-rxjava3/api/current.ignore
+++ b/paging/paging-rxjava3/api/current.ignore
@@ -1,3 +1,7 @@
 // Baseline format: 1.0
 ParameterNameChange: androidx.paging.rxjava3.RxPagingSource#load(androidx.paging.PagingSource.LoadParams<Key>, kotlin.coroutines.Continuation<? super androidx.paging.PagingSource.LoadResult<Key,Value>>) parameter #1:
     Attempted to remove parameter name from parameter arg2 in androidx.paging.rxjava3.RxPagingSource.load
+
+
+RemovedClass: androidx.paging.rxjava3.PagingRx:
+    Removed class androidx.paging.rxjava3.PagingRx
diff --git a/paging/paging-rxjava3/api/current.txt b/paging/paging-rxjava3/api/current.txt
index 7af17b8..b77cbbe 100644
--- a/paging/paging-rxjava3/api/current.txt
+++ b/paging/paging-rxjava3/api/current.txt
@@ -10,15 +10,6 @@
     method @CheckResult public static <T, R> androidx.paging.PagingData<R> map(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.rxjava3.core.Single<R>> transform);
   }
 
-  public final class PagingRx {
-    method @CheckResult public static <T> androidx.paging.PagingData<T> filter(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.rxjava3.core.Single<java.lang.Boolean>> predicate);
-    method @CheckResult public static <T, R> androidx.paging.PagingData<R> flatMap(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.rxjava3.core.Single<java.lang.Iterable<R>>> transform);
-    method public static <Key, Value> io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<Value>> getFlowable(androidx.paging.Pager<Key,Value>);
-    method public static <Key, Value> io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<Value>> getObservable(androidx.paging.Pager<Key,Value>);
-    method @CheckResult public static <T extends R, R> androidx.paging.PagingData<R> insertSeparators(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function2<? super T,? super T,? extends io.reactivex.rxjava3.core.Maybe<R>> generator);
-    method @CheckResult public static <T, R> androidx.paging.PagingData<R> map(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.rxjava3.core.Single<R>> transform);
-  }
-
   @Deprecated public final class RxPagedListBuilder<Key, Value> {
     ctor @Deprecated public RxPagedListBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory, androidx.paging.PagedList.Config config);
     ctor @Deprecated public RxPagedListBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory, int pageSize);
diff --git a/paging/paging-rxjava3/api/public_plus_experimental_current.txt b/paging/paging-rxjava3/api/public_plus_experimental_current.txt
index 6ed617d..68770f0 100644
--- a/paging/paging-rxjava3/api/public_plus_experimental_current.txt
+++ b/paging/paging-rxjava3/api/public_plus_experimental_current.txt
@@ -12,17 +12,6 @@
     method @CheckResult public static <T, R> androidx.paging.PagingData<R> map(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.rxjava3.core.Single<R>> transform);
   }
 
-  public final class PagingRx {
-    method @kotlinx.coroutines.ExperimentalCoroutinesApi public static <T> io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<T>> cachedIn(io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<T>>, kotlinx.coroutines.CoroutineScope scope);
-    method @kotlinx.coroutines.ExperimentalCoroutinesApi public static <T> io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<T>> cachedIn(io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<T>>, kotlinx.coroutines.CoroutineScope scope);
-    method @CheckResult public static <T> androidx.paging.PagingData<T> filter(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.rxjava3.core.Single<java.lang.Boolean>> predicate);
-    method @CheckResult public static <T, R> androidx.paging.PagingData<R> flatMap(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.rxjava3.core.Single<java.lang.Iterable<R>>> transform);
-    method public static <Key, Value> io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<Value>> getFlowable(androidx.paging.Pager<Key,Value>);
-    method public static <Key, Value> io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<Value>> getObservable(androidx.paging.Pager<Key,Value>);
-    method @CheckResult public static <T extends R, R> androidx.paging.PagingData<R> insertSeparators(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function2<? super T,? super T,? extends io.reactivex.rxjava3.core.Maybe<R>> generator);
-    method @CheckResult public static <T, R> androidx.paging.PagingData<R> map(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.rxjava3.core.Single<R>> transform);
-  }
-
   @Deprecated public final class RxPagedListBuilder<Key, Value> {
     ctor @Deprecated public RxPagedListBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory, androidx.paging.PagedList.Config config);
     ctor @Deprecated public RxPagedListBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory, int pageSize);
diff --git a/paging/paging-rxjava3/api/restricted_current.ignore b/paging/paging-rxjava3/api/restricted_current.ignore
index 6159e34..451c35d 100644
--- a/paging/paging-rxjava3/api/restricted_current.ignore
+++ b/paging/paging-rxjava3/api/restricted_current.ignore
@@ -1,3 +1,7 @@
 // Baseline format: 1.0
 ParameterNameChange: androidx.paging.rxjava3.RxPagingSource#load(androidx.paging.PagingSource.LoadParams<Key>, kotlin.coroutines.Continuation<? super androidx.paging.PagingSource.LoadResult<Key,Value>>) parameter #1:
     Attempted to remove parameter name from parameter arg2 in androidx.paging.rxjava3.RxPagingSource.load
+
+
+RemovedClass: androidx.paging.rxjava3.PagingRx:
+    Removed class androidx.paging.rxjava3.PagingRx
diff --git a/paging/paging-rxjava3/api/restricted_current.txt b/paging/paging-rxjava3/api/restricted_current.txt
index 7af17b8..b77cbbe 100644
--- a/paging/paging-rxjava3/api/restricted_current.txt
+++ b/paging/paging-rxjava3/api/restricted_current.txt
@@ -10,15 +10,6 @@
     method @CheckResult public static <T, R> androidx.paging.PagingData<R> map(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.rxjava3.core.Single<R>> transform);
   }
 
-  public final class PagingRx {
-    method @CheckResult public static <T> androidx.paging.PagingData<T> filter(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.rxjava3.core.Single<java.lang.Boolean>> predicate);
-    method @CheckResult public static <T, R> androidx.paging.PagingData<R> flatMap(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.rxjava3.core.Single<java.lang.Iterable<R>>> transform);
-    method public static <Key, Value> io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<Value>> getFlowable(androidx.paging.Pager<Key,Value>);
-    method public static <Key, Value> io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<Value>> getObservable(androidx.paging.Pager<Key,Value>);
-    method @CheckResult public static <T extends R, R> androidx.paging.PagingData<R> insertSeparators(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function2<? super T,? super T,? extends io.reactivex.rxjava3.core.Maybe<R>> generator);
-    method @CheckResult public static <T, R> androidx.paging.PagingData<R> map(androidx.paging.PagingData<T>, kotlin.jvm.functions.Function1<? super T,? extends io.reactivex.rxjava3.core.Single<R>> transform);
-  }
-
   @Deprecated public final class RxPagedListBuilder<Key, Value> {
     ctor @Deprecated public RxPagedListBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory, androidx.paging.PagedList.Config config);
     ctor @Deprecated public RxPagedListBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagingSource<Key,Value>> pagingSourceFactory, int pageSize);
diff --git a/palette/palette-ktx/build.gradle b/palette/palette-ktx/build.gradle
index 20b494b..4eef22b 100644
--- a/palette/palette-ktx/build.gradle
+++ b/palette/palette-ktx/build.gradle
@@ -23,11 +23,6 @@
 }
 
 dependencies {
-    // Atomic group
-    constraints {
-        implementation(project(":palette:palette"))
-    }
-
     api(project(":palette:palette"))
     api(libs.kotlinStdlib)
     androidTestImplementation(libs.junit)
diff --git a/palette/palette/build.gradle b/palette/palette/build.gradle
index bb524c8..90bdee7 100644
--- a/palette/palette/build.gradle
+++ b/palette/palette/build.gradle
@@ -6,11 +6,6 @@
 }
 
 dependencies {
-    // Atomic group
-    constraints {
-        implementation(project(":palette:palette-ktx"))
-    }
-
     api("androidx.core:core:1.1.0")
     implementation("androidx.collection:collection:1.1.0")
 
diff --git a/playground-common/playground-plugin/build.gradle b/playground-common/playground-plugin/build.gradle
index b65ae88..6012c6a 100644
--- a/playground-common/playground-plugin/build.gradle
+++ b/playground-common/playground-plugin/build.gradle
@@ -20,8 +20,8 @@
 }
 
 dependencies {
-    implementation("com.gradle:gradle-enterprise-gradle-plugin:3.10.2")
-    implementation("com.gradle:common-custom-user-data-gradle-plugin:1.7.2")
+    implementation("com.gradle:gradle-enterprise-gradle-plugin:3.12.4")
+    implementation("com.gradle:common-custom-user-data-gradle-plugin:1.9")
     testImplementation(libs.junit)
     testImplementation(libs.truth)
 }
diff --git a/playground-common/playground.properties b/playground-common/playground.properties
index c1daf8e..3cd6373 100644
--- a/playground-common/playground.properties
+++ b/playground-common/playground.properties
@@ -26,5 +26,5 @@
 # Disable docs
 androidx.enableDocumentation=false
 androidx.playground.snapshotBuildId=9725822
-androidx.playground.metalavaBuildId=9692962
+androidx.playground.metalavaBuildId=9753996
 androidx.studio.type=playground
diff --git a/preference/preference-ktx/build.gradle b/preference/preference-ktx/build.gradle
index 19e8159..ebef733 100644
--- a/preference/preference-ktx/build.gradle
+++ b/preference/preference-ktx/build.gradle
@@ -23,11 +23,6 @@
 }
 
 dependencies {
-    // Atomic group
-    constraints {
-        implementation(project(":preference:preference"))
-    }
-
     api(project(":preference:preference"))
     api("androidx.core:core-ktx:1.1.0") {
         because "Mirror preference dependency graph for -ktx artifacts"
diff --git a/preference/preference/build.gradle b/preference/preference/build.gradle
index 475c906..366ccdc 100644
--- a/preference/preference/build.gradle
+++ b/preference/preference/build.gradle
@@ -23,11 +23,6 @@
 }
 
 dependencies {
-    // Atomic group
-    constraints {
-        implementation(project(":preference:preference-ktx"))
-    }
-
     api("androidx.annotation:annotation:1.2.0")
     api("androidx.appcompat:appcompat:1.1.0")
     // Use the latest version of core library for verifying insets visibility
diff --git a/privacysandbox/ads/OWNERS b/privacysandbox/ads/OWNERS
index 67d0de6..213fd78 100644
--- a/privacysandbox/ads/OWNERS
+++ b/privacysandbox/ads/OWNERS
@@ -1,3 +1,11 @@
-# Please keep this list alphabetically sorted
-jmarkoff@google.com
-npattan@google.com
+# adservices.measurement OWNERS
+arpanah@google.com # Measurement Primary PoC
+lmohanan@google.com
+# adservices.customaudience, adservices.adselection OWNERS
+adigupt@google.com # FLEDGE Primary PoC
+galarragas@google.com
+# adservices.topics, .adid, .appsetid, .common OWNERS
+haoliuu@google.com # Primary PoC for Topics, Common
+npattan@google.com # Creator
+jmarkoff@google.com # DevRel Jetpack Lead
+carolinewang@google.com # DevRel TPM Lead
diff --git a/privacysandbox/ads/ads-adservices-java/api/1.0.0-beta02.txt b/privacysandbox/ads/ads-adservices-java/api/1.0.0-beta02.txt
new file mode 100644
index 0000000..26eea8b
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices-java/api/1.0.0-beta02.txt
@@ -0,0 +1,92 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.java.adid {
+
+  public abstract class AdIdManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adid.AdId> getAdIdAsync();
+    field public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures.Companion Companion;
+  }
+
+  public static final class AdIdManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.adselection {
+
+  public abstract class AdSelectionManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> reportImpressionAsync(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome> selectAdsAsync(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+    field public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures.Companion Companion;
+  }
+
+  public static final class AdSelectionManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.appsetid {
+
+  public abstract class AppSetIdManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.appsetid.AppSetId> getAppSetIdAsync();
+    field public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures.Companion Companion;
+  }
+
+  public static final class AppSetIdManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.customaudience {
+
+  public abstract class CustomAudienceManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> joinCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> leaveCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures.Companion Companion;
+  }
+
+  public static final class CustomAudienceManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.measurement {
+
+  public abstract class MeasurementManagerFutures {
+    method public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> deleteRegistrationsAsync(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest);
+    method public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Integer> getMeasurementApiStatusAsync();
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(android.net.Uri attributionSource, android.view.InputEvent? inputEvent);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerTriggerAsync(android.net.Uri trigger);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebSourceAsync(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebTriggerAsync(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures.Companion Companion;
+  }
+
+  public static final class MeasurementManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.topics {
+
+  public abstract class TopicsManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse> getTopicsAsync(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures.Companion Companion;
+  }
+
+  public static final class TopicsManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+  }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices-java/api/public_plus_experimental_1.0.0-beta02.txt b/privacysandbox/ads/ads-adservices-java/api/public_plus_experimental_1.0.0-beta02.txt
new file mode 100644
index 0000000..26eea8b
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices-java/api/public_plus_experimental_1.0.0-beta02.txt
@@ -0,0 +1,92 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.java.adid {
+
+  public abstract class AdIdManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adid.AdId> getAdIdAsync();
+    field public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures.Companion Companion;
+  }
+
+  public static final class AdIdManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.adselection {
+
+  public abstract class AdSelectionManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> reportImpressionAsync(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome> selectAdsAsync(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+    field public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures.Companion Companion;
+  }
+
+  public static final class AdSelectionManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.appsetid {
+
+  public abstract class AppSetIdManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.appsetid.AppSetId> getAppSetIdAsync();
+    field public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures.Companion Companion;
+  }
+
+  public static final class AppSetIdManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.customaudience {
+
+  public abstract class CustomAudienceManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> joinCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> leaveCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures.Companion Companion;
+  }
+
+  public static final class CustomAudienceManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.measurement {
+
+  public abstract class MeasurementManagerFutures {
+    method public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> deleteRegistrationsAsync(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest);
+    method public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Integer> getMeasurementApiStatusAsync();
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(android.net.Uri attributionSource, android.view.InputEvent? inputEvent);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerTriggerAsync(android.net.Uri trigger);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebSourceAsync(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebTriggerAsync(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures.Companion Companion;
+  }
+
+  public static final class MeasurementManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.topics {
+
+  public abstract class TopicsManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse> getTopicsAsync(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures.Companion Companion;
+  }
+
+  public static final class TopicsManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+  }
+
+}
+
diff --git a/ads/ads-identifier-common/api/res-current.txt b/privacysandbox/ads/ads-adservices-java/api/res-1.0.0-beta02.txt
similarity index 100%
copy from ads/ads-identifier-common/api/res-current.txt
copy to privacysandbox/ads/ads-adservices-java/api/res-1.0.0-beta02.txt
diff --git a/privacysandbox/ads/ads-adservices-java/api/restricted_1.0.0-beta02.txt b/privacysandbox/ads/ads-adservices-java/api/restricted_1.0.0-beta02.txt
new file mode 100644
index 0000000..26eea8b
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices-java/api/restricted_1.0.0-beta02.txt
@@ -0,0 +1,92 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.java.adid {
+
+  public abstract class AdIdManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adid.AdId> getAdIdAsync();
+    field public static final androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures.Companion Companion;
+  }
+
+  public static final class AdIdManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.adselection {
+
+  public abstract class AdSelectionManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> reportImpressionAsync(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome> selectAdsAsync(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+    field public static final androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures.Companion Companion;
+  }
+
+  public static final class AdSelectionManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.appsetid {
+
+  public abstract class AppSetIdManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.appsetid.AppSetId> getAppSetIdAsync();
+    field public static final androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures.Companion Companion;
+  }
+
+  public static final class AppSetIdManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.customaudience {
+
+  public abstract class CustomAudienceManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> joinCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> leaveCustomAudienceAsync(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures.Companion Companion;
+  }
+
+  public static final class CustomAudienceManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.measurement {
+
+  public abstract class MeasurementManagerFutures {
+    method public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> deleteRegistrationsAsync(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest);
+    method public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Integer> getMeasurementApiStatusAsync();
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(android.net.Uri attributionSource, android.view.InputEvent? inputEvent);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerTriggerAsync(android.net.Uri trigger);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebSourceAsync(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebTriggerAsync(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures.Companion Companion;
+  }
+
+  public static final class MeasurementManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.java.topics {
+
+  public abstract class TopicsManagerFutures {
+    method public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract com.google.common.util.concurrent.ListenableFuture<androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse> getTopicsAsync(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request);
+    field public static final androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures.Companion Companion;
+  }
+
+  public static final class TopicsManagerFutures.Companion {
+    method public androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures? from(android.content.Context context);
+  }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/TestUtil.java b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/TestUtil.java
index cb16955..5d17da4 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/TestUtil.java
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/TestUtil.java
@@ -24,15 +24,18 @@
 import android.os.Build;
 import android.util.Log;
 
+import androidx.core.os.BuildCompat;
 import androidx.test.core.app.ApplicationProvider;
 
 import java.util.List;
+import java.util.stream.Collectors;
 
 public class TestUtil {
     private Instrumentation mInstrumentation;
     private String mTag;
     // Used to get the package name. Copied over from com.android.adservices.AdServicesCommon
     private static final String TOPICS_SERVICE_NAME = "android.adservices.TOPICS_SERVICE";
+    private static final String EXT_SERVICES_PACKAGE_NAME = "ext.adservices";
     // The JobId of the Epoch Computation.
     private static final int EPOCH_JOB_ID = 2;
 
@@ -220,10 +223,18 @@
     // Used to get the package name. Copied over from com.android.adservices.AndroidServiceBinder
     public String getAdServicesPackageName() {
         final Intent intent = new Intent(TOPICS_SERVICE_NAME);
-        final List<ResolveInfo> resolveInfos = ApplicationProvider.getApplicationContext()
+        List<ResolveInfo> resolveInfos = ApplicationProvider.getApplicationContext()
                 .getPackageManager()
                 .queryIntentServices(intent, PackageManager.MATCH_SYSTEM_ONLY);
 
+        // TODO: b/271866693 avoid hardcoding package names
+        if (resolveInfos != null && BuildCompat.isAtLeastT()) {
+            resolveInfos = resolveInfos.stream()
+                    .filter(info ->
+                            !info.serviceInfo.packageName.contains(EXT_SERVICES_PACKAGE_NAME))
+                    .collect(Collectors.toList());
+        }
+
         if (resolveInfos == null || resolveInfos.isEmpty()) {
             Log.e(mTag, "Failed to find resolveInfo for adServices service. Intent action: "
                             + TOPICS_SERVICE_NAME);
diff --git a/privacysandbox/ads/ads-adservices/api/1.0.0-beta02.txt b/privacysandbox/ads/ads-adservices/api/1.0.0-beta02.txt
new file mode 100644
index 0000000..30cd307
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/api/1.0.0-beta02.txt
@@ -0,0 +1,345 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.adid {
+
+  public final class AdId {
+    method public String getAdId();
+    method public boolean isLimitAdTrackingEnabled();
+    property public final String adId;
+    property public final boolean isLimitAdTrackingEnabled;
+  }
+
+  public abstract class AdIdManager {
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract suspend Object? getAdId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adid.AdId>);
+    method public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager.Companion Companion;
+  }
+
+  public static final class AdIdManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.adselection {
+
+  public final class AdSelectionConfig {
+    ctor public AdSelectionConfig(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller, android.net.Uri decisionLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals, java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals, android.net.Uri trustedScoringSignalsUri);
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getAdSelectionSignals();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> getCustomAudienceBuyers();
+    method public android.net.Uri getDecisionLogicUri();
+    method public java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> getPerBuyerSignals();
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getSeller();
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getSellerSignals();
+    method public android.net.Uri getTrustedScoringSignalsUri();
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers;
+    property public final android.net.Uri decisionLogicUri;
+    property public final java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals;
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller;
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals;
+    property public final android.net.Uri trustedScoringSignalsUri;
+  }
+
+  public abstract class AdSelectionManager {
+    method public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? reportImpression(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? selectAds(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+    field public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager.Companion Companion;
+  }
+
+  public static final class AdSelectionManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+  }
+
+  public final class AdSelectionOutcome {
+    ctor public AdSelectionOutcome(long adSelectionId, android.net.Uri renderUri);
+    method public long getAdSelectionId();
+    method public android.net.Uri getRenderUri();
+    property public final long adSelectionId;
+    property public final android.net.Uri renderUri;
+  }
+
+  public final class ReportImpressionRequest {
+    ctor public ReportImpressionRequest(long adSelectionId, androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+    method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig getAdSelectionConfig();
+    method public long getAdSelectionId();
+    property public final androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig;
+    property public final long adSelectionId;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.appsetid {
+
+  public final class AppSetId {
+    ctor public AppSetId(String id, int scope);
+    method public String getId();
+    method public int getScope();
+    property public final String id;
+    property public final int scope;
+    field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetId.Companion Companion;
+    field public static final int SCOPE_APP = 1; // 0x1
+    field public static final int SCOPE_DEVELOPER = 2; // 0x2
+  }
+
+  public static final class AppSetId.Companion {
+  }
+
+  public abstract class AppSetIdManager {
+    method public abstract suspend Object? getAppSetId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.appsetid.AppSetId>);
+    method public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager.Companion Companion;
+  }
+
+  public static final class AppSetIdManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.common {
+
+  public final class AdData {
+    ctor public AdData(android.net.Uri renderUri, String metadata);
+    method public String getMetadata();
+    method public android.net.Uri getRenderUri();
+    property public final String metadata;
+    property public final android.net.Uri renderUri;
+  }
+
+  public final class AdSelectionSignals {
+    ctor public AdSelectionSignals(String signals);
+    method public String getSignals();
+    property public final String signals;
+  }
+
+  public final class AdTechIdentifier {
+    ctor public AdTechIdentifier(String identifier);
+    method public String getIdentifier();
+    property public final String identifier;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.customaudience {
+
+  public final class CustomAudience {
+    ctor public CustomAudience(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals, optional androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals);
+    method public java.time.Instant? getActivationTime();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> getAds();
+    method public android.net.Uri getBiddingLogicUri();
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+    method public android.net.Uri getDailyUpdateUri();
+    method public java.time.Instant? getExpirationTime();
+    method public String getName();
+    method public androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? getTrustedBiddingSignals();
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? getUserBiddingSignals();
+    property public final java.time.Instant? activationTime;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads;
+    property public final android.net.Uri biddingLogicUri;
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+    property public final android.net.Uri dailyUpdateUri;
+    property public final java.time.Instant? expirationTime;
+    property public final String name;
+    property public final androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals;
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals;
+  }
+
+  public static final class CustomAudience.Builder {
+    ctor public CustomAudience.Builder(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience build();
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setActivationTime(java.time.Instant activationTime);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setAds(java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBiddingLogicUri(android.net.Uri biddingLogicUri);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBuyer(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setDailyUpdateUri(android.net.Uri dailyUpdateUri);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setExpirationTime(java.time.Instant expirationTime);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setName(String name);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setTrustedBiddingData(androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData trustedBiddingSignals);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setUserBiddingSignals(androidx.privacysandbox.ads.adservices.common.AdSelectionSignals userBiddingSignals);
+  }
+
+  public abstract class CustomAudienceManager {
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? joinCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? leaveCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager.Companion Companion;
+  }
+
+  public static final class CustomAudienceManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+  }
+
+  public final class JoinCustomAudienceRequest {
+    ctor public JoinCustomAudienceRequest(androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience getCustomAudience();
+    property public final androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience;
+  }
+
+  public final class LeaveCustomAudienceRequest {
+    ctor public LeaveCustomAudienceRequest(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name);
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+    method public String getName();
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+    property public final String name;
+  }
+
+  public final class TrustedBiddingData {
+    ctor public TrustedBiddingData(android.net.Uri trustedBiddingUri, java.util.List<java.lang.String> trustedBiddingKeys);
+    method public java.util.List<java.lang.String> getTrustedBiddingKeys();
+    method public android.net.Uri getTrustedBiddingUri();
+    property public final java.util.List<java.lang.String> trustedBiddingKeys;
+    property public final android.net.Uri trustedBiddingUri;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.measurement {
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class DeletionRequest {
+    ctor public DeletionRequest(int deletionMode, int matchBehavior, optional java.time.Instant start, optional java.time.Instant end, optional java.util.List<? extends android.net.Uri> domainUris, optional java.util.List<? extends android.net.Uri> originUris);
+    method public int getDeletionMode();
+    method public java.util.List<android.net.Uri> getDomainUris();
+    method public java.time.Instant getEnd();
+    method public int getMatchBehavior();
+    method public java.util.List<android.net.Uri> getOriginUris();
+    method public java.time.Instant getStart();
+    property public final int deletionMode;
+    property public final java.util.List<android.net.Uri> domainUris;
+    property public final java.time.Instant end;
+    property public final int matchBehavior;
+    property public final java.util.List<android.net.Uri> originUris;
+    property public final java.time.Instant start;
+    field public static final androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Companion Companion;
+    field public static final int DELETION_MODE_ALL = 0; // 0x0
+    field public static final int DELETION_MODE_EXCLUDE_INTERNAL_DATA = 1; // 0x1
+    field public static final int MATCH_BEHAVIOR_DELETE = 0; // 0x0
+    field public static final int MATCH_BEHAVIOR_PRESERVE = 1; // 0x1
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class DeletionRequest.Builder {
+    ctor public DeletionRequest.Builder(int deletionMode, int matchBehavior);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest build();
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setDomainUris(java.util.List<? extends android.net.Uri> domainUris);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setEnd(java.time.Instant end);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setOriginUris(java.util.List<? extends android.net.Uri> originUris);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setStart(java.time.Instant start);
+  }
+
+  public static final class DeletionRequest.Companion {
+  }
+
+  public abstract class MeasurementManager {
+    ctor public MeasurementManager();
+    method public abstract suspend Object? deleteRegistrations(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? getMeasurementApiStatus(kotlin.coroutines.Continuation<? super java.lang.Integer>);
+    method public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerSource(android.net.Uri attributionSource, android.view.InputEvent? inputEvent, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerTrigger(android.net.Uri trigger, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebSource(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebTrigger(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    field public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager.Companion Companion;
+    field public static final int MEASUREMENT_API_STATE_DISABLED = 0; // 0x0
+    field public static final int MEASUREMENT_API_STATE_ENABLED = 1; // 0x1
+  }
+
+  public static final class MeasurementManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceParams {
+    ctor public WebSourceParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+    method public boolean getDebugKeyAllowed();
+    method public android.net.Uri getRegistrationUri();
+    property public final boolean debugKeyAllowed;
+    property public final android.net.Uri registrationUri;
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceRegistrationRequest {
+    ctor public WebSourceRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri, optional android.view.InputEvent? inputEvent, optional android.net.Uri? appDestination, optional android.net.Uri? webDestination, optional android.net.Uri? verifiedDestination);
+    method public android.net.Uri? getAppDestination();
+    method public android.view.InputEvent? getInputEvent();
+    method public android.net.Uri getTopOriginUri();
+    method public android.net.Uri? getVerifiedDestination();
+    method public android.net.Uri? getWebDestination();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> getWebSourceParams();
+    property public final android.net.Uri? appDestination;
+    property public final android.view.InputEvent? inputEvent;
+    property public final android.net.Uri topOriginUri;
+    property public final android.net.Uri? verifiedDestination;
+    property public final android.net.Uri? webDestination;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams;
+  }
+
+  public static final class WebSourceRegistrationRequest.Builder {
+    ctor public WebSourceRegistrationRequest.Builder(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest build();
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setAppDestination(android.net.Uri? appDestination);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setVerifiedDestination(android.net.Uri? verifiedDestination);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setWebDestination(android.net.Uri? webDestination);
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerParams {
+    ctor public WebTriggerParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+    method public boolean getDebugKeyAllowed();
+    method public android.net.Uri getRegistrationUri();
+    property public final boolean debugKeyAllowed;
+    property public final android.net.Uri registrationUri;
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerRegistrationRequest {
+    ctor public WebTriggerRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams, android.net.Uri destination);
+    method public android.net.Uri getDestination();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> getWebTriggerParams();
+    property public final android.net.Uri destination;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.topics {
+
+  public final class GetTopicsRequest {
+    ctor public GetTopicsRequest(optional String adsSdkName, optional boolean shouldRecordObservation);
+    method public String getAdsSdkName();
+    method public boolean getShouldRecordObservation();
+    property public final String adsSdkName;
+    property public final boolean shouldRecordObservation;
+  }
+
+  public static final class GetTopicsRequest.Builder {
+    ctor public GetTopicsRequest.Builder();
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest build();
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setAdsSdkName(String adsSdkName);
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setShouldRecordObservation(boolean shouldRecordObservation);
+  }
+
+  public final class GetTopicsResponse {
+    ctor public GetTopicsResponse(java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics);
+    method public java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> getTopics();
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics;
+  }
+
+  public final class Topic {
+    ctor public Topic(long taxonomyVersion, long modelVersion, int topicId);
+    method public long getModelVersion();
+    method public long getTaxonomyVersion();
+    method public int getTopicId();
+    property public final long modelVersion;
+    property public final long taxonomyVersion;
+    property public final int topicId;
+  }
+
+  public abstract class TopicsManager {
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract suspend Object? getTopics(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse>);
+    method public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager.Companion Companion;
+  }
+
+  public static final class TopicsManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+  }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices/api/public_plus_experimental_1.0.0-beta02.txt b/privacysandbox/ads/ads-adservices/api/public_plus_experimental_1.0.0-beta02.txt
new file mode 100644
index 0000000..30cd307
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/api/public_plus_experimental_1.0.0-beta02.txt
@@ -0,0 +1,345 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.adid {
+
+  public final class AdId {
+    method public String getAdId();
+    method public boolean isLimitAdTrackingEnabled();
+    property public final String adId;
+    property public final boolean isLimitAdTrackingEnabled;
+  }
+
+  public abstract class AdIdManager {
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract suspend Object? getAdId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adid.AdId>);
+    method public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager.Companion Companion;
+  }
+
+  public static final class AdIdManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.adselection {
+
+  public final class AdSelectionConfig {
+    ctor public AdSelectionConfig(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller, android.net.Uri decisionLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals, java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals, android.net.Uri trustedScoringSignalsUri);
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getAdSelectionSignals();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> getCustomAudienceBuyers();
+    method public android.net.Uri getDecisionLogicUri();
+    method public java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> getPerBuyerSignals();
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getSeller();
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getSellerSignals();
+    method public android.net.Uri getTrustedScoringSignalsUri();
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers;
+    property public final android.net.Uri decisionLogicUri;
+    property public final java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals;
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller;
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals;
+    property public final android.net.Uri trustedScoringSignalsUri;
+  }
+
+  public abstract class AdSelectionManager {
+    method public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? reportImpression(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? selectAds(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+    field public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager.Companion Companion;
+  }
+
+  public static final class AdSelectionManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+  }
+
+  public final class AdSelectionOutcome {
+    ctor public AdSelectionOutcome(long adSelectionId, android.net.Uri renderUri);
+    method public long getAdSelectionId();
+    method public android.net.Uri getRenderUri();
+    property public final long adSelectionId;
+    property public final android.net.Uri renderUri;
+  }
+
+  public final class ReportImpressionRequest {
+    ctor public ReportImpressionRequest(long adSelectionId, androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+    method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig getAdSelectionConfig();
+    method public long getAdSelectionId();
+    property public final androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig;
+    property public final long adSelectionId;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.appsetid {
+
+  public final class AppSetId {
+    ctor public AppSetId(String id, int scope);
+    method public String getId();
+    method public int getScope();
+    property public final String id;
+    property public final int scope;
+    field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetId.Companion Companion;
+    field public static final int SCOPE_APP = 1; // 0x1
+    field public static final int SCOPE_DEVELOPER = 2; // 0x2
+  }
+
+  public static final class AppSetId.Companion {
+  }
+
+  public abstract class AppSetIdManager {
+    method public abstract suspend Object? getAppSetId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.appsetid.AppSetId>);
+    method public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager.Companion Companion;
+  }
+
+  public static final class AppSetIdManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.common {
+
+  public final class AdData {
+    ctor public AdData(android.net.Uri renderUri, String metadata);
+    method public String getMetadata();
+    method public android.net.Uri getRenderUri();
+    property public final String metadata;
+    property public final android.net.Uri renderUri;
+  }
+
+  public final class AdSelectionSignals {
+    ctor public AdSelectionSignals(String signals);
+    method public String getSignals();
+    property public final String signals;
+  }
+
+  public final class AdTechIdentifier {
+    ctor public AdTechIdentifier(String identifier);
+    method public String getIdentifier();
+    property public final String identifier;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.customaudience {
+
+  public final class CustomAudience {
+    ctor public CustomAudience(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals, optional androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals);
+    method public java.time.Instant? getActivationTime();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> getAds();
+    method public android.net.Uri getBiddingLogicUri();
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+    method public android.net.Uri getDailyUpdateUri();
+    method public java.time.Instant? getExpirationTime();
+    method public String getName();
+    method public androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? getTrustedBiddingSignals();
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? getUserBiddingSignals();
+    property public final java.time.Instant? activationTime;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads;
+    property public final android.net.Uri biddingLogicUri;
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+    property public final android.net.Uri dailyUpdateUri;
+    property public final java.time.Instant? expirationTime;
+    property public final String name;
+    property public final androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals;
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals;
+  }
+
+  public static final class CustomAudience.Builder {
+    ctor public CustomAudience.Builder(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience build();
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setActivationTime(java.time.Instant activationTime);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setAds(java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBiddingLogicUri(android.net.Uri biddingLogicUri);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBuyer(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setDailyUpdateUri(android.net.Uri dailyUpdateUri);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setExpirationTime(java.time.Instant expirationTime);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setName(String name);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setTrustedBiddingData(androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData trustedBiddingSignals);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setUserBiddingSignals(androidx.privacysandbox.ads.adservices.common.AdSelectionSignals userBiddingSignals);
+  }
+
+  public abstract class CustomAudienceManager {
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? joinCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? leaveCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager.Companion Companion;
+  }
+
+  public static final class CustomAudienceManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+  }
+
+  public final class JoinCustomAudienceRequest {
+    ctor public JoinCustomAudienceRequest(androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience getCustomAudience();
+    property public final androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience;
+  }
+
+  public final class LeaveCustomAudienceRequest {
+    ctor public LeaveCustomAudienceRequest(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name);
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+    method public String getName();
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+    property public final String name;
+  }
+
+  public final class TrustedBiddingData {
+    ctor public TrustedBiddingData(android.net.Uri trustedBiddingUri, java.util.List<java.lang.String> trustedBiddingKeys);
+    method public java.util.List<java.lang.String> getTrustedBiddingKeys();
+    method public android.net.Uri getTrustedBiddingUri();
+    property public final java.util.List<java.lang.String> trustedBiddingKeys;
+    property public final android.net.Uri trustedBiddingUri;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.measurement {
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class DeletionRequest {
+    ctor public DeletionRequest(int deletionMode, int matchBehavior, optional java.time.Instant start, optional java.time.Instant end, optional java.util.List<? extends android.net.Uri> domainUris, optional java.util.List<? extends android.net.Uri> originUris);
+    method public int getDeletionMode();
+    method public java.util.List<android.net.Uri> getDomainUris();
+    method public java.time.Instant getEnd();
+    method public int getMatchBehavior();
+    method public java.util.List<android.net.Uri> getOriginUris();
+    method public java.time.Instant getStart();
+    property public final int deletionMode;
+    property public final java.util.List<android.net.Uri> domainUris;
+    property public final java.time.Instant end;
+    property public final int matchBehavior;
+    property public final java.util.List<android.net.Uri> originUris;
+    property public final java.time.Instant start;
+    field public static final androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Companion Companion;
+    field public static final int DELETION_MODE_ALL = 0; // 0x0
+    field public static final int DELETION_MODE_EXCLUDE_INTERNAL_DATA = 1; // 0x1
+    field public static final int MATCH_BEHAVIOR_DELETE = 0; // 0x0
+    field public static final int MATCH_BEHAVIOR_PRESERVE = 1; // 0x1
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class DeletionRequest.Builder {
+    ctor public DeletionRequest.Builder(int deletionMode, int matchBehavior);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest build();
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setDomainUris(java.util.List<? extends android.net.Uri> domainUris);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setEnd(java.time.Instant end);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setOriginUris(java.util.List<? extends android.net.Uri> originUris);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setStart(java.time.Instant start);
+  }
+
+  public static final class DeletionRequest.Companion {
+  }
+
+  public abstract class MeasurementManager {
+    ctor public MeasurementManager();
+    method public abstract suspend Object? deleteRegistrations(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? getMeasurementApiStatus(kotlin.coroutines.Continuation<? super java.lang.Integer>);
+    method public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerSource(android.net.Uri attributionSource, android.view.InputEvent? inputEvent, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerTrigger(android.net.Uri trigger, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebSource(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebTrigger(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    field public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager.Companion Companion;
+    field public static final int MEASUREMENT_API_STATE_DISABLED = 0; // 0x0
+    field public static final int MEASUREMENT_API_STATE_ENABLED = 1; // 0x1
+  }
+
+  public static final class MeasurementManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceParams {
+    ctor public WebSourceParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+    method public boolean getDebugKeyAllowed();
+    method public android.net.Uri getRegistrationUri();
+    property public final boolean debugKeyAllowed;
+    property public final android.net.Uri registrationUri;
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceRegistrationRequest {
+    ctor public WebSourceRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri, optional android.view.InputEvent? inputEvent, optional android.net.Uri? appDestination, optional android.net.Uri? webDestination, optional android.net.Uri? verifiedDestination);
+    method public android.net.Uri? getAppDestination();
+    method public android.view.InputEvent? getInputEvent();
+    method public android.net.Uri getTopOriginUri();
+    method public android.net.Uri? getVerifiedDestination();
+    method public android.net.Uri? getWebDestination();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> getWebSourceParams();
+    property public final android.net.Uri? appDestination;
+    property public final android.view.InputEvent? inputEvent;
+    property public final android.net.Uri topOriginUri;
+    property public final android.net.Uri? verifiedDestination;
+    property public final android.net.Uri? webDestination;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams;
+  }
+
+  public static final class WebSourceRegistrationRequest.Builder {
+    ctor public WebSourceRegistrationRequest.Builder(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest build();
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setAppDestination(android.net.Uri? appDestination);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setVerifiedDestination(android.net.Uri? verifiedDestination);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setWebDestination(android.net.Uri? webDestination);
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerParams {
+    ctor public WebTriggerParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+    method public boolean getDebugKeyAllowed();
+    method public android.net.Uri getRegistrationUri();
+    property public final boolean debugKeyAllowed;
+    property public final android.net.Uri registrationUri;
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerRegistrationRequest {
+    ctor public WebTriggerRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams, android.net.Uri destination);
+    method public android.net.Uri getDestination();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> getWebTriggerParams();
+    property public final android.net.Uri destination;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.topics {
+
+  public final class GetTopicsRequest {
+    ctor public GetTopicsRequest(optional String adsSdkName, optional boolean shouldRecordObservation);
+    method public String getAdsSdkName();
+    method public boolean getShouldRecordObservation();
+    property public final String adsSdkName;
+    property public final boolean shouldRecordObservation;
+  }
+
+  public static final class GetTopicsRequest.Builder {
+    ctor public GetTopicsRequest.Builder();
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest build();
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setAdsSdkName(String adsSdkName);
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setShouldRecordObservation(boolean shouldRecordObservation);
+  }
+
+  public final class GetTopicsResponse {
+    ctor public GetTopicsResponse(java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics);
+    method public java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> getTopics();
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics;
+  }
+
+  public final class Topic {
+    ctor public Topic(long taxonomyVersion, long modelVersion, int topicId);
+    method public long getModelVersion();
+    method public long getTaxonomyVersion();
+    method public int getTopicId();
+    property public final long modelVersion;
+    property public final long taxonomyVersion;
+    property public final int topicId;
+  }
+
+  public abstract class TopicsManager {
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract suspend Object? getTopics(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse>);
+    method public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager.Companion Companion;
+  }
+
+  public static final class TopicsManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+  }
+
+}
+
diff --git a/ads/ads-identifier-common/api/res-current.txt b/privacysandbox/ads/ads-adservices/api/res-1.0.0-beta02.txt
similarity index 100%
copy from ads/ads-identifier-common/api/res-current.txt
copy to privacysandbox/ads/ads-adservices/api/res-1.0.0-beta02.txt
diff --git a/privacysandbox/ads/ads-adservices/api/restricted_1.0.0-beta02.txt b/privacysandbox/ads/ads-adservices/api/restricted_1.0.0-beta02.txt
new file mode 100644
index 0000000..30cd307
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/api/restricted_1.0.0-beta02.txt
@@ -0,0 +1,345 @@
+// Signature format: 4.0
+package androidx.privacysandbox.ads.adservices.adid {
+
+  public final class AdId {
+    method public String getAdId();
+    method public boolean isLimitAdTrackingEnabled();
+    property public final String adId;
+    property public final boolean isLimitAdTrackingEnabled;
+  }
+
+  public abstract class AdIdManager {
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID) public abstract suspend Object? getAdId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adid.AdId>);
+    method public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.adid.AdIdManager.Companion Companion;
+  }
+
+  public static final class AdIdManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.adid.AdIdManager? obtain(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.adselection {
+
+  public final class AdSelectionConfig {
+    ctor public AdSelectionConfig(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller, android.net.Uri decisionLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals, androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals, java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals, android.net.Uri trustedScoringSignalsUri);
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getAdSelectionSignals();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> getCustomAudienceBuyers();
+    method public android.net.Uri getDecisionLogicUri();
+    method public java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> getPerBuyerSignals();
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getSeller();
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals getSellerSignals();
+    method public android.net.Uri getTrustedScoringSignalsUri();
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals adSelectionSignals;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier> customAudienceBuyers;
+    property public final android.net.Uri decisionLogicUri;
+    property public final java.util.Map<androidx.privacysandbox.ads.adservices.common.AdTechIdentifier,androidx.privacysandbox.ads.adservices.common.AdSelectionSignals> perBuyerSignals;
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier seller;
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals sellerSignals;
+    property public final android.net.Uri trustedScoringSignalsUri;
+  }
+
+  public abstract class AdSelectionManager {
+    method public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? reportImpression(androidx.privacysandbox.ads.adservices.adselection.ReportImpressionRequest reportImpressionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? selectAds(androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.adselection.AdSelectionOutcome>);
+    field public static final androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager.Companion Companion;
+  }
+
+  public static final class AdSelectionManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionManager? obtain(android.content.Context context);
+  }
+
+  public final class AdSelectionOutcome {
+    ctor public AdSelectionOutcome(long adSelectionId, android.net.Uri renderUri);
+    method public long getAdSelectionId();
+    method public android.net.Uri getRenderUri();
+    property public final long adSelectionId;
+    property public final android.net.Uri renderUri;
+  }
+
+  public final class ReportImpressionRequest {
+    ctor public ReportImpressionRequest(long adSelectionId, androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig);
+    method public androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig getAdSelectionConfig();
+    method public long getAdSelectionId();
+    property public final androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig adSelectionConfig;
+    property public final long adSelectionId;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.appsetid {
+
+  public final class AppSetId {
+    ctor public AppSetId(String id, int scope);
+    method public String getId();
+    method public int getScope();
+    property public final String id;
+    property public final int scope;
+    field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetId.Companion Companion;
+    field public static final int SCOPE_APP = 1; // 0x1
+    field public static final int SCOPE_DEVELOPER = 2; // 0x2
+  }
+
+  public static final class AppSetId.Companion {
+  }
+
+  public abstract class AppSetIdManager {
+    method public abstract suspend Object? getAppSetId(kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.appsetid.AppSetId>);
+    method public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager.Companion Companion;
+  }
+
+  public static final class AppSetIdManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.appsetid.AppSetIdManager? obtain(android.content.Context context);
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.common {
+
+  public final class AdData {
+    ctor public AdData(android.net.Uri renderUri, String metadata);
+    method public String getMetadata();
+    method public android.net.Uri getRenderUri();
+    property public final String metadata;
+    property public final android.net.Uri renderUri;
+  }
+
+  public final class AdSelectionSignals {
+    ctor public AdSelectionSignals(String signals);
+    method public String getSignals();
+    property public final String signals;
+  }
+
+  public final class AdTechIdentifier {
+    ctor public AdTechIdentifier(String identifier);
+    method public String getIdentifier();
+    property public final String identifier;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.customaudience {
+
+  public final class CustomAudience {
+    ctor public CustomAudience(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals, optional androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals);
+    method public java.time.Instant? getActivationTime();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> getAds();
+    method public android.net.Uri getBiddingLogicUri();
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+    method public android.net.Uri getDailyUpdateUri();
+    method public java.time.Instant? getExpirationTime();
+    method public String getName();
+    method public androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? getTrustedBiddingSignals();
+    method public androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? getUserBiddingSignals();
+    property public final java.time.Instant? activationTime;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads;
+    property public final android.net.Uri biddingLogicUri;
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+    property public final android.net.Uri dailyUpdateUri;
+    property public final java.time.Instant? expirationTime;
+    property public final String name;
+    property public final androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData? trustedBiddingSignals;
+    property public final androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals;
+  }
+
+  public static final class CustomAudience.Builder {
+    ctor public CustomAudience.Builder(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name, android.net.Uri dailyUpdateUri, android.net.Uri biddingLogicUri, java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience build();
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setActivationTime(java.time.Instant activationTime);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setAds(java.util.List<androidx.privacysandbox.ads.adservices.common.AdData> ads);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBiddingLogicUri(android.net.Uri biddingLogicUri);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setBuyer(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setDailyUpdateUri(android.net.Uri dailyUpdateUri);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setExpirationTime(java.time.Instant expirationTime);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setName(String name);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setTrustedBiddingData(androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData trustedBiddingSignals);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience.Builder setUserBiddingSignals(androidx.privacysandbox.ads.adservices.common.AdSelectionSignals userBiddingSignals);
+  }
+
+  public abstract class CustomAudienceManager {
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? joinCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public abstract suspend Object? leaveCustomAudience(androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager.Companion Companion;
+  }
+
+  public static final class CustomAudienceManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudienceManager? obtain(android.content.Context context);
+  }
+
+  public final class JoinCustomAudienceRequest {
+    ctor public JoinCustomAudienceRequest(androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience);
+    method public androidx.privacysandbox.ads.adservices.customaudience.CustomAudience getCustomAudience();
+    property public final androidx.privacysandbox.ads.adservices.customaudience.CustomAudience customAudience;
+  }
+
+  public final class LeaveCustomAudienceRequest {
+    ctor public LeaveCustomAudienceRequest(androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer, String name);
+    method public androidx.privacysandbox.ads.adservices.common.AdTechIdentifier getBuyer();
+    method public String getName();
+    property public final androidx.privacysandbox.ads.adservices.common.AdTechIdentifier buyer;
+    property public final String name;
+  }
+
+  public final class TrustedBiddingData {
+    ctor public TrustedBiddingData(android.net.Uri trustedBiddingUri, java.util.List<java.lang.String> trustedBiddingKeys);
+    method public java.util.List<java.lang.String> getTrustedBiddingKeys();
+    method public android.net.Uri getTrustedBiddingUri();
+    property public final java.util.List<java.lang.String> trustedBiddingKeys;
+    property public final android.net.Uri trustedBiddingUri;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.measurement {
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class DeletionRequest {
+    ctor public DeletionRequest(int deletionMode, int matchBehavior, optional java.time.Instant start, optional java.time.Instant end, optional java.util.List<? extends android.net.Uri> domainUris, optional java.util.List<? extends android.net.Uri> originUris);
+    method public int getDeletionMode();
+    method public java.util.List<android.net.Uri> getDomainUris();
+    method public java.time.Instant getEnd();
+    method public int getMatchBehavior();
+    method public java.util.List<android.net.Uri> getOriginUris();
+    method public java.time.Instant getStart();
+    property public final int deletionMode;
+    property public final java.util.List<android.net.Uri> domainUris;
+    property public final java.time.Instant end;
+    property public final int matchBehavior;
+    property public final java.util.List<android.net.Uri> originUris;
+    property public final java.time.Instant start;
+    field public static final androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Companion Companion;
+    field public static final int DELETION_MODE_ALL = 0; // 0x0
+    field public static final int DELETION_MODE_EXCLUDE_INTERNAL_DATA = 1; // 0x1
+    field public static final int MATCH_BEHAVIOR_DELETE = 0; // 0x0
+    field public static final int MATCH_BEHAVIOR_PRESERVE = 1; // 0x1
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class DeletionRequest.Builder {
+    ctor public DeletionRequest.Builder(int deletionMode, int matchBehavior);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest build();
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setDomainUris(java.util.List<? extends android.net.Uri> domainUris);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setEnd(java.time.Instant end);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setOriginUris(java.util.List<? extends android.net.Uri> originUris);
+    method public androidx.privacysandbox.ads.adservices.measurement.DeletionRequest.Builder setStart(java.time.Instant start);
+  }
+
+  public static final class DeletionRequest.Companion {
+  }
+
+  public abstract class MeasurementManager {
+    ctor public MeasurementManager();
+    method public abstract suspend Object? deleteRegistrations(androidx.privacysandbox.ads.adservices.measurement.DeletionRequest deletionRequest, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? getMeasurementApiStatus(kotlin.coroutines.Continuation<? super java.lang.Integer>);
+    method public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerSource(android.net.Uri attributionSource, android.view.InputEvent? inputEvent, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerTrigger(android.net.Uri trigger, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebSource(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebTrigger(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    field public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager.Companion Companion;
+    field public static final int MEASUREMENT_API_STATE_DISABLED = 0; // 0x0
+    field public static final int MEASUREMENT_API_STATE_ENABLED = 1; // 0x1
+  }
+
+  public static final class MeasurementManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceParams {
+    ctor public WebSourceParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+    method public boolean getDebugKeyAllowed();
+    method public android.net.Uri getRegistrationUri();
+    property public final boolean debugKeyAllowed;
+    property public final android.net.Uri registrationUri;
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceRegistrationRequest {
+    ctor public WebSourceRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri, optional android.view.InputEvent? inputEvent, optional android.net.Uri? appDestination, optional android.net.Uri? webDestination, optional android.net.Uri? verifiedDestination);
+    method public android.net.Uri? getAppDestination();
+    method public android.view.InputEvent? getInputEvent();
+    method public android.net.Uri getTopOriginUri();
+    method public android.net.Uri? getVerifiedDestination();
+    method public android.net.Uri? getWebDestination();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> getWebSourceParams();
+    property public final android.net.Uri? appDestination;
+    property public final android.view.InputEvent? inputEvent;
+    property public final android.net.Uri topOriginUri;
+    property public final android.net.Uri? verifiedDestination;
+    property public final android.net.Uri? webDestination;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams;
+  }
+
+  public static final class WebSourceRegistrationRequest.Builder {
+    ctor public WebSourceRegistrationRequest.Builder(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebSourceParams> webSourceParams, android.net.Uri topOriginUri);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest build();
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setAppDestination(android.net.Uri? appDestination);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setVerifiedDestination(android.net.Uri? verifiedDestination);
+    method public androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest.Builder setWebDestination(android.net.Uri? webDestination);
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerParams {
+    ctor public WebTriggerParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
+    method public boolean getDebugKeyAllowed();
+    method public android.net.Uri getRegistrationUri();
+    property public final boolean debugKeyAllowed;
+    property public final android.net.Uri registrationUri;
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebTriggerRegistrationRequest {
+    ctor public WebTriggerRegistrationRequest(java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams, android.net.Uri destination);
+    method public android.net.Uri getDestination();
+    method public java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> getWebTriggerParams();
+    property public final android.net.Uri destination;
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams> webTriggerParams;
+  }
+
+}
+
+package androidx.privacysandbox.ads.adservices.topics {
+
+  public final class GetTopicsRequest {
+    ctor public GetTopicsRequest(optional String adsSdkName, optional boolean shouldRecordObservation);
+    method public String getAdsSdkName();
+    method public boolean getShouldRecordObservation();
+    property public final String adsSdkName;
+    property public final boolean shouldRecordObservation;
+  }
+
+  public static final class GetTopicsRequest.Builder {
+    ctor public GetTopicsRequest.Builder();
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest build();
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setAdsSdkName(String adsSdkName);
+    method public androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest.Builder setShouldRecordObservation(boolean shouldRecordObservation);
+  }
+
+  public final class GetTopicsResponse {
+    ctor public GetTopicsResponse(java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics);
+    method public java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> getTopics();
+    property public final java.util.List<androidx.privacysandbox.ads.adservices.topics.Topic> topics;
+  }
+
+  public final class Topic {
+    ctor public Topic(long taxonomyVersion, long modelVersion, int topicId);
+    method public long getModelVersion();
+    method public long getTaxonomyVersion();
+    method public int getTopicId();
+    property public final long modelVersion;
+    property public final long taxonomyVersion;
+    property public final int topicId;
+  }
+
+  public abstract class TopicsManager {
+    method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS) public abstract suspend Object? getTopics(androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest request, kotlin.coroutines.Continuation<? super androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse>);
+    method public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+    field public static final androidx.privacysandbox.ads.adservices.topics.TopicsManager.Companion Companion;
+  }
+
+  public static final class TopicsManager.Companion {
+    method public androidx.privacysandbox.ads.adservices.topics.TopicsManager? obtain(android.content.Context context);
+  }
+
+}
+
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt
index dc5e5914..e316ab0 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt
@@ -21,6 +21,7 @@
 import android.content.Context
 import android.net.Uri
 import android.os.ext.SdkExtensions
+import android.util.Log
 import android.view.InputEvent
 import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresExtension
@@ -250,6 +251,7 @@
         @JvmStatic
         @SuppressLint("NewApi", "ClassVerificationFailure")
         fun obtain(context: Context): MeasurementManager? {
+            Log.d("MeasurementManager", "AdServicesInfo.version=${AdServicesInfo.version()}")
             return if (AdServicesInfo.version() >= 5) {
                 Api33Ext5Impl(context)
             } else {
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/api/api_lint.ignore b/privacysandbox/sdkruntime/sdkruntime-client/api/api_lint.ignore
new file mode 100644
index 0000000..725751e
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/api/api_lint.ignore
@@ -0,0 +1,5 @@
+// Baseline format: 1.0
+RegistrationName: androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat#addSdkSandboxProcessDeathCallback(java.util.concurrent.Executor, androidx.privacysandbox.sdkruntime.client.SdkSandboxProcessDeathCallbackCompat):
+    Callback methods should be named register/unregister; was addSdkSandboxProcessDeathCallback
+RegistrationName: androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat#removeSdkSandboxProcessDeathCallback(androidx.privacysandbox.sdkruntime.client.SdkSandboxProcessDeathCallbackCompat):
+    Callback methods should be named register/unregister; was removeSdkSandboxProcessDeathCallback
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/api/current.txt b/privacysandbox/sdkruntime/sdkruntime-client/api/current.txt
index e6f50d0..8d97c00 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/api/current.txt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/api/current.txt
@@ -1 +1,23 @@
 // Signature format: 4.0
+package androidx.privacysandbox.sdkruntime.client {
+
+  public final class SdkSandboxManagerCompat {
+    method public void addSdkSandboxProcessDeathCallback(java.util.concurrent.Executor callbackExecutor, androidx.privacysandbox.sdkruntime.client.SdkSandboxProcessDeathCallbackCompat callback);
+    method public static androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat from(android.content.Context context);
+    method public java.util.List<androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat> getSandboxedSdks();
+    method @kotlin.jvm.Throws(exceptionClasses=LoadSdkCompatException::class) public suspend Object? loadSdk(String sdkName, android.os.Bundle params, kotlin.coroutines.Continuation<? super androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat>) throws androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException;
+    method public void removeSdkSandboxProcessDeathCallback(androidx.privacysandbox.sdkruntime.client.SdkSandboxProcessDeathCallbackCompat callback);
+    method public void unloadSdk(String sdkName);
+    field public static final androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat.Companion Companion;
+  }
+
+  public static final class SdkSandboxManagerCompat.Companion {
+    method public androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat from(android.content.Context context);
+  }
+
+  public interface SdkSandboxProcessDeathCallbackCompat {
+    method public void onSdkSandboxDied();
+  }
+
+}
+
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/api/public_plus_experimental_current.txt b/privacysandbox/sdkruntime/sdkruntime-client/api/public_plus_experimental_current.txt
index e6f50d0..8d97c00 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/api/public_plus_experimental_current.txt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/api/public_plus_experimental_current.txt
@@ -1 +1,23 @@
 // Signature format: 4.0
+package androidx.privacysandbox.sdkruntime.client {
+
+  public final class SdkSandboxManagerCompat {
+    method public void addSdkSandboxProcessDeathCallback(java.util.concurrent.Executor callbackExecutor, androidx.privacysandbox.sdkruntime.client.SdkSandboxProcessDeathCallbackCompat callback);
+    method public static androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat from(android.content.Context context);
+    method public java.util.List<androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat> getSandboxedSdks();
+    method @kotlin.jvm.Throws(exceptionClasses=LoadSdkCompatException::class) public suspend Object? loadSdk(String sdkName, android.os.Bundle params, kotlin.coroutines.Continuation<? super androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat>) throws androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException;
+    method public void removeSdkSandboxProcessDeathCallback(androidx.privacysandbox.sdkruntime.client.SdkSandboxProcessDeathCallbackCompat callback);
+    method public void unloadSdk(String sdkName);
+    field public static final androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat.Companion Companion;
+  }
+
+  public static final class SdkSandboxManagerCompat.Companion {
+    method public androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat from(android.content.Context context);
+  }
+
+  public interface SdkSandboxProcessDeathCallbackCompat {
+    method public void onSdkSandboxDied();
+  }
+
+}
+
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/api/restricted_current.txt b/privacysandbox/sdkruntime/sdkruntime-client/api/restricted_current.txt
index e6f50d0..8d97c00 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/api/restricted_current.txt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/api/restricted_current.txt
@@ -1 +1,23 @@
 // Signature format: 4.0
+package androidx.privacysandbox.sdkruntime.client {
+
+  public final class SdkSandboxManagerCompat {
+    method public void addSdkSandboxProcessDeathCallback(java.util.concurrent.Executor callbackExecutor, androidx.privacysandbox.sdkruntime.client.SdkSandboxProcessDeathCallbackCompat callback);
+    method public static androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat from(android.content.Context context);
+    method public java.util.List<androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat> getSandboxedSdks();
+    method @kotlin.jvm.Throws(exceptionClasses=LoadSdkCompatException::class) public suspend Object? loadSdk(String sdkName, android.os.Bundle params, kotlin.coroutines.Continuation<? super androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat>) throws androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException;
+    method public void removeSdkSandboxProcessDeathCallback(androidx.privacysandbox.sdkruntime.client.SdkSandboxProcessDeathCallbackCompat callback);
+    method public void unloadSdk(String sdkName);
+    field public static final androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat.Companion Companion;
+  }
+
+  public static final class SdkSandboxManagerCompat.Companion {
+    method public androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat from(android.content.Context context);
+  }
+
+  public interface SdkSandboxProcessDeathCallbackCompat {
+    method public void onSdkSandboxDied();
+  }
+
+}
+
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/build.gradle b/privacysandbox/sdkruntime/sdkruntime-client/build.gradle
index 4ce9b50..a0f618d 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/build.gradle
+++ b/privacysandbox/sdkruntime/sdkruntime-client/build.gradle
@@ -24,9 +24,39 @@
 
 dependencies {
     api(libs.kotlinStdlib)
+    api(libs.kotlinCoroutinesCore)
+    implementation("androidx.core:core-ktx:1.8.0")
+
+    api project(path: ':privacysandbox:sdkruntime:sdkruntime-core')
+
+    implementation("androidx.core:core:1.8.0")
+
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
+    testImplementation project(":room:room-compiler-processing-testing")
+
+    // TODO(b/249982004): cleanup dependencies
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(project(":internal-testutils-truth")) // for assertThrows
+
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(libs.dexmakerMockitoInline, excludes.bytebuddy) // DexMaker has it"s own MockMaker
 }
 
 android {
+    sourceSets {
+        androidTest {
+            assets {
+                srcDirs += "src/androidTest/assets"
+            }
+        }
+    }
+
     namespace "androidx.privacysandbox.sdkruntime.client"
 }
 
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/lint-baseline.xml b/privacysandbox/sdkruntime/sdkruntime-client/lint-baseline.xml
new file mode 100644
index 0000000..4179d5e
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/lint-baseline.xml
@@ -0,0 +1,22 @@
+<?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">
+
+    <issue
+        id="BanThreadSleep"
+        message="Uses Thread.sleep()"
+        errorLine1="        Thread.sleep(100)"
+        errorLine2="               ~~~~~">
+        <location
+            file="src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/storage/CachedLocalSdkStorageTest.kt"/>
+    </issue>
+
+    <issue
+        id="BanThreadSleep"
+        message="Uses Thread.sleep()"
+        errorLine1="        Thread.sleep(100)"
+        errorLine2="               ~~~~~">
+        <location
+            file="src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/storage/CachedLocalSdkStorageTest.kt"/>
+    </issue>
+
+</issues>
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/AndroidManifest.xml b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..3e3ea90
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+</manifest>
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdkTable.xml b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdkTable.xml
new file mode 100644
index 0000000..c72c1a2
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdkTable.xml
@@ -0,0 +1,30 @@
+<!--
+  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.
+  -->
+<runtime-enabled-sdk-table>
+    <runtime-enabled-sdk>
+        <compat-config-path>RuntimeEnabledSdks/V1/CompatSdkConfig.xml</compat-config-path>
+        <package-name>androidx.privacysandbox.sdkruntime.test.v1</package-name>
+        <version-major>42</version-major>
+    </runtime-enabled-sdk>
+    <runtime-enabled-sdk>
+        <compat-config-path>RuntimeEnabledSdks/V2/CompatSdkConfig.xml</compat-config-path>
+        <package-name>androidx.privacysandbox.sdkruntime.test.v2</package-name>
+    </runtime-enabled-sdk>
+    <runtime-enabled-sdk>
+        <compat-config-path>RuntimeEnabledSdks/InvalidEntryPointSdkConfig.xml</compat-config-path>
+        <package-name>androidx.privacysandbox.sdkruntime.test.invalidEntryPoint</package-name>
+    </runtime-enabled-sdk>
+</runtime-enabled-sdk-table>
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/InvalidEntryPointSdkConfig.xml b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/InvalidEntryPointSdkConfig.xml
new file mode 100644
index 0000000..d28649c
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/InvalidEntryPointSdkConfig.xml
@@ -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.
+  -->
+<compat-config>
+    <compat-entrypoint>InvalidEntryPoint</compat-entrypoint>
+    <dex-path>RuntimeEnabledSdks/V1/classes.dex</dex-path>
+</compat-config>
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/RPackage.dex b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/RPackage.dex
new file mode 100644
index 0000000..d4862a1
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/RPackage.dex
Binary files differ
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/RPackage.md b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/RPackage.md
new file mode 100644
index 0000000..5c1e2a0
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/RPackage.md
@@ -0,0 +1,8 @@
+RPackage class for testing SDK resource remapping.
+
+RPackage.dex built from:
+
+1) androidx.privacysandbox.sdkruntime.test.RPackage
+   public class RPackage {
+      public static int packageId = 0;
+   }
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V1/CompatSdkCode.md b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V1/CompatSdkCode.md
new file mode 100644
index 0000000..a2f1cbf
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V1/CompatSdkCode.md
@@ -0,0 +1,125 @@
+Test sdk that was built with V1 library.
+
+DO NOT RECOMPILE WITH ANY CHANGES TO LIBRARY CLASSES.
+Main purpose of that provider is to test that old core versions could be loaded by new client.
+
+classes.dex built from:
+
+1) androidx.privacysandbox.sdkruntime.core.Versions
+@Keep
+object Versions {
+
+    const val API_VERSION = 1
+
+    @JvmField
+    var CLIENT_VERSION = -1
+
+    @JvmStatic
+    fun handShake(clientVersion: Int): Int {
+        CLIENT_VERSION = clientVersion
+        return API_VERSION
+    }
+}
+
+2) androidx.privacysandbox.sdkruntime.core.SandboxedSdkProviderCompat
+abstract class SandboxedSdkProviderCompat {
+    var context: Context? = null
+        private set
+
+    fun attachContext(context: Context) {
+        check(this.context == null) { "Context already set" }
+        this.context = context
+    }
+
+    @Throws(LoadSdkCompatException::class)
+    abstract fun onLoadSdk(params: Bundle): SandboxedSdkCompat
+
+    open fun beforeUnloadSdk() {}
+
+    abstract fun getView(
+            windowContext: Context,
+            params: Bundle,
+            width: Int,
+            height: Int
+    ): View
+}
+
+3) androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+sealed class SandboxedSdkCompat {
+
+    abstract fun getInterface(): IBinder?
+
+    private class CompatImpl(private val mInterface: IBinder) : SandboxedSdkCompat() {
+        override fun getInterface(): IBinder? {
+            return mInterface
+        }
+    }
+
+    companion object {
+        @JvmStatic
+        fun create(binder: IBinder): SandboxedSdkCompat {
+            return CompatImpl(binder)
+        }
+    }
+}
+
+4) androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
+class LoadSdkCompatException : Exception {
+
+    val loadSdkErrorCode: Int
+
+    val extraInformation: Bundle
+
+    @JvmOverloads
+    constructor(
+            loadSdkErrorCode: Int,
+            message: String?,
+            cause: Throwable?,
+            extraInformation: Bundle = Bundle()
+    ) : super(message, cause) {
+        this.loadSdkErrorCode = loadSdkErrorCode
+        this.extraInformation = extraInformation
+    }
+
+    constructor(
+            cause: Throwable,
+            extraInfo: Bundle
+    ) : this(LOAD_SDK_SDK_DEFINED_ERROR, "", cause, extraInfo)
+
+    companion object {
+        const val LOAD_SDK_SDK_DEFINED_ERROR = 102
+    }
+}
+
+5) androidx.privacysandbox.sdkruntime.test.v1.CompatProvider
+class CompatProvider : SandboxedSdkProviderCompat() {
+
+    @JvmField
+    val onLoadSdkBinder = Binder()
+
+    @JvmField
+    var lastOnLoadSdkParams: Bundle? = null
+
+    @JvmField
+    var isBeforeUnloadSdkCalled = false
+
+    @Throws(LoadSdkCompatException::class)
+    override fun onLoadSdk(params: Bundle): SandboxedSdkCompat {
+        lastOnLoadSdkParams = params
+        if (params.getBoolean("needFail", false)) {
+            throw LoadSdkCompatException(RuntimeException(), params)
+        }
+        return SandboxedSdkCompat.create(onLoadSdkBinder)
+    }
+
+    override fun beforeUnloadSdk() {
+        isBeforeUnloadSdkCalled = true
+    }
+
+    override fun getView(
+            windowContext: Context, params: Bundle, width: Int,
+            height: Int
+    ): View {
+        return View(windowContext)
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V1/CompatSdkConfig.xml b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V1/CompatSdkConfig.xml
new file mode 100644
index 0000000..dfeca90
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V1/CompatSdkConfig.xml
@@ -0,0 +1,20 @@
+<!--
+  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.
+  -->
+<compat-config>
+    <compat-entrypoint>androidx.privacysandbox.sdkruntime.test.v1.CompatProvider</compat-entrypoint>
+    <dex-path>RuntimeEnabledSdks/V1/classes.dex</dex-path>
+    <java-resources-root-path>RuntimeEnabledSdks/V1/javaresources</java-resources-root-path>
+</compat-config>
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V1/classes.dex b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V1/classes.dex
new file mode 100644
index 0000000..a6da7e9
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V1/classes.dex
Binary files differ
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V1/javaresources/test.txt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V1/javaresources/test.txt
new file mode 100644
index 0000000..30d74d2
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V1/javaresources/test.txt
@@ -0,0 +1 @@
+test
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V2/CompatSdkCode.md b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V2/CompatSdkCode.md
new file mode 100644
index 0000000..5dd9c4f
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V2/CompatSdkCode.md
@@ -0,0 +1,204 @@
+Test sdk that was built with V2 library.
+
+DO NOT RECOMPILE WITH ANY CHANGES TO LIBRARY CLASSES.
+Main purpose of that provider is to test that old core versions could be loaded by new client.
+
+classes.dex built from:
+
+1) androidx.privacysandbox.sdkruntime.core.Versions
+@Keep
+object Versions {
+
+    const val API_VERSION = 2
+
+    @JvmField
+    var CLIENT_VERSION: Int? = null
+
+    @JvmStatic
+    fun handShake(clientVersion: Int): Int {
+        CLIENT_VERSION = clientVersion
+        return API_VERSION
+    }
+}
+
+2) androidx.privacysandbox.sdkruntime.core.SandboxedSdkProviderCompat
+abstract class SandboxedSdkProviderCompat {
+    var context: Context? = null
+        private set
+
+    fun attachContext(context: Context) {
+        check(this.context == null) { "Context already set" }
+        this.context = context
+    }
+
+    @Throws(LoadSdkCompatException::class)
+    abstract fun onLoadSdk(params: Bundle): SandboxedSdkCompat
+
+    open fun beforeUnloadSdk() {}
+
+    abstract fun getView(
+            windowContext: Context,
+            params: Bundle,
+            width: Int,
+            height: Int
+    ): View
+}
+
+3) androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+class SandboxedSdkCompat private constructor(
+   private val sdkImpl: SandboxedSdkImpl
+) {
+
+   constructor(sdkInterface: IBinder) : this(sdkInterface, sdkInfo = null)
+
+   @Keep
+   constructor(
+      sdkInterface: IBinder,
+      sdkInfo: SandboxedSdkInfo?
+   ) : this(CompatImpl(sdkInterface, sdkInfo))
+
+   fun getInterface() = sdkImpl.getInterface()
+
+   fun getSdkInfo(): SandboxedSdkInfo? = sdkImpl.getSdkInfo()
+
+   internal interface SandboxedSdkImpl {
+      fun getInterface(): IBinder?
+      fun getSdkInfo(): SandboxedSdkInfo?
+   }
+
+   private class CompatImpl(
+      private val sdkInterface: IBinder,
+      private val sdkInfo: SandboxedSdkInfo?
+   ) : SandboxedSdkImpl {
+
+      override fun getInterface(): IBinder {
+         return sdkInterface
+      }
+
+      override fun getSdkInfo(): SandboxedSdkInfo? = sdkInfo
+   }
+}
+
+4) androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
+class LoadSdkCompatException : Exception {
+
+    val loadSdkErrorCode: Int
+
+    val extraInformation: Bundle
+
+    @JvmOverloads
+    constructor(
+            loadSdkErrorCode: Int,
+            message: String?,
+            cause: Throwable?,
+            extraInformation: Bundle = Bundle()
+    ) : super(message, cause) {
+        this.loadSdkErrorCode = loadSdkErrorCode
+        this.extraInformation = extraInformation
+    }
+
+    constructor(
+            cause: Throwable,
+            extraInfo: Bundle
+    ) : this(LOAD_SDK_SDK_DEFINED_ERROR, "", cause, extraInfo)
+
+    companion object {
+        const val LOAD_SDK_SDK_DEFINED_ERROR = 102
+    }
+}
+
+5) androidx.privacysandbox.sdkruntime.core.SandboxedSdkInfo
+class SandboxedSdkInfo(
+    val name: String,
+    val version: Long
+) {
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+        other as SandboxedSdkInfo
+        if (name != other.name) return false
+        if (version != other.version) return false
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = name.hashCode()
+        result = 31 * result + version.hashCode()
+        return result
+    }
+}
+
+6) androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
+class SdkSandboxControllerCompat internal constructor(
+    private val controllerImpl: SandboxControllerImpl
+) {
+    fun getSandboxedSdks(): List<SandboxedSdkCompat> =
+        controllerImpl.getSandboxedSdks()
+
+    interface SandboxControllerImpl {
+        fun getSandboxedSdks(): List<SandboxedSdkCompat>
+    }
+
+    companion object {
+        private var localImpl: SandboxControllerImpl? = null
+        @JvmStatic
+        fun from(context: Context): SdkSandboxControllerCompat {
+            val loadedLocally = Versions.CLIENT_VERSION != null
+            if (loadedLocally) {
+                val implFromClient = localImpl
+                if (implFromClient != null) {
+                    return SdkSandboxControllerCompat(implFromClient)
+                }
+            }
+            throw IllegalStateException("Should be loaded locally")
+        }
+        @JvmStatic
+        @Keep
+        fun injectLocalImpl(impl: SandboxControllerImpl) {
+            check(localImpl == null) { "Local implementation already injected" }
+            localImpl = impl
+        }
+    }
+}
+
+7) androidx.privacysandbox.sdkruntime.test.v2.CompatProvider
+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 = SdkImpl(context!!)
+        onLoadSdkBinder = result
+        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)
+    }
+
+    class SdkImpl(
+        private val context: Context
+    ) : Binder() {
+        fun getSandboxedSdks(): List<SandboxedSdkCompat> =
+            SdkSandboxControllerCompat.from(context).getSandboxedSdks()
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V2/CompatSdkConfig.xml b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V2/CompatSdkConfig.xml
new file mode 100644
index 0000000..06853cb
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V2/CompatSdkConfig.xml
@@ -0,0 +1,19 @@
+<!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<compat-config>
+    <compat-entrypoint>androidx.privacysandbox.sdkruntime.test.v2.CompatProvider</compat-entrypoint>
+    <dex-path>RuntimeEnabledSdks/V2/classes.dex</dex-path>
+</compat-config>
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V2/classes.dex b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V2/classes.dex
new file mode 100644
index 0000000..52702de
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/assets/RuntimeEnabledSdks/V2/classes.dex
Binary files differ
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
new file mode 100644
index 0000000..ab6b951
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatSandboxedTest.kt
@@ -0,0 +1,336 @@
+/*
+ * 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.client
+
+import android.app.sdksandbox.LoadSdkException
+import android.app.sdksandbox.SandboxedSdk
+import android.app.sdksandbox.SdkSandboxManager
+import android.content.Context
+import android.os.Binder
+import android.os.Build
+import android.os.Bundle
+import android.os.OutcomeReceiver
+import android.os.ext.SdkExtensions.AD_SERVICES
+import androidx.annotation.RequiresExtension
+import androidx.privacysandbox.sdkruntime.client.loader.asTestSdk
+import androidx.privacysandbox.sdkruntime.core.AdServicesInfo
+import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
+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 kotlinx.coroutines.runBlocking
+import org.junit.After
+import org.junit.Assert.assertThrows
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when`
+import org.mockito.invocation.InvocationOnMock
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+// TODO(b/249982507) Test should be rewritten to use real SDK in sandbox instead of mocking manager
+// TODO(b/262577044) Remove RequiresExtension after extensions support in @SdkSuppress
+@RequiresExtension(extension = AD_SERVICES, version = 4)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+class SdkSandboxManagerCompatSandboxedTest {
+
+    private lateinit var mContext: Context
+
+    @Before
+    fun setUp() {
+        assumeTrue("Requires Sandbox API available", isSandboxApiAvailable())
+        mContext = Mockito.spy(ApplicationProvider.getApplicationContext<Context>())
+    }
+
+    @After
+    fun tearDown() {
+        SdkSandboxManagerCompat.reset()
+    }
+
+    @Test
+    fun loadSdk_whenNoLocalSdkExistsAndSandboxAvailable_delegateToPlatformLoadSdk() {
+        val sdkSandboxManager = mockSandboxManager(mContext)
+        setupLoadSdkAnswer(sdkSandboxManager, SandboxedSdk(Binder()))
+
+        val managerCompat = SdkSandboxManagerCompat.from(mContext)
+        val sdkName = "test"
+        val params = Bundle()
+
+        runBlocking {
+            managerCompat.loadSdk(sdkName, params)
+        }
+
+        verify(sdkSandboxManager).loadSdk(
+            eq(sdkName),
+            eq(params),
+            any(),
+            any()
+        )
+    }
+
+    @Test
+    fun loadSdk_whenNoLocalSdkExistsAndSandboxAvailable_returnResultFromPlatformLoadSdk() {
+        val sdkSandboxManager = mockSandboxManager(mContext)
+
+        val sandboxedSdk = SandboxedSdk(Binder())
+        setupLoadSdkAnswer(sdkSandboxManager, sandboxedSdk)
+
+        val managerCompat = SdkSandboxManagerCompat.from(mContext)
+
+        val result = runBlocking {
+            managerCompat.loadSdk("test", Bundle())
+        }
+
+        assertThat(result.getInterface()).isEqualTo(sandboxedSdk.getInterface())
+    }
+
+    @Test
+    fun loadSdk_whenNoLocalSdkExistsAndSandboxAvailable_rethrowsExceptionFromPlatformLoadSdk() {
+        val sdkSandboxManager = mockSandboxManager(mContext)
+
+        val loadSdkException = LoadSdkException(
+            RuntimeException(),
+            Bundle()
+        )
+        setupLoadSdkAnswer(sdkSandboxManager, loadSdkException)
+
+        val managerCompat = SdkSandboxManagerCompat.from(mContext)
+
+        val result = assertThrows(LoadSdkCompatException::class.java) {
+            runBlocking {
+                managerCompat.loadSdk("test", Bundle())
+            }
+        }
+
+        assertThat(result.cause).isEqualTo(loadSdkException.cause)
+        assertThat(result.extraInformation).isEqualTo(loadSdkException.extraInformation)
+        assertThat(result.loadSdkErrorCode).isEqualTo(loadSdkException.loadSdkErrorCode)
+    }
+
+    @Test
+    fun unloadSdk_whenNoLocalSdkLoadedAndSandboxAvailable_delegateToPlatform() {
+        val sdkSandboxManager = mockSandboxManager(mContext)
+
+        val managerCompat = SdkSandboxManagerCompat.from(mContext)
+        val sdkName = "test"
+
+        managerCompat.unloadSdk(sdkName)
+
+        verify(sdkSandboxManager).unloadSdk(
+            eq(sdkName)
+        )
+    }
+
+    @Test
+    fun addSdkSandboxProcessDeathCallback_whenSandboxAvailable_delegateToPlatform() {
+        val sdkSandboxManager = mockSandboxManager(mContext)
+        val managerCompat = SdkSandboxManagerCompat.from(mContext)
+
+        val callback = mock(SdkSandboxProcessDeathCallbackCompat::class.java)
+
+        managerCompat.addSdkSandboxProcessDeathCallback(Runnable::run, callback)
+        val argumentCaptor = ArgumentCaptor.forClass(
+            SdkSandboxManager.SdkSandboxProcessDeathCallback::class.java
+        )
+        verify(sdkSandboxManager)
+            .addSdkSandboxProcessDeathCallback(any(), argumentCaptor.capture())
+        val platformCallback = argumentCaptor.value
+
+        platformCallback.onSdkSandboxDied()
+        verify(callback).onSdkSandboxDied()
+    }
+
+    @Test
+    fun removeSdkSandboxProcessDeathCallback_whenSandboxAvailable_removeAddedCallback() {
+        val sdkSandboxManager = mockSandboxManager(mContext)
+        val managerCompat = SdkSandboxManagerCompat.from(mContext)
+
+        val callback = mock(SdkSandboxProcessDeathCallbackCompat::class.java)
+
+        managerCompat.addSdkSandboxProcessDeathCallback(Runnable::run, callback)
+        val addArgumentCaptor = ArgumentCaptor.forClass(
+            SdkSandboxManager.SdkSandboxProcessDeathCallback::class.java
+        )
+        verify(sdkSandboxManager)
+            .addSdkSandboxProcessDeathCallback(any(), addArgumentCaptor.capture())
+        val addedPlatformCallback = addArgumentCaptor.value
+
+        managerCompat.removeSdkSandboxProcessDeathCallback(callback)
+        val removeArgumentCaptor = ArgumentCaptor.forClass(
+            SdkSandboxManager.SdkSandboxProcessDeathCallback::class.java
+        )
+        verify(sdkSandboxManager)
+            .removeSdkSandboxProcessDeathCallback(removeArgumentCaptor.capture())
+        val removedPlatformCallback = removeArgumentCaptor.value
+
+        assertThat(removedPlatformCallback).isSameInstanceAs(addedPlatformCallback)
+    }
+
+    @Test
+    fun removeSdkSandboxProcessDeathCallback_whenNoCallbackAdded_doNothing() {
+        val sdkSandboxManager = mockSandboxManager(mContext)
+        val managerCompat = SdkSandboxManagerCompat.from(mContext)
+
+        val callback = mock(SdkSandboxProcessDeathCallbackCompat::class.java)
+        managerCompat.removeSdkSandboxProcessDeathCallback(callback)
+
+        verify(sdkSandboxManager, never())
+            .removeSdkSandboxProcessDeathCallback(any())
+    }
+
+    @Test
+    fun getSandboxedSdks_whenLoadedSdkListNotAvailable_dontDelegateToSandbox() {
+        assumeFalse("Requires getSandboxedSdks API not available", isAtLeastV5())
+
+        val sdkSandboxManager = mockSandboxManager(mContext)
+        val managerCompat = SdkSandboxManagerCompat.from(mContext)
+
+        managerCompat.getSandboxedSdks()
+
+        verifyZeroInteractions(sdkSandboxManager)
+    }
+
+    @Test
+    fun getSandboxedSdks_whenLoadedSdkListNotAvailable_returnsLocallyLoadedSdkList() {
+        assumeFalse("Requires getSandboxedSdks API not available", isAtLeastV5())
+
+        // SdkSandboxManagerCompat.from require SandboxManager available for AdServices version >= 4
+        mockSandboxManager(mContext)
+        val managerCompat = SdkSandboxManagerCompat.from(mContext)
+
+        val localSdk = runBlocking {
+            managerCompat.loadSdk("androidx.privacysandbox.sdkruntime.test.v1", Bundle())
+        }
+
+        val sandboxedSdks = managerCompat.getSandboxedSdks()
+
+        assertThat(sandboxedSdks).containsExactly(localSdk)
+    }
+
+    @Test
+    // TODO(b/262577044) Remove RequiresExtension after extensions support in @SdkSuppress
+    @RequiresExtension(extension = AD_SERVICES, version = 5)
+    fun getSandboxedSdks_whenLoadedSdkListAvailable_returnCombinedLocalAndPlatformResult() {
+        assumeTrue("Requires getSandboxedSdks API available", isAtLeastV5())
+
+        val sdkSandboxManager = mockSandboxManager(mContext)
+        val sandboxedSdk = SandboxedSdk(Binder())
+        `when`(sdkSandboxManager.sandboxedSdks)
+            .thenReturn(listOf(sandboxedSdk))
+        val managerCompat = SdkSandboxManagerCompat.from(mContext)
+
+        val localSdk = runBlocking {
+            managerCompat.loadSdk("androidx.privacysandbox.sdkruntime.test.v1", Bundle())
+        }
+
+        val result = managerCompat.getSandboxedSdks().map { it.getInterface() }
+        assertThat(result).containsExactly(
+            sandboxedSdk.getInterface(), localSdk.getInterface()
+        )
+    }
+
+    @Test
+    // TODO(b/262577044) Remove RequiresExtension after extensions support in @SdkSuppress
+    @RequiresExtension(extension = AD_SERVICES, version = 5)
+    fun sdkController_getSandboxedSdks_dontIncludeSandboxedSdk() {
+        assumeTrue("Requires getSandboxedSdks API available", isAtLeastV5())
+
+        val sdkSandboxManager = mockSandboxManager(mContext)
+        val sandboxedSdk = SandboxedSdk(Binder())
+        `when`(sdkSandboxManager.sandboxedSdks)
+            .thenReturn(listOf(sandboxedSdk))
+        val managerCompat = SdkSandboxManagerCompat.from(mContext)
+
+        val localSdk = runBlocking {
+            managerCompat.loadSdk("androidx.privacysandbox.sdkruntime.test.v2", Bundle())
+        }
+
+        val testSdk = localSdk.asTestSdk()
+
+        val sdkList = testSdk.getSandboxedSdks()
+        assertThat(sdkList).hasSize(1)
+        val result = sdkList[0].getInterface()
+
+        assertThat(result).isEqualTo(localSdk.getInterface())
+    }
+
+    companion object SandboxApi {
+
+        private fun isSandboxApiAvailable() =
+            AdServicesInfo.isAtLeastV4()
+
+        private fun isAtLeastV5() =
+            AdServicesInfo.isAtLeastV5()
+
+        private fun mockSandboxManager(spyContext: Context): SdkSandboxManager {
+            val sdkSandboxManager = mock(SdkSandboxManager::class.java)
+            `when`(spyContext.getSystemService(SdkSandboxManager::class.java))
+                .thenReturn(sdkSandboxManager)
+            return sdkSandboxManager
+        }
+
+        private fun setupLoadSdkAnswer(
+            sdkSandboxManager: SdkSandboxManager,
+            sandboxedSdk: SandboxedSdk
+        ) {
+            val answer = { args: InvocationOnMock ->
+                val receiver = args.getArgument<OutcomeReceiver<SandboxedSdk, LoadSdkException>>(3)
+                receiver.onResult(sandboxedSdk)
+                null
+            }
+            doAnswer(answer)
+                .`when`(sdkSandboxManager).loadSdk(
+                    any(),
+                    any(),
+                    any(),
+                    any()
+                )
+        }
+
+        private fun setupLoadSdkAnswer(
+            sdkSandboxManager: SdkSandboxManager,
+            loadSdkException: LoadSdkException
+        ) {
+            val answer = { args: InvocationOnMock ->
+                val receiver = args.getArgument<OutcomeReceiver<SandboxedSdk, LoadSdkException>>(3)
+                receiver.onError(loadSdkException)
+                null
+            }
+            doAnswer(answer)
+                .`when`(sdkSandboxManager).loadSdk(
+                    any(),
+                    any(),
+                    any(),
+                    any()
+                )
+        }
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..b0e4474
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompatTest.kt
@@ -0,0 +1,328 @@
+/*
+ * 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.client
+
+import android.content.Context
+import android.content.ContextWrapper
+import android.os.Build
+import android.os.Bundle
+import androidx.privacysandbox.sdkruntime.client.loader.asTestSdk
+import androidx.privacysandbox.sdkruntime.client.loader.extractSdkProviderFieldValue
+import androidx.privacysandbox.sdkruntime.core.AdServicesInfo
+import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
+import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException.Companion.LOAD_SDK_INTERNAL_ERROR
+import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException.Companion.LOAD_SDK_SDK_DEFINED_ERROR
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkInfo
+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 kotlinx.coroutines.runBlocking
+import org.junit.After
+import org.junit.Assert.assertThrows
+import org.junit.Assume.assumeTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.Mockito.any
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SdkSandboxManagerCompatTest {
+
+    @After
+    fun tearDown() {
+        SdkSandboxManagerCompat.reset()
+    }
+
+    @Test
+    fun from_whenCalledOnSameContext_returnSameManager() {
+        val context = ApplicationProvider.getApplicationContext<Context>()
+
+        val managerCompat = SdkSandboxManagerCompat.from(context)
+        val managerCompat2 = SdkSandboxManagerCompat.from(context)
+
+        assertThat(managerCompat2).isSameInstanceAs(managerCompat)
+    }
+
+    @Test
+    fun from_whenCalledOnDifferentContext_returnDifferentManager() {
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val context2 = ContextWrapper(context)
+
+        val managerCompat = SdkSandboxManagerCompat.from(context)
+        val managerCompat2 = SdkSandboxManagerCompat.from(context2)
+
+        assertThat(managerCompat2).isNotSameInstanceAs(managerCompat)
+    }
+
+    @Test
+    // TODO(b/249982507) DexmakerMockitoInline requires P+. Rewrite to support P-
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+    fun loadSdk_whenNoLocalSdkExistsAndSandboxNotAvailable_dontDelegateToSandbox() {
+        // TODO(b/262577044) Replace with @SdkSuppress after supporting maxExtensionVersion
+        assumeTrue("Requires Sandbox API not available", isSandboxApiNotAvailable())
+
+        val context = spy(ApplicationProvider.getApplicationContext<Context>())
+        val managerCompat = SdkSandboxManagerCompat.from(context)
+
+        assertThrows(LoadSdkCompatException::class.java) {
+            runBlocking {
+                managerCompat.loadSdk("sdk-not-exists", Bundle())
+            }
+        }
+
+        verify(context, Mockito.never()).getSystemService(any())
+    }
+
+    @Test
+    fun loadSdk_whenNoLocalSdkExistsAndSandboxNotAvailable_throwsSdkNotFoundException() {
+        // TODO(b/262577044) Replace with @SdkSuppress after supporting maxExtensionVersion
+        assumeTrue("Requires Sandbox API not available", isSandboxApiNotAvailable())
+
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val managerCompat = SdkSandboxManagerCompat.from(context)
+
+        val result = assertThrows(LoadSdkCompatException::class.java) {
+            runBlocking {
+                managerCompat.loadSdk("sdk-not-exists", Bundle())
+            }
+        }
+
+        assertThat(result.loadSdkErrorCode)
+            .isEqualTo(LoadSdkCompatException.LOAD_SDK_NOT_FOUND)
+    }
+
+    @Test
+    fun loadSdk_whenLocalSdkExists_returnResultFromCompatLoadSdk() {
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val managerCompat = SdkSandboxManagerCompat.from(context)
+
+        val result = runBlocking {
+            managerCompat.loadSdk("androidx.privacysandbox.sdkruntime.test.v1", Bundle())
+        }
+
+        assertThat(result.getInterface()!!.javaClass.classLoader)
+            .isNotSameInstanceAs(managerCompat.javaClass.classLoader)
+
+        assertThat(result.getSdkInfo())
+            .isEqualTo(
+                SandboxedSdkInfo(
+                    name = "androidx.privacysandbox.sdkruntime.test.v1",
+                    version = 42
+                )
+            )
+    }
+
+    @Test
+    fun loadSdk_whenLocalSdkExists_rethrowsExceptionFromCompatLoadSdk() {
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val managerCompat = SdkSandboxManagerCompat.from(context)
+
+        val params = Bundle()
+        params.putBoolean("needFail", true)
+
+        val result = assertThrows(LoadSdkCompatException::class.java) {
+            runBlocking {
+                managerCompat.loadSdk("androidx.privacysandbox.sdkruntime.test.v1", params)
+            }
+        }
+
+        assertThat(result.extraInformation).isEqualTo(params)
+        assertThat(result.loadSdkErrorCode).isEqualTo(LOAD_SDK_SDK_DEFINED_ERROR)
+    }
+
+    @Test
+    fun loadSdk_whenLocalSdkFailedToLoad_throwsInternalErrorException() {
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val managerCompat = SdkSandboxManagerCompat.from(context)
+
+        val result = assertThrows(LoadSdkCompatException::class.java) {
+            runBlocking {
+                managerCompat.loadSdk(
+                    sdkName = "androidx.privacysandbox.sdkruntime.test.invalidEntryPoint",
+                    params = Bundle()
+                )
+            }
+        }
+
+        assertThat(result.loadSdkErrorCode).isEqualTo(LOAD_SDK_INTERNAL_ERROR)
+        assertThat(result.message).isEqualTo("Failed to instantiate local SDK")
+    }
+
+    @Test
+    fun loadSdk_afterUnloading_loadSdkAgain() {
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val managerCompat = SdkSandboxManagerCompat.from(context)
+
+        val sdkName = "androidx.privacysandbox.sdkruntime.test.v1"
+
+        val sdkToUnload = runBlocking {
+            managerCompat.loadSdk(sdkName, Bundle())
+        }
+
+        managerCompat.unloadSdk(sdkName)
+
+        val reloadedSdk = runBlocking {
+            managerCompat.loadSdk(sdkName, Bundle())
+        }
+
+        assertThat(managerCompat.getSandboxedSdks())
+            .containsExactly(reloadedSdk)
+        assertThat(reloadedSdk.getInterface())
+            .isNotEqualTo(sdkToUnload.getInterface())
+    }
+
+    @Test
+    // TODO(b/249982507) DexmakerMockitoInline requires P+. Rewrite to support P-
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+    fun unloadSdk_whenNoLocalSdkLoadedAndSandboxNotAvailable_dontDelegateToSandbox() {
+        // TODO(b/262577044) Replace with @SdkSuppress after supporting maxExtensionVersion
+        assumeTrue("Requires Sandbox API not available", isSandboxApiNotAvailable())
+
+        val context = spy(ApplicationProvider.getApplicationContext<Context>())
+        val managerCompat = SdkSandboxManagerCompat.from(context)
+
+        managerCompat.unloadSdk("sdk-not-loaded")
+
+        verify(context, Mockito.never()).getSystemService(any())
+    }
+
+    @Test
+    fun unloadSdk_whenLocalSdkLoaded_unloadLocalSdk() {
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val managerCompat = SdkSandboxManagerCompat.from(context)
+
+        val sdkName = "androidx.privacysandbox.sdkruntime.test.v1"
+
+        runBlocking {
+            managerCompat.loadSdk(sdkName, Bundle())
+        }
+        val sdkProvider = managerCompat.getLocallyLoadedSdk(sdkName)!!.sdkProvider
+
+        managerCompat.unloadSdk(sdkName)
+
+        val isBeforeUnloadSdkCalled = sdkProvider.extractSdkProviderFieldValue<Boolean>(
+            fieldName = "isBeforeUnloadSdkCalled"
+        )
+
+        assertThat(isBeforeUnloadSdkCalled)
+            .isTrue()
+
+        assertThat(managerCompat.getSandboxedSdks())
+            .isEmpty()
+    }
+
+    @Test
+    // TODO(b/249982507) DexmakerMockitoInline requires P+. Rewrite to support P-
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+    fun addSdkSandboxProcessDeathCallback_whenSandboxNotAvailable_dontDelegateToSandbox() {
+        // TODO(b/262577044) Replace with @SdkSuppress after supporting maxExtensionVersion
+        assumeTrue("Requires Sandbox API not available", isSandboxApiNotAvailable())
+
+        val context = spy(ApplicationProvider.getApplicationContext<Context>())
+        val managerCompat = SdkSandboxManagerCompat.from(context)
+
+        managerCompat.addSdkSandboxProcessDeathCallback(Runnable::run, object :
+            SdkSandboxProcessDeathCallbackCompat {
+            override fun onSdkSandboxDied() {
+            }
+        })
+
+        verify(context, Mockito.never()).getSystemService(any())
+    }
+
+    @Test
+    // TODO(b/249982507) DexmakerMockitoInline requires P+. Rewrite to support P-
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+    fun removeSdkSandboxProcessDeathCallback_whenSandboxNotAvailable_dontDelegateToSandbox() {
+        // TODO(b/262577044) Replace with @SdkSuppress after supporting maxExtensionVersion
+        assumeTrue("Requires Sandbox API not available", isSandboxApiNotAvailable())
+
+        val context = spy(ApplicationProvider.getApplicationContext<Context>())
+        val managerCompat = SdkSandboxManagerCompat.from(context)
+
+        managerCompat.removeSdkSandboxProcessDeathCallback(object :
+            SdkSandboxProcessDeathCallbackCompat {
+            override fun onSdkSandboxDied() {
+            }
+        })
+
+        verify(context, Mockito.never()).getSystemService(any())
+    }
+
+    @Test
+    // TODO(b/249982507) DexmakerMockitoInline requires P+. Rewrite to support P-
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+    fun getSandboxedSdks_whenSandboxNotAvailable_dontDelegateToSandbox() {
+        // TODO(b/262577044) Replace with @SdkSuppress after supporting maxExtensionVersion
+        assumeTrue("Requires Sandbox API not available", isSandboxApiNotAvailable())
+
+        val context = spy(ApplicationProvider.getApplicationContext<Context>())
+        val managerCompat = SdkSandboxManagerCompat.from(context)
+
+        managerCompat.getSandboxedSdks()
+
+        verify(context, Mockito.never()).getSystemService(any())
+    }
+
+    @Test
+    fun sdkController_getSandboxedSdks_returnsLocallyLoadedSdks() {
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val managerCompat = SdkSandboxManagerCompat.from(context)
+
+        val localSdk = runBlocking {
+            managerCompat.loadSdk("androidx.privacysandbox.sdkruntime.test.v2", Bundle())
+        }
+
+        val anotherLocalSdk = runBlocking {
+            managerCompat.loadSdk("androidx.privacysandbox.sdkruntime.test.v1", Bundle())
+        }
+
+        val testSdk = localSdk.asTestSdk()
+
+        val interfaces = testSdk.getSandboxedSdks()
+            .map { it.getInterface() }
+
+        assertThat(interfaces).containsExactly(
+            localSdk.getInterface(),
+            anotherLocalSdk.getInterface(),
+        )
+    }
+
+    @Test
+    fun getSandboxedSdks_whenSandboxNotAvailable_returnsLocallyLoadedSdkList() {
+        // TODO(b/262577044) Replace with @SdkSuppress after supporting maxExtensionVersion
+        assumeTrue("Requires Sandbox API not available", isSandboxApiNotAvailable())
+
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val managerCompat = SdkSandboxManagerCompat.from(context)
+
+        val localSdk = runBlocking {
+            managerCompat.loadSdk("androidx.privacysandbox.sdkruntime.test.v1", Bundle())
+        }
+
+        val sandboxedSdks = managerCompat.getSandboxedSdks()
+
+        assertThat(sandboxedSdks).containsExactly(localSdk)
+    }
+
+    private fun isSandboxApiNotAvailable() =
+        !AdServicesInfo.isAtLeastV4()
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/config/LocalSdkConfigParserTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/config/LocalSdkConfigParserTest.kt
new file mode 100644
index 0000000..f7e20ea
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/config/LocalSdkConfigParserTest.kt
@@ -0,0 +1,253 @@
+/*
+ * 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.client.config
+
+import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfigParser.Companion.parse
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.testutils.assertThrows
+import com.google.common.truth.Truth.assertThat
+import java.io.ByteArrayInputStream
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.xmlpull.v1.XmlPullParserException
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LocalSdkConfigParserTest {
+
+    @Test
+    fun parse_skipUnknownTagsAndReturnParsedResult() {
+        val xml = """
+            <compat-config>
+                <compat-entrypoint>compat.sdk.provider</compat-entrypoint>
+                <unknown-tag>new parameter from future library version</unknown-tag>
+                <dex-path>1.dex</dex-path>
+                <future-version-tag>
+                    <unknown-tag>new inner tag</unknown-tag>
+                </future-version-tag>
+                <dex-path>2.dex</dex-path>
+                <java-resources-root-path>javaResPath/</java-resources-root-path>
+                <resource-id-remapping>
+                    <unknown-tag>new remapping tag</unknown-tag>
+                    <r-package-class>com.test.sdk.RPackage</r-package-class>
+                    <resources-package-id>42</resources-package-id>
+                </resource-id-remapping>
+            </compat-config>
+        """.trimIndent()
+
+        val result = tryParse(xml, packageName = "com.test.sdk.package", versionMajor = 1)
+
+        assertThat(result)
+            .isEqualTo(
+                LocalSdkConfig(
+                    packageName = "com.test.sdk.package",
+                    versionMajor = 1,
+                    dexPaths = listOf("1.dex", "2.dex"),
+                    entryPoint = "compat.sdk.provider",
+                    javaResourcesRoot = "javaResPath/",
+                    resourceRemapping = ResourceRemappingConfig(
+                        rPackageClassName = "com.test.sdk.RPackage",
+                        packageId = 42
+                    )
+                )
+            )
+    }
+
+    @Test
+    fun parse_whenOnlyMandatoryElements_returnParsedResult() {
+        val xml = """
+            <compat-config>
+                <compat-entrypoint>compat.sdk.provider</compat-entrypoint>
+                <dex-path>1.dex</dex-path>
+            </compat-config>
+        """.trimIndent()
+
+        val result = tryParse(xml, packageName = "com.test.sdk.package")
+
+        assertThat(result)
+            .isEqualTo(
+                LocalSdkConfig(
+                    packageName = "com.test.sdk.package",
+                    versionMajor = null,
+                    dexPaths = listOf("1.dex"),
+                    entryPoint = "compat.sdk.provider",
+                    javaResourcesRoot = null,
+                    resourceRemapping = null
+                )
+            )
+    }
+
+    @Test
+    fun parse_whenNoEntryPoint_throwsException() {
+        assertParsingFailWithReason(
+            xml = """
+                <compat-config>
+                    <dex-path>1.dex</dex-path>
+                </compat-config>
+            """.trimIndent(),
+            reason = "No compat-entrypoint tag found"
+        )
+    }
+
+    @Test
+    fun parse_whenDuplicateEntryPoint_throwsException() {
+        assertParsingFailWithReason(
+            xml = """
+                <compat-config>
+                    <compat-entrypoint>compat.sdk.provider</compat-entrypoint>
+                    <compat-entrypoint>compat.sdk.provider2</compat-entrypoint>
+                    <dex-path>1.dex</dex-path>
+                </compat-config>
+            """.trimIndent(),
+            reason = "Duplicate compat-entrypoint tag found"
+        )
+    }
+
+    @Test
+    fun parse_whenNoDexPath_throwsException() {
+        assertParsingFailWithReason(
+            xml = """
+                <compat-config>
+                    <compat-entrypoint>compat.sdk.provider</compat-entrypoint>
+                </compat-config>
+            """.trimIndent(),
+            reason = "No dex-path tags found"
+        )
+    }
+
+    @Test
+    fun parse_whenDuplicateJavaResourceRoot_throwsException() {
+        assertParsingFailWithReason(
+            xml = """
+                <compat-config>
+                    <compat-entrypoint>compat.sdk.provider</compat-entrypoint>
+                    <dex-path>1.dex</dex-path>
+                    <java-resources-root-path>path1/</java-resources-root-path>
+                    <java-resources-root-path>path2/</java-resources-root-path>
+                </compat-config>
+            """.trimIndent(),
+            reason = "Duplicate java-resources-root-path tag found"
+        )
+    }
+
+    @Test
+    fun parse_whenDuplicateResourceRemapping_throwsException() {
+        assertParsingFailWithReason(
+            xml = """
+                <compat-config>
+                    <compat-entrypoint>compat.sdk.provider</compat-entrypoint>
+                    <dex-path>1.dex</dex-path>
+                    <resource-id-remapping>
+                        <r-package-class>com.test.sdk.RPackage</r-package-class>
+                        <resources-package-id>42</resources-package-id>
+                    </resource-id-remapping>
+                    <resource-id-remapping>
+                        <r-package-class>com.test.sdk.RPackage</r-package-class>
+                        <resources-package-id>42</resources-package-id>
+                    </resource-id-remapping>
+                </compat-config>
+            """.trimIndent(),
+            reason = "Duplicate resource-id-remapping tag found"
+        )
+    }
+
+    @Test
+    fun parse_whenNoClassInResourceRemapping_throwsException() {
+        assertParsingFailWithReason(
+            xml = """
+                <compat-config>
+                    <compat-entrypoint>compat.sdk.provider</compat-entrypoint>
+                    <dex-path>1.dex</dex-path>
+                    <resource-id-remapping>
+                        <resources-package-id>42</resources-package-id>
+                    </resource-id-remapping>
+                </compat-config>
+            """.trimIndent(),
+            reason = "No r-package-class tag found"
+        )
+    }
+
+    @Test
+    fun parse_whenDuplicateClassInResourceRemapping_throwsException() {
+        assertParsingFailWithReason(
+            xml = """
+                <compat-config>
+                    <compat-entrypoint>compat.sdk.provider</compat-entrypoint>
+                    <dex-path>1.dex</dex-path>
+                    <resource-id-remapping>
+                        <r-package-class>com.test.sdk.RPackage</r-package-class>
+                        <r-package-class>com.test.sdk.RPackage</r-package-class>
+                        <resources-package-id>42</resources-package-id>
+                    </resource-id-remapping>
+                </compat-config>
+            """.trimIndent(),
+            reason = "Duplicate r-package-class tag found"
+        )
+    }
+
+    @Test
+    fun parse_whenNoPackageIdInResourceRemapping_throwsException() {
+        assertParsingFailWithReason(
+            xml = """
+                <compat-config>
+                    <compat-entrypoint>compat.sdk.provider</compat-entrypoint>
+                    <dex-path>1.dex</dex-path>
+                    <resource-id-remapping>
+                        <r-package-class>com.test.sdk.RPackage</r-package-class>
+                    </resource-id-remapping>
+                </compat-config>
+            """.trimIndent(),
+            reason = "No resources-package-id tag found"
+        )
+    }
+
+    @Test
+    fun parse_whenDuplicatePackageIdInResourceRemapping_throwsException() {
+        assertParsingFailWithReason(
+            xml = """
+                <compat-config>
+                    <compat-entrypoint>compat.sdk.provider</compat-entrypoint>
+                    <dex-path>1.dex</dex-path>
+                    <resource-id-remapping>
+                        <r-package-class>com.test.sdk.RPackage</r-package-class>
+                        <resources-package-id>42</resources-package-id>
+                        <resources-package-id>42</resources-package-id>
+                    </resource-id-remapping>
+                </compat-config>
+            """.trimIndent(),
+            reason = "Duplicate resources-package-id tag found"
+        )
+    }
+
+    private fun assertParsingFailWithReason(xml: String, reason: String) {
+        assertThrows<XmlPullParserException> {
+            tryParse(xml)
+        }.hasMessageThat().isEqualTo(
+            reason
+        )
+    }
+
+    private fun tryParse(
+        xml: String,
+        packageName: String = "sdkPackageName",
+        versionMajor: Int? = null
+    ): LocalSdkConfig {
+        ByteArrayInputStream(xml.toByteArray()).use { inputStream ->
+            return parse(inputStream, packageName, versionMajor)
+        }
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/config/LocalSdkConfigsHolderTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/config/LocalSdkConfigsHolderTest.kt
new file mode 100644
index 0000000..21c0016
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/config/LocalSdkConfigsHolderTest.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.client.config
+
+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
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LocalSdkConfigsHolderTest {
+
+    @Test
+    fun load_whenSdkTableNotExists_doesNotThrowException() {
+        val configHolder = LocalSdkConfigsHolder.load(
+            ApplicationProvider.getApplicationContext(),
+            sdkTableAssetName = "not-exists"
+        )
+        val result = configHolder.getSdkConfig("sdk")
+        assertThat(result).isNull()
+    }
+
+    @Test
+    fun getSdkConfig_whenSdkExists_returnSdkInfo() {
+        val configHolder = LocalSdkConfigsHolder.load(
+            ApplicationProvider.getApplicationContext()
+        )
+
+        val result = configHolder.getSdkConfig(
+            "androidx.privacysandbox.sdkruntime.test.v1"
+        )
+
+        assertThat(result)
+            .isEqualTo(
+                LocalSdkConfig(
+                    packageName = "androidx.privacysandbox.sdkruntime.test.v1",
+                    versionMajor = 42,
+                    dexPaths = listOf("RuntimeEnabledSdks/V1/classes.dex"),
+                    entryPoint = "androidx.privacysandbox.sdkruntime.test.v1.CompatProvider",
+                    javaResourcesRoot = "RuntimeEnabledSdks/V1/javaresources"
+                )
+            )
+    }
+
+    @Test
+    fun getSdkConfig_whenSdkNotExists_returnNull() {
+        val configHolder = LocalSdkConfigsHolder.load(
+            ApplicationProvider.getApplicationContext()
+        )
+
+        val result = configHolder.getSdkConfig("not-exists")
+
+        assertThat(result).isNull()
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/config/SdkTableConfigParserTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/config/SdkTableConfigParserTest.kt
new file mode 100644
index 0000000..bc2c9d5
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/config/SdkTableConfigParserTest.kt
@@ -0,0 +1,202 @@
+/*
+ * 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.client.config
+
+import androidx.privacysandbox.sdkruntime.client.config.SdkTableConfigParser.Companion.parse
+import androidx.privacysandbox.sdkruntime.client.config.SdkTableConfigParser.SdkTableEntry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.testutils.assertThrows
+import com.google.common.truth.Truth.assertThat
+import java.io.ByteArrayInputStream
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.xmlpull.v1.XmlPullParserException
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SdkTableConfigParserTest {
+
+    @Test
+    fun parse_skipUnknownTagsAndReturnSetWithSdkTableEntries() {
+        val xml = """
+            <runtime-enabled-sdk-table>
+                <runtime-enabled-sdk>
+                    <package-name>sdk1</package-name>
+                    <unknown-tag>new parameter from future library version</unknown-tag>
+                    <compat-config-path>config1.xml</compat-config-path>
+                </runtime-enabled-sdk>
+                <future-version-runtime-enabled-sdk>
+                    <unknown-tag>new sdk type without old tags</unknown-tag>
+                </future-version-runtime-enabled-sdk>
+                <runtime-enabled-sdk>
+                    <unknown-tag2>new parameter from future library version</unknown-tag2>
+                    <package-name>sdk2</package-name>
+                    <version-major>42</version-major>
+                    <compat-config-path>config2.xml</compat-config-path>
+                </runtime-enabled-sdk>
+            </runtime-enabled-sdk-table>
+        """.trimIndent()
+
+        val result = tryParse(xml)
+
+        assertThat(result)
+            .containsExactly(
+                SdkTableEntry(
+                    packageName = "sdk1",
+                    versionMajor = null,
+                    compatConfigPath = "config1.xml"
+                ),
+                SdkTableEntry(
+                    packageName = "sdk2",
+                    versionMajor = 42,
+                    compatConfigPath = "config2.xml"
+                )
+            )
+    }
+
+    @Test
+    fun parse_whenEmptyTable_returnsEmptyMap() {
+        val xml = """
+            <runtime-enabled-sdk-table>
+            </runtime-enabled-sdk-table>
+        """.trimIndent()
+
+        val result = tryParse(xml)
+
+        assertThat(result).isEmpty()
+    }
+
+    @Test
+    fun parse_whenNoPackageName_throwsException() {
+        val xml = """
+            <runtime-enabled-sdk-table>
+                <runtime-enabled-sdk>
+                    <compat-config-path>config1.xml</compat-config-path>
+                </runtime-enabled-sdk>
+            </runtime-enabled-sdk-table>
+        """.trimIndent()
+
+        assertThrows<XmlPullParserException> {
+            tryParse(xml)
+        }.hasMessageThat().isEqualTo(
+            "No package-name tag found"
+        )
+    }
+
+    @Test
+    fun parse_whenMultiplePackageNames_throwsException() {
+        val xml = """
+            <runtime-enabled-sdk-table>
+                <runtime-enabled-sdk>
+                    <package-name>sdk1</package-name>
+                    <package-name>sdk2</package-name>
+                    <compat-config-path>config1.xml</compat-config-path>
+                </runtime-enabled-sdk>
+            </runtime-enabled-sdk-table>
+        """.trimIndent()
+
+        assertThrows<XmlPullParserException> {
+            tryParse(xml)
+        }.hasMessageThat().isEqualTo(
+            "Duplicate package-name tag found"
+        )
+    }
+
+    @Test
+    fun parse_whenMultipleVersionMajor_throwsException() {
+        val xml = """
+            <runtime-enabled-sdk-table>
+                <runtime-enabled-sdk>
+                    <package-name>sdk1</package-name>
+                    <version-major>1</version-major>
+                    <version-major>2</version-major>
+                    <compat-config-path>config1.xml</compat-config-path>
+                </runtime-enabled-sdk>
+            </runtime-enabled-sdk-table>
+        """.trimIndent()
+
+        assertThrows<XmlPullParserException> {
+            tryParse(xml)
+        }.hasMessageThat().isEqualTo(
+            "Duplicate version-major tag found"
+        )
+    }
+
+    @Test
+    fun parse_whenNoConfigPath_throwsException() {
+        val xml = """
+            <runtime-enabled-sdk-table>
+                <runtime-enabled-sdk>
+                    <package-name>sdk1</package-name>
+                </runtime-enabled-sdk>
+            </runtime-enabled-sdk-table>
+        """.trimIndent()
+
+        assertThrows<XmlPullParserException> {
+            tryParse(xml)
+        }.hasMessageThat().isEqualTo(
+            "No compat-config-path tag found"
+        )
+    }
+
+    @Test
+    fun parse_whenMultipleConfigPaths_throwsException() {
+        val xml = """
+            <runtime-enabled-sdk-table>
+                <runtime-enabled-sdk>
+                    <package-name>sdk1</package-name>
+                    <compat-config-path>config1.xml</compat-config-path>
+                    <compat-config-path>config2.xml</compat-config-path>
+                </runtime-enabled-sdk>
+            </runtime-enabled-sdk-table>
+        """.trimIndent()
+
+        assertThrows<XmlPullParserException> {
+            tryParse(xml)
+        }.hasMessageThat().isEqualTo(
+            "Duplicate compat-config-path tag found"
+        )
+    }
+
+    @Test
+    fun parse_whenDuplicatePackageName_throwsException() {
+        val xml = """
+            <runtime-enabled-sdk-table>
+                <runtime-enabled-sdk>
+                    <package-name>sdk1</package-name>
+                    <compat-config-path>config1.xml</compat-config-path>
+                </runtime-enabled-sdk>
+                <runtime-enabled-sdk>
+                    <package-name>sdk1</package-name>
+                    <compat-config-path>config2.xml</compat-config-path>
+                </runtime-enabled-sdk>
+            </runtime-enabled-sdk-table>
+        """.trimIndent()
+
+        assertThrows<XmlPullParserException> {
+            tryParse(xml)
+        }.hasMessageThat().isEqualTo(
+            "Duplicate entry for sdk1 found"
+        )
+    }
+
+    private fun tryParse(xml: String): Set<SdkTableEntry> {
+        ByteArrayInputStream(xml.toByteArray()).use { inputStream ->
+            return parse(inputStream)
+        }
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..b44ba98
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/controller/LocalControllerTest.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.client.controller
+
+import android.os.Binder
+import android.os.Bundle
+import androidx.privacysandbox.sdkruntime.client.loader.LocalSdkProvider
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LocalControllerTest {
+
+    private lateinit var locallyLoadedSdks: LocallyLoadedSdks
+    private lateinit var controller: LocalController
+
+    @Before
+    fun setUp() {
+        locallyLoadedSdks = LocallyLoadedSdks()
+        controller = LocalController(locallyLoadedSdks)
+    }
+
+    @Test
+    fun getSandboxedSdks_returnsResultsFromLocallyLoadedSdks() {
+        val sandboxedSdk = SandboxedSdkCompat(Binder())
+        locallyLoadedSdks.put(
+            "sdk", LocallyLoadedSdks.Entry(
+                sdkProvider = NoOpSdkProvider(),
+                sdk = sandboxedSdk
+            )
+        )
+
+        val result = controller.getSandboxedSdks()
+        assertThat(result).containsExactly(sandboxedSdk)
+    }
+
+    private class NoOpSdkProvider : LocalSdkProvider(Any()) {
+        override fun onLoadSdk(params: Bundle): SandboxedSdkCompat {
+            throw IllegalStateException("Unexpected call")
+        }
+
+        override fun beforeUnloadSdk() {
+            throw IllegalStateException("Unexpected call")
+        }
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/FileClassLoaderFactoryTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/FileClassLoaderFactoryTest.kt
new file mode 100644
index 0000000..31fee55
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/FileClassLoaderFactoryTest.kt
@@ -0,0 +1,145 @@
+/*
+ * 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.client.loader
+
+import android.content.Context
+import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
+import androidx.privacysandbox.sdkruntime.client.loader.storage.LocalSdkStorage
+import androidx.privacysandbox.sdkruntime.client.loader.storage.LocalSdkDexFiles
+import androidx.privacysandbox.sdkruntime.client.loader.storage.TestLocalSdkStorage
+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
+import java.io.File
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FileClassLoaderFactoryTest {
+
+    private lateinit var testSdkConfig: LocalSdkConfig
+
+    @Before
+    fun setUp() {
+        testSdkConfig = LocalSdkConfig(
+            packageName = "androidx.privacysandbox.sdkruntime.test.v1",
+            dexPaths = listOf(
+                "RuntimeEnabledSdks/V1/classes.dex",
+            ),
+            entryPoint = "androidx.privacysandbox.sdkruntime.test.v1.CompatProvider",
+        )
+    }
+
+    @Test
+    fun createClassLoaderFor_whenSdkStorageReturnFiles_returnClassloaderAndNotDelegateToFallback() {
+        val sdkDexFiles = extractTestSdkDexFiles()
+        val fallback = TestFallbackFactory()
+
+        val fileClassLoaderFactory = FileClassLoaderFactory(
+            StubSdkStorage(result = sdkDexFiles),
+            fallback
+        )
+
+        val classLoader = fileClassLoaderFactory.createClassLoaderFor(
+            testSdkConfig,
+            javaClass.classLoader!!.parent!!
+        )
+
+        val loadedEntryPointClass = classLoader.loadClass(testSdkConfig.entryPoint)
+        assertThat(loadedEntryPointClass.classLoader).isEqualTo(classLoader)
+
+        assertThat(fallback.loadSdkCalled).isFalse()
+    }
+
+    @Test
+    fun createClassLoaderFor_whenSdkStorageReturnNull_delegateToFallback() {
+        val fallback = TestFallbackFactory(testSdkConfig, javaClass.classLoader!!.parent)
+        val fileClassLoaderFactory = FileClassLoaderFactory(
+            StubSdkStorage(result = null),
+            fallback
+        )
+
+        fileClassLoaderFactory.createClassLoaderFor(
+            testSdkConfig,
+            javaClass.classLoader!!.parent!!
+        )
+
+        assertThat(fallback.loadSdkCalled).isTrue()
+    }
+
+    @Test
+    fun createClassLoaderFor_whenSdkStorageThrows_delegateToFallback() {
+        val fallback = TestFallbackFactory(testSdkConfig, javaClass.classLoader!!.parent)
+        val fileClassLoaderFactory = FileClassLoaderFactory(
+            ThrowingSdkStorage(exception = Exception("Something wrong")),
+            fallback
+        )
+
+        fileClassLoaderFactory.createClassLoaderFor(
+            testSdkConfig,
+            javaClass.classLoader!!.parent!!
+        )
+
+        assertThat(fallback.loadSdkCalled).isTrue()
+    }
+
+    private class StubSdkStorage(
+        private val result: LocalSdkDexFiles?
+    ) : LocalSdkStorage {
+        override fun dexFilesFor(sdkConfig: LocalSdkConfig) = result
+    }
+
+    private class ThrowingSdkStorage(
+        private val exception: Exception
+    ) : LocalSdkStorage {
+        override fun dexFilesFor(sdkConfig: LocalSdkConfig): LocalSdkDexFiles? {
+            throw exception
+        }
+    }
+
+    private class TestFallbackFactory(
+        private val expectedSdkConfig: LocalSdkConfig? = null,
+        private val expectedParent: ClassLoader? = null,
+    ) : SdkLoader.ClassLoaderFactory {
+
+        var loadSdkCalled: Boolean = false
+
+        override fun createClassLoaderFor(
+            sdkConfig: LocalSdkConfig,
+            parent: ClassLoader
+        ): ClassLoader {
+            assertThat(sdkConfig).isEqualTo(expectedSdkConfig)
+            assertThat(parent).isEqualTo(expectedParent)
+            loadSdkCalled = true
+            return parent
+        }
+    }
+
+    private fun extractTestSdkDexFiles(): LocalSdkDexFiles {
+        val context = ApplicationProvider.getApplicationContext<Context>()
+
+        val testStorage = TestLocalSdkStorage(
+            context,
+            rootFolder = File(context.cacheDir, "FileClassLoaderFactoryTest")
+        )
+
+        return testStorage.dexFilesFor(testSdkConfig)
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/InMemorySdkClassLoaderFactoryTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/InMemorySdkClassLoaderFactoryTest.kt
new file mode 100644
index 0000000..93aec37
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/InMemorySdkClassLoaderFactoryTest.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.client.loader
+
+import android.os.Build
+import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
+import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
+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 org.junit.Assert.assertThrows
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class InMemorySdkClassLoaderFactoryTest {
+
+    private lateinit var factoryUnderTest: InMemorySdkClassLoaderFactory
+    private lateinit var testSdkInfo: LocalSdkConfig
+
+    @Before
+    fun setUp() {
+        factoryUnderTest = InMemorySdkClassLoaderFactory.create(
+            ApplicationProvider.getApplicationContext()
+        )
+        testSdkInfo = LocalSdkConfig(
+            packageName = "androidx.privacysandbox.sdkruntime.test.v1",
+            dexPaths = listOf("RuntimeEnabledSdks/V1/classes.dex"),
+            entryPoint = "androidx.privacysandbox.sdkruntime.test.v1.CompatProvider",
+            javaResourcesRoot = "RuntimeEnabledSdks/V1/"
+        )
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O_MR1)
+    fun createClassLoaderFor_whenApi27_returnClassloader() {
+        val classLoader = factoryUnderTest.createClassLoaderFor(
+            testSdkInfo,
+            javaClass.classLoader!!
+        )
+        val loadedEntryPointClass = classLoader.loadClass(testSdkInfo.entryPoint)
+        assertThat(loadedEntryPointClass.classLoader).isEqualTo(classLoader)
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.O)
+    fun createClassLoaderFor_whenApiPre27_throwsSandboxDisabledException() {
+        val ex = assertThrows(LoadSdkCompatException::class.java) {
+            factoryUnderTest.createClassLoaderFor(
+                testSdkInfo,
+                javaClass.classLoader!!
+            )
+        }
+
+        assertThat(ex.loadSdkErrorCode)
+            .isEqualTo(LoadSdkCompatException.LOAD_SDK_SDK_SANDBOX_DISABLED)
+        assertThat(ex)
+            .hasMessageThat()
+            .isEqualTo("Can't use InMemoryDexClassLoader")
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/JavaResourcesLoadingClassLoaderFactoryTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/JavaResourcesLoadingClassLoaderFactoryTest.kt
new file mode 100644
index 0000000..3da2c8b
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/JavaResourcesLoadingClassLoaderFactoryTest.kt
@@ -0,0 +1,114 @@
+/*
+ * 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.client.loader
+
+import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class JavaResourcesLoadingClassLoaderFactoryTest {
+
+    private lateinit var appClassloader: ClassLoader
+    private lateinit var factoryUnderTest: JavaResourcesLoadingClassLoaderFactory
+    private lateinit var testSdkConfig: LocalSdkConfig
+
+    @Before
+    fun setUp() {
+        appClassloader = javaClass.classLoader!!
+        factoryUnderTest = JavaResourcesLoadingClassLoaderFactory(
+            appClassloader,
+            codeClassLoaderFactory = object : SdkLoader.ClassLoaderFactory {
+                override fun createClassLoaderFor(sdkConfig: LocalSdkConfig, parent: ClassLoader) =
+                    parent
+            }
+        )
+        testSdkConfig = LocalSdkConfig(
+            packageName = "androidx.privacysandbox.sdkruntime.test.v1",
+            dexPaths = listOf("RuntimeEnabledSdks/V1/classes.dex"),
+            entryPoint = "androidx.privacysandbox.sdkruntime.test.v1.CompatProvider",
+            javaResourcesRoot = "RuntimeEnabledSdks/V1/javaresources"
+        )
+    }
+
+    @Test
+    fun getResource_delegateToAppClassloaderWithPrefix() {
+        val classLoader = factoryUnderTest.createClassLoaderFor(
+            testSdkConfig,
+            appClassloader.parent!!
+        )
+        val resource = classLoader.getResource("test.txt")
+
+        val appResource = appClassloader.getResource(
+            "assets/RuntimeEnabledSdks/V1/javaresources/test.txt"
+        )
+        assertThat(resource).isNotNull()
+        assertThat(resource).isEqualTo(appResource)
+    }
+
+    @Test
+    fun getResource_whenAppResource_returnNull() {
+        val classLoader = factoryUnderTest.createClassLoaderFor(
+            testSdkConfig,
+            appClassloader.parent!!
+        )
+
+        val resource = classLoader.getResource("assets/RuntimeEnabledSdkTable.xml")
+        val appResource = appClassloader.getResource("assets/RuntimeEnabledSdkTable.xml")
+
+        assertThat(appResource).isNotNull()
+        assertThat(resource).isNull()
+    }
+
+    @Test
+    fun getResources_delegateToAppClassloaderWithPrefix() {
+        val classLoader = factoryUnderTest.createClassLoaderFor(
+            testSdkConfig,
+            appClassloader.parent!!
+        )
+
+        val resources = classLoader
+            .getResources("test.txt")
+            .toList()
+        assertThat(resources.isEmpty()).isFalse()
+
+        val appResources = appClassloader
+            .getResources("assets/RuntimeEnabledSdks/V1/javaresources/test.txt")
+            .toList()
+
+        assertThat(appResources).isEqualTo(resources)
+    }
+
+    @Test
+    fun getResources_whenAppResource_returnEmpty() {
+        val classLoader = factoryUnderTest.createClassLoaderFor(
+            testSdkConfig,
+            appClassloader.parent!!
+        )
+
+        val resources = classLoader.getResources("assets/RuntimeEnabledSdkTable.xml")
+        val appResources = appClassloader.getResources("assets/RuntimeEnabledSdkTable.xml")
+
+        assertThat(appResources.hasMoreElements()).isTrue()
+        assertThat(resources.hasMoreElements()).isFalse()
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..f34de7b1
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkProviderTest.kt
@@ -0,0 +1,300 @@
+/*
+ * 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.client.loader
+
+import android.content.Context
+import android.os.Binder
+import android.os.Bundle
+import android.view.View
+import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
+import androidx.privacysandbox.sdkruntime.client.loader.impl.SandboxedSdkContextCompat
+import androidx.privacysandbox.sdkruntime.client.loader.storage.TestLocalSdkStorage
+import androidx.privacysandbox.sdkruntime.client.loader.storage.toClassPathString
+import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkInfo
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkProviderCompat
+import androidx.privacysandbox.sdkruntime.core.Versions
+import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import dalvik.system.BaseDexClassLoader
+import java.io.File
+import org.junit.Assert.assertThrows
+import org.junit.Assume.assumeTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@SmallTest
+@RunWith(Parameterized::class)
+internal class LocalSdkProviderTest(
+    @Suppress("unused") private val sdkPath: String,
+    private val sdkVersion: Int,
+    private val controller: TestStubController,
+    private val loadedSdk: LocalSdkProvider
+) {
+
+    @Test
+    fun loadSdk_attachCorrectContext() {
+        val sdkContext = loadedSdk.extractSdkContext()
+        assertThat(sdkContext.javaClass.name)
+            .isEqualTo(SandboxedSdkContextCompat::class.java.name)
+    }
+
+    @Test
+    fun onLoadSdk_callOnLoadSdkAndReturnResult() {
+        val params = Bundle()
+
+        val sandboxedSdkCompat = loadedSdk.onLoadSdk(params)
+
+        val expectedBinder = loadedSdk.extractSdkProviderFieldValue<Binder>(
+            fieldName = "onLoadSdkBinder",
+        )
+        assertThat(sandboxedSdkCompat.getInterface()).isEqualTo(expectedBinder)
+
+        val lastParams = loadedSdk.extractSdkProviderFieldValue<Bundle>(
+            fieldName = "lastOnLoadSdkParams",
+        )
+        assertThat(lastParams).isEqualTo(params)
+    }
+
+    @Test
+    fun onLoadSdk_callOnLoadSdkAndThrowException() {
+        val params = Bundle()
+        params.putBoolean("needFail", true)
+
+        val ex = assertThrows(LoadSdkCompatException::class.java) {
+            loadedSdk.onLoadSdk(params)
+        }
+
+        assertThat(ex.extraInformation).isEqualTo(params)
+    }
+
+    @Test
+    fun beforeUnloadSdk_callBeforeUnloadSdk() {
+        loadedSdk.beforeUnloadSdk()
+
+        val isBeforeUnloadSdkCalled = loadedSdk.extractSdkProviderFieldValue<Boolean>(
+            fieldName = "isBeforeUnloadSdkCalled"
+        )
+
+        assertThat(isBeforeUnloadSdkCalled).isTrue()
+    }
+
+    @Test
+    fun getSandboxedSdks_delegateToSdkController() {
+        assumeTrue(
+            "Requires Versions.API_VERSION >= 2",
+            sdkVersion >= 2
+        )
+
+        val expectedResult = SandboxedSdkCompat(
+            sdkInterface = Binder(),
+            sdkInfo = SandboxedSdkInfo(
+                name = "sdkName",
+                version = 42
+            )
+        )
+        controller.sandboxedSdksResult = listOf(
+            expectedResult
+        )
+
+        val testSdk = loadedSdk.loadTestSdk()
+        val sandboxedSdks = testSdk.getSandboxedSdks()
+        assertThat(sandboxedSdks).hasSize(1)
+        val result = sandboxedSdks[0]
+
+        assertThat(result.getInterface()).isEqualTo(expectedResult.getInterface())
+        assertThat(result.getSdkName()).isEqualTo(expectedResult.getSdkInfo()!!.name)
+        assertThat(result.getSdkVersion()).isEqualTo(expectedResult.getSdkInfo()!!.version)
+    }
+
+    class CurrentVersionProviderLoadTest : 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 = CurrentVersionSdkTest(context!!)
+            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)
+        }
+    }
+
+    @Suppress("unused") // Reflection calls
+    internal class CurrentVersionSdkTest(
+        private val context: Context
+    ) : Binder() {
+        fun getSandboxedSdks(): List<SandboxedSdkCompat> =
+            SdkSandboxControllerCompat.from(context).getSandboxedSdks()
+    }
+
+    internal class TestClassLoaderFactory(
+        private val testStorage: TestLocalSdkStorage
+    ) : SdkLoader.ClassLoaderFactory {
+        override fun createClassLoaderFor(
+            sdkConfig: LocalSdkConfig,
+            parent: ClassLoader
+        ): ClassLoader {
+            val sdkDexFiles = testStorage.dexFilesFor(sdkConfig)
+
+            val optimizedDirectory = File(sdkDexFiles.files[0].parentFile, "DexOpt")
+            if (!optimizedDirectory.exists()) {
+                optimizedDirectory.mkdirs()
+            }
+
+            return BaseDexClassLoader(
+                sdkDexFiles.toClassPathString(),
+                optimizedDirectory,
+                /* librarySearchPath = */ null,
+                parent
+            )
+        }
+    }
+
+    internal class TestSdkInfo internal constructor(
+        val apiVersion: Int,
+        dexPath: String,
+        sdkProviderClass: String
+    ) {
+        val localSdkConfig = LocalSdkConfig(
+            packageName = "test.$apiVersion.$sdkProviderClass",
+            dexPaths = listOf(dexPath),
+            entryPoint = sdkProviderClass
+        )
+    }
+
+    companion object {
+        private val SDKS = arrayOf(
+            TestSdkInfo(
+                1,
+                "RuntimeEnabledSdks/V1/classes.dex",
+                "androidx.privacysandbox.sdkruntime.test.v1.CompatProvider"
+            ),
+            TestSdkInfo(
+                2,
+                "RuntimeEnabledSdks/V2/classes.dex",
+                "androidx.privacysandbox.sdkruntime.test.v2.CompatProvider"
+            )
+        )
+
+        @Parameterized.Parameters(name = "sdk: {0}, version: {1}")
+        @JvmStatic
+        fun params(): List<Array<Any>> = buildList {
+            assertThat(SDKS.size).isEqualTo(Versions.API_VERSION)
+
+            val controller = TestStubController()
+
+            val assetsSdkLoader = createAssetsSdkLoader(controller)
+            for (i in SDKS.indices) {
+                val sdk = SDKS[i]
+                assertThat(sdk.apiVersion).isEqualTo(i + 1)
+
+                val loadedSdk = assetsSdkLoader.loadSdk(sdk.localSdkConfig)
+                assertThat(loadedSdk.extractApiVersion())
+                    .isEqualTo(sdk.apiVersion)
+
+                add(
+                    arrayOf(
+                        sdk.localSdkConfig.dexPaths[0],
+                        sdk.apiVersion,
+                        controller,
+                        loadedSdk
+                    )
+                )
+            }
+
+            // add SDK loaded from test sources
+            add(
+                arrayOf(
+                    "BuiltFromSource",
+                    Versions.API_VERSION,
+                    controller,
+                    loadTestSdkFromSource(controller),
+                )
+            )
+        }
+
+        private fun loadTestSdkFromSource(controller: TestStubController): LocalSdkProvider {
+            val sdkLoader = SdkLoader(
+                object : SdkLoader.ClassLoaderFactory {
+                    override fun createClassLoaderFor(
+                        sdkConfig: LocalSdkConfig,
+                        parent: ClassLoader
+                    ): ClassLoader = javaClass.classLoader!!
+                },
+                ApplicationProvider.getApplicationContext(),
+                controller
+            )
+
+            return sdkLoader.loadSdk(
+                LocalSdkConfig(
+                    packageName = "test.CurrentVersionProviderLoadTest",
+                    dexPaths = emptyList(),
+                    entryPoint = CurrentVersionProviderLoadTest::class.java.name
+                )
+            )
+        }
+
+        private fun createAssetsSdkLoader(controller: TestStubController): SdkLoader {
+            val context = ApplicationProvider.getApplicationContext<Context>()
+            val testStorage = TestLocalSdkStorage(
+                context,
+                rootFolder = File(context.cacheDir, "LocalSdkTest")
+            )
+            return SdkLoader(
+                TestClassLoaderFactory(testStorage),
+                context,
+                controller
+            )
+        }
+    }
+
+    internal class TestStubController : SdkSandboxControllerCompat.SandboxControllerImpl {
+
+        var sandboxedSdksResult: List<SandboxedSdkCompat> = emptyList()
+
+        override fun getSandboxedSdks(): List<SandboxedSdkCompat> {
+            return sandboxedSdksResult
+        }
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..def9887
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkTestUtils.kt
@@ -0,0 +1,155 @@
+/*
+ * 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.client.loader
+
+import android.content.Context
+import android.os.Bundle
+import android.os.IBinder
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import androidx.privacysandbox.sdkruntime.core.Versions
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkProviderCompat
+import kotlin.reflect.cast
+
+/**
+ * Extract value of [Versions.API_VERSION] from loaded SDK.
+ */
+internal fun LocalSdkProvider.extractApiVersion(): Int =
+    extractVersionValue("API_VERSION")
+
+/**
+ * Extract value of [Versions.CLIENT_VERSION] from loaded SDK.
+ */
+internal fun LocalSdkProvider.extractClientVersion(): Int =
+    extractVersionValue("CLIENT_VERSION")
+
+/**
+ * Extract [SandboxedSdkProviderCompat.context] from loaded SDK.
+ */
+internal fun LocalSdkProvider.extractSdkContext(): Context {
+    val getContextMethod = sdkProvider
+        .javaClass
+        .getMethod("getContext")
+
+    val rawContext = getContextMethod.invoke(sdkProvider)
+
+    return Context::class.cast(rawContext)
+}
+
+/**
+ * Extract field value from [SandboxedSdkProviderCompat]
+ */
+internal inline fun <reified T> LocalSdkProvider.extractSdkProviderFieldValue(
+    fieldName: String
+): T {
+    return sdkProvider
+        .javaClass
+        .getField(fieldName)
+        .get(sdkProvider)!! as T
+}
+
+/**
+ * Extract classloader that was used for loading of [SandboxedSdkProviderCompat].
+ */
+internal fun LocalSdkProvider.extractSdkProviderClassloader(): ClassLoader =
+    sdkProvider.javaClass.classLoader!!
+
+/**
+ * Reflection wrapper for TestSDK object.
+ * Underlying TestSDK should implement and delegate to
+ * [androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat]:
+ *  1) getSandboxedSdks() : List<SandboxedSdkCompat>
+ */
+internal class TestSdkWrapper(
+    private val sdk: Any
+) {
+    fun getSandboxedSdks(): List<SandboxedSdkWrapper> {
+        val sdks = sdk.callMethod(
+            methodName = "getSandboxedSdks"
+        ) as List<*>
+        return sdks.map { SandboxedSdkWrapper(it!!) }
+    }
+}
+
+/**
+ * Reflection wrapper for [SandboxedSdkCompat]
+ */
+internal class SandboxedSdkWrapper(
+    private val sdk: Any
+) {
+    fun getInterface(): IBinder? {
+        return sdk.callMethod(
+            methodName = "getInterface"
+        ) as IBinder?
+    }
+
+    fun getSdkName(): String? {
+        val sdkInfo = getSdkInfo()
+        if (sdkInfo != null) {
+            return sdkInfo.callMethod(
+                methodName = "getName"
+            ) as String
+        }
+        return null
+    }
+
+    fun getSdkVersion(): Long? {
+        val sdkInfo = getSdkInfo()
+        if (sdkInfo != null) {
+            return sdkInfo.callMethod(
+                methodName = "getVersion"
+            ) as Long
+        }
+        return null
+    }
+
+    private fun getSdkInfo(): Any? {
+        return sdk.callMethod(
+            methodName = "getSdkInfo"
+        )
+    }
+}
+
+/**
+ * Load SDK and wrap it as TestSDK.
+ * @see [TestSdkWrapper]
+ */
+internal fun LocalSdkProvider.loadTestSdk(): TestSdkWrapper {
+    return onLoadSdk(Bundle()).asTestSdk()
+}
+
+/**
+ * Wrap SandboxedSdkCompat as TestSDK.
+ * @see [SandboxedSdkWrapper]
+ */
+internal fun SandboxedSdkCompat.asTestSdk(): TestSdkWrapper {
+    return TestSdkWrapper(sdk = getInterface()!!)
+}
+
+private fun Any.callMethod(methodName: String): Any? {
+    return javaClass
+        .getMethod(methodName)
+        .invoke(this)
+}
+
+private fun LocalSdkProvider.extractVersionValue(versionFieldName: String): Int {
+    val versionsClass = Class.forName(
+        Versions::class.java.name,
+        false,
+        extractSdkProviderClassloader()
+    )
+    return versionsClass.getDeclaredField(versionFieldName).get(null) as Int
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..11c62973
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/SandboxedSdkContextCompatTest.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.client.loader
+
+import androidx.privacysandbox.sdkruntime.client.loader.impl.SandboxedSdkContextCompat
+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
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SandboxedSdkContextCompatTest {
+
+    @Test
+    fun getClassloader_returnSdkClassloader() {
+        val sdkClassLoader = javaClass.classLoader!!.parent!!
+
+        val sdkContextCompat = SandboxedSdkContextCompat(
+            ApplicationProvider.getApplicationContext(),
+            sdkClassLoader
+        )
+        assertThat(sdkContextCompat.classLoader)
+            .isEqualTo(sdkClassLoader)
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..341d08f
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/SdkLoaderTest.kt
@@ -0,0 +1,153 @@
+/*
+ * 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.client.loader
+
+import android.content.Context
+import android.os.Build
+import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
+import androidx.privacysandbox.sdkruntime.client.config.ResourceRemappingConfig
+import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import androidx.privacysandbox.sdkruntime.core.Versions
+import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import androidx.testutils.assertThrows
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SdkLoaderTest {
+
+    private lateinit var sdkLoader: SdkLoader
+
+    private lateinit var testSdkConfig: LocalSdkConfig
+
+    @Before
+    fun setUp() {
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        sdkLoader = SdkLoader.create(
+            context = context,
+            controller = NoOpImpl(),
+        )
+        testSdkConfig = LocalSdkConfig(
+            packageName = "androidx.privacysandbox.sdkruntime.test.v1",
+            dexPaths = listOf(
+                "RuntimeEnabledSdks/V1/classes.dex",
+                "RuntimeEnabledSdks/RPackage.dex"
+            ),
+            entryPoint = "androidx.privacysandbox.sdkruntime.test.v1.CompatProvider",
+            javaResourcesRoot = "RuntimeEnabledSdks/V1/javaresources",
+            resourceRemapping = ResourceRemappingConfig(
+                rPackageClassName = "androidx.privacysandbox.sdkruntime.test.RPackage",
+                packageId = 42
+            )
+        )
+
+        // Clean extracted SDKs between tests
+        File(context.cacheDir, "RuntimeEnabledSdk").deleteRecursively()
+    }
+
+    @Test
+    fun loadSdk_callVersionsHandShake() {
+        val loadedSdk = sdkLoader.loadSdk(testSdkConfig)
+
+        assertThat(loadedSdk.extractClientVersion())
+            .isEqualTo(Versions.API_VERSION)
+    }
+
+    @Test
+    fun testContextClassloader() {
+        val loadedSdk = sdkLoader.loadSdk(testSdkConfig)
+
+        val classLoader = loadedSdk.extractSdkProviderClassloader()
+        val sdkContext = loadedSdk.extractSdkContext()
+
+        assertThat(sdkContext.classLoader)
+            .isSameInstanceAs(classLoader)
+    }
+
+    @Test
+    fun testJavaResources() {
+        val loadedSdk = sdkLoader.loadSdk(testSdkConfig)
+
+        val classLoader = loadedSdk.extractSdkProviderClassloader()
+        val content = classLoader.getResourceAsStream("test.txt").use { inputStream ->
+            inputStream.bufferedReader().readLine()
+        }
+
+        assertThat(content)
+            .isEqualTo("test")
+    }
+
+    @Test
+    fun testRPackageUpdate() {
+        val loadedSdk = sdkLoader.loadSdk(testSdkConfig)
+
+        val classLoader = loadedSdk.extractSdkProviderClassloader()
+
+        val rPackageClass =
+            classLoader.loadClass("androidx.privacysandbox.sdkruntime.test.RPackage")
+
+        val packageIdField = rPackageClass.getDeclaredField("packageId")
+        val value = packageIdField.get(null)
+
+        assertThat(value).isEqualTo(42)
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.O)
+    fun testLowSpace_failPreApi27() {
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val sdkLoaderWithLowSpaceMode = SdkLoader.create(
+            context = context,
+            controller = NoOpImpl(),
+            lowSpaceThreshold = Long.MAX_VALUE
+        )
+
+        assertThrows(LoadSdkCompatException::class.java) {
+            sdkLoaderWithLowSpaceMode.loadSdk(testSdkConfig)
+        }.hasMessageThat().isEqualTo("Can't use InMemoryDexClassLoader")
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O_MR1)
+    fun testLowSpace_notFailApi27() {
+        val sdkLoaderWithLowSpaceMode = SdkLoader.create(
+            context = ApplicationProvider.getApplicationContext(),
+            controller = NoOpImpl(),
+            lowSpaceThreshold = Long.MAX_VALUE
+        )
+
+        val loadedSdk = sdkLoaderWithLowSpaceMode.loadSdk(testSdkConfig)
+        val classLoader = loadedSdk.extractSdkProviderClassloader()
+
+        val entryPointClass = classLoader.loadClass(testSdkConfig.entryPoint)
+        assertThat(entryPointClass).isNotNull()
+    }
+
+    private class NoOpImpl : SdkSandboxControllerCompat.SandboxControllerImpl {
+        override fun getSandboxedSdks(): List<SandboxedSdkCompat> {
+            throw UnsupportedOperationException("NoOp")
+        }
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/storage/CachedLocalSdkStorageTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/storage/CachedLocalSdkStorageTest.kt
new file mode 100644
index 0000000..428c85f
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/storage/CachedLocalSdkStorageTest.kt
@@ -0,0 +1,192 @@
+/*
+ * 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.client.loader.storage
+
+import android.content.Context
+import android.os.Environment
+import android.os.StatFs
+import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.testutils.assertThrows
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import java.io.FileNotFoundException
+import java.io.InputStream
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CachedLocalSdkStorageTest {
+
+    private lateinit var context: Context
+
+    private lateinit var storageUnderTest: CachedLocalSdkStorage
+
+    private lateinit var testSdkConfig: LocalSdkConfig
+
+    private lateinit var sdkFolder: File
+
+    @Before
+    fun setUp() {
+        context = ApplicationProvider.getApplicationContext()
+
+        storageUnderTest = CachedLocalSdkStorage.create(
+            context,
+            lowSpaceThreshold = disabledLowSpaceModeThreshold()
+        )
+
+        testSdkConfig = LocalSdkConfig(
+            packageName = "androidx.privacysandbox.sdkruntime.test.v1",
+            dexPaths = listOf(
+                "RuntimeEnabledSdks/V1/classes.dex",
+                "RuntimeEnabledSdks/RPackage.dex"
+            ),
+            entryPoint = "androidx.privacysandbox.sdkruntime.test.v1.CompatProvider",
+        )
+
+        sdkFolder = LocalSdkFolderProvider
+            .create(context)
+            .dexFolderFor(testSdkConfig)
+
+        // Clean up between tests
+        sdkFolder.deleteRecursively()
+        sdkFolder.deleteOnExit()
+    }
+
+    @Test
+    fun dexFilesFor_extractSdkDexFilesAndMakeThemReadOnly() {
+        val dexFiles = storageUnderTest.dexFilesFor(testSdkConfig)
+        assertThat(dexFiles).isNotNull()
+        val dexList = dexFiles!!.files
+
+        assertThat(dexList.size).isEqualTo(testSdkConfig.dexPaths.size)
+        for (index in testSdkConfig.dexPaths.indices) {
+            assertAssetExtractedToReadOnlyFile(testSdkConfig.dexPaths[index], dexList[index])
+        }
+    }
+
+    @Test
+    fun dexFilesFor_whenAlreadyExtracted_returnExistingFilesWithoutModification() {
+        val firstResult = storageUnderTest.dexFilesFor(testSdkConfig)!!.files
+        val lastModifiedBefore = firstResult[0].lastModified()
+
+        // Wait some time to check that no new files will be created (same lastModified)
+        Thread.sleep(100)
+
+        val secondResult = storageUnderTest.dexFilesFor(testSdkConfig)!!.files
+        val lastModifiedAfter = secondResult[0].lastModified()
+
+        assertThat(secondResult).isEqualTo(firstResult)
+        assertThat(lastModifiedAfter).isEqualTo(lastModifiedBefore)
+    }
+
+    @Test
+    fun dexFilesFor_whenFileMissing_extractOnlyThisFile() {
+        val firstResult = storageUnderTest.dexFilesFor(testSdkConfig)!!.files
+        val fileToDelete = firstResult[0]
+        val fileToKeep = firstResult[1]
+
+        fileToDelete.delete()
+        val fileToKeepLastModified = fileToKeep.lastModified()
+
+        // Wait some time to check that existing file will not be modified (same lastModified)
+        Thread.sleep(100)
+
+        val secondResult = storageUnderTest.dexFilesFor(testSdkConfig)!!.files
+        val extractedFile = secondResult[0]
+        val notModifiedFile = secondResult[1]
+
+        assertThat(secondResult).isEqualTo(firstResult)
+        assertAssetExtractedToReadOnlyFile(testSdkConfig.dexPaths[0], extractedFile)
+        assertThat(notModifiedFile.lastModified()).isEqualTo(fileToKeepLastModified)
+    }
+
+    @Test
+    fun dexFilesFor_whenLowSpaceAndNoExtractedFiles_returnNull() {
+        val storageWithLowSpaceEnabled = CachedLocalSdkStorage.create(
+            context,
+            lowSpaceThreshold = enabledLowSpaceModeThreshold()
+        )
+        val result = storageWithLowSpaceEnabled.dexFilesFor(testSdkConfig)
+        assertThat(result).isNull()
+    }
+
+    @Test
+    fun dexFilesFor_whenLowSpaceAndFilesExtractedBefore_returnExistingFiles() {
+        val extractedFiles = storageUnderTest.dexFilesFor(testSdkConfig)
+        assertThat(extractedFiles).isNotNull()
+
+        val storageWithLowSpaceEnabled = CachedLocalSdkStorage.create(
+            context,
+            lowSpaceThreshold = enabledLowSpaceModeThreshold()
+        )
+        val result = storageWithLowSpaceEnabled.dexFilesFor(testSdkConfig)
+        assertThat(result).isEqualTo(extractedFiles)
+    }
+
+    @Test
+    fun dexFilesFor_whenFailedToExtract_deleteFolderAndThrowException() {
+        val invalidConfig = LocalSdkConfig(
+            packageName = "storage.test.invalid.dexPath",
+            dexPaths = listOf("NOT_EXISTS"),
+            entryPoint = "EntryPoint"
+        )
+
+        val rootFolder = LocalSdkFolderProvider
+            .create(context)
+            .dexFolderFor(invalidConfig)
+
+        val fileToDelete = File(rootFolder, "toDelete")
+        fileToDelete.createNewFile()
+        assertThat(fileToDelete.exists()).isTrue()
+
+        assertThrows<FileNotFoundException> {
+            storageUnderTest.dexFilesFor(invalidConfig)
+        }.hasMessageThat().contains("NOT_EXISTS")
+
+        assertThat(fileToDelete.exists()).isFalse()
+    }
+
+    private fun assertAssetExtractedToReadOnlyFile(assetPath: String, outputFile: File) {
+        assertThat(outputFile.exists()).isTrue()
+        assertThat(outputFile.parentFile).isEqualTo(sdkFolder)
+        assertThat(outputFile.canWrite()).isFalse()
+
+        val assetContent = context.assets.open(assetPath).use(InputStream::readBytes)
+        val fileContent = outputFile.inputStream().use(InputStream::readBytes)
+        assertThat(fileContent).isEqualTo(assetContent)
+    }
+
+    private fun enabledLowSpaceModeThreshold(): Long =
+        availableBytes() + 10_000_000
+
+    private fun disabledLowSpaceModeThreshold(): Long =
+        availableBytes() - 10_000_000
+
+    @Suppress("DEPRECATION")
+    private fun availableBytes(): Long {
+        val path = Environment.getDataDirectory()
+        val stat = StatFs(path.path)
+        val blockSize = stat.blockSize.toLong()
+        val availableBlocks = stat.availableBlocks.toLong()
+        return availableBlocks * blockSize
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..8e06bf7
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkFolderProviderTest.kt
@@ -0,0 +1,186 @@
+/*
+ * 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.client.loader.storage
+
+import android.content.Context
+import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
+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
+import java.io.DataInputStream
+import java.io.DataOutputStream
+import java.io.File
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LocalSdkFolderProviderTest {
+
+    private lateinit var context: Context
+
+    private lateinit var sdkRootFolder: File
+    private lateinit var versionFile: File
+
+    private var appLastUpdateTime = 0L
+
+    @Before
+    fun setUp() {
+        context = ApplicationProvider.getApplicationContext()
+
+        sdkRootFolder = File(context.cacheDir, "RuntimeEnabledSdk")
+        versionFile = File(sdkRootFolder, "Folder.version")
+
+        @Suppress("DEPRECATION")
+        appLastUpdateTime =
+            context.packageManager.getPackageInfo(context.packageName, 0).lastUpdateTime
+
+        sdkRootFolder.deleteRecursively()
+    }
+
+    @Test
+    fun create_whenNoSdkRootFolder_createSdkRootFolderAndVersionFile() {
+        LocalSdkFolderProvider.create(context)
+
+        assertThat(sdkRootFolder.exists()).isTrue()
+        val version = readVersionFromFile()
+        assertThat(version).isEqualTo(appLastUpdateTime)
+    }
+
+    @Test
+    fun create_whenVersionNotChanged_doNotRemoveContents() {
+        // Initial create
+        LocalSdkFolderProvider.create(context)
+        val fileToKeep = File(sdkRootFolder, "file")
+        fileToKeep.createNewFile()
+
+        LocalSdkFolderProvider.create(context)
+
+        assertThat(fileToKeep.exists()).isTrue()
+    }
+
+    @Test
+    fun create_whenVersionChanged_deleteSdkRootFolderContentAndCreateVersionFile() {
+        val fileToDelete = createFileToDeleteInSdkRootFolder()
+        createVersionFile {
+            it.writeLong(42)
+        }
+
+        LocalSdkFolderProvider.create(context)
+
+        assertThat(fileToDelete.exists()).isFalse()
+        val version = readVersionFromFile()
+        assertThat(version).isEqualTo(appLastUpdateTime)
+    }
+
+    @Test
+    fun create_whenNoVersionFile_deleteSdkRootFolderContentAndCreateVersionFile() {
+        val fileToDelete = createFileToDeleteInSdkRootFolder()
+
+        LocalSdkFolderProvider.create(context)
+
+        assertThat(sdkRootFolder.exists()).isTrue()
+        assertThat(fileToDelete.exists()).isFalse()
+
+        val version = readVersionFromFile()
+        assertThat(version).isEqualTo(appLastUpdateTime)
+    }
+
+    @Test
+    fun create_whenInvalidVersionFile_deleteSdkRootFolderContentAndCreateVersionFile() {
+        val fileToDelete = createFileToDeleteInSdkRootFolder()
+        createVersionFile {
+            // Version is long type, byte is not enough
+            it.writeByte(1)
+        }
+
+        LocalSdkFolderProvider.create(context)
+
+        assertThat(fileToDelete.exists()).isFalse()
+        val version = readVersionFromFile()
+        assertThat(version).isEqualTo(appLastUpdateTime)
+    }
+
+    @Test
+    fun dexFolderFor_returnPathToSdkDexFolder() {
+        val sdkFolderProvider = LocalSdkFolderProvider.create(context)
+        val dexFolder = sdkFolderProvider.dexFolderFor(
+            LocalSdkConfig(
+                packageName = "com.test.sdk.package",
+                dexPaths = listOf("1.dex", "2.dex"),
+                entryPoint = "compat.sdk.provider",
+            )
+        )
+        assertThat(dexFolder.exists()).isTrue()
+        assertThat(dexFolder).isEqualTo(
+            File(sdkRootFolder, "com.test.sdk.package")
+        )
+    }
+
+    @Test
+    fun dexFolderFor_doNotRemoveExistingFiles() {
+        val sdkFolderProvider = LocalSdkFolderProvider.create(context)
+
+        val sdkConfig = LocalSdkConfig(
+            packageName = "com.test.sdk.package",
+            dexPaths = listOf("1.dex", "2.dex"),
+            entryPoint = "compat.sdk.provider",
+        )
+
+        val dexFolder = sdkFolderProvider.dexFolderFor(sdkConfig)
+
+        val fileToKeep = File(dexFolder, "testFile")
+        fileToKeep.createNewFile()
+
+        val dexFolder2 = sdkFolderProvider.dexFolderFor(sdkConfig)
+        assertThat(dexFolder).isEqualTo(dexFolder2)
+
+        assertThat(fileToKeep.exists()).isTrue()
+    }
+
+    private fun createFileToDeleteInSdkRootFolder(): File {
+        val folder = File(sdkRootFolder, "folder")
+        folder.mkdirs()
+        val file = File(folder, "file")
+        file.createNewFile()
+        file.setReadOnly()
+        assertThat(file.exists()).isTrue()
+        return file
+    }
+
+    private fun readVersionFromFile(): Long {
+        return versionFile.inputStream().use { inputStream ->
+            DataInputStream(inputStream).use { dataStream ->
+                dataStream.readLong()
+            }
+        }
+    }
+
+    private fun createVersionFile(versionWriter: (DataOutputStream) -> Unit) {
+        if (!sdkRootFolder.exists()) {
+            sdkRootFolder.mkdirs()
+        }
+        versionFile.createNewFile()
+        versionFile.outputStream().use { outputStream ->
+            DataOutputStream(outputStream).use { dataStream ->
+                versionWriter(dataStream)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/storage/TestLocalSdkStorage.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/storage/TestLocalSdkStorage.kt
new file mode 100644
index 0000000..22cf607
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/storage/TestLocalSdkStorage.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.client.loader.storage
+
+import android.content.Context
+import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
+import java.io.File
+
+/**
+ * Extract SDK DEX files in [rootFolder] / <packageName>.
+ */
+internal class TestLocalSdkStorage(
+    private val context: Context,
+    private val rootFolder: File
+) : LocalSdkStorage {
+    override fun dexFilesFor(sdkConfig: LocalSdkConfig): LocalSdkDexFiles {
+        val outputFolder = File(rootFolder, sdkConfig.packageName)
+        outputFolder.deleteRecursively()
+        outputFolder.mkdirs()
+
+        val fileList = buildList {
+            for (index in sdkConfig.dexPaths.indices) {
+                val dexFile = File(outputFolder, "$index.dex")
+                dexFile.createNewFile()
+                context.assets.open(sdkConfig.dexPaths[index]).use { inputStream ->
+                    dexFile.outputStream().use { outputStream ->
+                        inputStream.copyTo(outputStream)
+                    }
+                }
+                dexFile.setReadOnly()
+                add(dexFile)
+            }
+        }
+
+        return LocalSdkDexFiles(fileList)
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompat.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompat.kt
new file mode 100644
index 0000000..049e853
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxManagerCompat.kt
@@ -0,0 +1,400 @@
+/*
+ * 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.client
+
+import android.annotation.SuppressLint
+import android.app.sdksandbox.LoadSdkException
+import android.app.sdksandbox.SandboxedSdk
+import android.app.sdksandbox.SdkSandboxManager
+import android.content.Context
+import android.os.Bundle
+import android.os.ext.SdkExtensions.AD_SERVICES
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresApi
+import androidx.annotation.RequiresExtension
+import androidx.core.os.asOutcomeReceiver
+import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfigsHolder
+import androidx.privacysandbox.sdkruntime.client.controller.LocalController
+import androidx.privacysandbox.sdkruntime.client.controller.LocallyLoadedSdks
+import androidx.privacysandbox.sdkruntime.client.loader.SdkLoader
+import androidx.privacysandbox.sdkruntime.core.AdServicesInfo
+import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
+import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException.Companion.LOAD_SDK_ALREADY_LOADED
+import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException.Companion.LOAD_SDK_NOT_FOUND
+import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException.Companion.toLoadCompatSdkException
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import java.util.WeakHashMap
+import java.util.concurrent.Executor
+import kotlinx.coroutines.suspendCancellableCoroutine
+import org.jetbrains.annotations.TestOnly
+
+/**
+ * Compat version of [SdkSandboxManager].
+ *
+ * Provides APIs to load [androidx.privacysandbox.sdkruntime.core.SandboxedSdkProviderCompat]
+ * into SDK sandbox process or locally, and then interact with them.
+ *
+ * SdkSandbox process is a java process running in a separate uid range. Each app has its own
+ * SDK sandbox process.
+ *
+ * First app needs to declare SDKs it depends on in it's AndroidManifest.xml
+ * using <uses-sdk-library> tag. App can only load SDKs it depends on into the
+ * SDK sandbox process.
+ *
+ * For loading SDKs locally App need to bundle and declare local SDKs in
+ * assets/RuntimeEnabledSdkTable.xml with following format:
+ *
+ * <runtime-enabled-sdk-table>
+ *     <runtime-enabled-sdk>
+ *         <package-name>com.sdk1</package-name>
+ *         <compat-config-path>assets/RuntimeEnabledSdk-com.sdk1/CompatSdkConfig.xml</compat-config-path>
+ *     </runtime-enabled-sdk>
+ *     <runtime-enabled-sdk>
+ *         <package-name>com.sdk2</package-name>
+ *         <compat-config-path>assets/RuntimeEnabledSdk-com.sdk2/CompatSdkConfig.xml</compat-config-path>
+ *     </runtime-enabled-sdk>
+ * </runtime-enabled-sdk-table>
+ *
+ * Each local SDK should have config with following format:
+ *
+ * <compat-config>
+ *     <dex-path>RuntimeEnabledSdk-sdk.package.name/dex/classes.dex</dex-path>
+ *     <dex-path>RuntimeEnabledSdk-sdk.package.name/dex/classes2.dex</dex-path>
+ *     <java-resources-root-path>RuntimeEnabledSdk-sdk.package.name/res</java-resources-root-path>
+ *     <compat-entrypoint>com.sdk.EntryPointClass</compat-entrypoint>
+ *     <resource-id-remapping>
+ *         <r-package-class>com.test.sdk.RPackage</r-package-class>
+ *         <resources-package-id>123</resources-package-id>
+ *     </resource-id-remapping>
+ * </compat-config>
+ *
+ * @see [SdkSandboxManager]
+ */
+class SdkSandboxManagerCompat private constructor(
+    private val platformApi: PlatformApi,
+    private val configHolder: LocalSdkConfigsHolder,
+    private val localLocallyLoadedSdks: LocallyLoadedSdks,
+    private val sdkLoader: SdkLoader
+) {
+    /**
+     * Load SDK in a SDK sandbox java process or locally.
+     *
+     * App should already declare SDKs it depends on in its AndroidManifest using
+     * <use-sdk-library> tag. App can only load SDKs it depends on into the SDK Sandbox process.
+     *
+     * When client application loads the first SDK, a new SdkSandbox process will be
+     * created, otherwise other SDKs will be loaded into the same sandbox which already created for
+     * the client application.
+     *
+     * Alternatively App could bundle and declare local SDKs dependencies in
+     * assets/RuntimeEnabledSdkTable.xml to load SDKs locally.
+     *
+     * This API may only be called while the caller is running in the foreground. Calls from the
+     * background will result in a [LoadSdkCompatException] being thrown.
+     *
+     * @param sdkName name of the SDK to be loaded.
+     * @param params additional parameters to be passed to the SDK in the form of a [Bundle]
+     *  as agreed between the client and the SDK.
+     * @return [SandboxedSdkCompat] from SDK on a successful run.
+     * @throws [LoadSdkCompatException] on fail.
+     *
+     * @see [SdkSandboxManager.loadSdk]
+     */
+    @Throws(LoadSdkCompatException::class)
+    suspend fun loadSdk(
+        sdkName: String,
+        params: Bundle
+    ): SandboxedSdkCompat {
+        if (localLocallyLoadedSdks.isLoaded(sdkName)) {
+            throw LoadSdkCompatException(LOAD_SDK_ALREADY_LOADED, "$sdkName already loaded")
+        }
+
+        val sdkConfig = configHolder.getSdkConfig(sdkName)
+        if (sdkConfig != null) {
+            val sdkProvider = sdkLoader.loadSdk(sdkConfig)
+            val sandboxedSdkCompat = sdkProvider.onLoadSdk(params)
+            localLocallyLoadedSdks.put(
+                sdkName, LocallyLoadedSdks.Entry(
+                    sdkProvider = sdkProvider,
+                    sdk = sandboxedSdkCompat
+                )
+            )
+            return sandboxedSdkCompat
+        }
+
+        return platformApi.loadSdk(sdkName, params)
+    }
+
+    /**
+     * Unloads an SDK that has been previously loaded by the caller.
+     *
+     * It is not guaranteed that the memory allocated for this SDK will be freed immediately.
+     *
+     * @param sdkName name of the SDK to be unloaded.
+     *
+     * @see [SdkSandboxManager.unloadSdk]
+     */
+    fun unloadSdk(sdkName: String) {
+        val localEntry = localLocallyLoadedSdks.remove(sdkName)
+        if (localEntry == null) {
+            platformApi.unloadSdk(sdkName)
+        } else {
+            localEntry.sdkProvider.beforeUnloadSdk()
+        }
+    }
+
+    /**
+     * Adds a callback which gets registered for SDK sandbox lifecycle events, such as SDK sandbox
+     * death. If the sandbox has not yet been created when this is called, the request will be
+     * stored until a sandbox is created, at which point it is activated for that sandbox. Multiple
+     * callbacks can be added to detect death.
+     *
+     * @param callbackExecutor the [Executor] on which to invoke the callback
+     * @param callback the [SdkSandboxProcessDeathCallbackCompat] which will receive SDK sandbox
+     *  lifecycle events.
+     *
+     * @see [SdkSandboxManager.addSdkSandboxProcessDeathCallback]
+     */
+    fun addSdkSandboxProcessDeathCallback(
+        callbackExecutor: Executor,
+        callback: SdkSandboxProcessDeathCallbackCompat
+    ) {
+        platformApi.addSdkSandboxProcessDeathCallback(callbackExecutor, callback)
+    }
+
+    /**
+     * Removes an [SdkSandboxProcessDeathCallbackCompat] that was previously added using
+     * [SdkSandboxManagerCompat.addSdkSandboxProcessDeathCallback]
+     *
+     * @param callback the [SdkSandboxProcessDeathCallbackCompat] which was previously added using
+     *  [SdkSandboxManagerCompat.addSdkSandboxProcessDeathCallback]
+     *
+     * @see [SdkSandboxManager.removeSdkSandboxProcessDeathCallback]
+     */
+    fun removeSdkSandboxProcessDeathCallback(
+        callback: SdkSandboxProcessDeathCallbackCompat
+    ) {
+        platformApi.removeSdkSandboxProcessDeathCallback(callback)
+    }
+
+    /**
+     * Fetches information about Sdks that are loaded in the sandbox or locally.
+     *
+     * @return List of [SandboxedSdkCompat] containing all currently loaded sdks
+     *
+     * @see [SdkSandboxManager.getSandboxedSdks]
+     */
+    fun getSandboxedSdks(): List<SandboxedSdkCompat> {
+        val platformResult = platformApi.getSandboxedSdks()
+        val localResult = localLocallyLoadedSdks.getLoadedSdks()
+        return platformResult + localResult
+    }
+
+    @TestOnly
+    internal fun getLocallyLoadedSdk(sdkName: String): LocallyLoadedSdks.Entry? =
+        localLocallyLoadedSdks.get(sdkName)
+
+    private interface PlatformApi {
+        @DoNotInline
+        suspend fun loadSdk(sdkName: String, params: Bundle): SandboxedSdkCompat
+
+        @DoNotInline
+        fun unloadSdk(sdkName: String)
+
+        @DoNotInline
+        fun addSdkSandboxProcessDeathCallback(
+            callbackExecutor: Executor,
+            callback: SdkSandboxProcessDeathCallbackCompat
+        )
+
+        @DoNotInline
+        fun removeSdkSandboxProcessDeathCallback(
+            callback: SdkSandboxProcessDeathCallbackCompat
+        )
+
+        @DoNotInline
+        fun getSandboxedSdks(): List<SandboxedSdkCompat> = emptyList()
+    }
+
+    @RequiresApi(33)
+    @RequiresExtension(extension = AD_SERVICES, version = 4)
+    private open class ApiAdServicesV4Impl(context: Context) : PlatformApi {
+        protected val sdkSandboxManager = context.getSystemService(
+            SdkSandboxManager::class.java
+        )
+
+        private val sandboxDeathCallbackDelegates:
+            MutableList<SdkSandboxProcessDeathCallbackDelegate> = mutableListOf()
+
+        @DoNotInline
+        override suspend fun loadSdk(
+            sdkName: String,
+            params: Bundle
+        ): SandboxedSdkCompat {
+            try {
+                val sandboxedSdk = loadSdkInternal(sdkName, params)
+                return SandboxedSdkCompat(sandboxedSdk)
+            } catch (ex: LoadSdkException) {
+                throw toLoadCompatSdkException(ex)
+            }
+        }
+
+        override fun unloadSdk(sdkName: String) {
+            sdkSandboxManager.unloadSdk(sdkName)
+        }
+
+        @DoNotInline
+        override fun addSdkSandboxProcessDeathCallback(
+            callbackExecutor: Executor,
+            callback: SdkSandboxProcessDeathCallbackCompat
+        ) {
+            synchronized(sandboxDeathCallbackDelegates) {
+                val delegate = SdkSandboxProcessDeathCallbackDelegate(callback)
+                sdkSandboxManager.addSdkSandboxProcessDeathCallback(callbackExecutor, delegate)
+                sandboxDeathCallbackDelegates.add(delegate)
+            }
+        }
+
+        @DoNotInline
+        override fun removeSdkSandboxProcessDeathCallback(
+            callback: SdkSandboxProcessDeathCallbackCompat
+        ) {
+            synchronized(sandboxDeathCallbackDelegates) {
+                for (i in sandboxDeathCallbackDelegates.lastIndex downTo 0) {
+                    val delegate = sandboxDeathCallbackDelegates[i]
+                    if (delegate.callback == callback) {
+                        sdkSandboxManager.removeSdkSandboxProcessDeathCallback(delegate)
+                        sandboxDeathCallbackDelegates.removeAt(i)
+                    }
+                }
+            }
+        }
+
+        private suspend fun loadSdkInternal(
+            sdkName: String,
+            params: Bundle
+        ): SandboxedSdk {
+            return suspendCancellableCoroutine { continuation ->
+                sdkSandboxManager.loadSdk(
+                    sdkName,
+                    params,
+                    Runnable::run,
+                    continuation.asOutcomeReceiver()
+                )
+            }
+        }
+
+        private class SdkSandboxProcessDeathCallbackDelegate(
+            val callback: SdkSandboxProcessDeathCallbackCompat
+        ) : SdkSandboxManager.SdkSandboxProcessDeathCallback {
+            @SuppressLint("Override") // b/273473397
+            override fun onSdkSandboxDied() {
+                callback.onSdkSandboxDied()
+            }
+        }
+    }
+
+    @RequiresApi(33)
+    @RequiresExtension(extension = AD_SERVICES, version = 5)
+    private class ApiAdServicesV5Impl(
+        context: Context
+    ) : ApiAdServicesV4Impl(context) {
+        @DoNotInline
+        override fun getSandboxedSdks(): List<SandboxedSdkCompat> {
+            return sdkSandboxManager
+                .sandboxedSdks
+                .map { platformSdk -> SandboxedSdkCompat(platformSdk) }
+        }
+    }
+
+    private class FailImpl : PlatformApi {
+        @DoNotInline
+        override suspend fun loadSdk(
+            sdkName: String,
+            params: Bundle
+        ): SandboxedSdkCompat {
+            throw LoadSdkCompatException(LOAD_SDK_NOT_FOUND, "$sdkName not bundled with app")
+        }
+
+        override fun unloadSdk(sdkName: String) {
+        }
+
+        override fun addSdkSandboxProcessDeathCallback(
+            callbackExecutor: Executor,
+            callback: SdkSandboxProcessDeathCallbackCompat
+        ) {
+        }
+
+        override fun removeSdkSandboxProcessDeathCallback(
+            callback: SdkSandboxProcessDeathCallbackCompat
+        ) {
+        }
+    }
+
+    companion object {
+
+        private val sInstances = WeakHashMap<Context, SdkSandboxManagerCompat>()
+
+        /**
+         *  Creates [SdkSandboxManagerCompat].
+         *
+         *  @param context Application context
+         *
+         *  @return SdkSandboxManagerCompat object.
+         */
+        @JvmStatic
+        fun from(context: Context): SdkSandboxManagerCompat {
+            synchronized(sInstances) {
+                var instance = sInstances[context]
+                if (instance == null) {
+                    val configHolder = LocalSdkConfigsHolder.load(context)
+                    val localSdks = LocallyLoadedSdks()
+                    val controller = LocalController(localSdks)
+                    val sdkLoader = SdkLoader.create(context, controller)
+                    val platformApi = PlatformApiFactory.create(context)
+                    instance = SdkSandboxManagerCompat(
+                        platformApi,
+                        configHolder,
+                        localSdks,
+                        sdkLoader
+                    )
+                    sInstances[context] = instance
+                }
+                return instance
+            }
+        }
+
+        @TestOnly
+        internal fun reset() {
+            synchronized(sInstances) {
+                sInstances.clear()
+            }
+        }
+    }
+
+    private object PlatformApiFactory {
+        @SuppressLint("NewApi", "ClassVerificationFailure")
+        fun create(context: Context): PlatformApi {
+            return if (AdServicesInfo.isAtLeastV5()) {
+                ApiAdServicesV5Impl(context)
+            } else if (AdServicesInfo.isAtLeastV4()) {
+                ApiAdServicesV4Impl(context)
+            } else {
+                FailImpl()
+            }
+        }
+    }
+}
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxProcessDeathCallbackCompat.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxProcessDeathCallbackCompat.kt
new file mode 100644
index 0000000..32c5b15
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/SdkSandboxProcessDeathCallbackCompat.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.client
+
+/**
+ * A callback for tracking events SDK sandbox death.
+ *
+ * The callback can be added using [SdkSandboxManagerCompat.addSdkSandboxProcessDeathCallback]
+ * and removed using [SdkSandboxManagerCompat.removeSdkSandboxProcessDeathCallback]
+ *
+ * @see [android.app.sdksandbox.SdkSandboxManager.SdkSandboxProcessDeathCallback]
+ */
+interface SdkSandboxProcessDeathCallbackCompat {
+    /**
+     * Notifies the client application that the SDK sandbox has died. The sandbox could die for
+     * various reasons, for example, due to memory pressure on the system, or a crash in the
+     * sandbox.
+     *
+     * The system will automatically restart the sandbox process if it died due to a crash.
+     * However, the state of the sandbox will be lost - so any SDKs that were loaded previously
+     * would have to be loaded again, using [SdkSandboxManagerCompat.loadSdk] to continue using them.
+     *
+     * @see [android.app.sdksandbox.SdkSandboxManager.SdkSandboxProcessDeathCallback.onSdkSandboxDied]
+     */
+    fun onSdkSandboxDied()
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/androidx/privacysandbox/sdkruntime/androidx-privacysandbox-sdkruntime-sdkruntime-client-documentation.md b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/androidx-privacysandbox-sdkruntime-sdkruntime-client-documentation.md
similarity index 100%
rename from privacysandbox/sdkruntime/sdkruntime-client/src/main/androidx/privacysandbox/sdkruntime/androidx-privacysandbox-sdkruntime-sdkruntime-client-documentation.md
rename to privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/androidx-privacysandbox-sdkruntime-sdkruntime-client-documentation.md
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/config/LocalSdkConfig.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/config/LocalSdkConfig.kt
new file mode 100644
index 0000000..1276a7e
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/config/LocalSdkConfig.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.client.config
+
+/**
+ * Information required for loading SDK bundled with App.
+ *
+ */
+internal data class LocalSdkConfig(
+    val packageName: String,
+    val versionMajor: Int? = null,
+    val dexPaths: List<String>,
+    val entryPoint: String,
+    val javaResourcesRoot: String? = null,
+    val resourceRemapping: ResourceRemappingConfig? = null
+)
+
+internal data class ResourceRemappingConfig(
+    val rPackageClassName: String,
+    val packageId: Int
+)
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/config/LocalSdkConfigParser.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/config/LocalSdkConfigParser.kt
new file mode 100644
index 0000000..5558336
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/config/LocalSdkConfigParser.kt
@@ -0,0 +1,188 @@
+/*
+ * 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.client.config
+
+import android.util.Xml
+import java.io.InputStream
+import org.xmlpull.v1.XmlPullParser
+import org.xmlpull.v1.XmlPullParser.END_TAG
+import org.xmlpull.v1.XmlPullParser.START_TAG
+import org.xmlpull.v1.XmlPullParserException
+
+/**
+ * Parser for SDK config.
+ *
+ * The expected XML structure is:
+ * <compat-config>
+ *     <dex-path>RuntimeEnabledSdk-sdk.package.name/dex/classes.dex</dex-path>
+ *     <dex-path>RuntimeEnabledSdk-sdk.package.name/dex/classes2.dex</dex-path>
+ *     <java-resources-root-path>RuntimeEnabledSdk-sdk.package.name/res</java-resources-root-path>
+ *     <compat-entrypoint>com.sdk.EntryPointClass</compat-entrypoint>
+ *     <resource-id-remapping>
+ *         <r-package-class>com.test.sdk.RPackage</r-package-class>
+ *         <resources-package-id>123</resources-package-id>
+ *     </resource-id-remapping>
+ * </compat-config>
+ */
+internal class LocalSdkConfigParser private constructor(
+    private val xmlParser: XmlPullParser
+) {
+
+    private fun readConfig(
+        packageName: String,
+        versionMajor: Int?
+    ): LocalSdkConfig {
+        xmlParser.require(XmlPullParser.START_DOCUMENT, NAMESPACE, null)
+        xmlParser.nextTag()
+
+        val dexPaths = mutableListOf<String>()
+        var javaResourcesRoot: String? = null
+        var entryPoint: String? = null
+        var resourceRemapping: ResourceRemappingConfig? = null
+
+        xmlParser.require(START_TAG, NAMESPACE, CONFIG_ELEMENT_NAME)
+        while (xmlParser.next() != END_TAG) {
+            if (xmlParser.eventType != START_TAG) {
+                continue
+            }
+            when (xmlParser.name) {
+                DEX_PATH_ELEMENT_NAME -> {
+                    val dexPath = xmlParser.nextText()
+                    dexPaths.add(dexPath)
+                }
+
+                RESOURCE_ROOT_ELEMENT_NAME -> {
+                    if (javaResourcesRoot != null) {
+                        throw XmlPullParserException(
+                            "Duplicate $RESOURCE_ROOT_ELEMENT_NAME tag found"
+                        )
+                    }
+                    javaResourcesRoot = xmlParser.nextText()
+                }
+
+                ENTRYPOINT_ELEMENT_NAME -> {
+                    if (entryPoint != null) {
+                        throw XmlPullParserException(
+                            "Duplicate $ENTRYPOINT_ELEMENT_NAME tag found"
+                        )
+                    }
+                    entryPoint = xmlParser.nextText()
+                }
+
+                RESOURCE_REMAPPING_ENTRY_ELEMENT_NAME -> {
+                    if (resourceRemapping != null) {
+                        throw XmlPullParserException(
+                            "Duplicate $RESOURCE_REMAPPING_ENTRY_ELEMENT_NAME tag found"
+                        )
+                    }
+                    resourceRemapping = readResourceRemappingConfig()
+                }
+
+                else -> xmlParser.skipCurrentTag()
+            }
+        }
+        xmlParser.require(END_TAG, NAMESPACE, CONFIG_ELEMENT_NAME)
+
+        if (entryPoint == null) {
+            throw XmlPullParserException("No $ENTRYPOINT_ELEMENT_NAME tag found")
+        }
+        if (dexPaths.isEmpty()) {
+            throw XmlPullParserException("No $DEX_PATH_ELEMENT_NAME tags found")
+        }
+
+        return LocalSdkConfig(
+            packageName,
+            versionMajor,
+            dexPaths,
+            entryPoint,
+            javaResourcesRoot,
+            resourceRemapping
+        )
+    }
+
+    private fun readResourceRemappingConfig(): ResourceRemappingConfig {
+        var rPackageClassName: String? = null
+        var packageId: Int? = null
+
+        xmlParser.require(START_TAG, NAMESPACE, RESOURCE_REMAPPING_ENTRY_ELEMENT_NAME)
+        while (xmlParser.next() != END_TAG) {
+            if (xmlParser.eventType != START_TAG) {
+                continue
+            }
+            when (xmlParser.name) {
+                RESOURCE_REMAPPING_CLASS_ELEMENT_NAME -> {
+                    if (rPackageClassName != null) {
+                        throw XmlPullParserException(
+                            "Duplicate $RESOURCE_REMAPPING_CLASS_ELEMENT_NAME tag found"
+                        )
+                    }
+                    rPackageClassName = xmlParser.nextText()
+                }
+
+                RESOURCE_REMAPPING_ID_ELEMENT_NAME -> {
+                    if (packageId != null) {
+                        throw XmlPullParserException(
+                            "Duplicate $RESOURCE_REMAPPING_ID_ELEMENT_NAME tag found"
+                        )
+                    }
+                    packageId = xmlParser.nextText().toInt()
+                }
+
+                else -> xmlParser.skipCurrentTag()
+            }
+        }
+        xmlParser.require(END_TAG, NAMESPACE, RESOURCE_REMAPPING_ENTRY_ELEMENT_NAME)
+
+        if (rPackageClassName == null) {
+            throw XmlPullParserException(
+                "No $RESOURCE_REMAPPING_CLASS_ELEMENT_NAME tag found"
+            )
+        }
+        if (packageId == null) {
+            throw XmlPullParserException(
+                "No $RESOURCE_REMAPPING_ID_ELEMENT_NAME tag found"
+            )
+        }
+
+        return ResourceRemappingConfig(rPackageClassName, packageId)
+    }
+
+    companion object {
+        private val NAMESPACE: String? = null // We don't use namespaces
+        private const val CONFIG_ELEMENT_NAME = "compat-config"
+        private const val DEX_PATH_ELEMENT_NAME = "dex-path"
+        private const val RESOURCE_ROOT_ELEMENT_NAME = "java-resources-root-path"
+        private const val ENTRYPOINT_ELEMENT_NAME = "compat-entrypoint"
+        private const val RESOURCE_REMAPPING_ENTRY_ELEMENT_NAME = "resource-id-remapping"
+        private const val RESOURCE_REMAPPING_CLASS_ELEMENT_NAME = "r-package-class"
+        private const val RESOURCE_REMAPPING_ID_ELEMENT_NAME = "resources-package-id"
+
+        fun parse(
+            inputStream: InputStream,
+            packageName: String,
+            versionMajor: Int?
+        ): LocalSdkConfig {
+            val parser = Xml.newPullParser()
+            try {
+                parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
+                parser.setInput(inputStream, null)
+                return LocalSdkConfigParser(parser).readConfig(packageName, versionMajor)
+            } finally {
+                parser.setInput(null)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/config/LocalSdkConfigsHolder.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/config/LocalSdkConfigsHolder.kt
new file mode 100644
index 0000000..0db3714e
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/config/LocalSdkConfigsHolder.kt
@@ -0,0 +1,74 @@
+/*
+ * 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.client.config
+
+import android.content.Context
+import androidx.annotation.RestrictTo
+import java.io.FileNotFoundException
+
+/**
+ * Holds information about all SDKs bundled with App.
+ *
+ * @suppress
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+internal class LocalSdkConfigsHolder private constructor(
+    private val configs: Map<String, LocalSdkConfig>
+) {
+
+    fun getSdkConfig(sdkName: String): LocalSdkConfig? {
+        return configs[sdkName]
+    }
+
+    companion object {
+        private const val SDK_TABLE_ASSET_NAME = "RuntimeEnabledSdkTable.xml"
+
+        fun load(
+            context: Context,
+            sdkTableAssetName: String = SDK_TABLE_ASSET_NAME
+        ): LocalSdkConfigsHolder {
+            val sdkTable = loadSdkTable(context, sdkTableAssetName)
+
+            val data = buildMap {
+                for ((packageName, versionMajor, configPath) in sdkTable) {
+                    context.assets.open(configPath).use { sdkConfigAsset ->
+                        val sdkInfo = LocalSdkConfigParser.parse(
+                            sdkConfigAsset,
+                            packageName,
+                            versionMajor
+                        )
+                        put(packageName, sdkInfo)
+                    }
+                }
+            }
+
+            return LocalSdkConfigsHolder(data)
+        }
+
+        private fun loadSdkTable(
+            context: Context,
+            sdkTableAssetName: String
+        ): Set<SdkTableConfigParser.SdkTableEntry> {
+            try {
+                context.assets.open(sdkTableAssetName).use { sdkTableAsset ->
+                    return SdkTableConfigParser.parse(sdkTableAsset)
+                }
+            } catch (ignored: FileNotFoundException) {
+                return emptySet()
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/config/SdkTableConfigParser.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/config/SdkTableConfigParser.kt
new file mode 100644
index 0000000..6bea778
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/config/SdkTableConfigParser.kt
@@ -0,0 +1,160 @@
+/*
+ * 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.client.config
+
+import android.util.Xml
+import androidx.annotation.RestrictTo
+import java.io.InputStream
+import org.xmlpull.v1.XmlPullParser
+import org.xmlpull.v1.XmlPullParser.END_TAG
+import org.xmlpull.v1.XmlPullParser.START_TAG
+import org.xmlpull.v1.XmlPullParserException
+
+/**
+ * Parser for config with paths to compat SDK configs for each SDK that bundled with app.
+ *
+ * The expected XML structure is:
+ * <runtime-enabled-sdk-table>
+ *     <runtime-enabled-sdk>
+ *         <package-name>com.sdk1</package-name>
+ *         <version-major>1</version-major>
+ *         <compat-config-path>assets/RuntimeEnabledSdk-com.sdk1/CompatSdkConfig.xml</compat-config-path>
+ *     </runtime-enabled-sdk>
+ *     <runtime-enabled-sdk>
+ *         <package-name>com.sdk2</package-name>
+ *         <version-major>42</version-major>
+ *         <compat-config-path>assets/RuntimeEnabledSdk-com.sdk2/CompatSdkConfig.xml</compat-config-path>
+ *     </runtime-enabled-sdk>
+ * </runtime-enabled-sdk-table>
+ *
+ * @suppress
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+internal class SdkTableConfigParser private constructor(
+    private val xmlParser: XmlPullParser
+) {
+
+    private fun readSdkTable(): Set<SdkTableEntry> {
+        xmlParser.require(XmlPullParser.START_DOCUMENT, NAMESPACE, null)
+        xmlParser.nextTag()
+
+        val packages = mutableSetOf<String>()
+
+        return buildSet {
+            xmlParser.require(START_TAG, NAMESPACE, SDK_TABLE_ELEMENT_NAME)
+            while (xmlParser.next() != END_TAG) {
+                if (xmlParser.eventType != START_TAG) {
+                    continue
+                }
+                if (xmlParser.name == SDK_ENTRY_ELEMENT_NAME) {
+                    val entry = readSdkEntry()
+                    if (!packages.add(entry.packageName)) {
+                        throw XmlPullParserException(
+                            "Duplicate entry for ${entry.packageName} found"
+                        )
+                    }
+                    add(entry)
+                } else {
+                    xmlParser.skipCurrentTag()
+                }
+            }
+            xmlParser.require(END_TAG, NAMESPACE, SDK_TABLE_ELEMENT_NAME)
+        }
+    }
+
+    private fun readSdkEntry(): SdkTableEntry {
+        var packageName: String? = null
+        var versionMajor: Int? = null
+        var configPath: String? = null
+
+        xmlParser.require(START_TAG, NAMESPACE, SDK_ENTRY_ELEMENT_NAME)
+        while (xmlParser.next() != END_TAG) {
+            if (xmlParser.eventType != START_TAG) {
+                continue
+            }
+            when (xmlParser.name) {
+                SDK_PACKAGE_NAME_ELEMENT_NAME -> {
+                    if (packageName != null) {
+                        throw XmlPullParserException(
+                            "Duplicate $SDK_PACKAGE_NAME_ELEMENT_NAME tag found"
+                        )
+                    }
+                    packageName = xmlParser.nextText()
+                }
+
+                VERSION_MAJOR_ELEMENT_NAME -> {
+                    if (versionMajor != null) {
+                        throw XmlPullParserException(
+                            "Duplicate $VERSION_MAJOR_ELEMENT_NAME tag found"
+                        )
+                    }
+                    versionMajor = xmlParser.nextText().toInt()
+                }
+
+                COMPAT_CONFIG_PATH_ELEMENT_NAME -> {
+                    if (configPath != null) {
+                        throw XmlPullParserException(
+                            "Duplicate $COMPAT_CONFIG_PATH_ELEMENT_NAME tag found"
+                        )
+                    }
+                    configPath = xmlParser.nextText()
+                }
+
+                else -> xmlParser.skipCurrentTag()
+            }
+        }
+        xmlParser.require(END_TAG, NAMESPACE, SDK_ENTRY_ELEMENT_NAME)
+
+        if (packageName == null) {
+            throw XmlPullParserException(
+                "No $SDK_PACKAGE_NAME_ELEMENT_NAME tag found"
+            )
+        }
+        if (configPath == null) {
+            throw XmlPullParserException(
+                "No $COMPAT_CONFIG_PATH_ELEMENT_NAME tag found"
+            )
+        }
+
+        return SdkTableEntry(packageName, versionMajor, configPath)
+    }
+
+    internal data class SdkTableEntry(
+        val packageName: String,
+        val versionMajor: Int?,
+        val compatConfigPath: String,
+    )
+
+    companion object {
+        private val NAMESPACE: String? = null // We don't use namespaces
+        private const val SDK_TABLE_ELEMENT_NAME = "runtime-enabled-sdk-table"
+        private const val SDK_ENTRY_ELEMENT_NAME = "runtime-enabled-sdk"
+        private const val SDK_PACKAGE_NAME_ELEMENT_NAME = "package-name"
+        private const val VERSION_MAJOR_ELEMENT_NAME = "version-major"
+        private const val COMPAT_CONFIG_PATH_ELEMENT_NAME = "compat-config-path"
+
+        fun parse(inputStream: InputStream): Set<SdkTableEntry> {
+            val parser = Xml.newPullParser()
+            try {
+                parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
+                parser.setInput(inputStream, null)
+                return SdkTableConfigParser(parser).readSdkTable()
+            } finally {
+                parser.setInput(null)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/config/XmlUtils.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/config/XmlUtils.kt
new file mode 100644
index 0000000..6a91815
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/config/XmlUtils.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:RestrictTo(RestrictTo.Scope.LIBRARY)
+
+package androidx.privacysandbox.sdkruntime.client.config
+
+import androidx.annotation.RestrictTo
+import org.xmlpull.v1.XmlPullParser
+import org.xmlpull.v1.XmlPullParser.END_TAG
+import org.xmlpull.v1.XmlPullParser.START_TAG
+
+/**
+ * Skip current tag (including inner tags)
+ *
+ * @suppress
+ */
+internal fun XmlPullParser.skipCurrentTag() {
+    require(START_TAG, null, null)
+    var depth = 1
+    while (depth != 0) {
+        when (next()) {
+            END_TAG -> depth--
+            START_TAG -> depth++
+        }
+    }
+}
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
new file mode 100644
index 0000000..ecddba5
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/LocalController.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.client.controller
+
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
+
+/**
+ * Local implementation that will be injected to locally loaded SDKs.
+ */
+internal class LocalController(
+    private val locallyLoadedSdks: LocallyLoadedSdks
+) : SdkSandboxControllerCompat.SandboxControllerImpl {
+
+    override fun getSandboxedSdks(): List<SandboxedSdkCompat> {
+        return locallyLoadedSdks.getLoadedSdks()
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/LocallyLoadedSdks.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/LocallyLoadedSdks.kt
new file mode 100644
index 0000000..1f2856d
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/controller/LocallyLoadedSdks.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.privacysandbox.sdkruntime.client.controller
+
+import androidx.privacysandbox.sdkruntime.client.loader.LocalSdkProvider
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import org.jetbrains.annotations.TestOnly
+
+/**
+ * Represents list of locally loaded SDKs.
+ * Shared between:
+ * 1) [androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat]
+ * 2) [androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat]
+ */
+internal class LocallyLoadedSdks {
+
+    private val sdks = HashMap<String, Entry>()
+
+    fun isLoaded(sdkName: String): Boolean {
+        return sdks.containsKey(sdkName)
+    }
+
+    fun put(sdkName: String, entry: Entry) {
+        sdks[sdkName] = entry
+    }
+
+    @TestOnly
+    fun get(sdkName: String): Entry? = sdks[sdkName]
+
+    fun remove(sdkName: String): Entry? {
+        return sdks.remove(sdkName)
+    }
+
+    fun getLoadedSdks(): List<SandboxedSdkCompat> {
+        return sdks.values.map { it.sdk }
+    }
+
+    data class Entry(
+        val sdkProvider: LocalSdkProvider,
+        val sdk: SandboxedSdkCompat
+    )
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/FileClassLoaderFactory.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/FileClassLoaderFactory.kt
new file mode 100644
index 0000000..e6d8eb6
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/FileClassLoaderFactory.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.client.loader
+
+import android.util.Log
+import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
+import androidx.privacysandbox.sdkruntime.client.loader.storage.LocalSdkStorage
+import androidx.privacysandbox.sdkruntime.client.loader.storage.toClassPathString
+import dalvik.system.BaseDexClassLoader
+import java.io.File
+
+/**
+ * Loading SDK using BaseDexClassLoader.
+ * Using [LocalSdkStorage] to get SDK DEX files,
+ * if no files available - delegating to fallback.
+ */
+internal class FileClassLoaderFactory(
+    private val localSdkStorage: LocalSdkStorage,
+    private val fallback: SdkLoader.ClassLoaderFactory,
+) : SdkLoader.ClassLoaderFactory {
+
+    override fun createClassLoaderFor(
+        sdkConfig: LocalSdkConfig,
+        parent: ClassLoader
+    ): ClassLoader {
+        return tryCreateBaseDexClassLoaderFor(sdkConfig, parent)
+            ?: fallback.createClassLoaderFor(
+                sdkConfig,
+                parent
+            )
+    }
+
+    private fun tryCreateBaseDexClassLoaderFor(
+        sdkConfig: LocalSdkConfig,
+        parent: ClassLoader
+    ): ClassLoader? {
+        try {
+            val dexFiles = localSdkStorage.dexFilesFor(sdkConfig)
+            if (dexFiles == null) {
+                Log.w(
+                    LOG_TAG,
+                    "Can't use BaseDexClassLoader for ${sdkConfig.packageName} - no dexFiles"
+                )
+                return null
+            }
+
+            val optimizedDirectory = File(dexFiles.files[0].parentFile, "DexOpt")
+            if (!optimizedDirectory.exists()) {
+                optimizedDirectory.mkdirs()
+            }
+
+            return BaseDexClassLoader(
+                dexFiles.toClassPathString(),
+                optimizedDirectory,
+                /* librarySearchPath = */ null,
+                parent
+            )
+        } catch (ex: Exception) {
+            Log.e(
+                LOG_TAG,
+                "Failed to use BaseDexClassLoader for ${sdkConfig.packageName} - exception",
+                ex
+            )
+            return null
+        }
+    }
+
+    companion object {
+        const val LOG_TAG =
+            "FileClassLoaderFactory"
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..2fa4a55
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/InMemorySdkClassLoaderFactory.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.client.loader
+
+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
+import dalvik.system.InMemoryDexClassLoader
+import java.nio.ByteBuffer
+import java.nio.channels.Channels
+
+/**
+ * Loading SDK in memory on API 27+
+ */
+internal abstract class InMemorySdkClassLoaderFactory : SdkLoader.ClassLoaderFactory {
+
+    @RequiresApi(Build.VERSION_CODES.O_MR1)
+    internal class InMemoryImpl(
+        private val assetManager: AssetManager
+    ) : InMemorySdkClassLoaderFactory() {
+
+        @DoNotInline
+        override fun createClassLoaderFor(
+            sdkConfig: LocalSdkConfig,
+            parent: ClassLoader
+        ): ClassLoader {
+            try {
+                val buffers = arrayOfNulls<ByteBuffer>(sdkConfig.dexPaths.size)
+                for (i in sdkConfig.dexPaths.indices) {
+                    assetManager.open(sdkConfig.dexPaths[i]).use { inputStream ->
+                        val byteBuffer = ByteBuffer.allocate(inputStream.available())
+                        Channels.newChannel(inputStream).read(byteBuffer)
+                        byteBuffer.flip()
+                        buffers[i] = byteBuffer
+                    }
+                }
+                return InMemoryDexClassLoader(buffers, parent)
+            } catch (ex: Exception) {
+                throw LoadSdkCompatException(
+                    LoadSdkCompatException.LOAD_SDK_INTERNAL_ERROR,
+                    "Failed to instantiate classloader",
+                    ex
+                )
+            }
+        }
+    }
+
+    internal class FailImpl : InMemorySdkClassLoaderFactory() {
+        @DoNotInline
+        override fun createClassLoaderFor(
+            sdkConfig: LocalSdkConfig,
+            parent: ClassLoader
+        ): ClassLoader {
+            throw LoadSdkCompatException(
+                LoadSdkCompatException.LOAD_SDK_SDK_SANDBOX_DISABLED,
+                "Can't use InMemoryDexClassLoader"
+            )
+        }
+    }
+
+    companion object {
+        fun create(context: Context): InMemorySdkClassLoaderFactory {
+            return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
+                InMemoryImpl(context.assets)
+            } else {
+                FailImpl()
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/JavaResourcesLoadingClassLoaderFactory.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/JavaResourcesLoadingClassLoaderFactory.kt
new file mode 100644
index 0000000..b669af8
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/JavaResourcesLoadingClassLoaderFactory.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.client.loader
+
+import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
+import java.io.File
+import java.io.IOException
+import java.net.URL
+import java.util.Enumeration
+
+/**
+ * Delegate java resources related calls to app classloader.
+ *
+ * Classloaders normally delegate calls to parent classloader first, that's why this factory
+ * creates classloader that will work with java resources and pass it as parent to
+ * [codeClassLoaderFactory] thus overrides java resources for all classes loaded down the line.
+ *
+ * Add [LocalSdkConfig.javaResourcesRoot] as prefix to resource names before delegating calls,
+ * thus allowing isolating java resources for different local sdks.
+ */
+internal class JavaResourcesLoadingClassLoaderFactory(
+    private val appClassloader: ClassLoader,
+    private val codeClassLoaderFactory: SdkLoader.ClassLoaderFactory
+) : SdkLoader.ClassLoaderFactory {
+    override fun createClassLoaderFor(
+        sdkConfig: LocalSdkConfig,
+        parent: ClassLoader
+    ): ClassLoader {
+        val javaResourcesLoadingClassLoader = createJavaResourcesLoadingClassLoader(
+            sdkConfig,
+            parent
+        )
+        return codeClassLoaderFactory.createClassLoaderFor(
+            sdkConfig,
+            parent = javaResourcesLoadingClassLoader
+        )
+    }
+
+    private fun createJavaResourcesLoadingClassLoader(
+        sdkConfig: LocalSdkConfig,
+        parent: ClassLoader
+    ): ClassLoader {
+        return if (sdkConfig.javaResourcesRoot == null) {
+            parent
+        } else {
+            JavaResourcesLoadingClassLoader(
+                parent,
+                appClassloader,
+                File(ASSETS_DIR, sdkConfig.javaResourcesRoot)
+            )
+        }
+    }
+
+    private class JavaResourcesLoadingClassLoader constructor(
+        parent: ClassLoader,
+        private val appClassloader: ClassLoader,
+        private val javaResourcePrefix: File
+    ) : ClassLoader(parent) {
+        override fun findResource(name: String): URL? {
+            return appClassloader.getResource(File(javaResourcePrefix, name).path)
+        }
+
+        @Throws(IOException::class)
+        override fun findResources(name: String): Enumeration<URL> {
+            return appClassloader.getResources(File(javaResourcePrefix, name).path)
+        }
+    }
+
+    companion object {
+        const val ASSETS_DIR = "assets/"
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkProvider.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkProvider.kt
new file mode 100644
index 0000000..56af6b0
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkProvider.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.client.loader
+
+import android.os.Bundle
+import androidx.annotation.RestrictTo
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import org.jetbrains.annotations.TestOnly
+
+/**
+ * Provides interface for interaction with locally loaded SDK.
+ * Handle different protocol versions inside.
+ *
+ * @suppress
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+internal abstract class LocalSdkProvider protected constructor(
+    @get:TestOnly val sdkProvider: Any
+) {
+
+    abstract fun onLoadSdk(params: Bundle): SandboxedSdkCompat
+
+    abstract fun beforeUnloadSdk()
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/ResourceRemapping.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/ResourceRemapping.kt
new file mode 100644
index 0000000..711d7b1
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/ResourceRemapping.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.client.loader
+
+import androidx.privacysandbox.sdkruntime.client.config.ResourceRemappingConfig
+
+/**
+ * Update RPackage.packageId for supporting Android Resource remapping for SDK.
+ * Each resource has id calculated as id = RPackage.packageId + index.
+ * Updating packageId effectively shifting all SDK resource ids in resource table.
+ * IMPORTANT: ResourceRemapping should happen before ANY interactions with R.class
+ */
+internal object ResourceRemapping {
+
+    private const val PACKAGE_ID_FIELD_NAME = "packageId"
+
+    fun apply(
+        sdkClassLoader: ClassLoader,
+        remappingConfig: ResourceRemappingConfig?
+    ) {
+        if (remappingConfig == null)
+            return
+
+        val rPackageClass = Class.forName(
+            remappingConfig.rPackageClassName,
+            /* initialize = */ false,
+            sdkClassLoader
+        )
+
+        val field = rPackageClass.getDeclaredField(PACKAGE_ID_FIELD_NAME)
+
+        field.setInt(null, remappingConfig.packageId)
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..3fcefd8
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/SdkLoader.kt
@@ -0,0 +1,139 @@
+/*
+ * 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.client.loader
+
+import android.content.Context
+import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
+import androidx.privacysandbox.sdkruntime.client.loader.impl.SandboxControllerInjector
+import androidx.privacysandbox.sdkruntime.client.loader.impl.SdkProviderV1
+import androidx.privacysandbox.sdkruntime.client.loader.storage.CachedLocalSdkStorage
+import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
+import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
+
+/**
+ * Load SDK bundled with App.
+ */
+internal class SdkLoader internal constructor(
+    private val classLoaderFactory: ClassLoaderFactory,
+    private val appContext: Context,
+    private val controller: SdkSandboxControllerCompat.SandboxControllerImpl
+) {
+
+    internal interface ClassLoaderFactory {
+        fun createClassLoaderFor(sdkConfig: LocalSdkConfig, parent: ClassLoader): ClassLoader
+    }
+
+    /**
+     * Loading SDK in separate classloader:
+     *  1. Create classloader for sdk;
+     *  2. Performing handshake to determine api version;
+     *  3. (optional) Update RPackage.packageId to support Android Resource remapping for SDK
+     *  4. Select [LocalSdkProvider] implementation that could work with that api version.
+     *
+     * @param sdkConfig sdk to load
+     * @return LocalSdk implementation for loaded SDK
+     */
+    fun loadSdk(sdkConfig: LocalSdkConfig): LocalSdkProvider {
+        val classLoader = classLoaderFactory.createClassLoaderFor(
+            sdkConfig,
+            getParentClassLoader()
+        )
+        return createLocalSdk(classLoader, sdkConfig)
+    }
+
+    private fun getParentClassLoader(): ClassLoader = appContext.classLoader.parent!!
+
+    private fun createLocalSdk(
+        classLoader: ClassLoader,
+        sdkConfig: LocalSdkConfig
+    ): LocalSdkProvider {
+        try {
+            val apiVersion = VersionHandshake.perform(classLoader)
+            ResourceRemapping.apply(classLoader, sdkConfig.resourceRemapping)
+            if (apiVersion >= 2) {
+                return createSdkProviderV2(classLoader, sdkConfig)
+            } else if (apiVersion >= 1) {
+                return createSdkProviderV1(classLoader, sdkConfig)
+            }
+        } catch (ex: Exception) {
+            throw LoadSdkCompatException(
+                LoadSdkCompatException.LOAD_SDK_INTERNAL_ERROR,
+                "Failed to instantiate local SDK",
+                ex
+            )
+        }
+
+        throw LoadSdkCompatException(
+            LoadSdkCompatException.LOAD_SDK_NOT_FOUND,
+            "Incorrect Api version"
+        )
+    }
+
+    private fun createSdkProviderV1(
+        sdkClassLoader: ClassLoader,
+        sdkConfig: LocalSdkConfig
+    ): LocalSdkProvider {
+        return SdkProviderV1.create(sdkClassLoader, sdkConfig, appContext)
+    }
+
+    private fun createSdkProviderV2(
+        sdkClassLoader: ClassLoader,
+        sdkConfig: LocalSdkConfig
+    ): LocalSdkProvider {
+        SandboxControllerInjector.inject(sdkClassLoader, controller)
+        return SdkProviderV1.create(sdkClassLoader, sdkConfig, appContext)
+    }
+
+    companion object {
+        /**
+         * Build chain of [ClassLoaderFactory] that could load SDKs with their resources.
+         * Order is important because classloaders normally delegate calls to parent classloader
+         * first:
+         *  1. [JavaResourcesLoadingClassLoaderFactory] - to provide java resources to classes
+         *  loaded by child classloaders;
+         *  2a. [FileClassLoaderFactory] - first trying to use factory that extracting SDK Dex
+         *  to storage and load it using [dalvik.system.BaseDexClassLoader].
+         *  Supports all platform versions (Api14+, minSdkVersion for library).
+         *  2b. [InMemorySdkClassLoaderFactory] - fallback for low available space. Trying to load
+         *  SDK in-memory using [dalvik.system.InMemoryDexClassLoader].
+         *  Supports Api27+ only, fails SDK loading on non-supported platform versions.
+         *
+         * @param context App context
+         * @param lowSpaceThreshold Minimal available space in bytes required to proceed with
+         * extracting SDK Dex files.
+         *
+         * @return SdkLoader that could load SDKs with their resources.
+         */
+        fun create(
+            context: Context,
+            controller: SdkSandboxControllerCompat.SandboxControllerImpl,
+            lowSpaceThreshold: Long = 100 * 1024 * 1024
+        ): SdkLoader {
+            val cachedLocalSdkStorage = CachedLocalSdkStorage.create(
+                context,
+                lowSpaceThreshold
+            )
+            val classLoaderFactory = JavaResourcesLoadingClassLoaderFactory(
+                context.classLoader,
+                codeClassLoaderFactory = FileClassLoaderFactory(
+                    cachedLocalSdkStorage,
+                    fallback = InMemorySdkClassLoaderFactory.create(context)
+                )
+            )
+            return SdkLoader(classLoaderFactory, context, controller)
+        }
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/VersionHandshake.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/VersionHandshake.kt
new file mode 100644
index 0000000..21a6a17
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/VersionHandshake.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.client.loader
+
+import android.annotation.SuppressLint
+import androidx.annotation.RestrictTo
+import androidx.privacysandbox.sdkruntime.core.Versions
+
+/**
+ * Performing version handshake.
+ *
+ * @suppress
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+internal object VersionHandshake {
+    @SuppressLint("BanUncheckedReflection") // calling method on Versions class
+    fun perform(classLoader: ClassLoader?): Int {
+        val versionsClass = Class.forName(
+            Versions::class.java.name,
+            false,
+            classLoader
+        )
+        val handShakeMethod = versionsClass.getMethod("handShake", Int::class.javaPrimitiveType)
+        return handShakeMethod.invoke(null, Versions.API_VERSION) as Int
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..745c352
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxControllerInjector.kt
@@ -0,0 +1,146 @@
+/*
+ * 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.client.loader.impl
+
+import android.os.IBinder
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkInfo
+import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
+import java.lang.reflect.Constructor
+import java.lang.reflect.InvocationHandler
+import java.lang.reflect.Method
+import java.lang.reflect.Proxy
+
+/**
+ * Injects local implementation of [SdkSandboxControllerCompat.SandboxControllerImpl]
+ * to [SdkSandboxControllerCompat] loaded by SDK Classloader.
+ * Using [Proxy] to allow interaction between classes loaded by different classloaders.
+ */
+internal object SandboxControllerInjector {
+
+    /**
+     * Injects local implementation to SDK instance of [SdkSandboxControllerCompat].
+     * 1) Retrieve [SdkSandboxControllerCompat] loaded by [sdkClassLoader]
+     * 2) Create proxy that implements class from (1) and delegate to [controller]
+     * 3) Call (via reflection) [SdkSandboxControllerCompat.injectLocalImpl] with proxy from (2)
+     */
+    fun inject(
+        sdkClassLoader: ClassLoader,
+        controller: SdkSandboxControllerCompat.SandboxControllerImpl
+    ) {
+        val controllerClass = Class.forName(
+            SdkSandboxControllerCompat::class.java.name,
+            false,
+            sdkClassLoader
+        )
+
+        val controllerImplClass = Class.forName(
+            SdkSandboxControllerCompat.SandboxControllerImpl::class.java.name,
+            false,
+            sdkClassLoader
+        )
+
+        val injectMethod = controllerClass.getMethod("injectLocalImpl", controllerImplClass)
+
+        val sdkCompatBuilder = CompatSdkBuilder.createFor(sdkClassLoader)
+
+        val proxy = Proxy.newProxyInstance(
+            sdkClassLoader,
+            arrayOf(controllerImplClass),
+            Handler(
+                controller,
+                sdkCompatBuilder
+            )
+        )
+
+        injectMethod.invoke(null, proxy)
+    }
+
+    private class Handler(
+        private val controller: SdkSandboxControllerCompat.SandboxControllerImpl,
+        private val compatSdkBuilder: CompatSdkBuilder
+    ) : InvocationHandler {
+        override fun invoke(proxy: Any, method: Method, args: Array<out Any?>?): Any {
+            return when (method.name) {
+                "getSandboxedSdks" -> getSandboxedSdks()
+                else -> {
+                    throw UnsupportedOperationException(
+                        "Unexpected method call object:$proxy, method: $method, args: $args"
+                    )
+                }
+            }
+        }
+
+        private fun getSandboxedSdks(): List<Any> {
+            return controller
+                .getSandboxedSdks()
+                .map { compatSdkBuilder.createFrom(it) }
+        }
+    }
+
+    private class CompatSdkBuilder(
+        private val sandboxedSdkInfoConstructor: Constructor<out Any>,
+        private val sandboxedSdkCompatConstructor: Constructor<out Any>,
+    ) {
+        /**
+         * Creates instance of [SandboxedSdkCompat] class loaded by SDK Classloader.
+         *
+         * @param source instance of SandboxedSdkCompat loaded by app classloader.
+         * @return instance of SandboxedSdkCompat loaded by SDK classloader.
+         */
+        fun createFrom(source: SandboxedSdkCompat): Any {
+            val sdkInfo = createSdkInfoFrom(source.getSdkInfo())
+            return sandboxedSdkCompatConstructor.newInstance(source.getInterface(), sdkInfo)
+        }
+
+        fun createSdkInfoFrom(source: SandboxedSdkInfo?): Any? {
+            if (source == null) {
+                return null
+            }
+            return sandboxedSdkInfoConstructor.newInstance(source.name, source.version)
+        }
+
+        companion object {
+            fun createFor(classLoader: ClassLoader): CompatSdkBuilder {
+                val sandboxedSdkCompatClass = Class.forName(
+                    SandboxedSdkCompat::class.java.name,
+                    /* initialize = */ false,
+                    classLoader
+                )
+                val sandboxedSdkInfoClass = Class.forName(
+                    SandboxedSdkInfo::class.java.name,
+                    /* initialize = */ false,
+                    classLoader
+                )
+                val sandboxedSdkCompatConstructor =
+                    sandboxedSdkCompatClass.getConstructor(
+                        /* parameter1 */ IBinder::class.java,
+                        /* parameter2 */ sandboxedSdkInfoClass
+                    )
+                val sandboxedSdkInfoConstructor =
+                    sandboxedSdkInfoClass.getConstructor(
+                        /* parameter1 */ String::class.java,
+                        /* parameter2 */ Long::class.java
+                    )
+                return CompatSdkBuilder(
+                    sandboxedSdkInfoConstructor = sandboxedSdkInfoConstructor,
+                    sandboxedSdkCompatConstructor = sandboxedSdkCompatConstructor
+                )
+            }
+        }
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..4a9fd3b
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxedSdkContextCompat.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.client.loader.impl
+
+import android.content.Context
+import android.content.ContextWrapper
+import androidx.annotation.RestrictTo
+
+/**
+ * Refers to the context of the SDK loaded locally.
+ *
+ * @suppress
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+internal class SandboxedSdkContextCompat(
+    base: Context,
+    private val classLoader: ClassLoader?
+) : ContextWrapper(base) {
+    override fun getClassLoader(): ClassLoader? {
+        return classLoader
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SdkProviderV1.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SdkProviderV1.kt
new file mode 100644
index 0000000..ce51c0ef
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SdkProviderV1.kt
@@ -0,0 +1,190 @@
+/*
+ * 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.client.loader.impl
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.Bundle
+import android.os.IBinder
+import androidx.annotation.RestrictTo
+import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
+import androidx.privacysandbox.sdkruntime.client.loader.LocalSdkProvider
+import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkInfo
+import java.lang.reflect.InvocationTargetException
+import java.lang.reflect.Method
+
+/**
+ * Provides interface for interaction with locally loaded SDK with ApiVersion 1.
+ *
+ * @suppress
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+internal class SdkProviderV1 private constructor(
+    sdkProvider: Any,
+
+    private val onLoadSdkMethod: Method,
+    private val beforeUnloadSdkMethod: Method,
+
+    private val sandboxedSdkCompatBuilder: SandboxedSdkCompatBuilderV1,
+    private val loadSdkCompatExceptionBuilder: LoadSdkCompatExceptionBuilderV1
+) : LocalSdkProvider(sdkProvider) {
+
+    @SuppressLint("BanUncheckedReflection") // using reflection on library classes
+    override fun onLoadSdk(params: Bundle): SandboxedSdkCompat {
+        try {
+            val rawResult = onLoadSdkMethod.invoke(sdkProvider, params)
+            return sandboxedSdkCompatBuilder.build(rawResult!!)
+        } catch (e: InvocationTargetException) {
+            throw loadSdkCompatExceptionBuilder.tryRebuildCompatException(e.targetException)
+        } catch (ex: Exception) {
+            throw LoadSdkCompatException(
+                LoadSdkCompatException.LOAD_SDK_INTERNAL_ERROR,
+                "Failed during onLoadSdk call",
+                ex
+            )
+        }
+    }
+
+    @SuppressLint("BanUncheckedReflection") // using reflection on library classes
+    override fun beforeUnloadSdk() {
+        beforeUnloadSdkMethod.invoke(sdkProvider)
+    }
+
+    internal class SandboxedSdkCompatBuilderV1 private constructor(
+        private val sdkInfo: SandboxedSdkInfo?,
+        private val getInterfaceMethod: Method
+    ) {
+
+        @SuppressLint("BanUncheckedReflection") // calling method on SandboxedSdkCompat class
+        fun build(rawObject: Any): SandboxedSdkCompat {
+            val binder = getInterfaceMethod.invoke(rawObject) as IBinder
+            return SandboxedSdkCompat(binder, sdkInfo)
+        }
+
+        companion object {
+
+            fun create(
+                classLoader: ClassLoader,
+                sdkConfig: LocalSdkConfig
+            ): SandboxedSdkCompatBuilderV1 {
+                val sandboxedSdkCompatClass = Class.forName(
+                    SandboxedSdkCompat::class.java.name,
+                    /* initialize = */ false,
+                    classLoader
+                )
+                val getInterfaceMethod = sandboxedSdkCompatClass.getMethod("getInterface")
+                val sdkInfo = sdkInfo(sdkConfig)
+                return SandboxedSdkCompatBuilderV1(sdkInfo, getInterfaceMethod)
+            }
+
+            private fun sdkInfo(sdkConfig: LocalSdkConfig): SandboxedSdkInfo? {
+                return if (sdkConfig.versionMajor == null) {
+                    null
+                } else {
+                    SandboxedSdkInfo(sdkConfig.packageName, sdkConfig.versionMajor.toLong())
+                }
+            }
+        }
+    }
+
+    internal class LoadSdkCompatExceptionBuilderV1 private constructor(
+        private val getLoadSdkErrorCodeMethod: Method,
+        private val getExtraInformationMethod: Method
+    ) {
+        @SuppressLint("BanUncheckedReflection") // calling method on LoadSdkCompatException class
+        fun tryRebuildCompatException(rawException: Throwable): Throwable {
+            if (rawException.javaClass.name != LoadSdkCompatException::class.java.name) {
+                return rawException
+            }
+
+            return try {
+                val loadSdkErrorCode = getLoadSdkErrorCodeMethod.invoke(rawException) as Int
+                val extraInformation = getExtraInformationMethod.invoke(rawException) as Bundle
+                LoadSdkCompatException(
+                    loadSdkErrorCode,
+                    rawException.message,
+                    rawException.cause,
+                    extraInformation
+                )
+            } catch (ex: Throwable) {
+                // failed to rebuild, just wrap original
+                LoadSdkCompatException(
+                    LoadSdkCompatException.LOAD_SDK_INTERNAL_ERROR,
+                    "Failed to rebuild exception with error ${ex.message}",
+                    rawException
+                )
+            }
+        }
+
+        companion object {
+            fun create(classLoader: ClassLoader): LoadSdkCompatExceptionBuilderV1 {
+                val loadSdkCompatExceptionClass = Class.forName(
+                    LoadSdkCompatException::class.java.name,
+                    /* initialize = */ false,
+                    classLoader
+                )
+                val getLoadSdkErrorCodeMethod = loadSdkCompatExceptionClass.getMethod(
+                    "getLoadSdkErrorCode"
+                )
+                val getExtraInformationMethod = loadSdkCompatExceptionClass.getMethod(
+                    "getExtraInformation"
+                )
+                return LoadSdkCompatExceptionBuilderV1(
+                    getLoadSdkErrorCodeMethod,
+                    getExtraInformationMethod
+                )
+            }
+        }
+    }
+
+    companion object {
+
+        @SuppressLint("BanUncheckedReflection") // calling method of SandboxedSdkProviderCompat
+        fun create(
+            classLoader: ClassLoader,
+            sdkConfig: LocalSdkConfig,
+            appContext: Context
+        ): SdkProviderV1 {
+            val sdkProviderClass = Class.forName(
+                sdkConfig.entryPoint,
+                /* initialize = */ false,
+                classLoader
+            )
+            val attachContextMethod =
+                sdkProviderClass.getMethod("attachContext", Context::class.java)
+            val onLoadSdkMethod = sdkProviderClass.getMethod("onLoadSdk", Bundle::class.java)
+            val beforeUnloadSdkMethod = sdkProviderClass.getMethod("beforeUnloadSdk")
+            val sandboxedSdkCompatBuilder =
+                SandboxedSdkCompatBuilderV1.create(classLoader, sdkConfig)
+            val loadSdkCompatExceptionBuilder =
+                LoadSdkCompatExceptionBuilderV1.create(classLoader)
+
+            val sdkProvider = sdkProviderClass.getConstructor().newInstance()
+            val sandboxedSdkContextCompat = SandboxedSdkContextCompat(appContext, classLoader)
+            attachContextMethod.invoke(sdkProvider, sandboxedSdkContextCompat)
+
+            return SdkProviderV1(
+                sdkProvider,
+                onLoadSdkMethod,
+                beforeUnloadSdkMethod,
+                sandboxedSdkCompatBuilder,
+                loadSdkCompatExceptionBuilder
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/storage/CachedLocalSdkStorage.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/storage/CachedLocalSdkStorage.kt
new file mode 100644
index 0000000..82251fc
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/storage/CachedLocalSdkStorage.kt
@@ -0,0 +1,148 @@
+/*
+ * 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.client.loader.storage
+
+import android.content.Context
+import android.os.Build
+import android.os.Build.VERSION_CODES.JELLY_BEAN_MR2
+import android.os.Environment
+import android.os.StatFs
+import android.util.Log
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresApi
+import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
+import java.io.File
+
+/**
+ * Caching implementation of [LocalSdkStorage].
+ *
+ * Extract SDK DEX files into folder provided by [LocalSdkFolderProvider].
+ * Not extracting new files if available space lower than lowSpaceThreshold.
+ * Return result only if all files successfully extracted.
+ */
+internal class CachedLocalSdkStorage private constructor(
+    private val context: Context,
+    private val rootFolderProvider: LocalSdkFolderProvider,
+    private val lowSpaceThreshold: Long
+) : LocalSdkStorage {
+
+    /**
+     * Return SDK DEX files from folder provided by [LocalSdkFolderProvider].
+     * Extract missing files from assets if available space bigger than lowSpaceThreshold.
+     *
+     * @param sdkConfig sdk config
+     * @return [LocalSdkDexFiles] if all SDK DEX files available in SDK folder
+     * or null if something missing and couldn't be extracted because of
+     * available space lower than lowSpaceThreshold
+     */
+    override fun dexFilesFor(sdkConfig: LocalSdkConfig): LocalSdkDexFiles? {
+        val disableExtracting = availableBytes() < lowSpaceThreshold
+        val targetFolder = rootFolderProvider.dexFolderFor(sdkConfig)
+
+        try {
+            val files = buildList {
+                for (index in sdkConfig.dexPaths.indices) {
+                    val assetName = sdkConfig.dexPaths[index]
+                    val outputFileName = "$index.dex"
+                    val outputDexFile = File(targetFolder, outputFileName)
+
+                    if (!outputDexFile.exists()) {
+                        if (disableExtracting) {
+                            Log.i(LOG_TAG, "Can't extract $assetName because of low space")
+                            return null
+                        }
+                        extractAssetToFile(assetName, outputDexFile)
+                    }
+
+                    add(outputDexFile)
+                }
+            }
+            return LocalSdkDexFiles(files)
+        } catch (ex: Exception) {
+            Log.e(
+                LOG_TAG,
+                "Failed to extract ${sdkConfig.packageName}, deleting $targetFolder.",
+                ex
+            )
+
+            if (!targetFolder.deleteRecursively()) {
+                Log.e(
+                    LOG_TAG,
+                    "Failed to delete $targetFolder during cleanup.",
+                    ex
+                )
+            }
+
+            throw ex
+        }
+    }
+
+    private fun extractAssetToFile(
+        assetName: String,
+        outputFile: File,
+    ) {
+        outputFile.createNewFile()
+        context.assets.open(assetName).use { fromStream ->
+            outputFile.outputStream().use { toStream ->
+                fromStream.copyTo(toStream)
+            }
+        }
+        outputFile.setReadOnly()
+    }
+
+    private fun availableBytes(): Long {
+        val dataDirectory = Environment.getDataDirectory()
+        val statFs = StatFs(dataDirectory.path)
+        if (Build.VERSION.SDK_INT >= JELLY_BEAN_MR2) {
+            return Api18Impl.availableBytes(statFs)
+        }
+
+        @Suppress("DEPRECATION")
+        val blockSize = statFs.blockSize.toLong()
+
+        @Suppress("DEPRECATION")
+        val availableBlocks = statFs.availableBlocks.toLong()
+
+        return availableBlocks * blockSize
+    }
+
+    @RequiresApi(JELLY_BEAN_MR2)
+    private object Api18Impl {
+        @DoNotInline
+        fun availableBytes(statFs: StatFs) = statFs.availableBytes
+    }
+
+    companion object {
+
+        const val LOG_TAG = "CachedLocalSdkStorage"
+
+        /**
+         * Create CachedLocalSdkStorage.
+         *
+         * @param context Application context
+         * @param lowSpaceThreshold Minimal available space in bytes required to proceed with
+         * extracting new SDK Dex files.
+         */
+        fun create(
+            context: Context,
+            lowSpaceThreshold: Long
+        ): CachedLocalSdkStorage {
+            val localSdkFolderProvider = LocalSdkFolderProvider.create(context)
+            return CachedLocalSdkStorage(context, localSdkFolderProvider, lowSpaceThreshold)
+        }
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkDexFiles.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkDexFiles.kt
new file mode 100644
index 0000000..6cf8035
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkDexFiles.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+@file:RestrictTo(RestrictTo.Scope.LIBRARY)
+
+package androidx.privacysandbox.sdkruntime.client.loader.storage
+
+import androidx.annotation.RestrictTo
+import java.io.File
+
+/**
+ * Represent SDK Dex files extracted to device storage.
+ */
+internal data class LocalSdkDexFiles(
+    val files: List<File>
+)
+
+/**
+ * Convert [LocalSdkDexFiles] to ClassPath string.
+ */
+internal fun LocalSdkDexFiles.toClassPathString() =
+    files.joinToString(separator = File.pathSeparator, transform = File::getPath)
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
new file mode 100644
index 0000000..72aaffb
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkFolderProvider.kt
@@ -0,0 +1,128 @@
+/*
+ * 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.client.loader.storage
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Build
+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
+import java.io.DataOutputStream
+import java.io.File
+
+/**
+ * Create folders for Local SDKs in ([Context.getCacheDir] / RuntimeEnabledSdk / <packageName>)
+ *
+ * Store Application update time ([android.content.pm.PackageInfo.lastUpdateTime]) in
+ * ([Context.getCacheDir] / RuntimeEnabledSdk / Folder.version) file.
+ * Remove SDK Folders if Application was updated after folders were created.
+ */
+internal class LocalSdkFolderProvider private constructor(
+    private val sdkRootFolder: File
+) {
+
+    /**
+     * Return folder on storage that should be used for storing SDK DEX files.
+     */
+    fun dexFolderFor(sdkConfig: LocalSdkConfig): File {
+        val sdkDexFolder = File(sdkRootFolder, sdkConfig.packageName)
+        if (!sdkDexFolder.exists()) {
+            sdkDexFolder.mkdirs()
+        }
+        return sdkDexFolder
+    }
+
+    companion object {
+
+        private const val SDK_ROOT_FOLDER = "RuntimeEnabledSdk"
+        private const val VERSION_FILE_NAME = "Folder.version"
+
+        /**
+         * Create LocalSdkFolderProvider.
+         *
+         * Check if current root folder created in same app installation
+         * and remove folder content if not.
+         */
+        fun create(context: Context): LocalSdkFolderProvider {
+            val sdkRootFolder = createSdkRootFolder(context)
+            return LocalSdkFolderProvider(sdkRootFolder)
+        }
+
+        private fun createSdkRootFolder(context: Context): File {
+            val rootFolder = File(context.cacheDir, SDK_ROOT_FOLDER)
+            val versionFile = File(rootFolder, VERSION_FILE_NAME)
+
+            val sdkRootFolderVersion = readVersion(versionFile)
+            val lastUpdateTime = appLastUpdateTime(context)
+
+            if (lastUpdateTime != sdkRootFolderVersion) {
+                if (rootFolder.exists()) {
+                    rootFolder.deleteRecursively()
+                }
+                rootFolder.mkdirs()
+                versionFile.createNewFile()
+
+                versionFile.outputStream().use { outputStream ->
+                    DataOutputStream(outputStream).use { dataStream ->
+                        dataStream.writeLong(lastUpdateTime)
+                    }
+                }
+            }
+
+            return rootFolder
+        }
+
+        private fun appLastUpdateTime(context: Context): Long {
+            if (Build.VERSION.SDK_INT >= TIRAMISU) {
+                return Api33Impl.getLastUpdateTime(context)
+            }
+
+            @Suppress("DEPRECATION")
+            val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
+            return packageInfo.lastUpdateTime
+        }
+
+        private fun readVersion(versionFile: File): Long? {
+            if (!versionFile.exists()) {
+                return null
+            }
+            try {
+                versionFile.inputStream().use { inputStream ->
+                    DataInputStream(inputStream).use { dataStream ->
+                        return dataStream.readLong()
+                    }
+                }
+            } catch (e: Exception) {
+                // Failed to parse or IOException, treat as no version file exists.
+                return null
+            }
+        }
+    }
+
+    @RequiresApi(TIRAMISU)
+    private object Api33Impl {
+        @DoNotInline
+        fun getLastUpdateTime(context: Context): Long =
+            context.packageManager.getPackageInfo(
+                context.packageName,
+                PackageManager.PackageInfoFlags.of(0)
+            ).lastUpdateTime
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkStorage.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkStorage.kt
new file mode 100644
index 0000000..66fdab7
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/storage/LocalSdkStorage.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.client.loader.storage
+
+import androidx.privacysandbox.sdkruntime.client.config.LocalSdkConfig
+
+/**
+ * Provides interface for getting SDK related files.
+ */
+internal interface LocalSdkStorage {
+    /**
+     * Get [LocalSdkDexFiles] for bundled SDK.
+     *
+     * @param sdkConfig sdk config
+     * @return [LocalSdkDexFiles] if DEX files available or null if not.
+     */
+    fun dexFilesFor(sdkConfig: LocalSdkConfig): LocalSdkDexFiles?
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/test/java/androidx/privacysandbox/sdkruntime/client/loader/ResourceRemappingTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/test/java/androidx/privacysandbox/sdkruntime/client/loader/ResourceRemappingTest.kt
new file mode 100644
index 0000000..cb17a81
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/test/java/androidx/privacysandbox/sdkruntime/client/loader/ResourceRemappingTest.kt
@@ -0,0 +1,149 @@
+/*
+ * 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.client.loader
+
+import androidx.privacysandbox.sdkruntime.client.config.ResourceRemappingConfig
+import androidx.room.compiler.processing.util.Source
+import androidx.room.compiler.processing.util.compiler.TestCompilationArguments
+import androidx.room.compiler.processing.util.compiler.compile
+import com.google.common.truth.Truth.assertThat
+import java.net.URLClassLoader
+import kotlin.reflect.KClass
+import org.junit.Assert.assertThrows
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ResourceRemappingTest {
+
+    @field:Rule
+    @JvmField
+    val temporaryFolder = TemporaryFolder()
+
+    @Test
+    fun apply_whenNonNullConfig_updatePackageId() {
+        val classLoader = compileAndLoad(
+            Source.java(
+                "RPackage", """
+                   public class RPackage {
+                        public static int packageId = 0;
+                   }
+                """
+            )
+        )
+
+        ResourceRemapping.apply(
+            classLoader,
+            ResourceRemappingConfig(
+                rPackageClassName = "RPackage",
+                packageId = 42
+            )
+        )
+
+        val rPackageClass = classLoader.loadClass("RPackage")
+        val packageIdField = rPackageClass.getDeclaredField("packageId")
+        val value = packageIdField.getInt(null)
+
+        assertThat(value).isEqualTo(42)
+    }
+
+    @Test
+    fun apply_whenNullConfig_doesntThrow() {
+        val classLoader = compileAndLoad(
+            Source.java(
+                "AnotherClass", """
+                   public class AnotherClass {
+                   }
+                """
+            )
+        )
+
+        ResourceRemapping.apply(
+            classLoader,
+            remappingConfig = null
+        )
+    }
+
+    @Test
+    fun apply_whenNoRPackageClass_throwsClassNotFoundException() {
+        val source = Source.java(
+            "AnotherClass", """
+                public class AnotherClass {
+                }
+                """
+        )
+
+        val config = ResourceRemappingConfig(
+            rPackageClassName = "RPackage",
+            packageId = 42
+        )
+
+        assertThrows(ClassNotFoundException::class, source, config)
+    }
+
+    @Test
+    fun apply_whenNoPackageIdField_throwsNoSuchFieldException() {
+        val source = Source.java(
+            "RPackage", """
+                   public class RPackage {
+                   }
+                """
+        )
+
+        val config = ResourceRemappingConfig(
+            rPackageClassName = "RPackage",
+            packageId = 42
+        )
+
+        assertThrows(NoSuchFieldException::class, source, config)
+    }
+
+    private fun assertThrows(
+        expectedThrowable: KClass<out Exception>,
+        source: Source,
+        config: ResourceRemappingConfig
+    ) {
+        val classLoader = compileAndLoad(source)
+        assertThrows(expectedThrowable.java) {
+            ResourceRemapping.apply(
+                classLoader,
+                config
+            )
+        }
+    }
+
+    private fun compileAndLoad(source: Source): ClassLoader {
+        val compilationResult = compile(
+            temporaryFolder.root,
+            TestCompilationArguments(
+                sources = listOf(source),
+            )
+        )
+
+        assertThat(compilationResult.success).isTrue()
+
+        return URLClassLoader.newInstance(
+            compilationResult.outputClasspath.map {
+                it.toURI().toURL()
+            }.toTypedArray(),
+            /* parent = */ null
+        )
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/api/current.txt b/privacysandbox/sdkruntime/sdkruntime-core/api/current.txt
index e6f50d0..56a2116 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/api/current.txt
+++ b/privacysandbox/sdkruntime/sdkruntime-core/api/current.txt
@@ -1 +1,67 @@
 // Signature format: 4.0
+package androidx.privacysandbox.sdkruntime.core {
+
+  public final class LoadSdkCompatException extends java.lang.Exception {
+    ctor public LoadSdkCompatException(Throwable cause, android.os.Bundle extraInfo);
+    method public android.os.Bundle getExtraInformation();
+    method public int getLoadSdkErrorCode();
+    property public final android.os.Bundle extraInformation;
+    property public final int loadSdkErrorCode;
+    field public static final androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException.Companion Companion;
+    field public static final int LOAD_SDK_ALREADY_LOADED = 101; // 0x65
+    field public static final int LOAD_SDK_INTERNAL_ERROR = 500; // 0x1f4
+    field public static final int LOAD_SDK_NOT_FOUND = 100; // 0x64
+    field public static final int LOAD_SDK_SDK_DEFINED_ERROR = 102; // 0x66
+    field public static final int LOAD_SDK_SDK_SANDBOX_DISABLED = 103; // 0x67
+    field public static final int SDK_SANDBOX_PROCESS_NOT_AVAILABLE = 503; // 0x1f7
+  }
+
+  public static final class LoadSdkCompatException.Companion {
+  }
+
+  public final class SandboxedSdkCompat {
+    ctor public SandboxedSdkCompat(android.os.IBinder sdkInterface);
+    method public android.os.IBinder? getInterface();
+    method public androidx.privacysandbox.sdkruntime.core.SandboxedSdkInfo? getSdkInfo();
+  }
+
+  public final class SandboxedSdkInfo {
+    ctor public SandboxedSdkInfo(String name, long version);
+    method public String getName();
+    method public long getVersion();
+    property public final String name;
+    property public final long version;
+  }
+
+  @RequiresExtension(extension=android.os.ext.SdkExtensions.AD_SERVICES, version=4) public final class SandboxedSdkProviderAdapter extends android.app.sdksandbox.SandboxedSdkProvider {
+    ctor public SandboxedSdkProviderAdapter();
+    method public android.view.View getView(android.content.Context windowContext, android.os.Bundle params, int width, int height);
+    method @kotlin.jvm.Throws(exceptionClasses=LoadSdkException::class) public android.app.sdksandbox.SandboxedSdk onLoadSdk(android.os.Bundle params) throws android.app.sdksandbox.LoadSdkException;
+  }
+
+  public abstract class SandboxedSdkProviderCompat {
+    ctor public SandboxedSdkProviderCompat();
+    method public final void attachContext(android.content.Context context);
+    method public void beforeUnloadSdk();
+    method public final android.content.Context? getContext();
+    method public abstract android.view.View getView(android.content.Context windowContext, android.os.Bundle params, int width, int height);
+    method @kotlin.jvm.Throws(exceptionClasses=LoadSdkCompatException::class) public abstract androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat onLoadSdk(android.os.Bundle params) throws androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException;
+    property public final android.content.Context? context;
+  }
+
+}
+
+package androidx.privacysandbox.sdkruntime.core.controller {
+
+  public final class SdkSandboxControllerCompat {
+    method public static androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat from(android.content.Context context);
+    method public java.util.List<androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat> getSandboxedSdks();
+    field public static final androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat.Companion Companion;
+  }
+
+  public static final class SdkSandboxControllerCompat.Companion {
+    method public androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat from(android.content.Context context);
+  }
+
+}
+
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/api/public_plus_experimental_current.txt b/privacysandbox/sdkruntime/sdkruntime-core/api/public_plus_experimental_current.txt
index e6f50d0..56a2116 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/api/public_plus_experimental_current.txt
+++ b/privacysandbox/sdkruntime/sdkruntime-core/api/public_plus_experimental_current.txt
@@ -1 +1,67 @@
 // Signature format: 4.0
+package androidx.privacysandbox.sdkruntime.core {
+
+  public final class LoadSdkCompatException extends java.lang.Exception {
+    ctor public LoadSdkCompatException(Throwable cause, android.os.Bundle extraInfo);
+    method public android.os.Bundle getExtraInformation();
+    method public int getLoadSdkErrorCode();
+    property public final android.os.Bundle extraInformation;
+    property public final int loadSdkErrorCode;
+    field public static final androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException.Companion Companion;
+    field public static final int LOAD_SDK_ALREADY_LOADED = 101; // 0x65
+    field public static final int LOAD_SDK_INTERNAL_ERROR = 500; // 0x1f4
+    field public static final int LOAD_SDK_NOT_FOUND = 100; // 0x64
+    field public static final int LOAD_SDK_SDK_DEFINED_ERROR = 102; // 0x66
+    field public static final int LOAD_SDK_SDK_SANDBOX_DISABLED = 103; // 0x67
+    field public static final int SDK_SANDBOX_PROCESS_NOT_AVAILABLE = 503; // 0x1f7
+  }
+
+  public static final class LoadSdkCompatException.Companion {
+  }
+
+  public final class SandboxedSdkCompat {
+    ctor public SandboxedSdkCompat(android.os.IBinder sdkInterface);
+    method public android.os.IBinder? getInterface();
+    method public androidx.privacysandbox.sdkruntime.core.SandboxedSdkInfo? getSdkInfo();
+  }
+
+  public final class SandboxedSdkInfo {
+    ctor public SandboxedSdkInfo(String name, long version);
+    method public String getName();
+    method public long getVersion();
+    property public final String name;
+    property public final long version;
+  }
+
+  @RequiresExtension(extension=android.os.ext.SdkExtensions.AD_SERVICES, version=4) public final class SandboxedSdkProviderAdapter extends android.app.sdksandbox.SandboxedSdkProvider {
+    ctor public SandboxedSdkProviderAdapter();
+    method public android.view.View getView(android.content.Context windowContext, android.os.Bundle params, int width, int height);
+    method @kotlin.jvm.Throws(exceptionClasses=LoadSdkException::class) public android.app.sdksandbox.SandboxedSdk onLoadSdk(android.os.Bundle params) throws android.app.sdksandbox.LoadSdkException;
+  }
+
+  public abstract class SandboxedSdkProviderCompat {
+    ctor public SandboxedSdkProviderCompat();
+    method public final void attachContext(android.content.Context context);
+    method public void beforeUnloadSdk();
+    method public final android.content.Context? getContext();
+    method public abstract android.view.View getView(android.content.Context windowContext, android.os.Bundle params, int width, int height);
+    method @kotlin.jvm.Throws(exceptionClasses=LoadSdkCompatException::class) public abstract androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat onLoadSdk(android.os.Bundle params) throws androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException;
+    property public final android.content.Context? context;
+  }
+
+}
+
+package androidx.privacysandbox.sdkruntime.core.controller {
+
+  public final class SdkSandboxControllerCompat {
+    method public static androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat from(android.content.Context context);
+    method public java.util.List<androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat> getSandboxedSdks();
+    field public static final androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat.Companion Companion;
+  }
+
+  public static final class SdkSandboxControllerCompat.Companion {
+    method public androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat from(android.content.Context context);
+  }
+
+}
+
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/api/restricted_current.txt b/privacysandbox/sdkruntime/sdkruntime-core/api/restricted_current.txt
index e6f50d0..56a2116 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/api/restricted_current.txt
+++ b/privacysandbox/sdkruntime/sdkruntime-core/api/restricted_current.txt
@@ -1 +1,67 @@
 // Signature format: 4.0
+package androidx.privacysandbox.sdkruntime.core {
+
+  public final class LoadSdkCompatException extends java.lang.Exception {
+    ctor public LoadSdkCompatException(Throwable cause, android.os.Bundle extraInfo);
+    method public android.os.Bundle getExtraInformation();
+    method public int getLoadSdkErrorCode();
+    property public final android.os.Bundle extraInformation;
+    property public final int loadSdkErrorCode;
+    field public static final androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException.Companion Companion;
+    field public static final int LOAD_SDK_ALREADY_LOADED = 101; // 0x65
+    field public static final int LOAD_SDK_INTERNAL_ERROR = 500; // 0x1f4
+    field public static final int LOAD_SDK_NOT_FOUND = 100; // 0x64
+    field public static final int LOAD_SDK_SDK_DEFINED_ERROR = 102; // 0x66
+    field public static final int LOAD_SDK_SDK_SANDBOX_DISABLED = 103; // 0x67
+    field public static final int SDK_SANDBOX_PROCESS_NOT_AVAILABLE = 503; // 0x1f7
+  }
+
+  public static final class LoadSdkCompatException.Companion {
+  }
+
+  public final class SandboxedSdkCompat {
+    ctor public SandboxedSdkCompat(android.os.IBinder sdkInterface);
+    method public android.os.IBinder? getInterface();
+    method public androidx.privacysandbox.sdkruntime.core.SandboxedSdkInfo? getSdkInfo();
+  }
+
+  public final class SandboxedSdkInfo {
+    ctor public SandboxedSdkInfo(String name, long version);
+    method public String getName();
+    method public long getVersion();
+    property public final String name;
+    property public final long version;
+  }
+
+  @RequiresExtension(extension=android.os.ext.SdkExtensions.AD_SERVICES, version=4) public final class SandboxedSdkProviderAdapter extends android.app.sdksandbox.SandboxedSdkProvider {
+    ctor public SandboxedSdkProviderAdapter();
+    method public android.view.View getView(android.content.Context windowContext, android.os.Bundle params, int width, int height);
+    method @kotlin.jvm.Throws(exceptionClasses=LoadSdkException::class) public android.app.sdksandbox.SandboxedSdk onLoadSdk(android.os.Bundle params) throws android.app.sdksandbox.LoadSdkException;
+  }
+
+  public abstract class SandboxedSdkProviderCompat {
+    ctor public SandboxedSdkProviderCompat();
+    method public final void attachContext(android.content.Context context);
+    method public void beforeUnloadSdk();
+    method public final android.content.Context? getContext();
+    method public abstract android.view.View getView(android.content.Context windowContext, android.os.Bundle params, int width, int height);
+    method @kotlin.jvm.Throws(exceptionClasses=LoadSdkCompatException::class) public abstract androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat onLoadSdk(android.os.Bundle params) throws androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException;
+    property public final android.content.Context? context;
+  }
+
+}
+
+package androidx.privacysandbox.sdkruntime.core.controller {
+
+  public final class SdkSandboxControllerCompat {
+    method public static androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat from(android.content.Context context);
+    method public java.util.List<androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat> getSandboxedSdks();
+    field public static final androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat.Companion Companion;
+  }
+
+  public static final class SdkSandboxControllerCompat.Companion {
+    method public androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat from(android.content.Context context);
+  }
+
+}
+
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/build.gradle b/privacysandbox/sdkruntime/sdkruntime-core/build.gradle
index bbd8542..68c7464 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/build.gradle
+++ b/privacysandbox/sdkruntime/sdkruntime-core/build.gradle
@@ -24,9 +24,28 @@
 
 dependencies {
     api(libs.kotlinStdlib)
+    api("androidx.annotation:annotation:1.6.0")
+
+    implementation("androidx.core:core:1.8.0")
+
+    // TODO(b/249982004): cleanup dependencies
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.junit)
+
+    androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(libs.dexmakerMockitoInline, excludes.bytebuddy) // DexMaker has it"s own MockMaker
 }
 
 android {
+    lintOptions {
+        // All components could be loaded from another app via client library
+        disable("BanKeepAnnotation")
+    }
+
     namespace "androidx.privacysandbox.sdkruntime.core"
 }
 
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/AndroidManifest.xml b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..3f2e804
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+</manifest>
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/assets/SandboxedSdkProviderCompatClassName.txt b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/assets/SandboxedSdkProviderCompatClassName.txt
new file mode 100644
index 0000000..3d062e4
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/assets/SandboxedSdkProviderCompatClassName.txt
@@ -0,0 +1 @@
+androidx.privacysandbox.sdkruntime.core.SandboxedSdkProviderAdapterTest$TestOnLoadReturnResultSdkProvider
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/LoadSdkCompatExceptionTest.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/LoadSdkCompatExceptionTest.kt
new file mode 100644
index 0000000..1386c41
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/LoadSdkCompatExceptionTest.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.os.Build.VERSION_CODES.TIRAMISU
+import android.os.Bundle
+import android.os.ext.SdkExtensions.AD_SERVICES
+import androidx.annotation.RequiresExtension
+import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException.Companion.toLoadCompatSdkException
+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.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+// TODO(b/262577044) Remove RequiresExtension after extensions support in @SdkSuppress
+@RequiresExtension(extension = AD_SERVICES, version = 4)
+@SdkSuppress(minSdkVersion = TIRAMISU)
+class LoadSdkCompatExceptionTest {
+
+    @Before
+    fun setUp() {
+        assumeTrue("Requires Sandbox API available", isSandboxApiAvailable())
+    }
+
+    @Test
+    fun toLoadSdkException_returnLoadSdkException() {
+        val loadSdkCompatException = LoadSdkCompatException(RuntimeException(), Bundle())
+
+        val loadSdkException = loadSdkCompatException.toLoadSdkException()
+
+        assertThat(loadSdkException.cause)
+            .isSameInstanceAs(loadSdkCompatException.cause)
+        assertThat(loadSdkException.extraInformation)
+            .isSameInstanceAs(loadSdkCompatException.extraInformation)
+        assertThat(loadSdkException.loadSdkErrorCode)
+            .isEqualTo(loadSdkCompatException.loadSdkErrorCode)
+    }
+
+    @Test
+    fun toLoadCompatSdkException_returnLoadCompatSdkException() {
+        val loadSdkException = LoadSdkException(
+            RuntimeException(),
+            Bundle()
+        )
+
+        val loadCompatSdkException = toLoadCompatSdkException(loadSdkException)
+
+        assertThat(loadCompatSdkException.cause)
+            .isSameInstanceAs(loadSdkException.cause)
+        assertThat(loadCompatSdkException.extraInformation)
+            .isSameInstanceAs(loadSdkException.extraInformation)
+        assertThat(loadCompatSdkException.loadSdkErrorCode)
+            .isEqualTo(loadSdkException.loadSdkErrorCode)
+    }
+
+    private fun isSandboxApiAvailable() =
+        AdServicesInfo.isAtLeastV4()
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkCompatTest.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkCompatTest.kt
new file mode 100644
index 0000000..7ff1ce7
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkCompatTest.kt
@@ -0,0 +1,176 @@
+/*
+ * 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.SandboxedSdk
+import android.content.pm.SharedLibraryInfo
+import android.os.Binder
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.os.ext.SdkExtensions
+import androidx.annotation.RequiresApi
+import androidx.annotation.RequiresExtension
+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.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.`when`
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SandboxedSdkCompatTest {
+
+    @Test
+    fun getInterface_returnsBinderPassedToCreate() {
+        val binder = Binder()
+
+        val sandboxedSdkCompat = SandboxedSdkCompat(binder)
+
+        assertThat(sandboxedSdkCompat.getInterface())
+            .isSameInstanceAs(binder)
+    }
+
+    @Test
+    // TODO(b/262577044) Remove RequiresExtension after extensions support in @SdkSuppress
+    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
+    @SdkSuppress(minSdkVersion = TIRAMISU)
+    fun toSandboxedSdk_whenCreatedFromBinder_returnsSandboxedSdkWithSameBinder() {
+        assumeTrue("Requires Sandbox API available", isSandboxApiAvailable())
+
+        val binder = Binder()
+
+        val toSandboxedSdkResult = SandboxedSdkCompat(binder).toSandboxedSdk()
+
+        assertThat(toSandboxedSdkResult.getInterface())
+            .isSameInstanceAs(binder)
+    }
+
+    @Test
+    // TODO(b/262577044) Remove RequiresExtension after extensions support in @SdkSuppress
+    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
+    @SdkSuppress(minSdkVersion = TIRAMISU)
+    fun toSandboxedSdk_whenCreatedFromSandboxedSdk_returnsSameSandboxedSdk() {
+        assumeTrue("Requires Sandbox API available", isSandboxApiAvailable())
+
+        val binder = Binder()
+        val sandboxedSdk = SandboxedSdk(binder)
+
+        val toSandboxedSdkResult = SandboxedSdkCompat(sandboxedSdk).toSandboxedSdk()
+
+        assertThat(toSandboxedSdkResult)
+            .isSameInstanceAs(sandboxedSdk)
+    }
+
+    @Test
+    fun getSdkInfo_whenCreatedFromBinder_returnsNull() {
+        val binder = Binder()
+        val sandboxedSdkCompat = SandboxedSdkCompat(binder)
+
+        assertThat(sandboxedSdkCompat.getSdkInfo()).isNull()
+    }
+
+    @Test
+    fun getSdkInfo_whenCreatedFromBinderAndSdkInfo_returnsSdkInfo() {
+        val binder = Binder()
+        val sdkInfo = SandboxedSdkInfo(name = "sdkname", version = 42)
+        val sandboxedSdkCompat = SandboxedSdkCompat(binder, sdkInfo)
+
+        assertThat(sandboxedSdkCompat.getSdkInfo()).isEqualTo(sdkInfo)
+    }
+
+    @Test
+    // TODO(b/262577044) Remove RequiresExtension after extensions support in @SdkSuppress
+    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
+    @SdkSuppress(minSdkVersion = TIRAMISU)
+    fun getSdkInfo_whenCreatedFromSandboxedSdkAndNoSharedLibraryInfo_returnsNull() {
+        assumeTrue("Requires Sandbox API available", isSandboxApiAvailable())
+        assumeFalse(
+            "Requires SharedLibraryInfo not available",
+            isSharedLibraryInfoAvailable()
+        )
+
+        val binder = Binder()
+        val sandboxedSdk = SandboxedSdk(binder)
+        val sandboxedSdkCompat = SandboxedSdkCompat(sandboxedSdk)
+
+        assertThat(sandboxedSdkCompat.getSdkInfo()).isNull()
+    }
+
+    @Test
+    // TODO(b/262577044) Remove RequiresExtension after extensions support in @SdkSuppress
+    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+    @SdkSuppress(minSdkVersion = TIRAMISU)
+    fun getSdkInfo_whenCreatedFromSandboxedSdkAndSharedLibraryInfoAvailable_returnsSdkInfo() {
+        assumeTrue("Requires Sandbox API available", isSandboxApiAvailable())
+        assumeTrue(
+            "Requires SharedLibraryInfo available",
+            isSharedLibraryInfoAvailable()
+        )
+
+        val sdkName = "sdkName"
+        val sdkVersion = 1L
+        val sharedLibraryInfo = ApiAdServicesV5.mockSharedLibraryInfo(sdkName, sdkVersion)
+        val sandboxedSdk = ApiAdServicesV5.mockSandboxedSdkWithSharedLibraryInfo(sharedLibraryInfo)
+
+        val sandboxedSdkCompat = SandboxedSdkCompat(sandboxedSdk)
+        val sdkInfo = sandboxedSdkCompat.getSdkInfo()
+
+        assertThat(sdkInfo).isEqualTo(
+            SandboxedSdkInfo(
+                sdkName,
+                sdkVersion
+            )
+        )
+    }
+
+    private object ApiAdServicesV5 {
+        @RequiresApi(28)
+        fun mockSharedLibraryInfo(
+            sdkName: String,
+            sdkVersion: Long
+        ): SharedLibraryInfo {
+            // No public constructor for SharedLibraryInfo available.
+            val sharedLibraryInfo = Mockito.mock(SharedLibraryInfo::class.java)
+            `when`(sharedLibraryInfo.name).thenReturn(sdkName)
+            `when`(sharedLibraryInfo.longVersion).thenReturn(sdkVersion)
+            return sharedLibraryInfo
+        }
+
+        @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+        fun mockSandboxedSdkWithSharedLibraryInfo(
+            sharedLibraryInfo: SharedLibraryInfo
+        ): SandboxedSdk {
+            val binder = Binder()
+            val sandboxedSdk = SandboxedSdk(binder)
+            val sandboxedSdkSpy = spy(sandboxedSdk)
+            // Platform uses attachSharedLibraryInfo (hidden) to set sharedLibraryInfo.
+            doReturn(sharedLibraryInfo).`when`(sandboxedSdkSpy).sharedLibraryInfo
+            return sandboxedSdkSpy
+        }
+    }
+
+    private fun isSandboxApiAvailable() =
+        AdServicesInfo.isAtLeastV4()
+
+    private fun isSharedLibraryInfoAvailable() =
+        AdServicesInfo.isAtLeastV5()
+}
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkProviderAdapterTest.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkProviderAdapterTest.kt
new file mode 100644
index 0000000..1cda2cb
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkProviderAdapterTest.kt
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.privacysandbox.sdkruntime.core
+
+import android.app.sdksandbox.LoadSdkException
+import android.content.Context
+import android.os.Binder
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.os.Bundle
+import android.os.ext.SdkExtensions.AD_SERVICES
+import android.view.View
+import androidx.annotation.RequiresExtension
+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 kotlin.reflect.KClass
+import org.junit.Assert.assertThrows
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+// TODO(b/262577044) Remove RequiresExtension after extensions support in @SdkSuppress
+@RequiresExtension(extension = AD_SERVICES, version = 4)
+@SdkSuppress(minSdkVersion = TIRAMISU)
+class SandboxedSdkProviderAdapterTest {
+
+    private lateinit var context: Context
+
+    @Before
+    fun setUp() {
+        assumeTrue("Requires Sandbox API available", isSandboxApiAvailable())
+        context = ApplicationProvider.getApplicationContext()
+    }
+
+    @Test
+    fun testAdapterGetCompatClassNameFromAsset() {
+        val expectedClassName = context.assets
+            .open("SandboxedSdkProviderCompatClassName.txt")
+            .use { inputStream ->
+                inputStream.bufferedReader().readLine()
+            }
+
+        val adapter = SandboxedSdkProviderAdapter()
+        adapter.attachContext(context)
+
+        adapter.onLoadSdk(Bundle())
+
+        val delegate = adapter.extractDelegate<SandboxedSdkProviderCompat>()
+        assertThat(delegate.javaClass.name)
+            .isEqualTo(expectedClassName)
+    }
+
+    @Test
+    fun onLoadSdk_shouldInstantiateDelegateAndAttachContext() {
+        val adapter = createAdapterFor(TestOnLoadReturnResultSdkProvider::class)
+
+        adapter.onLoadSdk(Bundle())
+
+        val delegate = adapter.extractDelegate<TestOnLoadReturnResultSdkProvider>()
+        assertThat(delegate.context)
+            .isSameInstanceAs(context)
+    }
+
+    @Test
+    fun onLoadSdk_shouldDelegateToCompatClassAndReturnResult() {
+        val adapter = createAdapterFor(TestOnLoadReturnResultSdkProvider::class)
+        val params = Bundle()
+
+        val result = adapter.onLoadSdk(params)
+
+        val delegate = adapter.extractDelegate<TestOnLoadReturnResultSdkProvider>()
+        assertThat(delegate.mLastOnLoadSdkBundle)
+            .isSameInstanceAs(params)
+        assertThat(result.getInterface())
+            .isEqualTo(delegate.mResult.getInterface())
+    }
+
+    @Test
+    fun loadSdk_shouldRethrowExceptionFromCompatClass() {
+        val adapter = createAdapterFor(TestOnLoadThrowSdkProvider::class)
+
+        val ex = assertThrows(LoadSdkException::class.java) {
+            adapter.onLoadSdk(Bundle())
+        }
+
+        val delegate = adapter.extractDelegate<TestOnLoadThrowSdkProvider>()
+        assertThat(ex.cause)
+            .isSameInstanceAs(delegate.mError.cause)
+        assertThat(ex.extraInformation)
+            .isSameInstanceAs(delegate.mError.extraInformation)
+    }
+
+    @Test
+    fun loadSdk_shouldThrowIfCompatClassNotExists() {
+        val adapter = createAdapterFor("NOTEXISTS")
+
+        assertThrows(ClassNotFoundException::class.java) {
+            adapter.onLoadSdk(Bundle())
+        }
+    }
+
+    @Test
+    fun beforeUnloadSdk_shouldDelegateToCompatProvider() {
+        val adapter = createAdapterFor(TestOnBeforeUnloadDelegateSdkProvider::class)
+
+        adapter.beforeUnloadSdk()
+
+        val delegate = adapter.extractDelegate<TestOnBeforeUnloadDelegateSdkProvider>()
+        assertThat(delegate.mBeforeUnloadSdkCalled)
+            .isTrue()
+    }
+
+    @Test
+    fun getView_shouldDelegateToCompatProviderAndReturnResult() {
+        val adapter = createAdapterFor(TestGetViewSdkProvider::class)
+        val windowContext = mock(Context::class.java)
+        val params = Bundle()
+        val width = 1
+        val height = 2
+
+        val result = adapter.getView(windowContext, params, width, height)
+
+        val delegate = adapter.extractDelegate<TestGetViewSdkProvider>()
+        assertThat(result)
+            .isSameInstanceAs(delegate.mView)
+        assertThat(delegate.mLastWindowContext)
+            .isSameInstanceAs(windowContext)
+        assertThat(delegate.mLastParams)
+            .isSameInstanceAs(params)
+        assertThat(delegate.mLastWidth)
+            .isSameInstanceAs(width)
+        assertThat(delegate.mLastHeigh)
+            .isSameInstanceAs(height)
+    }
+
+    private fun createAdapterFor(
+        clazz: KClass<out SandboxedSdkProviderCompat>
+    ): SandboxedSdkProviderAdapter = createAdapterFor(clazz.java.name)
+
+    private fun createAdapterFor(delegateClassName: String): SandboxedSdkProviderAdapter {
+        val adapter = SandboxedSdkProviderAdapter(
+            object : SandboxedSdkProviderAdapter.CompatClassNameProvider {
+                override fun getCompatProviderClassName(context: Context): String {
+                    return delegateClassName
+                }
+            })
+        adapter.attachContext(context)
+        return adapter
+    }
+
+    private inline fun <reified T : SandboxedSdkProviderCompat>
+        SandboxedSdkProviderAdapter.extractDelegate(): T = delegate as T
+
+    class TestOnLoadReturnResultSdkProvider : SandboxedSdkProviderCompat() {
+        var mResult = SandboxedSdkCompat(Binder())
+        var mLastOnLoadSdkBundle: Bundle? = null
+
+        override fun onLoadSdk(params: Bundle): SandboxedSdkCompat {
+            mLastOnLoadSdkBundle = params
+            return mResult
+        }
+
+        override fun getView(
+            windowContext: Context,
+            params: Bundle,
+            width: Int,
+            height: Int
+        ): View {
+            throw RuntimeException("Not implemented")
+        }
+    }
+
+    class TestOnLoadThrowSdkProvider : SandboxedSdkProviderCompat() {
+        var mError = LoadSdkCompatException(RuntimeException(), Bundle())
+
+        @Throws(LoadSdkCompatException::class)
+        override fun onLoadSdk(params: Bundle): SandboxedSdkCompat {
+            throw mError
+        }
+
+        override fun getView(
+            windowContext: Context,
+            params: Bundle,
+            width: Int,
+            height: Int
+        ): View {
+            throw RuntimeException("Stub!")
+        }
+    }
+
+    class TestOnBeforeUnloadDelegateSdkProvider : SandboxedSdkProviderCompat() {
+        var mBeforeUnloadSdkCalled = false
+
+        override fun onLoadSdk(params: Bundle): SandboxedSdkCompat {
+            throw RuntimeException("Not implemented")
+        }
+
+        override fun beforeUnloadSdk() {
+            mBeforeUnloadSdkCalled = true
+        }
+
+        override fun getView(
+            windowContext: Context,
+            params: Bundle,
+            width: Int,
+            height: Int
+        ): View {
+            throw RuntimeException("Not implemented")
+        }
+    }
+
+    class TestGetViewSdkProvider : SandboxedSdkProviderCompat() {
+        val mView: View = mock(View::class.java)
+
+        var mLastWindowContext: Context? = null
+        var mLastParams: Bundle? = null
+        var mLastWidth = 0
+        var mLastHeigh = 0
+
+        override fun onLoadSdk(params: Bundle): SandboxedSdkCompat {
+            throw RuntimeException("Not implemented")
+        }
+
+        override fun getView(
+            windowContext: Context,
+            params: Bundle,
+            width: Int,
+            height: Int
+        ): View {
+            mLastWindowContext = windowContext
+            mLastParams = params
+            mLastWidth = width
+            mLastHeigh = height
+
+            return mView
+        }
+    }
+
+    private fun isSandboxApiAvailable() =
+        AdServicesInfo.isAtLeastV4()
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..1307cec
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompatLocalTest.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.core.controller
+
+import android.content.Context
+import android.os.Binder
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import androidx.privacysandbox.sdkruntime.core.Versions
+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
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SdkSandboxControllerCompatLocalTest {
+
+    private lateinit var context: Context
+
+    @Before
+    fun setUp() {
+        // Emulate loading via client lib
+        Versions.handShake(Versions.API_VERSION)
+
+        context = ApplicationProvider.getApplicationContext()
+    }
+
+    @After
+    fun tearDown() {
+        // Reset version back to avoid failing non-compat tests
+        Versions.resetClientVersion()
+        SdkSandboxControllerCompat.resetLocalImpl()
+    }
+
+    @Test
+    fun getSandboxedSdks_withoutLocalImpl_returnsEmptyList() {
+        val controllerCompat = SdkSandboxControllerCompat.from(context)
+        val sandboxedSdks = controllerCompat.getSandboxedSdks()
+        assertThat(sandboxedSdks).isEmpty()
+    }
+
+    @Test
+    fun getSandboxedSdks_withLocalImpl_returnsListFromLocalImpl() {
+        val expectedResult = listOf(SandboxedSdkCompat(Binder()))
+        SdkSandboxControllerCompat.injectLocalImpl(
+            TestStubImpl(
+                sandboxedSdks = expectedResult
+            )
+        )
+
+        val controllerCompat = SdkSandboxControllerCompat.from(context)
+        val sandboxedSdks = controllerCompat.getSandboxedSdks()
+        assertThat(sandboxedSdks).isEqualTo(expectedResult)
+    }
+
+    private class TestStubImpl(
+        private val sandboxedSdks: List<SandboxedSdkCompat> = emptyList()
+    ) : SdkSandboxControllerCompat.SandboxControllerImpl {
+        override fun getSandboxedSdks() = sandboxedSdks
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompatSandboxedTest.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompatSandboxedTest.kt
new file mode 100644
index 0000000..b133fe0
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/androidTest/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompatSandboxedTest.kt
@@ -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.privacysandbox.sdkruntime.core.controller
+
+import android.app.sdksandbox.SandboxedSdk
+import android.app.sdksandbox.sdkprovider.SdkSandboxController
+import android.content.Context
+import android.os.Binder
+import android.os.Build
+import android.os.ext.SdkExtensions
+import androidx.annotation.RequiresExtension
+import androidx.privacysandbox.sdkruntime.core.AdServicesInfo
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
+import org.junit.Test
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when`
+
+// TODO(b/249982507) Rewrite test to use real SDK in sandbox instead of mocking controller
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+class SdkSandboxControllerCompatSandboxedTest {
+
+    @Test
+    fun getSandboxedSdks_whenApiNotAvailable_notDelegateToSandbox() {
+        assumeFalse(
+            "Requires SandboxController API not available",
+            isSandboxControllerAvailable()
+        )
+
+        val context = spy(ApplicationProvider.getApplicationContext<Context>())
+        val controllerCompat = SdkSandboxControllerCompat.from(context)
+
+        controllerCompat.getSandboxedSdks()
+
+        verifyZeroInteractions(context)
+    }
+
+    @Test
+    fun getSandboxedSdks_whenApiNotAvailable_returnsEmptyList() {
+        assumeFalse(
+            "Requires SandboxController API not available",
+            isSandboxControllerAvailable()
+        )
+
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val controllerCompat = SdkSandboxControllerCompat.from(context)
+
+        val sandboxedSdks = controllerCompat.getSandboxedSdks()
+
+        assertThat(sandboxedSdks).isEmpty()
+    }
+
+    @Test
+    // TODO(b/262577044) Remove RequiresExtension after extensions support in @SdkSuppress
+    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    fun getSandboxedSdks_whenApiAvailable_returnsListFromPlatformApi() {
+        assumeTrue(
+            "Requires SandboxController API available",
+            isSandboxControllerAvailable()
+        )
+
+        val context = spy(ApplicationProvider.getApplicationContext<Context>())
+        val sdkSandboxController = mock(SdkSandboxController::class.java)
+        doReturn(sdkSandboxController)
+            .`when`(context).getSystemService(SdkSandboxController::class.java)
+
+        val sandboxedSdk = SandboxedSdk(Binder())
+        `when`(sdkSandboxController.sandboxedSdks)
+            .thenReturn(listOf(sandboxedSdk))
+
+        val controllerCompat = SdkSandboxControllerCompat.from(context)
+        val sandboxedSdks = controllerCompat.getSandboxedSdks()
+        assertThat(sandboxedSdks).hasSize(1)
+        val result = sandboxedSdks[0]
+
+        assertThat(result.getInterface()).isEqualTo(sandboxedSdk.getInterface())
+    }
+
+    private fun isSandboxControllerAvailable() =
+        AdServicesInfo.isAtLeastV5()
+}
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/AdServicesInfo.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/AdServicesInfo.kt
new file mode 100644
index 0000000..67e884b
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/AdServicesInfo.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.os.Build
+import android.os.ext.SdkExtensions
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+
+/**
+ * Temporary replacement for BuildCompat.AD_SERVICES_EXTENSION_INT.
+ * TODO(b/249981547) Replace with AD_SERVICES_EXTENSION_INT after new core library release
+ *
+ * @suppress
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+object AdServicesInfo {
+
+    fun isAtLeastV4(): Boolean {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
+            Extensions30Impl.isAtLeastV4()
+    }
+
+    fun isAtLeastV5(): Boolean {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
+            Extensions30Impl.isAtLeastV5()
+    }
+
+    @RequiresApi(30)
+    private object Extensions30Impl {
+        @DoNotInline
+        fun isAtLeastV4() =
+            SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES) >= 4
+
+        @DoNotInline
+        fun isAtLeastV5() =
+            SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES) >= 5
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..2222ada
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/LoadSdkCompatException.kt
@@ -0,0 +1,224 @@
+/*
+ * 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.os.Bundle
+import android.os.ext.SdkExtensions.AD_SERVICES
+import androidx.annotation.DoNotInline
+import androidx.annotation.IntDef
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RestrictTo
+import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
+
+/**
+ * Compat alternative for [LoadSdkException].
+ * Thrown from [SandboxedSdkProviderCompat.onLoadSdk].
+ *
+ * @see [LoadSdkException]
+ */
+class LoadSdkCompatException : Exception {
+
+    /**
+     * Result code this exception was constructed with.
+     *
+     * @see [LoadSdkException.getLoadSdkErrorCode]
+     */
+    @field:LoadSdkErrorCode
+    @get:LoadSdkErrorCode
+    val loadSdkErrorCode: Int
+
+    /**
+     * Extra error information this exception was constructed with.
+     *
+     * @see [LoadSdkException.getExtraInformation]
+     */
+    val extraInformation: Bundle
+
+    /**
+     * Initializes a LoadSdkCompatException with a result code, a message, a cause and extra
+     * information.
+     *
+     * @param loadSdkErrorCode The result code.
+     * @param message The detailed message.
+     * @param cause The cause of the exception. A null value is permitted, and indicates that the
+     *  cause is nonexistent or unknown.
+     * @param extraInformation Extra error information. This is empty if there is no such information.
+     * @suppress
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    @JvmOverloads
+    constructor(
+        @LoadSdkErrorCode loadSdkErrorCode: Int,
+        message: String?,
+        cause: Throwable?,
+        extraInformation: Bundle = Bundle()
+    ) : super(message, cause) {
+        this.loadSdkErrorCode = loadSdkErrorCode
+        this.extraInformation = extraInformation
+    }
+
+    /**
+     * Initializes a LoadSdkCompatException with a result code and a message
+     *
+     * @param loadSdkErrorCode The result code.
+     * @param message The detailed message.
+     * @suppress
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    constructor(
+        @LoadSdkErrorCode loadSdkErrorCode: Int,
+        message: String?
+    ) : this(loadSdkErrorCode, message, cause = null)
+
+    /**
+     * Initializes a LoadSdkCompatException with a Throwable and a Bundle.
+     *
+     * @param cause The cause of the exception.
+     * @param extraInfo Extra error information. This is empty if there is no such information.
+     */
+    constructor(
+        cause: Throwable,
+        extraInfo: Bundle
+    ) : this(LOAD_SDK_SDK_DEFINED_ERROR, "", cause, extraInfo)
+
+    /** @suppress */
+    @IntDef(
+        SDK_SANDBOX_PROCESS_NOT_AVAILABLE,
+        LOAD_SDK_NOT_FOUND,
+        LOAD_SDK_ALREADY_LOADED,
+        LOAD_SDK_SDK_DEFINED_ERROR,
+        LOAD_SDK_SDK_SANDBOX_DISABLED,
+        LOAD_SDK_INTERNAL_ERROR,
+    )
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @Retention(AnnotationRetention.SOURCE)
+    annotation class LoadSdkErrorCode
+
+    /**
+     *  Create platform [LoadSdkException] from compat exception.
+     *
+     *  @return Platform exception.
+     */
+    @RequiresExtension(extension = AD_SERVICES, version = 4)
+    internal fun toLoadSdkException(): LoadSdkException {
+        return ApiAdServicesV4Impl.toLoadSdkException(this)
+    }
+
+    @RequiresExtension(extension = AD_SERVICES, version = 4)
+    private object ApiAdServicesV4Impl {
+
+        @DoNotInline
+        fun toLoadSdkException(ex: LoadSdkCompatException): LoadSdkException {
+            return LoadSdkException(
+                ex.cause!!,
+                ex.extraInformation
+            )
+        }
+
+        @DoNotInline
+        fun toLoadCompatSdkException(ex: LoadSdkException): LoadSdkCompatException {
+            return LoadSdkCompatException(
+                toLoadSdkErrorCodeCompat(ex.loadSdkErrorCode),
+                ex.message,
+                ex.cause,
+                ex.extraInformation
+            )
+        }
+
+        @LoadSdkErrorCode
+        private fun toLoadSdkErrorCodeCompat(
+            value: Int
+        ): Int {
+            return value // TODO(b/249982002): Validate and convert
+        }
+    }
+
+    companion object {
+
+        /**
+         * Sdk sandbox process is not available.
+         *
+         * This indicates that the sdk sandbox process is not available, either because it has died,
+         * disconnected or was not created in the first place.
+         *
+         * @see [android.app.sdksandbox.SdkSandboxManager.SDK_SANDBOX_PROCESS_NOT_AVAILABLE]
+         */
+        const val SDK_SANDBOX_PROCESS_NOT_AVAILABLE = 503
+
+        /**
+         * SDK not found.
+         *
+         * This indicates that client application tried to load a non-existing SDK.
+         *
+         * @see [android.app.sdksandbox.SdkSandboxManager.LOAD_SDK_NOT_FOUND]
+         */
+        const val LOAD_SDK_NOT_FOUND = 100
+
+        /**
+         * SDK is already loaded.
+         *
+         * This indicates that client application tried to reload the same SDK after being
+         * successfully loaded.
+         *
+         * @see [android.app.sdksandbox.SdkSandboxManager.LOAD_SDK_ALREADY_LOADED]
+         */
+        const val LOAD_SDK_ALREADY_LOADED = 101
+
+        /**
+         * SDK error after being loaded.
+         *
+         * This indicates that the SDK encountered an error during post-load initialization. The
+         * details of this can be obtained from the Bundle returned in [LoadSdkCompatException].
+         *
+         * @see [android.app.sdksandbox.SdkSandboxManager.LOAD_SDK_SDK_DEFINED_ERROR]
+         */
+        const val LOAD_SDK_SDK_DEFINED_ERROR = 102
+
+        /**
+         * SDK sandbox is disabled.
+         *
+         * This indicates that the SDK sandbox is disabled. Any subsequent attempts to load SDKs in
+         * this boot will also fail.
+         *
+         * @see [android.app.sdksandbox.SdkSandboxManager.LOAD_SDK_SDK_SANDBOX_DISABLED]
+         */
+        const val LOAD_SDK_SDK_SANDBOX_DISABLED = 103
+
+        /**
+         * Internal error while loading SDK.
+         *
+         * This indicates a generic internal error happened while applying the call from
+         * client application.
+         *
+         * @see [android.app.sdksandbox.SdkSandboxManager.LOAD_SDK_INTERNAL_ERROR]
+         */
+        const val LOAD_SDK_INTERNAL_ERROR = 500
+
+        /**
+         *  Create compat exception from platform [LoadSdkException].
+         *
+         *  @param ex Platform exception
+         *  @return Compat exception.
+         *  @suppress
+         */
+        @RequiresExtension(extension = AD_SERVICES, version = 4)
+        @RestrictTo(LIBRARY_GROUP)
+        fun toLoadCompatSdkException(ex: LoadSdkException): LoadSdkCompatException {
+            return ApiAdServicesV4Impl.toLoadCompatSdkException(ex)
+        }
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..2d55aff
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkCompat.kt
@@ -0,0 +1,192 @@
+/*
+ * 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.SandboxedSdk
+import android.os.IBinder
+import android.os.ext.SdkExtensions.AD_SERVICES
+import androidx.annotation.DoNotInline
+import androidx.annotation.Keep
+import androidx.annotation.RequiresApi
+import androidx.annotation.RequiresExtension
+import androidx.annotation.RestrictTo
+import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
+
+/**
+ * Compat wrapper for [SandboxedSdk].
+ * Represents an SDK loaded in the sandbox process or locally.
+ * An application should use this object to obtain an interface to the SDK through [getInterface].
+ *
+ * The SDK should create it when [SandboxedSdkProviderCompat.onLoadSdk] is called, and drop all
+ * references to it when [SandboxedSdkProviderCompat.beforeUnloadSdk] is called. Additionally, the
+ * SDK should fail calls made to the [IBinder] returned from [getInterface] after
+ * [SandboxedSdkProviderCompat.beforeUnloadSdk] has been called.
+ *
+ * @see [SandboxedSdk]
+ *
+ */
+class SandboxedSdkCompat private constructor(
+    private val sdkImpl: SandboxedSdkImpl
+) {
+
+    /**
+     * Creates SandboxedSdkCompat from SDK Binder object.
+     *
+     * @param sdkInterface The SDK's interface. This will be the entrypoint into the sandboxed SDK
+     * for the application. The SDK should keep this valid until it's loaded in the sandbox, and
+     * start failing calls to this interface once it has been unloaded
+     *
+     * This interface can later be retrieved using [getInterface].
+     *
+     * @see [SandboxedSdk]
+     */
+    constructor(sdkInterface: IBinder) : this(sdkInterface, sdkInfo = null)
+
+    /**
+     * Creates SandboxedSdkCompat from SDK [IBinder] object and [SandboxedSdkInfo].
+     *
+     * @param sdkInterface The SDK's interface.
+     * @param sdkInfo Information about SDK's name and version.
+     * @suppress
+     */
+    @Keep // Reflection call from client part
+    @RestrictTo(LIBRARY_GROUP)
+    constructor(
+        sdkInterface: IBinder,
+        sdkInfo: SandboxedSdkInfo?
+    ) : this(CompatImpl(sdkInterface, sdkInfo))
+
+    /**
+     * Creates SandboxedSdkCompat wrapper around existing [SandboxedSdk] object.
+     *
+     * @param sandboxedSdk SandboxedSdk object. All calls will be delegated to that object.
+     * @suppress
+     */
+    @RequiresExtension(extension = AD_SERVICES, version = 4)
+    @RestrictTo(LIBRARY_GROUP)
+    constructor(sandboxedSdk: SandboxedSdk) : this(
+        SdkImplFactory.createSdkImpl(sandboxedSdk)
+    )
+
+    /**
+     * Returns the interface to the loaded SDK.
+     * A null interface is returned if the Binder has since
+     * become unavailable, in response to the SDK being unloaded.
+     *
+     * @return [IBinder] object for loaded SDK.
+     *
+     * @see [SandboxedSdk.getInterface]
+     */
+    fun getInterface() = sdkImpl.getInterface()
+
+    /**
+     * Returns information about loaded SDK.
+     *
+     * @return [SandboxedSdkInfo] object for loaded SDK or null if no information available.
+     *
+     * @see [SandboxedSdk.getSharedLibraryInfo]
+     */
+    fun getSdkInfo(): SandboxedSdkInfo? = sdkImpl.getSdkInfo()
+
+    /**
+     * Create [SandboxedSdk] from compat object.
+     *
+     * @return Platform SandboxedSdk
+     */
+    @RequiresExtension(extension = AD_SERVICES, version = 4)
+    internal fun toSandboxedSdk() = sdkImpl.toSandboxedSdk()
+
+    internal interface SandboxedSdkImpl {
+        fun getInterface(): IBinder?
+
+        fun getSdkInfo(): SandboxedSdkInfo?
+
+        @RequiresExtension(extension = AD_SERVICES, version = 4)
+        @DoNotInline
+        fun toSandboxedSdk(): SandboxedSdk
+    }
+
+    @RequiresExtension(extension = AD_SERVICES, version = 4)
+    private open class ApiAdServicesV4Impl(
+        protected val sandboxedSdk: SandboxedSdk
+    ) : SandboxedSdkImpl {
+
+        @DoNotInline
+        override fun getInterface(): IBinder? {
+            return sandboxedSdk.getInterface()
+        }
+
+        override fun getSdkInfo(): SandboxedSdkInfo? = null
+
+        @DoNotInline
+        override fun toSandboxedSdk(): SandboxedSdk {
+            return sandboxedSdk
+        }
+
+        companion object {
+            @DoNotInline
+            fun createSandboxedSdk(sdkInterface: IBinder): SandboxedSdk {
+                return SandboxedSdk(sdkInterface)
+            }
+        }
+    }
+
+    @RequiresApi(33)
+    @RequiresExtension(extension = AD_SERVICES, version = 5)
+    private class ApiAdServicesV5Impl(
+        sandboxedSdk: SandboxedSdk
+    ) : ApiAdServicesV4Impl(sandboxedSdk) {
+
+        override fun getSdkInfo(): SandboxedSdkInfo {
+            val sharedLibraryInfo = sandboxedSdk.sharedLibraryInfo
+            return SandboxedSdkInfo(
+                name = sharedLibraryInfo.name,
+                version = sharedLibraryInfo.longVersion,
+            )
+        }
+    }
+
+    @RequiresExtension(extension = AD_SERVICES, version = 4)
+    private object SdkImplFactory {
+        fun createSdkImpl(sandboxedSdk: SandboxedSdk): SandboxedSdkImpl {
+            return if (AdServicesInfo.isAtLeastV5()) {
+                ApiAdServicesV5Impl(sandboxedSdk)
+            } else {
+                ApiAdServicesV4Impl(sandboxedSdk)
+            }
+        }
+    }
+
+    private class CompatImpl(
+        private val sdkInterface: IBinder,
+        private val sdkInfo: SandboxedSdkInfo?
+    ) : SandboxedSdkImpl {
+
+        override fun getInterface(): IBinder {
+            // This will be null if the SDK has been unloaded and the IBinder originally provided
+            // is now a dead object.
+            return sdkInterface
+        }
+
+        override fun getSdkInfo(): SandboxedSdkInfo? = sdkInfo
+
+        @RequiresExtension(extension = AD_SERVICES, version = 4)
+        override fun toSandboxedSdk(): SandboxedSdk {
+            // avoid class verifications errors
+            return ApiAdServicesV4Impl.createSandboxedSdk(sdkInterface)
+        }
+    }
+}
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkInfo.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkInfo.kt
new file mode 100644
index 0000000..b2caca8
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkInfo.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.core
+
+/**
+ * Information about runtime enabled SDK.
+ * Could represent SDK loaded in sandbox or locally loaded SDK.
+ */
+class SandboxedSdkInfo(
+    /**
+     * Sdk Name.
+     * This is a value of `android:name` attribute <sdk-library> tag of SDK Manifest.
+     */
+    val name: String,
+    /**
+     * Sdk Version.
+     * This is a value of `android:versionMajor` attribute <sdk-library> tag of SDK Manifest.
+     */
+    val version: Long
+) {
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as SandboxedSdkInfo
+
+        if (name != other.name) return false
+        if (version != other.version) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = name.hashCode()
+        result = 31 * result + version.hashCode()
+        return result
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..7ece46d
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkProviderAdapter.kt
@@ -0,0 +1,94 @@
+/*
+ * 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.annotation.SuppressLint
+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.os.ext.SdkExtensions.AD_SERVICES
+import android.view.View
+import androidx.annotation.RequiresExtension
+
+/**
+ * Implementation of platform [SandboxedSdkProvider] that delegate to [SandboxedSdkProviderCompat]
+ * Gets compat class name from asset "SandboxedSdkProviderCompatClassName.txt"
+ *
+ */
+@SuppressLint("Override") // b/273473397
+@RequiresExtension(extension = AD_SERVICES, version = 4)
+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"
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkProviderCompat.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkProviderCompat.kt
new file mode 100644
index 0000000..8882d6f
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkProviderCompat.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.content.Context
+import android.os.Bundle
+import android.view.View
+
+/**
+ * Compat version of [android.app.sdksandbox.SandboxedSdkProvider].
+ *
+ * Encapsulates API which SDK sandbox can use to interact with SDKs loaded into it.
+ *
+ * SDK has to implement this abstract class to generate an entry point for SDK sandbox to be able
+ *  to call it through.
+ *
+ * @see [android.app.sdksandbox.SandboxedSdkProvider]
+ */
+abstract class SandboxedSdkProviderCompat {
+    /**
+     * Context previously set through [SandboxedSdkProviderCompat.attachContext].
+     * This will return null if no context has been previously set.
+     */
+    var context: Context? = null
+        private set
+
+    /**
+     * Sets the SDK [Context] which can then be received using [SandboxedSdkProviderCompat.context]
+     *
+     * This is called before [SandboxedSdkProviderCompat.onLoadSdk] is invoked.
+     * No operations requiring a [Context] should be performed before then, as
+     * [SandboxedSdkProviderCompat.context] will return null until this method has been called.
+     *
+     * @throws IllegalStateException if a base context has already been set.
+     *
+     * @param context The new base context.
+     *
+     * @see [android.app.sdksandbox.SandboxedSdkProvider.attachContext]
+     */
+    fun attachContext(context: Context) {
+        check(this.context == null) { "Context already set" }
+        this.context = context
+    }
+
+    /**
+     * Does the work needed for the SDK to start handling requests.
+     *
+     * This function is called by the SDK sandbox after it loads the SDK.
+     *
+     * SDK should do any work to be ready to handle upcoming requests. It should not do any
+     * long-running tasks here, like I/O and network calls. Doing so can prevent the SDK from
+     * receiving requests from the client. Additionally, it should not do initialization that
+     * depends on other SDKs being loaded into the SDK sandbox.
+     *
+     * The SDK should not do any operations requiring a [Context] object before this method
+     * has been called.
+     *
+     * @param params list of params passed from the client when it loads the SDK. This can be empty.
+     * @return Returns a [SandboxedSdkCompat], passed back to the client. The IBinder used to create
+     * the [SandboxedSdkCompat] object will be used by the client to call into the SDK.
+     *
+     * @throws LoadSdkCompatException if initialization failed.
+     *
+     * @see [android.app.sdksandbox.SandboxedSdkProvider.onLoadSdk]
+     */
+    @Throws(LoadSdkCompatException::class)
+    abstract fun onLoadSdk(params: Bundle): SandboxedSdkCompat
+
+    /**
+     * Does the work needed for the SDK to free its resources before being unloaded.
+     *
+     * This function is called by the SDK sandbox manager before it unloads the SDK. The SDK
+     * should fail any invocations on the Binder previously returned to the client through
+     * [SandboxedSdkCompat.getInterface]
+     *
+     * The SDK should not do any long-running tasks here, like I/O and network calls.
+     *
+     * @see [android.app.sdksandbox.SandboxedSdkProvider.beforeUnloadSdk]
+     */
+    open fun beforeUnloadSdk() {}
+
+    /**
+     * Requests a view to be remotely rendered to the client app process.
+     *
+     * @see [android.app.sdksandbox.SandboxedSdkProvider.getView]
+     */
+    abstract fun getView(
+        windowContext: Context,
+        params: Bundle,
+        width: Int,
+        height: Int
+    ): View
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/Versions.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/Versions.kt
new file mode 100644
index 0000000..18585d0
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/Versions.kt
@@ -0,0 +1,50 @@
+/*
+ * 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 androidx.annotation.Keep
+import androidx.annotation.RestrictTo
+import org.jetbrains.annotations.TestOnly
+
+/**
+ * Store internal API version (for Client-Core communication).
+ * Methods invoked via reflection.
+ *
+ * @suppress
+ */
+@Suppress("unused")
+@Keep
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+object Versions {
+
+    const val API_VERSION = 2
+
+    @JvmField
+    var CLIENT_VERSION: Int? = null
+
+    @JvmStatic
+    fun handShake(clientVersion: Int): Int {
+        CLIENT_VERSION = clientVersion
+        return API_VERSION
+    }
+
+    @TestOnly
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    fun resetClientVersion() {
+        CLIENT_VERSION = null
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/androidx/privacysandbox/sdkruntime/androidx-privacysandbox-sdkruntime-sdkruntime-core-documentation.md b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/androidx-privacysandbox-sdkruntime-sdkruntime-core-documentation.md
similarity index 100%
rename from privacysandbox/sdkruntime/sdkruntime-core/src/main/androidx/privacysandbox/sdkruntime/androidx-privacysandbox-sdkruntime-sdkruntime-core-documentation.md
rename to privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/androidx-privacysandbox-sdkruntime-sdkruntime-core-documentation.md
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompat.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompat.kt
new file mode 100644
index 0000000..9575052
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/SdkSandboxControllerCompat.kt
@@ -0,0 +1,115 @@
+/*
+ * 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.core.controller
+
+import android.content.Context
+import androidx.annotation.Keep
+import androidx.annotation.RestrictTo
+import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
+import androidx.privacysandbox.sdkruntime.core.AdServicesInfo
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkProviderCompat
+import androidx.privacysandbox.sdkruntime.core.Versions
+import androidx.privacysandbox.sdkruntime.core.controller.impl.NoOpImpl
+import androidx.privacysandbox.sdkruntime.core.controller.impl.PlatformImpl
+import org.jetbrains.annotations.TestOnly
+
+/**
+ * Compat version of [android.app.sdksandbox.sdkprovider.SdkSandboxController].
+ *
+ * Controller that is used by SDK loaded in the sandbox or locally to access information provided
+ * by the sandbox environment.
+ *
+ * It enables the SDK to communicate with other SDKS and know about the state of the sdks that are
+ * currently loaded.
+ *
+ * An instance can be obtained using [SdkSandboxControllerCompat.from].
+ * The [Context] can be obtained using [SandboxedSdkProviderCompat.context].
+ *
+ * @see [android.app.sdksandbox.sdkprovider.SdkSandboxController]
+ */
+class SdkSandboxControllerCompat internal constructor(
+    private val controllerImpl: SandboxControllerImpl
+) {
+
+    /**
+     * Fetches information about Sdks that are loaded in the sandbox or locally.
+     *
+     * @return List of [SandboxedSdkCompat] containing all currently loaded sdks
+     *
+     * @see [android.app.sdksandbox.sdkprovider.SdkSandboxController.getSandboxedSdks]
+     */
+    fun getSandboxedSdks(): List<SandboxedSdkCompat> =
+        controllerImpl.getSandboxedSdks()
+
+    /** @suppress */
+    @RestrictTo(LIBRARY_GROUP)
+    interface SandboxControllerImpl {
+        fun getSandboxedSdks(): List<SandboxedSdkCompat>
+    }
+
+    companion object {
+
+        private var localImpl: SandboxControllerImpl? = null
+
+        /**
+         *  Creates [SdkSandboxControllerCompat].
+         *
+         *  @param context SDK context
+         *
+         *  @return SdkSandboxControllerCompat object.
+         */
+        @JvmStatic
+        fun from(context: Context): SdkSandboxControllerCompat {
+            val loadedLocally = Versions.CLIENT_VERSION != null
+            if (loadedLocally) {
+                val implFromClient = localImpl
+                if (implFromClient != null) {
+                    return SdkSandboxControllerCompat(implFromClient)
+                }
+                return SdkSandboxControllerCompat(NoOpImpl())
+            }
+
+            if (AdServicesInfo.isAtLeastV5()) {
+                return SdkSandboxControllerCompat(PlatformImpl.from(context))
+            }
+
+            return SdkSandboxControllerCompat(NoOpImpl())
+        }
+
+        /**
+         * Inject implementation from client library.
+         * Implementation will be used only if loaded locally.
+         * This method will be called from client side via reflection during loading SDK.
+         *
+         * @suppress
+         */
+        @JvmStatic
+        @Keep
+        @RestrictTo(LIBRARY_GROUP)
+        fun injectLocalImpl(impl: SandboxControllerImpl) {
+            check(localImpl == null) { "Local implementation already injected" }
+            localImpl = impl
+        }
+
+        @TestOnly
+        @RestrictTo(LIBRARY_GROUP)
+        fun resetLocalImpl() {
+            localImpl = null
+        }
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/NoOpImpl.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/NoOpImpl.kt
new file mode 100644
index 0000000..cc0bfa3
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/NoOpImpl.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.core.controller.impl
+
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
+
+/**
+ * NoOp implementation for cases when [SdkSandboxControllerCompat] not supported.
+ */
+internal class NoOpImpl : SdkSandboxControllerCompat.SandboxControllerImpl {
+    override fun getSandboxedSdks(): List<SandboxedSdkCompat> = emptyList()
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/PlatformImpl.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/PlatformImpl.kt
new file mode 100644
index 0000000..ce2e035
--- /dev/null
+++ b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/controller/impl/PlatformImpl.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.privacysandbox.sdkruntime.core.controller.impl
+
+import android.app.sdksandbox.sdkprovider.SdkSandboxController
+import android.content.Context
+import android.os.ext.SdkExtensions.AD_SERVICES
+import androidx.annotation.RequiresApi
+import androidx.annotation.RequiresExtension
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
+
+/**
+ * Implementation that delegates to platform [SdkSandboxController].
+ */
+@RequiresApi(33)
+@RequiresExtension(extension = AD_SERVICES, version = 5)
+internal class PlatformImpl(
+    private val controller: SdkSandboxController
+) : SdkSandboxControllerCompat.SandboxControllerImpl {
+
+    override fun getSandboxedSdks(): List<SandboxedSdkCompat> {
+        return controller
+            .sandboxedSdks
+            .map { platformSdk -> SandboxedSdkCompat(platformSdk) }
+    }
+
+    companion object {
+        fun from(context: Context): PlatformImpl {
+            val sdkSandboxController = context.getSystemService(SdkSandboxController::class.java)
+            return PlatformImpl(sdkSandboxController)
+        }
+    }
+}
diff --git a/privacysandbox/tools/tools-apicompiler/build.gradle b/privacysandbox/tools/tools-apicompiler/build.gradle
index 010551c..ce65d8c 100644
--- a/privacysandbox/tools/tools-apicompiler/build.gradle
+++ b/privacysandbox/tools/tools-apicompiler/build.gradle
@@ -36,6 +36,7 @@
     testImplementation(project(":room:room-compiler-processing-testing"))
     testImplementationAarAsJar(project(":privacysandbox:ui:ui-core"))
     testImplementationAarAsJar(project(":privacysandbox:ui:ui-provider"))
+    testImplementationAarAsJar(project(":privacysandbox:sdkruntime:sdkruntime-core"))
     testImplementation(libs.junit)
     testImplementation(libs.truth)
     testImplementation(libs.kotlinCoroutinesCore)
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/TestUtils.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/TestUtils.kt
index 7eecf79..553be79 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/TestUtils.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/TestUtils.kt
@@ -16,7 +16,6 @@
 
 package androidx.privacysandbox.tools.apicompiler
 
-import androidx.privacysandbox.tools.testing.allTestLibraryStubs
 import androidx.privacysandbox.tools.testing.CompilationTestHelper
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.compiler.TestCompilationResult
@@ -29,7 +28,6 @@
  */
 fun compileWithPrivacySandboxKspCompiler(
     sources: List<Source>,
-    addLibraryStubs: Boolean = true,
     extraProcessorOptions: Map<String, String> = mapOf(),
 ): TestCompilationResult {
     val provider = PrivacySandboxKspCompiler.Provider()
@@ -46,8 +44,7 @@
     }
 
     return CompilationTestHelper.compileAll(
-        if (addLibraryStubs) sources + allTestLibraryStubs
-        else sources,
+        sources,
         symbolProcessorProviders = listOf(provider),
         processorOptions = processorOptions,
     )
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/WithoutRuntimeLibrarySdkTest.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/WithoutRuntimeLibrarySdkTest.kt
index 388e130..fa4ec0d 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/WithoutRuntimeLibrarySdkTest.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/WithoutRuntimeLibrarySdkTest.kt
@@ -34,7 +34,6 @@
 
         val result = compileWithPrivacySandboxKspCompiler(
             inputSources,
-            addLibraryStubs = false,
             extraProcessorOptions = mapOf("skip_sdk_runtime_compat_library" to "true")
         )
         assertThat(result).succeeds()
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/util/KspTestRunner.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/util/KspTestRunner.kt
index 31aee1f2..2f6420e 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/util/KspTestRunner.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/util/KspTestRunner.kt
@@ -20,7 +20,6 @@
 import androidx.privacysandbox.tools.core.model.ParsedApi
 import androidx.privacysandbox.tools.testing.CompilationResultSubject
 import androidx.privacysandbox.tools.testing.CompilationTestHelper.assertThat
-import androidx.privacysandbox.tools.testing.allTestLibraryStubs
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.compiler.TestCompilationArguments
 import androidx.room.compiler.processing.util.compiler.compile
@@ -41,7 +40,7 @@
         compile(
             Files.createTempDirectory("test").toFile(),
             TestCompilationArguments(
-                sources = sources.toList() + allTestLibraryStubs,
+                sources = sources.toList(),
                 symbolProcessorProviders = listOf(provider),
             )
         )
diff --git a/privacysandbox/tools/tools-apigenerator/build.gradle b/privacysandbox/tools/tools-apigenerator/build.gradle
index ee9cc66..b509312 100644
--- a/privacysandbox/tools/tools-apigenerator/build.gradle
+++ b/privacysandbox/tools/tools-apigenerator/build.gradle
@@ -41,6 +41,8 @@
     testImplementation(project(":room:room-compiler-processing-testing"))
     testImplementationAarAsJar(project(":privacysandbox:ui:ui-core"))
     testImplementationAarAsJar(project(":privacysandbox:ui:ui-client"))
+    testImplementationAarAsJar(project(":privacysandbox:sdkruntime:sdkruntime-client"))
+    testImplementationAarAsJar(project(":privacysandbox:sdkruntime:sdkruntime-core"))
     testImplementation(libs.kotlinCoroutinesCore)
     testImplementation(libs.junit)
     testImplementation(libs.truth)
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/BaseApiGeneratorTest.kt b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/BaseApiGeneratorTest.kt
index de5a4c6..349bc7f 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/BaseApiGeneratorTest.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/BaseApiGeneratorTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.privacysandbox.tools.core.Metadata
 import androidx.privacysandbox.tools.testing.CompilationTestHelper.assertCompiles
-import androidx.privacysandbox.tools.testing.allTestLibraryStubs
 import androidx.privacysandbox.tools.testing.hasAllExpectedGeneratedSourceFilesAndContent
 import androidx.privacysandbox.tools.testing.loadSourcesFromDirectory
 import androidx.room.compiler.processing.util.Source
@@ -52,7 +51,7 @@
 
     @Test
     fun generatedApi_compiles() {
-        assertCompiles(generatedSources + allTestLibraryStubs)
+        assertCompiles(generatedSources)
     }
 
     @Test
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/TestUtils.kt b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/TestUtils.kt
index ea01aa8..fade128 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/TestUtils.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/TestUtils.kt
@@ -18,7 +18,6 @@
 
 import androidx.privacysandbox.tools.apipackager.PrivacySandboxApiPackager
 import androidx.privacysandbox.tools.testing.CompilationTestHelper.assertCompiles
-import androidx.privacysandbox.tools.testing.allTestLibraryStubs
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.compiler.TestCompilationResult
 import java.nio.file.Files.createTempDirectory
@@ -32,17 +31,13 @@
  *
  * @param descriptorResources map of extra resources that will be added to descriptors jar keyed by
  *      their relative path.
- * @param addLibraryStubs whether to include latest Android platform API stubs that support the
- * Privacy Sandbox.
  */
 fun compileIntoInterfaceDescriptorsJar(
     sources: List<Source>,
     descriptorResources: Map<Path, ByteArray> = mapOf(),
-    addLibraryStubs: Boolean = true,
 ): Path {
-    val testSources = if (addLibraryStubs) sources + allTestLibraryStubs else sources
     val tempDir = createTempDirectory("compile").also { it.toFile().deleteOnExit() }
-    val result = assertCompiles(testSources.toList())
+    val result = assertCompiles(sources.toList())
     val sdkInterfaceDescriptors = tempDir.resolve("sdk-interface-descriptors.jar")
     val outputClasspath = mergedClasspath(result)
     descriptorResources.forEach { (relativePath, contents) ->
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParserTest.kt b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParserTest.kt
index 1909e41..3c40caa0 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParserTest.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParserTest.kt
@@ -27,7 +27,6 @@
 import androidx.privacysandbox.tools.core.model.Types.asNullable
 import androidx.privacysandbox.tools.core.model.ValueProperty
 import androidx.privacysandbox.tools.testing.CompilationTestHelper.assertCompiles
-import androidx.privacysandbox.tools.testing.allTestLibraryStubs
 import androidx.room.compiler.processing.util.Source
 import androidx.testutils.assertThrows
 import com.google.common.truth.Truth.assertThat
@@ -404,7 +403,7 @@
     }
 
     private fun compileAndParseApi(vararg sources: Source): ParsedApi {
-        val classpath = mergedClasspath(assertCompiles(sources.toList() + allTestLibraryStubs))
+        val classpath = mergedClasspath(assertCompiles(sources.toList()))
         return ApiStubParser.parse(classpath)
     }
 }
diff --git a/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/LibraryStubs.kt b/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/LibraryStubs.kt
deleted file mode 100644
index 6082ce2..0000000
--- a/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/LibraryStubs.kt
+++ /dev/null
@@ -1,64 +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.tools.testing
-
-import androidx.room.compiler.processing.util.Source
-
-// SDK Runtime library is not available in AndroidX prebuilts, so while that's the case we use fake
-// stubs to run our compilation tests.
-val syntheticSdkRuntimeLibraryStubs = listOf(
-    Source.kotlin(
-        "androidx/privacysandbox/sdkruntime/core/SandboxedSdkCompat.kt", """
-        |package androidx.privacysandbox.sdkruntime.core
-        |
-        |import android.os.IBinder
-        |
-        |@Suppress("UNUSED_PARAMETER")
-        |class SandboxedSdkCompat(sdkInterface: IBinder) {
-        |    fun getInterface(): IBinder? = throw RuntimeException("Stub!")
-        |}
-        |""".trimMargin()
-    ),
-    Source.kotlin(
-        "androidx/privacysandbox/sdkruntime/core/SandboxedSdkProviderCompat.kt", """
-        |package androidx.privacysandbox.sdkruntime.core
-        |
-        |import android.content.Context
-        |import android.os.Bundle
-        |import android.view.View
-        |
-        |@Suppress("UNUSED_PARAMETER")
-        |abstract class SandboxedSdkProviderCompat {
-        |   var context: Context? = null
-        |       private set
-        |   fun attachContext(context: Context): Unit = throw RuntimeException("Stub!")
-        |
-        |   abstract fun onLoadSdk(params: Bundle): SandboxedSdkCompat
-        |
-        |   open fun beforeUnloadSdk() {}
-        |
-        |   abstract fun getView(
-        |       windowContext: Context,
-        |       params: Bundle,
-        |       width: Int,
-        |       height: Int
-        |   ): View
-        |}
-        |""".trimMargin()
-    ),
-)
-
-val allTestLibraryStubs = syntheticSdkRuntimeLibraryStubs
\ No newline at end of file
diff --git a/recyclerview/recyclerview/build.gradle b/recyclerview/recyclerview/build.gradle
index 1f6736c..7a03c8b 100644
--- a/recyclerview/recyclerview/build.gradle
+++ b/recyclerview/recyclerview/build.gradle
@@ -12,7 +12,7 @@
     implementation("androidx.collection:collection:1.0.0")
     api("androidx.customview:customview:1.0.0")
     implementation("androidx.customview:customview-poolingcontainer:1.0.0")
-    implementation("androidx.profileinstaller:profileinstaller:1.2.1")
+    implementation("androidx.profileinstaller:profileinstaller:1.3.0")
 
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
diff --git a/room/integration-tests/kotlintestapp/build.gradle b/room/integration-tests/kotlintestapp/build.gradle
index a72cbce..4d02fd26 100644
--- a/room/integration-tests/kotlintestapp/build.gradle
+++ b/room/integration-tests/kotlintestapp/build.gradle
@@ -124,6 +124,7 @@
     androidTestImplementation(libs.rxjava2)
     androidTestImplementation(libs.kotlinCoroutinesTest)
     testImplementation(libs.mockitoCore4)
+    androidTestImplementation("androidx.collection:collection")
 }
 
 class RoomSchemaArgProvider implements CommandLineArgumentProvider {
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/TestDatabase.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/TestDatabase.kt
index 474ae6c..5ecead7 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/TestDatabase.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/TestDatabase.kt
@@ -18,32 +18,54 @@
 
 import androidx.room.Database
 import androidx.room.RoomDatabase
+import androidx.room.TypeConverter
+import androidx.room.TypeConverters
 import androidx.room.integration.kotlintestapp.dao.AbstractDao
 import androidx.room.integration.kotlintestapp.dao.BooksDao
 import androidx.room.integration.kotlintestapp.dao.CounterDao
 import androidx.room.integration.kotlintestapp.dao.DependencyDao
 import androidx.room.integration.kotlintestapp.dao.DerivedDao
+import androidx.room.integration.kotlintestapp.dao.MusicDao
+import androidx.room.integration.kotlintestapp.dao.PetDao
+import androidx.room.integration.kotlintestapp.dao.ToyDao
 import androidx.room.integration.kotlintestapp.dao.UsersDao
+import androidx.room.integration.kotlintestapp.vo.Album
+import androidx.room.integration.kotlintestapp.vo.Artist
 import androidx.room.integration.kotlintestapp.vo.Author
 import androidx.room.integration.kotlintestapp.vo.Book
 import androidx.room.integration.kotlintestapp.vo.BookAuthor
 import androidx.room.integration.kotlintestapp.vo.Counter
 import androidx.room.integration.kotlintestapp.vo.DataClassFromDependency
 import androidx.room.integration.kotlintestapp.vo.EntityWithJavaPojoList
+import androidx.room.integration.kotlintestapp.vo.Image
 import androidx.room.integration.kotlintestapp.vo.JavaEntity
 import androidx.room.integration.kotlintestapp.vo.NoArgClass
+import androidx.room.integration.kotlintestapp.vo.Pet
+import androidx.room.integration.kotlintestapp.vo.PetUser
+import androidx.room.integration.kotlintestapp.vo.PetWithUser
+import androidx.room.integration.kotlintestapp.vo.Playlist
+import androidx.room.integration.kotlintestapp.vo.PlaylistSongXRef
 import androidx.room.integration.kotlintestapp.vo.Publisher
+import androidx.room.integration.kotlintestapp.vo.Song
+import java.nio.ByteBuffer
+import java.util.Date
+import java.util.UUID
+import androidx.room.integration.kotlintestapp.vo.Toy
 import androidx.room.integration.kotlintestapp.vo.User
 
 @Database(
     entities = [
         Book::class, Author::class, Publisher::class, BookAuthor::class,
         NoArgClass::class, DataClassFromDependency::class, JavaEntity::class,
-        EntityWithJavaPojoList::class, User::class, Counter::class
+        EntityWithJavaPojoList::class, User::class, Counter::class, Toy::class,
+        Pet::class, PetUser::class, Song::class, Playlist::class, PlaylistSongXRef::class,
+        Artist::class, Album::class, Image::class
     ],
+    views = [PetWithUser::class],
     version = 1,
-    exportSchema = false
+    exportSchema = false,
 )
+@TypeConverters(TestDatabase.Converters::class)
 abstract class TestDatabase : RoomDatabase() {
 
     abstract fun usersDao(): UsersDao
@@ -57,4 +79,59 @@
     abstract fun abstractDao(): AbstractDao
 
     abstract fun counterDao(): CounterDao
+
+    abstract fun toyDao(): ToyDao
+
+    abstract fun petDao(): PetDao
+
+    abstract fun musicDao(): MusicDao
+
+    class Converters {
+        @TypeConverter
+        fun fromTimestamp(value: Long): Date {
+            return Date(value)
+        }
+
+        @TypeConverter
+        fun dateToTimestamp(date: Date): Long {
+            return date.time
+        }
+
+        @TypeConverter
+        fun decomposeDays(flags: Int): Set<androidx.room.integration.kotlintestapp.vo.Day> {
+            val result: MutableSet<androidx.room.integration.kotlintestapp.vo.Day> =
+                HashSet()
+            for (day in androidx.room.integration.kotlintestapp.vo.Day.values()) {
+                if (flags and (1 shl day.ordinal) != 0) {
+                    result.add(day)
+                }
+            }
+            return result
+        }
+
+        @TypeConverter
+        fun composeDays(days: Set<androidx.room.integration.kotlintestapp.vo.Day>): Int {
+            var result = 0
+            for (day in days) {
+                result = result or (1 shl day.ordinal)
+            }
+            return result
+        }
+
+        @TypeConverter
+        fun asUuid(bytes: ByteArray): UUID {
+            val bb = ByteBuffer.wrap(bytes)
+            val firstLong = bb.long
+            val secondLong = bb.long
+            return UUID(firstLong, secondLong)
+        }
+
+        @TypeConverter
+        fun asBytes(uuid: UUID): ByteArray {
+            val bb = ByteBuffer.wrap(ByteArray(16))
+            bb.putLong(uuid.mostSignificantBits)
+            bb.putLong(uuid.leastSignificantBits)
+            return bb.array()
+        }
+    }
 }
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/MusicDao.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/MusicDao.kt
new file mode 100644
index 0000000..a881249
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/MusicDao.kt
@@ -0,0 +1,301 @@
+/*
+ * 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.room.integration.kotlintestapp.dao
+
+import androidx.collection.ArrayMap
+import androidx.collection.LongSparseArray
+import androidx.collection.SparseArrayCompat
+import androidx.lifecycle.LiveData
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.MapInfo
+import androidx.room.Query
+import androidx.room.RawQuery
+import androidx.room.RewriteQueriesToDropUnusedColumns
+import androidx.room.Transaction
+import androidx.room.integration.kotlintestapp.vo.Album
+import androidx.room.integration.kotlintestapp.vo.AlbumNameAndBandName
+import androidx.room.integration.kotlintestapp.vo.AlbumWithSongs
+import androidx.room.integration.kotlintestapp.vo.Artist
+import androidx.room.integration.kotlintestapp.vo.Image
+import androidx.room.integration.kotlintestapp.vo.ImageFormat
+import androidx.room.integration.kotlintestapp.vo.ReleasedAlbum
+import androidx.room.integration.kotlintestapp.vo.Song
+import androidx.sqlite.db.SupportSQLiteQuery
+import com.google.common.collect.ImmutableListMultimap
+import com.google.common.collect.ImmutableMap
+import com.google.common.collect.ImmutableSetMultimap
+import io.reactivex.Flowable
+import java.nio.ByteBuffer
+import java.util.Date
+
+@JvmDefaultWithCompatibility
+@Dao
+interface MusicDao {
+    @Insert
+    fun addSongs(vararg songs: Song)
+
+    @Insert
+    fun addArtists(vararg artists: Artist)
+
+    @Insert
+    fun addAlbums(vararg albums: Album)
+    @Insert
+    fun addImages(vararg images: Image)
+
+    /* Map of Object to Object */
+    @Query("SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist")
+    fun getArtistAndFirstSongMap(): Map<Artist, Song>
+
+    @Query("SELECT * FROM Song JOIN Artist ON Song.mArtist = Artist.mArtistName")
+    fun getSongAndArtist(): Map<Song, Artist>
+
+    @Query("SELECT * FROM Artist JOIN Album ON Artist.mArtistName = Album.mAlbumArtist")
+    @Transaction
+    fun getAllArtistAndTheirAlbumsWithSongs(): Map<Artist, AlbumWithSongs>
+
+    @RawQuery
+    fun getAllArtistAndTheirSongsRawQuery(query: SupportSQLiteQuery): Map<Artist, Song>
+
+    @Query("SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist")
+    fun getAllArtistAndTheirSongsAsLiveData(): LiveData<Map<Artist, Song>>
+
+    /* Map of Object to List */
+    @Query("SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist")
+    fun getAllArtistAndTheirSongsList(): Map<Artist, List<Song>>
+
+    @Query(
+        "SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist " +
+            "ORDER BY mArtistName ASC"
+    )
+    fun getAllArtistAndTheirSongsListOrdered(): Map<Artist, List<Song>>
+
+    @Query("SELECT * FROM Artist JOIN Album ON Artist.mArtistName = Album.mAlbumArtist")
+    @Transaction
+    fun getAllArtistAndTheirAlbumsWithSongsList(): Map<Artist, List<AlbumWithSongs>>
+
+    @RawQuery
+    fun getAllArtistAndTheirSongsRawQueryList(query: SupportSQLiteQuery): Map<Artist, List<Song>>
+
+    @Query("SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist")
+    fun getAllArtistAndTheirSongsAsLiveDataList(): LiveData<Map<Artist, List<Song>>>
+
+    @Query("SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist")
+    fun getAllArtistAndTheirSongsAsFlowableList(): Flowable<Map<Artist, List<Song>>>
+
+    @Query(
+        "SELECT Album.mAlbumReleaseYear as mReleaseYear, Album.mAlbumName, Album.mAlbumArtist " +
+            "as mBandName" +
+            " from Album " +
+            "JOIN Song " +
+            "ON Album.mAlbumArtist = Song.mArtist AND Album.mAlbumName = Song.mAlbum"
+    )
+    fun getReleaseYearToAlbumsAndBandsList(): Map<ReleasedAlbum, List<AlbumNameAndBandName>>
+
+    /* Map of Object to Set */
+    @Query("SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist")
+    fun getAllArtistAndTheirSongsSet(): Map<Artist, Set<Song>>
+
+    @RawQuery
+    fun getAllArtistAndTheirSongsRawQuerySet(query: SupportSQLiteQuery): Map<Artist, Set<Song>>
+
+    @Query("SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist")
+    fun allArtistAndTheirSongsAsLiveDataSet(): LiveData<Map<Artist, Set<Song>>>
+
+    @Query("SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist")
+    fun allArtistAndTheirSongsAsFlowableSet(): Flowable<Map<Artist, Set<Song>>>
+
+    /* Guava ImmutableMap */
+    @Query("SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist")
+    fun allArtistAndTheirSongsImmutableMap(): ImmutableMap<Artist, List<Song>>
+
+    @RawQuery
+    fun getAllArtistAndTheirSongsRawQueryImmutableMap(
+        query: SupportSQLiteQuery
+    ): ImmutableMap<Artist, List<Song>>
+
+    @Query("SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist")
+    fun allArtistAndTheirSongsAsLiveDataImmutableMap(): LiveData<ImmutableMap<Artist, Set<Song>>>
+
+    /* Guava Multimap */
+    @Query("SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist")
+    fun allArtistAndTheirSongsGuavaImmutableSetMultimap(): ImmutableSetMultimap<Artist, Song>
+
+    @Query("SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist")
+    fun allArtistAndTheirSongsGuavaImmutableListMultimap(): ImmutableListMultimap<Artist, Song>
+
+    @Query("SELECT * FROM Artist JOIN Album ON Artist.mArtistName = Album.mAlbumArtist")
+    @Transaction
+    fun allArtistAndTheirAlbumsWithSongsGuavaImmutableSetMultimap():
+        ImmutableSetMultimap<Artist, AlbumWithSongs>
+
+    @Query("SELECT * FROM Artist JOIN Album ON Artist.mArtistName = Album.mAlbumArtist")
+    @Transaction
+    fun allArtistAndTheirAlbumsWithSongsGuavaImmutableListMultimap():
+        ImmutableListMultimap<Artist, AlbumWithSongs>
+
+    @RawQuery
+    fun getAllArtistAndTheirSongsRawQueryGuavaImmutableSetMultimap(
+        query: SupportSQLiteQuery
+    ): ImmutableSetMultimap<Artist, Song>
+
+    @RawQuery
+    fun getAllArtistAndTheirSongsRawQueryGuavaImmutableListMultimap(
+        query: SupportSQLiteQuery
+    ): ImmutableListMultimap<Artist, Song>
+
+    @Query("SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist")
+    fun allArtistAndTheirSongsAsLiveDataGuavaImmutableSetMultimap():
+        LiveData<ImmutableSetMultimap<Artist, Song>>
+
+    @Query("SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist")
+    fun allArtistAndTheirSongsAsLiveDataGuavaImmutableListMultimap():
+        LiveData<ImmutableListMultimap<Artist, Song>>
+
+    @Query("SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist")
+    @MapInfo(keyColumn = "mArtistName")
+    @RewriteQueriesToDropUnusedColumns
+    fun artistNameToSongs(): Map<String, List<Song>>
+
+    @Query("SELECT * FROM Album JOIN Song ON Song.mReleasedYear = Album.mAlbumReleaseYear")
+    @MapInfo(keyColumn = "mReleasedYear", valueColumn = "mReleasedYear")
+    @RewriteQueriesToDropUnusedColumns
+    fun releaseYearToAlbums(): Map<Int, List<Song>>
+
+    @Query("SELECT * FROM Album JOIN Song ON Song.mReleasedYear = Album.mAlbumReleaseYear")
+    @MapInfo(keyColumn = "mReleasedYear", valueColumn = "mTitle")
+    @RewriteQueriesToDropUnusedColumns
+    fun releaseYearToSongNames(): Map<Int, List<String>>
+
+    @RewriteQueriesToDropUnusedColumns
+    @MapInfo(keyColumn = "mArtistName", valueColumn = "mArtist")
+    @RawQuery
+    fun getArtistNameToSongsRawQuery(query: SupportSQLiteQuery): Map<String, List<Song>>
+
+    @RewriteQueriesToDropUnusedColumns
+    @MapInfo(keyColumn = "mReleasedYear", valueColumn = "mReleasedYear")
+    @RawQuery
+    fun getReleaseYearToAlbumsRawQuery(query: SupportSQLiteQuery): Map<Int, List<Song>>
+
+    @RewriteQueriesToDropUnusedColumns
+    @MapInfo(keyColumn = "mReleasedYear", valueColumn = "mTitle")
+    @RawQuery
+    fun getReleaseYearToSongNamesRawQuery(query: SupportSQLiteQuery): Map<Int, List<String>>
+
+    @Query(
+        "SELECT *, COUNT(mSongId) as songCount FROM Artist JOIN Song ON Artist.mArtistName = " +
+            "Song.mArtist GROUP BY mArtistName"
+    )
+    @MapInfo(valueColumn = "songCount")
+    @RewriteQueriesToDropUnusedColumns
+    fun artistAndSongCountMap(): Map<Artist, Int>
+
+    @RewriteQueriesToDropUnusedColumns
+    @MapInfo(valueColumn = "songCount")
+    @RawQuery
+    fun getArtistAndSongCountMapRawQuery(query: SupportSQLiteQuery): Map<Artist, Int>
+
+    // Other Map Key/Value Types
+    @Query("SELECT * FROM Artist JOIN Image ON Artist.mArtistName = Image.mArtistInImage")
+    @MapInfo(valueColumn = "mAlbumCover")
+    @RewriteQueriesToDropUnusedColumns
+    fun allArtistsWithAlbumCovers(): ImmutableMap<Artist, ByteBuffer>
+
+    @MapInfo(valueColumn = "mAlbumCover")
+    @RawQuery
+    fun getAllArtistsWithAlbumCoversRawQuery(query: SupportSQLiteQuery):
+        ImmutableMap<Artist, ByteBuffer>
+
+    @Query("SELECT * FROM Artist JOIN Image ON Artist.mArtistName = Image.mArtistInImage")
+    @MapInfo(valueColumn = "mImageYear")
+    @RewriteQueriesToDropUnusedColumns
+    fun allArtistsWithAlbumCoverYear(): ImmutableMap<Artist, Long>
+
+    @MapInfo(keyColumn = "mImageYear")
+    @RawQuery
+    fun getAllAlbumCoverYearToArtistsWithRawQuery(query: SupportSQLiteQuery):
+        ImmutableMap<Long, Artist>
+
+    @Query("SELECT * FROM Image JOIN Artist ON Artist.mArtistName = Image.mArtistInImage")
+    @MapInfo(keyColumn = "mAlbumCover", valueColumn = "mIsActive")
+    fun albumCoversWithBandActivity(): ImmutableMap<ByteBuffer, Boolean>
+
+    @MapInfo(keyColumn = "mAlbumCover", valueColumn = "mIsActive")
+    @RawQuery
+    fun getAlbumCoversWithBandActivityRawQuery(
+        query: SupportSQLiteQuery
+    ): ImmutableMap<ByteBuffer, Boolean>
+
+    @Query("SELECT * FROM Image JOIN Artist ON Artist.mArtistName = Image.mArtistInImage")
+    @MapInfo(keyColumn = "mDateReleased", valueColumn = "mIsActive")
+    fun albumDateWithBandActivity(): ImmutableMap<Date, Boolean>
+
+    @MapInfo(keyColumn = "mDateReleased", valueColumn = "mIsActive")
+    @RawQuery
+    fun getAlbumDateWithBandActivityRawQuery(query: SupportSQLiteQuery): ImmutableMap<Date, Boolean>
+
+    @Query("SELECT * FROM Image JOIN Artist ON Artist.mArtistName = Image.mArtistInImage")
+    @MapInfo(keyColumn = "mFormat", valueColumn = "mIsActive")
+    fun imageFormatWithBandActivity(): ImmutableMap<ImageFormat, Boolean>
+
+    @MapInfo(keyColumn = "mFormat", valueColumn = "mIsActive")
+    @RawQuery
+    fun getImageFormatWithBandActivityRawQuery(
+        query: SupportSQLiteQuery
+    ): ImmutableMap<ImageFormat, Boolean>
+
+    @MapInfo(keyColumn = "dog", valueColumn = "cat")
+    @RawQuery
+    fun getMapWithInvalidColumnRawQuery(query: SupportSQLiteQuery): Map<Artist, Int>
+
+    @Query("SELECT * FROM Artist LEFT JOIN Album ON Artist.mArtistName = Album.mAlbumArtist")
+    fun artistAndAlbumsLeftJoin(): Map<Artist, List<Album>>
+
+    @Query("SELECT * FROM Artist LEFT JOIN Album ON Artist.mArtistName = Album.mAlbumArtist")
+    fun artistAndAlbumsLeftJoinGuava(): ImmutableListMultimap<Artist, Album>
+
+    @Query("SELECT * FROM Artist LEFT JOIN Album ON Artist.mArtistName = Album.mAlbumArtist")
+    @MapInfo(valueColumn = "mAlbumName")
+    @RewriteQueriesToDropUnusedColumns
+    fun artistAndAlbumNamesLeftJoin(): Map<Artist, List<String>>
+
+    @Query("SELECT * FROM Album LEFT JOIN Artist ON Artist.mArtistName = Album.mAlbumArtist")
+    fun albumToArtistLeftJoin(): Map<Album, Artist>
+
+    @Query("SELECT * FROM Album LEFT JOIN Artist ON Artist.mArtistName = Album.mAlbumArtist")
+    fun artistToAlbumLeftJoin(): Map<Artist, Album>
+
+    @Query("SELECT * FROM Artist JOIN Image ON Artist.mArtistName = Image.mArtistInImage")
+    @MapInfo(valueColumn = "mImageYear")
+    @RewriteQueriesToDropUnusedColumns
+    fun allArtistsWithAlbumCoverYearArrayMap(): ArrayMap<Artist, Long>
+
+    @MapInfo(keyColumn = "mImageYear")
+    @RawQuery
+    fun getAllAlbumCoverYearToArtistsWithRawQueryArrayMap(
+        query: SupportSQLiteQuery
+    ): ArrayMap<Long, Artist>
+
+    @Query("SELECT * FROM Artist JOIN Image ON Artist.mArtistName = Image.mArtistInImage")
+    @MapInfo(keyColumn = "mImageYear")
+    @RewriteQueriesToDropUnusedColumns
+    fun allAlbumCoverYearToArtistsWithLongSparseArray(): LongSparseArray<Artist>
+
+    @Query("SELECT * FROM Artist JOIN Image ON Artist.mArtistName = Image.mArtistInImage")
+    @MapInfo(keyColumn = "mImageYear")
+    @RewriteQueriesToDropUnusedColumns
+    fun allAlbumCoverYearToArtistsWithIntSparseArray(): SparseArrayCompat<Artist>
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/PetDao.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/PetDao.kt
new file mode 100644
index 0000000..c45bc5b
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/PetDao.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.room.integration.kotlintestapp.dao
+
+import androidx.lifecycle.LiveData
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import androidx.room.Transaction
+import androidx.room.integration.kotlintestapp.vo.Pet
+import androidx.room.integration.kotlintestapp.vo.PetAndOwner
+import androidx.room.integration.kotlintestapp.vo.PetWithToyIds
+import androidx.room.integration.kotlintestapp.vo.PetWithUser
+import com.google.common.base.Optional
+import com.google.common.util.concurrent.ListenableFuture
+import io.reactivex.Flowable
+
+@JvmDefaultWithCompatibility
+@Dao
+interface PetDao {
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    fun insertOrReplace(vararg pets: Pet)
+
+    @Insert
+    fun insertAll(pets: Array<Pet>)
+
+    @Query("SELECT COUNT(*) FROM Pet")
+    fun count(): Int
+
+    @Transaction
+    @Query("SELECT * FROM Pet ORDER BY Pet.mPetId ASC")
+    fun allPetsWithToyIds(): List<PetWithToyIds>
+
+    @Transaction
+    @Query("SELECT * FROM Pet")
+    fun allPetsWithOwners(): List<PetAndOwner>
+
+    @Query("SELECT * FROM Pet WHERE Pet.mPetId = :id")
+    fun petWithIdFuture(id: Int): ListenableFuture<Optional<Pet>>
+
+    @Query("SELECT * FROM Pet WHERE Pet.mPetId = :id")
+    fun petWithIdFlowable(id: Int): Flowable<Pet>
+
+    @Query("SELECT * FROM Pet WHERE Pet.mPetId = :id")
+    fun petWithId(id: Int): Pet
+
+    @Query("SELECT * FROM Pet WHERE Pet.mPetId = :id")
+    fun petWithIdLiveData(id: Int): LiveData<Pet>
+
+    @Query("SELECT * FROM PetWithUser WHERE mPetId = :id")
+    fun petWithUserLiveData(id: Int): LiveData<PetWithUser>
+
+    @Delete
+    fun delete(pet: Pet)
+
+    @Query("SELECT mPetId FROM Pet")
+    fun allIds(): IntArray
+
+    @Transaction
+    fun deleteAndInsert(oldPet: Pet, newPet: Pet, shouldFail: Boolean) {
+        delete(oldPet)
+        if (shouldFail) {
+            throw RuntimeException()
+        }
+        insertOrReplace(newPet)
+    }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/ToyDao.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/ToyDao.kt
new file mode 100644
index 0000000..fbbd6c5
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/ToyDao.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.room.integration.kotlintestapp.dao
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import androidx.room.Update
+import androidx.room.integration.kotlintestapp.vo.Toy
+
+@JvmDefaultWithCompatibility
+@Dao
+interface ToyDao {
+    @Insert
+    fun insert(vararg toys: Toy)
+
+    @Query("SELECT * FROM Toy WHERE mId = :id")
+    fun getToy(id: Int): Toy?
+
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    fun insertOrReplace(toy: Toy)
+
+    @Insert(onConflict = OnConflictStrategy.IGNORE)
+    fun insertOrIgnore(toy: Toy)
+
+    @Update(onConflict = OnConflictStrategy.REPLACE)
+    fun updateOrReplace(toy: Toy): Int
+
+    @Update(onConflict = OnConflictStrategy.IGNORE)
+    fun updateOrIgnore(toy: Toy): Int
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/DaoConflictStrategyTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/DaoConflictStrategyTest.kt
new file mode 100644
index 0000000..671fff8
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/DaoConflictStrategyTest.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.room.integration.kotlintestapp.test
+
+import android.content.Context
+import androidx.room.Room
+import androidx.room.integration.kotlintestapp.TestDatabase
+import androidx.room.integration.kotlintestapp.dao.PetDao
+import androidx.room.integration.kotlintestapp.dao.ToyDao
+import androidx.room.integration.kotlintestapp.vo.Pet
+import androidx.room.integration.kotlintestapp.vo.Toy
+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
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DaoConflictStrategyTest {
+    private lateinit var mToyDao: ToyDao
+    private lateinit var mOriginalToy: Toy
+    private lateinit var mPetDao: PetDao
+    private lateinit var mPet: Pet
+
+    @Before
+    fun createDbAndSetUpToys() {
+        val context: Context = ApplicationProvider.getApplicationContext()
+        val db: TestDatabase =
+            Room.inMemoryDatabaseBuilder(context, TestDatabase::class.java).build()
+        mToyDao = db.toyDao()
+        mPetDao = db.petDao()
+        mPet = TestUtil.createPet(1)
+        mOriginalToy = Toy(10, "originalToy", 1)
+        mPetDao.insertOrReplace(mPet)
+        mToyDao.insert(mOriginalToy)
+    }
+
+    @Test
+    fun testInsertOnConflictReplace() {
+        val newToy = Toy(10, "newToy", 1)
+        mToyDao.insertOrReplace(newToy)
+        val output: Toy? = mToyDao.getToy(10)
+        assertThat(output).isNotNull()
+        assertThat(output!!.mName).isEqualTo(newToy.mName)
+    }
+
+    @Test
+    fun testInsertOnConflictIgnore() {
+        val newToy = Toy(10, "newToy", 1)
+        mToyDao.insertOrIgnore(newToy)
+        val output: Toy? = mToyDao.getToy(10)
+        assertThat(output).isNotNull()
+        assertThat(output!!.mName).isEqualTo(mOriginalToy.mName)
+    }
+
+    @Test
+    fun testUpdateOnConflictReplace() {
+        val newToy = Toy(11, "newToy", 1)
+        mToyDao.insert(newToy)
+        val conflictToy = Toy(11, "originalToy", 1)
+        mToyDao.updateOrReplace(conflictToy)
+
+        // Conflicting row is deleted
+        assertThat(mToyDao.getToy(10)).isNull()
+
+        // Row is updated
+        val output: Toy? = mToyDao.getToy(11)
+        assertThat(output).isNotNull()
+        assertThat(output!!.mName).isEqualTo(conflictToy.mName)
+    }
+
+    @Test
+    fun testUpdateOnConflictIgnore() {
+        val newToy = Toy(11, "newToy", 1)
+        mToyDao.insert(newToy)
+        val conflictToy = Toy(11, "newToy", 1)
+        mToyDao.updateOrIgnore(conflictToy)
+
+        // Conflicting row is kept
+        assertThat(mToyDao.getToy(10)).isNotNull()
+
+        // Row is not updated
+        val output: Toy? = mToyDao.getToy(11)
+        assertThat(output).isNotNull()
+        assertThat(output!!.mName).isEqualTo(newToy.mName)
+    }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/DaoNameConflictTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/DaoNameConflictTest.kt
new file mode 100644
index 0000000..21e7ca9
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/DaoNameConflictTest.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.room.integration.kotlintestapp.test
+
+import androidx.room.Dao
+import androidx.room.Database
+import androidx.room.Entity
+import androidx.room.Insert
+import androidx.room.PrimaryKey
+import androidx.room.Query
+import androidx.room.Room.inMemoryDatabaseBuilder
+import androidx.room.RoomDatabase
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import org.hamcrest.CoreMatchers
+import org.hamcrest.MatcherAssert
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class DaoNameConflictTest {
+    private lateinit var mDb: ConflictDatabase
+    @Before
+    fun init() {
+        mDb = inMemoryDatabaseBuilder(
+            ApplicationProvider.getApplicationContext(),
+            ConflictDatabase::class.java
+        ).build()
+    }
+
+    @After
+    fun close() {
+        mDb.close()
+    }
+
+    @Test
+    fun readFromItem1() {
+        val item1 = Item1(1, "a")
+        mDb.item1Dao().insert(item1)
+        val item2 = Item2(2, "b")
+        mDb.item2Dao().insert(item2)
+        MatcherAssert.assertThat(
+            mDb.item1Dao().get(), CoreMatchers.`is`(item1)
+        )
+        MatcherAssert.assertThat(
+            mDb.item2Dao().get(), CoreMatchers.`is`(item2)
+        )
+    }
+
+    @Entity
+    class Item1(@field:PrimaryKey var id: Int, var name: String?) {
+        @Dao
+        interface Store {
+            @Query("SELECT * FROM Item1 LIMIT 1")
+            fun get(): Item1
+
+            @Insert
+            fun insert(vararg items: Item1)
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (other == null || javaClass != other.javaClass) return false
+            val item1 = other as Item1
+            if (id != item1.id) return false
+            return if (name != null) name == item1.name else item1.name == null
+        }
+
+        override fun hashCode(): Int {
+            var result = id
+            result = 31 * result + if (name != null) name.hashCode() else 0
+            return result
+        }
+    }
+
+    @Entity
+    class Item2(@field:PrimaryKey var id: Int, var name: String?) {
+        @Dao
+        interface Store {
+            @Query("SELECT * FROM Item2 LIMIT 1")
+            fun get(): Item2
+
+            @Insert
+            fun insert(vararg items: Item2)
+        }
+
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (other == null || javaClass != other.javaClass) return false
+            val item2 = other as Item2
+            if (id != item2.id) return false
+            return if (name != null) name == item2.name else item2.name == null
+        }
+
+        override fun hashCode(): Int {
+            var result = id
+            result = 31 * result + if (name != null) name.hashCode() else 0
+            return result
+        }
+    }
+
+    @Database(version = 1, exportSchema = false, entities = [Item1::class, Item2::class])
+    abstract class ConflictDatabase : RoomDatabase() {
+        abstract fun item1Dao(): Item1.Store
+        abstract fun item2Dao(): Item2.Store
+    }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/MultimapQueryTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/MultimapQueryTest.kt
new file mode 100644
index 0000000..4311020
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/MultimapQueryTest.kt
@@ -0,0 +1,1185 @@
+/*
+ * 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.room.integration.kotlintestapp.test
+
+import android.content.Context
+import androidx.arch.core.executor.testing.CountingTaskExecutorRule
+import androidx.collection.ArrayMap
+import androidx.collection.LongSparseArray
+import androidx.collection.SparseArrayCompat
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.room.Room
+import androidx.room.integration.kotlintestapp.TestDatabase
+import androidx.room.integration.kotlintestapp.dao.MusicDao
+import androidx.room.integration.kotlintestapp.vo.Album
+import androidx.room.integration.kotlintestapp.vo.AlbumNameAndBandName
+import androidx.room.integration.kotlintestapp.vo.AlbumWithSongs
+import androidx.room.integration.kotlintestapp.vo.Artist
+import androidx.room.integration.kotlintestapp.vo.Image
+import androidx.room.integration.kotlintestapp.vo.ImageFormat
+import androidx.room.integration.kotlintestapp.vo.ReleasedAlbum
+import androidx.room.integration.kotlintestapp.vo.Song
+import androidx.sqlite.db.SimpleSQLiteQuery
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.collect.ImmutableList
+import com.google.common.collect.ImmutableListMultimap
+import com.google.common.collect.ImmutableMap
+import com.google.common.collect.ImmutableMultimap
+import com.google.common.collect.ImmutableSet
+import com.google.common.collect.ImmutableSetMultimap
+import com.google.common.truth.Truth.assertThat
+import io.reactivex.Flowable
+import java.nio.ByteBuffer
+import java.util.Date
+import java.util.concurrent.ExecutionException
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeoutException
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests multimap return type for JOIN statements.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class MultimapQueryTest {
+    private lateinit var mMusicDao: MusicDao
+    private val mRhcpSong1: Song = Song(
+        1,
+        "Dani California",
+        "Red Hot Chili Peppers",
+        "Stadium Arcadium",
+        442,
+        2006
+    )
+    private val mRhcpSong2: Song = Song(
+        2,
+        "Snow (Hey Oh)",
+        "Red Hot Chili Peppers",
+        "Stadium Arcadium",
+        514,
+        2006
+    )
+    private val mAcdcSong1: Song = Song(
+        3,
+        "Highway to Hell",
+        "AC/DC",
+        "Highway to Hell",
+        328,
+        1979
+    )
+    private val mPinkFloydSong1: Song = Song(
+        4,
+        "The Great Gig in the Sky",
+        "Pink Floyd",
+        "The Dark Side of the Moon",
+        443,
+        1973
+    )
+    private val mRhcp: Artist = Artist(
+        1,
+        "Red Hot Chili Peppers",
+        true
+    )
+    private val mAcDc: Artist = Artist(
+        2,
+        "AC/DC",
+        true
+    )
+    private val mTheClash: Artist = Artist(
+        3,
+        "The Clash",
+        false
+    )
+    private val mPinkFloyd: Artist = Artist(
+        4,
+        "Pink Floyd",
+        false
+    )
+    private val mGlassAnimals: Artist = Artist(
+        5,
+        "Glass Animals",
+        true
+    )
+    private val mStadiumArcadium: Album = Album(
+        1,
+        "Stadium Arcadium",
+        "Red Hot Chili Peppers",
+        2006,
+        "N/A"
+    )
+    private val mCalifornication: Album = Album(
+        2,
+        "Californication",
+        "Red Hot Chili Peppers",
+        1999,
+        "N/A"
+    )
+    private val mHighwayToHell: Album = Album(
+        3,
+        "Highway to Hell",
+        "AC/DC",
+        1979,
+        null
+    )
+    private val mTheDarkSideOfTheMoon: Album = Album(
+        4,
+        "The Dark Side of the Moon",
+        "Pink Floyd",
+        1973,
+        "N/A"
+    )
+    private val mDreamland: Album = Album(
+        5,
+        "Dreamland",
+        null,
+        2020,
+        null
+    )
+    private val mPinkFloydAlbumCover: Image = Image(
+        1,
+        1973L,
+        "Pink Floyd",
+        "dark_side_of_the_moon_image".toByteArray(),
+        Date(101779200000L),
+        ImageFormat.JPG
+    )
+    private val mRhcpAlbumCover: Image = Image(
+        2,
+        2006L,
+        "Red Hot Chili Peppers",
+        "stadium_arcadium_image".toByteArray(),
+        Date(1146787200000L),
+        ImageFormat.MPEG
+    )
+
+    @JvmField
+    @Rule
+    var mExecutorRule = CountingTaskExecutorRule()
+    @Throws(TimeoutException::class, InterruptedException::class)
+    private fun drain() {
+        mExecutorRule.drainTasks(1, TimeUnit.MINUTES)
+        assertThat(mExecutorRule.isIdle).isTrue()
+    }
+
+    private open inner class MyTestObserver<T> : TestObserver<T>() {
+        @Throws(TimeoutException::class, InterruptedException::class)
+        override fun drain() {
+            this@MultimapQueryTest.drain()
+        }
+    }
+
+    @Before
+    fun createDb() {
+        val context: Context = ApplicationProvider.getApplicationContext()
+        val db: TestDatabase =
+            Room.inMemoryDatabaseBuilder(context, TestDatabase::class.java)
+                .build()
+        mMusicDao = db.musicDao()
+    }
+
+    /**
+     * Tests a simple JOIN query between two tables.
+     */
+    @Test
+    fun testGetFirstSongForArtist() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongsMap: Map<Artist, Song> = mMusicDao.getArtistAndFirstSongMap()
+        assertThat(artistToSongsMap[mAcDc]).isEqualTo(mAcdcSong1)
+        assertThat(artistToSongsMap[mRhcp]).isEqualTo(mRhcpSong1)
+    }
+
+    @Test
+    fun testGetSongToArtistMapping() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val songToArtistMap: Map<Song, Artist> = mMusicDao.getSongAndArtist()
+        assertThat(songToArtistMap[mAcdcSong1]).isEqualTo(mAcDc)
+        assertThat(songToArtistMap[mPinkFloydSong1]).isEqualTo(mPinkFloyd)
+        assertThat(songToArtistMap[mRhcpSong1]).isEqualTo(mRhcp)
+        assertThat(songToArtistMap[mRhcpSong2]).isEqualTo(mRhcp)
+    }
+
+    @Test
+    fun testJoinByArtistNameList() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongsMap: Map<Artist, List<Song>> = mMusicDao.getAllArtistAndTheirSongsList()
+        assertContentsOfResultMapWithList(artistToSongsMap)
+    }
+
+    @Test
+    fun testJoinByArtistNameListOrdered() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+
+        assertThat(mMusicDao.getAllArtistAndTheirSongsListOrdered().keys).containsExactlyElementsIn(
+            arrayOf(mRhcp, mAcDc, mPinkFloyd)
+        )
+    }
+
+    @Test
+    fun testJoinByArtistNameSet() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongsSet: Map<Artist, Set<Song>> = mMusicDao.getAllArtistAndTheirSongsSet()
+        assertContentsOfResultMapWithSet(artistToSongsSet)
+    }
+
+    /**
+     * Tests a JOIN using [androidx.room.RawQuery] between two tables.
+     */
+    @Test
+    fun testJoinByArtistNameRawQuery() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongsMap: Map<Artist, Song> = mMusicDao.getAllArtistAndTheirSongsRawQuery(
+            SimpleSQLiteQuery(
+                "SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist"
+            )
+        )
+        assertThat(artistToSongsMap[mAcDc]).isEqualTo(mAcdcSong1)
+    }
+
+    @Test
+    fun testJoinByArtistNameRawQueryList() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongsMap: Map<Artist, List<Song>> =
+            mMusicDao.getAllArtistAndTheirSongsRawQueryList(
+                SimpleSQLiteQuery(
+                    "SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist"
+                )
+            )
+        assertContentsOfResultMapWithList(artistToSongsMap)
+    }
+
+    @Test
+    fun testJoinByArtistNameRawQuerySet() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongsMap: Map<Artist, Set<Song>> =
+            mMusicDao.getAllArtistAndTheirSongsRawQuerySet(
+                SimpleSQLiteQuery(
+                    "SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist"
+                )
+            )
+        assertContentsOfResultMapWithSet(artistToSongsMap)
+    }
+
+    /**
+     * Tests a simple JOIN query between two tables with a [LiveData] map return type.
+     */
+    @Test
+    @Throws(ExecutionException::class, InterruptedException::class, TimeoutException::class)
+    fun testJoinByArtistNameLiveData() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongsMapLiveData: LiveData<Map<Artist, Song>> =
+            mMusicDao.getAllArtistAndTheirSongsAsLiveData()
+        val testOwner = TestLifecycleOwner(Lifecycle.State.CREATED)
+        val observer: TestObserver<Map<Artist, Song>> = MyTestObserver()
+        TestUtil.observeOnMainThread(artistToSongsMapLiveData, testOwner, observer)
+        assertThat(observer.hasValue()).isFalse()
+        observer.reset()
+        testOwner.handleLifecycleEvent(Lifecycle.Event.ON_START)
+        assertThat(observer.get()).isNotNull()
+        assertThat(observer.get()?.get(mAcDc)).isEqualTo(mAcdcSong1)
+    }
+
+    @Test
+    @Throws(ExecutionException::class, InterruptedException::class, TimeoutException::class)
+    fun testJoinByArtistNameLiveDataList() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongsMapLiveData: LiveData<Map<Artist, List<Song>>> =
+            mMusicDao.getAllArtistAndTheirSongsAsLiveDataList()
+        val testOwner = TestLifecycleOwner(Lifecycle.State.CREATED)
+        val observer: TestObserver<Map<Artist, List<Song>>> = MyTestObserver()
+        TestUtil.observeOnMainThread(artistToSongsMapLiveData, testOwner, observer)
+        assertThat(observer.hasValue()).isFalse()
+        observer.reset()
+        testOwner.handleLifecycleEvent(Lifecycle.Event.ON_START)
+        assertThat(observer.get()).isNotNull()
+        assertContentsOfResultMapWithList(observer.get()!!)
+    }
+
+    @Test
+    @Throws(ExecutionException::class, InterruptedException::class, TimeoutException::class)
+    fun testJoinByArtistNameLiveDataSet() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongsMapLiveData: LiveData<Map<Artist, Set<Song>>> =
+            mMusicDao.allArtistAndTheirSongsAsLiveDataSet()
+        val testOwner = TestLifecycleOwner(Lifecycle.State.CREATED)
+        val observer: TestObserver<Map<Artist, Set<Song>>> = MyTestObserver()
+        TestUtil.observeOnMainThread(artistToSongsMapLiveData, testOwner, observer)
+        assertThat(observer.hasValue()).isFalse()
+        observer.reset()
+        testOwner.handleLifecycleEvent(Lifecycle.Event.ON_START)
+        assertThat(observer.get()).isNotNull()
+        assertContentsOfResultMapWithSet(observer.get()!!)
+    }
+
+    /**
+     * Tests a simple JOIN query between two tables with a [Flowable] map return type.
+     */
+    @Test
+    fun testJoinByArtistNameFlowableList() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongsMapFlowable: Flowable<Map<Artist, List<Song>>> =
+            mMusicDao.getAllArtistAndTheirSongsAsFlowableList()
+        assertContentsOfResultMapWithList(artistToSongsMapFlowable.blockingFirst())
+    }
+
+    @Test
+    fun testJoinByArtistNameFlowableSet() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongsMapFlowable: Flowable<Map<Artist, Set<Song>>> =
+            mMusicDao.allArtistAndTheirSongsAsFlowableSet()
+        assertContentsOfResultMapWithSet(artistToSongsMapFlowable.blockingFirst())
+    }
+
+    /**
+     * Tests a simple JOIN query between two tables with a return type of a map with a key that
+     * is an entity [Artist] and a POJO [AlbumWithSongs] that use
+     * [androidx.room.Embedded] and [androidx.room.Relation].
+     */
+    @Test
+    fun testPojoWithEmbeddedAndRelation() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addAlbums(
+            mStadiumArcadium,
+            mCalifornication,
+            mTheDarkSideOfTheMoon,
+            mHighwayToHell
+        )
+        val artistToAlbumsWithSongsMap: Map<Artist, AlbumWithSongs> =
+            mMusicDao.getAllArtistAndTheirAlbumsWithSongs()
+        val rhcpAlbum: AlbumWithSongs? = artistToAlbumsWithSongsMap[mRhcp]
+
+        assertThat(rhcpAlbum).isNotNull()
+        assertThat(artistToAlbumsWithSongsMap.keys).containsExactlyElementsIn(
+            arrayOf(mRhcp, mAcDc, mPinkFloyd)
+        )
+        assertThat(artistToAlbumsWithSongsMap.containsKey(mTheClash)).isFalse()
+        assertThat(artistToAlbumsWithSongsMap[mPinkFloyd]?.album)
+            .isEqualTo(mTheDarkSideOfTheMoon)
+        assertThat(artistToAlbumsWithSongsMap[mAcDc]?.album)
+            .isEqualTo(mHighwayToHell)
+        assertThat(artistToAlbumsWithSongsMap[mAcDc]?.songs?.get(0)).isEqualTo(mAcdcSong1)
+        if (rhcpAlbum?.album?.equals(mStadiumArcadium) == true) {
+            assertThat(rhcpAlbum.songs).containsExactlyElementsIn(
+                listOf(mRhcpSong1, mRhcpSong2)
+            )
+        } else if (rhcpAlbum?.album?.equals(mCalifornication) == true) {
+            assertThat(rhcpAlbum.songs).isEmpty()
+        } else {
+            Assert.fail()
+        }
+    }
+
+    /**
+     * Tests a simple JOIN query between two tables with a return type of a map with a key that
+     * is an entity [Artist] and a list of entity POJOs [AlbumWithSongs] that use
+     * [androidx.room.Embedded] and [androidx.room.Relation].
+     */
+    @Test
+    fun testPojoWithEmbeddedAndRelationList() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addAlbums(
+            mStadiumArcadium,
+            mCalifornication,
+            mTheDarkSideOfTheMoon,
+            mHighwayToHell
+        )
+        val artistToAlbumsWithSongsMap: Map<Artist, List<AlbumWithSongs>> =
+            mMusicDao.getAllArtistAndTheirAlbumsWithSongsList()
+        mMusicDao.getAllArtistAndTheirAlbumsWithSongs()
+        val rhcpList: List<AlbumWithSongs> = artistToAlbumsWithSongsMap[mRhcp]!!
+        assertThat(artistToAlbumsWithSongsMap.keys).containsExactlyElementsIn(
+            listOf<Any>(mRhcp, mAcDc, mPinkFloyd)
+        )
+        assertThat(artistToAlbumsWithSongsMap.containsKey(mTheClash)).isFalse()
+        assertThat(artistToAlbumsWithSongsMap[mPinkFloyd]?.single()?.album)
+            .isEqualTo(mTheDarkSideOfTheMoon)
+        assertThat(artistToAlbumsWithSongsMap[mAcDc]?.single()?.album)
+            .isEqualTo(mHighwayToHell)
+        assertThat(artistToAlbumsWithSongsMap[mAcDc]?.single()?.songs?.get(0))
+            .isEqualTo(mAcdcSong1)
+        for (albumAndSong in rhcpList) {
+            when (albumAndSong.album) {
+                mStadiumArcadium -> {
+                    assertThat(albumAndSong.songs).containsExactlyElementsIn(
+                        listOf(mRhcpSong1, mRhcpSong2)
+                    )
+                }
+                mCalifornication -> {
+                    assertThat(albumAndSong.songs).isEmpty()
+                }
+                else -> {
+                    Assert.fail()
+                }
+            }
+        }
+    }
+
+    /**
+     * Tests a simple JOIN query between two tables with a return type of a map with a key
+     * [ReleasedAlbum] and value (list of [AlbumNameAndBandName]) that are non-entity
+     * POJOs.
+     */
+    @Test
+    fun testNonEntityPojosList() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addAlbums(
+            mStadiumArcadium,
+            mCalifornication,
+            mTheDarkSideOfTheMoon,
+            mHighwayToHell
+        )
+        val map: Map<ReleasedAlbum, List<AlbumNameAndBandName>> =
+            mMusicDao.getReleaseYearToAlbumsAndBandsList()
+        val allReleasedAlbums: Set<ReleasedAlbum> = map.keys
+        assertThat(allReleasedAlbums.size).isEqualTo(3)
+        allReleasedAlbums.forEach { album ->
+            when (album.mAlbumName) {
+                mStadiumArcadium.mAlbumName -> {
+                    assertThat(album.mReleaseYear).isEqualTo(
+                        mStadiumArcadium.mAlbumReleaseYear
+                    )
+                    val resultList = map[album] ?: emptyList()
+                    assertThat(resultList.size).isEqualTo(2)
+                    assertThat(resultList[0].mBandName)
+                        .isEqualTo(mRhcp.mArtistName)
+                    assertThat(resultList[0].mAlbumName)
+                        .isEqualTo(mStadiumArcadium.mAlbumName)
+                    assertThat(resultList[1].mBandName)
+                        .isEqualTo(mRhcp.mArtistName)
+                    assertThat(map[album]!![1].mAlbumName)
+                        .isEqualTo(mStadiumArcadium.mAlbumName)
+                }
+                mHighwayToHell.mAlbumName -> {
+                    assertThat(album.mReleaseYear).isEqualTo(mHighwayToHell.mAlbumReleaseYear)
+                    val resultList = map[album] ?: emptyList()
+                    assertThat(resultList.size).isEqualTo(1)
+                    assertThat(resultList[0].mBandName)
+                        .isEqualTo(mAcDc.mArtistName)
+                    assertThat(resultList[0].mAlbumName)
+                        .isEqualTo(mHighwayToHell.mAlbumName)
+                }
+                mTheDarkSideOfTheMoon.mAlbumName -> {
+                    assertThat(album.mReleaseYear)
+                        .isEqualTo(mTheDarkSideOfTheMoon.mAlbumReleaseYear)
+                    val resultList = map[album] ?: emptyList()
+                    assertThat(resultList.size).isEqualTo(1)
+                    assertThat(resultList[0].mBandName).isEqualTo(mPinkFloyd.mArtistName)
+                    assertThat(resultList[0].mAlbumName).isEqualTo(mTheDarkSideOfTheMoon.mAlbumName)
+                }
+                else -> {
+                    // Shouldn't get here as we expect only the 3 albums to be keys in the map
+                    Assert.fail()
+                }
+            }
+        }
+    }
+
+    @Test
+    fun testJoinByArtistNameGuavaImmutableListMultimap() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongs: ImmutableListMultimap<Artist, Song> =
+            mMusicDao.allArtistAndTheirSongsGuavaImmutableListMultimap()
+        assertContentsOfResultMultimap(artistToSongs)
+    }
+
+    @Test
+    fun testJoinByArtistNameGuavaImmutableSetMultimap() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongs: ImmutableSetMultimap<Artist, Song> =
+            mMusicDao.allArtistAndTheirSongsGuavaImmutableSetMultimap()
+        assertContentsOfResultMultimap(artistToSongs)
+    }
+
+    @Test
+    fun testJoinByArtistNameRawQueryGuavaImmutableListMultimap() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongsMap: ImmutableListMultimap<Artist, Song> =
+            mMusicDao.getAllArtistAndTheirSongsRawQueryGuavaImmutableListMultimap(
+                SimpleSQLiteQuery(
+                    "SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song" +
+                        ".mArtist"
+                )
+            )
+        assertThat(artistToSongsMap[mAcDc]).containsExactly(mAcdcSong1)
+    }
+
+    @Test
+    fun testJoinByArtistNameRawQueryGuavaImmutableSetMultimap() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongsMap: ImmutableSetMultimap<Artist, Song> =
+            mMusicDao.getAllArtistAndTheirSongsRawQueryGuavaImmutableSetMultimap(
+                SimpleSQLiteQuery(
+                    "SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song" +
+                        ".mArtist"
+                )
+            )
+        assertThat(artistToSongsMap[mAcDc]).containsExactly(mAcdcSong1)
+    }
+
+    @Test
+    @Throws(ExecutionException::class, InterruptedException::class, TimeoutException::class)
+    fun testJoinByArtistNameLiveDataGuavaImmutableListMultimap() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongsMapLiveData: LiveData<ImmutableListMultimap<Artist, Song>> =
+            mMusicDao.allArtistAndTheirSongsAsLiveDataGuavaImmutableListMultimap()
+        val testOwner = TestLifecycleOwner(Lifecycle.State.CREATED)
+        val observer: TestObserver<ImmutableListMultimap<Artist, Song>> = MyTestObserver()
+        TestUtil.observeOnMainThread(artistToSongsMapLiveData, testOwner, observer)
+        assertThat(observer.hasValue()).isFalse()
+        observer.reset()
+        testOwner.handleLifecycleEvent(Lifecycle.Event.ON_START)
+        assertThat(observer.get()).isNotNull()
+        assertContentsOfResultMultimap(observer.get()!!)
+    }
+
+    @Test
+    @Throws(ExecutionException::class, InterruptedException::class, TimeoutException::class)
+    fun testJoinByArtistNameLiveDataGuavaImmutableSetMultimap() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongsMapLiveData: LiveData<ImmutableSetMultimap<Artist, Song>> =
+            mMusicDao.allArtistAndTheirSongsAsLiveDataGuavaImmutableSetMultimap()
+        val testOwner = TestLifecycleOwner(Lifecycle.State.CREATED)
+        val observer: TestObserver<ImmutableSetMultimap<Artist, Song>> = MyTestObserver()
+        TestUtil.observeOnMainThread(artistToSongsMapLiveData, testOwner, observer)
+        assertThat(observer.hasValue()).isFalse()
+        observer.reset()
+        testOwner.handleLifecycleEvent(Lifecycle.Event.ON_START)
+        assertThat(observer.get()).isNotNull()
+        assertContentsOfResultMultimap(observer.get()!!)
+    }
+
+    @Test
+    fun testPojoWithEmbeddedAndRelationGuavaImmutableListMultimap() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addAlbums(
+            mStadiumArcadium,
+            mCalifornication,
+            mTheDarkSideOfTheMoon,
+            mHighwayToHell
+        )
+        val artistToAlbumsWithSongsMap: ImmutableListMultimap<Artist, AlbumWithSongs> =
+            mMusicDao.allArtistAndTheirAlbumsWithSongsGuavaImmutableListMultimap()
+        val rhcpList: ImmutableList<AlbumWithSongs> = artistToAlbumsWithSongsMap[mRhcp]
+        assertThat(artistToAlbumsWithSongsMap.keySet()).containsExactlyElementsIn(
+            listOf<Any>(mRhcp, mAcDc, mPinkFloyd)
+        )
+        assertThat(artistToAlbumsWithSongsMap.containsKey(mTheClash)).isFalse()
+        assertThat(artistToAlbumsWithSongsMap[mPinkFloyd][0].album)
+            .isEqualTo(mTheDarkSideOfTheMoon)
+        assertThat(artistToAlbumsWithSongsMap[mAcDc][0].album)
+            .isEqualTo(mHighwayToHell)
+        assertThat(
+            artistToAlbumsWithSongsMap[mAcDc][0].songs[0]
+        ).isEqualTo(mAcdcSong1)
+        for (albumAndSong in rhcpList) {
+            when (albumAndSong.album) {
+                mStadiumArcadium -> {
+                    assertThat(albumAndSong.songs).containsExactlyElementsIn(
+                        listOf(mRhcpSong1, mRhcpSong2)
+                    )
+                }
+                mCalifornication -> {
+                    assertThat(albumAndSong.songs).isEmpty()
+                }
+                else -> {
+                    Assert.fail()
+                }
+            }
+        }
+    }
+
+    @Test
+    fun testPojoWithEmbeddedAndRelationGuavaImmutableSetMultimap() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addAlbums(
+            mStadiumArcadium,
+            mCalifornication,
+            mTheDarkSideOfTheMoon,
+            mHighwayToHell
+        )
+        val artistToAlbumsWithSongsMap: ImmutableSetMultimap<Artist, AlbumWithSongs> =
+            mMusicDao.allArtistAndTheirAlbumsWithSongsGuavaImmutableSetMultimap()
+        val rhcpList: ImmutableSet<AlbumWithSongs> = artistToAlbumsWithSongsMap[mRhcp]
+        assertThat(artistToAlbumsWithSongsMap.keySet()).containsExactlyElementsIn(
+            listOf<Any>(mRhcp, mAcDc, mPinkFloyd)
+        )
+        assertThat(artistToAlbumsWithSongsMap.containsKey(mTheClash)).isFalse()
+        assertThat(artistToAlbumsWithSongsMap[mPinkFloyd].asList()[0].album)
+            .isEqualTo(mTheDarkSideOfTheMoon)
+        assertThat(artistToAlbumsWithSongsMap[mAcDc].asList()[0].album)
+            .isEqualTo(mHighwayToHell)
+        assertThat(
+            artistToAlbumsWithSongsMap[mAcDc].asList()[0].songs[0]
+        ).isEqualTo(mAcdcSong1)
+        for (albumAndSong in rhcpList) {
+            when (albumAndSong.album) {
+                mStadiumArcadium -> {
+                    assertThat(albumAndSong.songs).containsExactlyElementsIn(
+                        listOf(mRhcpSong1, mRhcpSong2)
+                    )
+                }
+                mCalifornication -> {
+                    assertThat(albumAndSong.songs).isEmpty()
+                }
+                else -> {
+                    Assert.fail()
+                }
+            }
+        }
+    }
+
+    @Test
+    fun testJoinByArtistNameImmutableMap() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongsMap: ImmutableMap<Artist, List<Song>> =
+            mMusicDao.allArtistAndTheirSongsImmutableMap()
+        assertContentsOfResultMapWithList(artistToSongsMap)
+    }
+
+    @Test
+    fun testJoinByArtistNameRawQueryImmutableMap() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongsMap: ImmutableMap<Artist, List<Song>> =
+            mMusicDao.getAllArtistAndTheirSongsRawQueryImmutableMap(
+                SimpleSQLiteQuery(
+                    "SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song" +
+                        ".mArtist"
+                )
+            )
+        assertContentsOfResultMapWithList(artistToSongsMap)
+    }
+
+    @Test
+    @Throws(ExecutionException::class, InterruptedException::class, TimeoutException::class)
+    fun testJoinByArtistNameImmutableMapWithSet() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistToSongsMapLiveData: LiveData<ImmutableMap<Artist, Set<Song>>> =
+            mMusicDao.allArtistAndTheirSongsAsLiveDataImmutableMap()
+        val testOwner = TestLifecycleOwner(Lifecycle.State.CREATED)
+        val observer: TestObserver<ImmutableMap<Artist, Set<Song>>> = MyTestObserver()
+        TestUtil.observeOnMainThread(artistToSongsMapLiveData, testOwner, observer)
+        assertThat(observer.hasValue()).isFalse()
+        observer.reset()
+        testOwner.handleLifecycleEvent(Lifecycle.Event.ON_START)
+        assertThat(observer.get()).isNotNull()
+        assertContentsOfResultMapWithSet(observer.get()!!)
+    }
+
+    @Test
+    fun testStringToListOfSongs() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistNameToSongsMap: Map<String, List<Song>> = mMusicDao.artistNameToSongs()
+        assertThat(artistNameToSongsMap.containsKey("Pink Floyd")).isTrue()
+        assertThat(artistNameToSongsMap["Red Hot Chili Peppers"]).containsExactlyElementsIn(
+            listOf<Any>(mRhcpSong1, mRhcpSong2)
+        )
+    }
+
+    @Test
+    fun testIntegerToListOfAlbums() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addAlbums(
+            mStadiumArcadium,
+            mCalifornication,
+            mTheDarkSideOfTheMoon,
+            mHighwayToHell
+        )
+        val releaseYearToAlbumsMap: Map<Int, List<Song>> = mMusicDao.releaseYearToAlbums()
+        assertThat(releaseYearToAlbumsMap.containsKey(2006)).isTrue()
+        assertThat(releaseYearToAlbumsMap[2006]).containsExactlyElementsIn(
+            listOf<Any>(mRhcpSong1, mRhcpSong2)
+        )
+        assertThat(releaseYearToAlbumsMap[1979]).containsExactlyElementsIn(
+            listOf<Any>(mAcdcSong1)
+        )
+    }
+
+    @Test
+    fun testIntegerToStringOfAlbumNames() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addAlbums(
+            mStadiumArcadium,
+            mCalifornication,
+            mTheDarkSideOfTheMoon,
+            mHighwayToHell
+        )
+        val releaseYearToAlbumNameMap: Map<Int, List<String>> =
+            mMusicDao.releaseYearToSongNames()
+        assertThat(releaseYearToAlbumNameMap.containsKey(2006)).isTrue()
+        assertThat(releaseYearToAlbumNameMap[2006]).containsExactlyElementsIn(
+            listOf("Snow (Hey Oh)", "Dani California")
+        )
+    }
+
+    @Test
+    fun testStringToListOfSongsRawQuery() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistNameToSongsMap: Map<String, List<Song>> = mMusicDao.getArtistNameToSongsRawQuery(
+            SimpleSQLiteQuery(
+                "SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist"
+            )
+        )
+        assertThat(artistNameToSongsMap.containsKey("Pink Floyd")).isTrue()
+        assertThat(artistNameToSongsMap["Red Hot Chili Peppers"]).containsExactlyElementsIn(
+            listOf<Any>(mRhcpSong1, mRhcpSong2)
+        )
+    }
+
+    @Test
+    fun testIntegerToListOfAlbumsRawQuery() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addAlbums(
+            mStadiumArcadium,
+            mCalifornication,
+            mTheDarkSideOfTheMoon,
+            mHighwayToHell
+        )
+        val releaseYearToAlbumsMap: Map<Int, List<Song>> =
+            mMusicDao.getReleaseYearToAlbumsRawQuery(
+                SimpleSQLiteQuery(
+                    "SELECT * FROM Album JOIN Song ON Song.mReleasedYear = Album" +
+                        ".mAlbumReleaseYear"
+                )
+            )
+        assertThat(releaseYearToAlbumsMap.containsKey(2006)).isTrue()
+        assertThat(releaseYearToAlbumsMap[2006]).containsExactlyElementsIn(
+            listOf<Any>(mRhcpSong1, mRhcpSong2)
+        )
+        assertThat(releaseYearToAlbumsMap[1979]).containsExactlyElementsIn(
+            listOf<Any>(mAcdcSong1)
+        )
+    }
+
+    @Test
+    fun testIntegerToStringOfAlbumNamesRawQuery() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addAlbums(
+            mStadiumArcadium,
+            mCalifornication,
+            mTheDarkSideOfTheMoon,
+            mHighwayToHell
+        )
+        val releaseYearToAlbumNameMap: Map<Int, List<String>> =
+            mMusicDao.getReleaseYearToSongNamesRawQuery(
+                SimpleSQLiteQuery(
+                    "SELECT * FROM Album JOIN Song ON Song.mReleasedYear = Album" +
+                        ".mAlbumReleaseYear"
+                )
+            )
+        assertThat(releaseYearToAlbumNameMap.containsKey(2006)).isTrue()
+        assertThat(releaseYearToAlbumNameMap[2006]).containsExactlyElementsIn(
+            listOf("Snow (Hey Oh)", "Dani California")
+        )
+    }
+
+    @Test
+    fun testArtistToSongCount() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistNameToSongsMap: Map<Artist, Int> = mMusicDao.artistAndSongCountMap()
+        assertThat(artistNameToSongsMap.containsKey(mPinkFloyd)).isTrue()
+        assertThat(artistNameToSongsMap[mRhcp]).isEqualTo(2)
+    }
+
+    @Test
+    fun testArtistToSongCountWithRawQuery() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        val artistNameToSongsMap: Map<Artist, Int> = mMusicDao.getArtistAndSongCountMapRawQuery(
+            SimpleSQLiteQuery(
+                "SELECT *, COUNT(mSongId) as songCount FROM Artist JOIN Song ON Artist" +
+                    ".mArtistName = Song.mArtist GROUP BY mArtistName"
+            )
+        )
+        assertThat(artistNameToSongsMap.containsKey(mPinkFloyd)).isTrue()
+        assertThat(artistNameToSongsMap[mRhcp]).isEqualTo(2)
+    }
+
+    @Test
+    fun testArtistToImage() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addImages(mPinkFloydAlbumCover, mRhcpAlbumCover)
+        val artistNameToImagesMap: ImmutableMap<Artist, ByteBuffer> =
+            mMusicDao.allArtistsWithAlbumCovers()
+        assertThat(artistNameToImagesMap.containsKey(mPinkFloyd)).isTrue()
+        assertThat(artistNameToImagesMap[mRhcp]).isEqualTo(
+            ByteBuffer.wrap("stadium_arcadium_image".toByteArray())
+        )
+    }
+
+    @Test
+    fun testArtistToImageRawQuery() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addImages(mPinkFloydAlbumCover, mRhcpAlbumCover)
+        val artistNameToImagesMap: ImmutableMap<Artist, ByteBuffer> =
+            mMusicDao.getAllArtistsWithAlbumCoversRawQuery(
+                SimpleSQLiteQuery(
+                    "SELECT * FROM Artist JOIN Image ON Artist.mArtistName = Image" +
+                        ".mArtistInImage"
+                )
+            )
+        assertThat(artistNameToImagesMap.containsKey(mPinkFloyd)).isTrue()
+        assertThat(artistNameToImagesMap[mRhcp]).isEqualTo(
+            ByteBuffer.wrap("stadium_arcadium_image".toByteArray())
+        )
+    }
+
+    @Test
+    fun testArtistToImageYear() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addImages(mPinkFloydAlbumCover, mRhcpAlbumCover)
+        val artistNameToImagesMap: ImmutableMap<Artist, Long> =
+            mMusicDao.allArtistsWithAlbumCoverYear()
+        assertThat(artistNameToImagesMap[mRhcp]).isEqualTo(2006L)
+    }
+
+    @Test
+    fun testImageYearToArtistRawQuery() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addImages(mPinkFloydAlbumCover, mRhcpAlbumCover)
+        val imageToArtistsMap: ImmutableMap<Long, Artist> =
+            mMusicDao.getAllAlbumCoverYearToArtistsWithRawQuery(
+                SimpleSQLiteQuery(
+                    "SELECT * FROM Image JOIN Artist ON Artist.mArtistName = Image" +
+                        ".mArtistInImage"
+                )
+            )
+        assertThat(imageToArtistsMap[2006L]).isEqualTo(mRhcp)
+        assertThat(
+            imageToArtistsMap.keys
+        ).containsExactlyElementsIn(
+            listOf(2006L, 1973L)
+        )
+    }
+
+    @Test
+    fun testAlbumCoversWithBandActivity() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addImages(mPinkFloydAlbumCover, mRhcpAlbumCover)
+        val imageToArtistsMap: ImmutableMap<ByteBuffer, Boolean> =
+            mMusicDao.albumCoversWithBandActivity()
+        assertThat(
+            imageToArtistsMap[ByteBuffer.wrap("stadium_arcadium_image".toByteArray())]
+        ).isEqualTo(true)
+        assertThat(
+            imageToArtistsMap[ByteBuffer.wrap("dark_side_of_the_moon_image".toByteArray())]
+        ).isEqualTo(false)
+    }
+
+    @Test
+    fun testAlbumCoversWithBandActivityRawQuery() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addImages(mPinkFloydAlbumCover, mRhcpAlbumCover)
+        val imageToArtistsMap: ImmutableMap<ByteBuffer, Boolean> =
+            mMusicDao.getAlbumCoversWithBandActivityRawQuery(
+                SimpleSQLiteQuery(
+                    "SELECT * FROM Image JOIN Artist ON Artist.mArtistName = Image" +
+                        ".mArtistInImage"
+                )
+            )
+        assertThat(imageToArtistsMap[ByteBuffer.wrap("stadium_arcadium_image".toByteArray())])
+            .isEqualTo(true)
+        assertThat(
+            imageToArtistsMap[ByteBuffer.wrap("dark_side_of_the_moon_image".toByteArray())]
+        ).isEqualTo(false)
+    }
+
+    @Test
+    fun testAlbumReleaseDateWithBandActivity() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addImages(mPinkFloydAlbumCover, mRhcpAlbumCover)
+        val imageToArtistsMap: ImmutableMap<Date, Boolean> =
+            mMusicDao.albumDateWithBandActivity()
+        assertThat(imageToArtistsMap[Date(101779200000L)]).isEqualTo(false)
+        assertThat(imageToArtistsMap[Date(1146787200000L)]).isEqualTo(true)
+    }
+
+    @Test
+    fun testAlbumReleaseDateWithBandActivityRawQuery() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addImages(mPinkFloydAlbumCover, mRhcpAlbumCover)
+        val imageToArtistsMap: ImmutableMap<Date, Boolean> =
+            mMusicDao.getAlbumDateWithBandActivityRawQuery(
+                SimpleSQLiteQuery(
+                    "SELECT * FROM Image JOIN Artist ON Artist.mArtistName = Image" +
+                        ".mArtistInImage"
+                )
+            )
+        assertThat(imageToArtistsMap[Date(101779200000L)]).isEqualTo(false)
+        assertThat(imageToArtistsMap[Date(1146787200000L)]).isEqualTo(true)
+    }
+
+    @Test
+    fun testEnumMap() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addImages(mPinkFloydAlbumCover, mRhcpAlbumCover)
+        val imageToArtistsMap: ImmutableMap<ImageFormat, Boolean> =
+            mMusicDao.imageFormatWithBandActivity()
+        assertThat(imageToArtistsMap[ImageFormat.JPG]).isEqualTo(false)
+        assertThat(imageToArtistsMap[ImageFormat.MPEG]).isEqualTo(true)
+    }
+
+    @Test
+    fun testEnumMapWithRawQuery() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addImages(mPinkFloydAlbumCover, mRhcpAlbumCover)
+        val imageToArtistsMap: ImmutableMap<ImageFormat, Boolean> =
+            mMusicDao.getImageFormatWithBandActivityRawQuery(
+                SimpleSQLiteQuery(
+                    "SELECT * FROM Image JOIN Artist ON Artist.mArtistName = Image" +
+                        ".mArtistInImage"
+                )
+            )
+        assertThat(imageToArtistsMap[ImageFormat.JPG]).isEqualTo(false)
+        assertThat(imageToArtistsMap[ImageFormat.MPEG]).isEqualTo(true)
+    }
+
+    @Test
+    fun testInvalidMapInfoColumnsWithRawQuery() {
+        mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        try {
+            mMusicDao.getMapWithInvalidColumnRawQuery(
+                SimpleSQLiteQuery(
+                    "SELECT *, COUNT(mSongId) as songCount FROM Artist JOIN Song ON Artist" +
+                        ".mArtistName = Song.mArtist GROUP BY mArtistName"
+                )
+            )
+        } catch (e: IllegalArgumentException) {
+            assertThat(e.message!!.contains("column 'cat' does not exist"))
+        }
+    }
+
+    @Test
+    fun testLeftJoin() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addAlbums(
+            mStadiumArcadium,
+            mCalifornication,
+            mTheDarkSideOfTheMoon,
+            mHighwayToHell
+        )
+        val map: Map<Artist, List<Album>> = mMusicDao.artistAndAlbumsLeftJoin()
+        assertThat(map.containsKey(mTheClash))
+        assertThat(map[mTheClash]).isEmpty()
+    }
+
+    @Test
+    fun testLeftJoinGuava() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addAlbums(
+            mStadiumArcadium,
+            mCalifornication,
+            mTheDarkSideOfTheMoon,
+            mHighwayToHell
+        )
+        val map: ImmutableListMultimap<Artist, Album> = mMusicDao.artistAndAlbumsLeftJoinGuava()
+        assertThat(map.containsKey(mTheClash))
+        assertThat(map[mTheClash]).isEmpty()
+    }
+
+    @Test
+    fun testNonPojoLeftJoin() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addAlbums(
+            mStadiumArcadium,
+            mCalifornication,
+            mTheDarkSideOfTheMoon,
+            mHighwayToHell
+        )
+        val map: Map<Artist, List<String>> = mMusicDao.artistAndAlbumNamesLeftJoin()
+        assertThat(map.containsKey(mTheClash))
+        assertThat(map[mTheClash]).isEmpty()
+    }
+
+    @Test
+    fun nullKeyColumnLeftJoin() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addAlbums(
+            mStadiumArcadium,
+            mCalifornication,
+            mTheDarkSideOfTheMoon,
+            mHighwayToHell
+        )
+        val map: Map<Album, Artist> = mMusicDao.albumToArtistLeftJoin()
+        assertThat(map.containsKey(mHighwayToHell))
+        assertThat(map[mHighwayToHell]).isEqualTo(mAcDc)
+    }
+
+    @Test
+    fun nullValueColumnLeftJoin() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addAlbums(
+            mStadiumArcadium,
+            mCalifornication,
+            mTheDarkSideOfTheMoon,
+            mHighwayToHell
+        )
+        val map: Map<Artist, Album> = mMusicDao.artistToAlbumLeftJoin()
+        assertThat(map.containsKey(mAcDc))
+        assertThat(map[mAcDc]).isEqualTo(mHighwayToHell)
+    }
+
+    @Test
+    fun nullAlbumAddedLeftJoin() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd, mGlassAnimals)
+        mMusicDao.addAlbums(
+            mStadiumArcadium,
+            mCalifornication,
+            mTheDarkSideOfTheMoon,
+            mHighwayToHell,
+            mDreamland
+        )
+        val map: Map<Artist, Album> = mMusicDao.artistToAlbumLeftJoin()
+        assertThat(map.containsKey(mGlassAnimals)).isFalse()
+    }
+
+    @Test
+    fun testImageYearToArtistLongSparseArray() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addImages(mPinkFloydAlbumCover, mRhcpAlbumCover)
+        val imageToArtistsMap: LongSparseArray<Artist> =
+            mMusicDao.allAlbumCoverYearToArtistsWithLongSparseArray()
+        assertThat(imageToArtistsMap.size()).isEqualTo(2)
+        assertThat(imageToArtistsMap[2006L]).isEqualTo(mRhcp)
+    }
+
+    @Test
+    fun testImageYearToArtistSparseArrayCompat() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addImages(mPinkFloydAlbumCover, mRhcpAlbumCover)
+        val imageToArtistsMap: SparseArrayCompat<Artist> =
+            mMusicDao.allAlbumCoverYearToArtistsWithIntSparseArray()
+        assertThat(imageToArtistsMap.size()).isEqualTo(2)
+        assertThat(imageToArtistsMap[2006]).isEqualTo(mRhcp)
+        assertThat(imageToArtistsMap[1973]).isEqualTo(mPinkFloyd)
+    }
+
+    @Test
+    fun testImageYearToArtistRawQueryArrayMap() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addImages(mPinkFloydAlbumCover, mRhcpAlbumCover)
+        val imageToArtistsMap: ArrayMap<Long, Artist> =
+            mMusicDao.getAllAlbumCoverYearToArtistsWithRawQueryArrayMap(
+                SimpleSQLiteQuery(
+                    "SELECT * FROM Image JOIN Artist ON Artist.mArtistName = Image" +
+                        ".mArtistInImage"
+                )
+            )
+        assertThat(imageToArtistsMap[2006L]).isEqualTo(mRhcp)
+        assertThat(
+            imageToArtistsMap.keys
+        ).containsExactlyElementsIn(
+            listOf(2006L, 1973L)
+        )
+    }
+
+    @Test
+    fun testArtistToImageYearArrayMap() {
+        mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+        mMusicDao.addImages(mPinkFloydAlbumCover, mRhcpAlbumCover)
+        val artistNameToImagesMap: ArrayMap<Artist, Long> =
+            mMusicDao.allArtistsWithAlbumCoverYearArrayMap()
+        assertThat(artistNameToImagesMap[mRhcp]).isEqualTo(2006L)
+    }
+
+    /**
+     * Checks that the contents of the map are as expected.
+     *
+     * @param artistToSongsMap Map of Artists to list of Songs joined by the artist name
+     */
+    private fun assertContentsOfResultMapWithList(artistToSongsMap: Map<Artist, List<Song>>) {
+        assertThat(artistToSongsMap.keys).containsExactlyElementsIn(
+            listOf<Any>(mRhcp, mAcDc, mPinkFloyd)
+        )
+        assertThat(artistToSongsMap.containsKey(mTheClash)).isFalse()
+        assertThat(artistToSongsMap[mPinkFloyd]).containsExactly(mPinkFloydSong1)
+        assertThat(artistToSongsMap[mRhcp]).containsExactlyElementsIn(
+            listOf<Any>(mRhcpSong1, mRhcpSong2)
+        )
+        assertThat(artistToSongsMap[mAcDc]).containsExactly(mAcdcSong1)
+    }
+
+    /**
+     * Checks that the contents of the map are as expected.
+     *
+     * @param artistToSongsMap Map of Artists to set of Songs joined by the artist name
+     */
+    private fun assertContentsOfResultMapWithSet(artistToSongsMap: Map<Artist, Set<Song>>) {
+        assertThat(artistToSongsMap.keys).containsExactlyElementsIn(
+            listOf<Any>(mRhcp, mAcDc, mPinkFloyd)
+        )
+        assertThat(artistToSongsMap.containsKey(mTheClash)).isFalse()
+        assertThat(artistToSongsMap[mPinkFloyd]).containsExactly(mPinkFloydSong1)
+        assertThat(artistToSongsMap[mRhcp]).containsExactlyElementsIn(
+            listOf<Any>(mRhcpSong1, mRhcpSong2)
+        )
+        assertThat(artistToSongsMap[mAcDc]).containsExactly(mAcdcSong1)
+    }
+
+    /**
+     * Checks that the contents of the map are as expected.
+     *
+     * @param artistToSongsMap Map of Artists to Collection of Songs joined by the artist name
+     */
+    private fun assertContentsOfResultMultimap(artistToSongsMap: ImmutableMultimap<Artist, Song>) {
+        assertThat(artistToSongsMap.keySet()).containsExactlyElementsIn(
+            listOf<Any>(mRhcp, mAcDc, mPinkFloyd)
+        )
+        assertThat(artistToSongsMap.containsKey(mTheClash)).isFalse()
+        assertThat(artistToSongsMap[mPinkFloyd]).containsExactly(mPinkFloydSong1)
+        assertThat(artistToSongsMap[mRhcp]).containsExactlyElementsIn(
+            listOf<Any>(mRhcpSong1, mRhcpSong2)
+        )
+        assertThat(artistToSongsMap[mAcDc]).containsExactly(mAcdcSong1)
+    }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/TestObserver.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/TestObserver.kt
new file mode 100644
index 0000000..fffd296
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/TestObserver.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.room.integration.kotlintestapp.test
+
+import androidx.lifecycle.Observer
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.TimeoutException
+
+abstract class TestObserver<T> : Observer<T> {
+    private var mLastData: T? = null
+    private var mHasValue = false
+    fun reset() {
+        mHasValue = false
+        mLastData = null
+    }
+
+    override fun onChanged(value: T) {
+        mLastData = value
+        mHasValue = true
+    }
+
+    @Throws(TimeoutException::class, InterruptedException::class)
+    fun hasValue(): Boolean {
+        drain()
+        return mHasValue
+    }
+
+    @Throws(InterruptedException::class, TimeoutException::class)
+    fun get(): T? {
+        drain()
+        assertThat(hasValue()).isTrue()
+        return mLastData
+    }
+
+    @Throws(TimeoutException::class, InterruptedException::class)
+    protected abstract fun drain()
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/TestUtil.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/TestUtil.kt
index c94c1cb..1b401bc 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/TestUtil.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/TestUtil.kt
@@ -24,18 +24,21 @@
 import androidx.room.integration.kotlintestapp.vo.Book
 import androidx.room.integration.kotlintestapp.vo.BookAuthor
 import androidx.room.integration.kotlintestapp.vo.Lang
+import androidx.room.integration.kotlintestapp.vo.Pet
 import androidx.room.integration.kotlintestapp.vo.Publisher
+import java.util.Date
+import java.util.UUID
 import java.util.concurrent.FutureTask
 
 class TestUtil {
 
     companion object {
-        fun observeOnMainThread(
-            liveData: LiveData<Book>,
+        fun <T> observeOnMainThread(
+            liveData: LiveData<T>,
             provider: LifecycleOwner,
-            observer: Observer<Book>
+            observer: Observer<T>
         ) {
-            val futureTask = FutureTask<Unit> {
+            val futureTask = FutureTask {
                 liveData.observe(provider, observer)
             }
             ArchTaskExecutor.getInstance().executeOnMainThread(futureTask)
@@ -65,5 +68,13 @@
         val BOOK_AUTHOR_1_1 = BookAuthor(BOOK_1.bookId, AUTHOR_1.authorId)
         val BOOK_AUTHOR_1_2 = BookAuthor(BOOK_1.bookId, AUTHOR_2.authorId)
         val BOOK_AUTHOR_2_2 = BookAuthor(BOOK_2.bookId, AUTHOR_2.authorId)
+
+        fun createPet(id: Int): Pet {
+            val pet = Pet()
+            pet.mPetId = id
+            pet.mName = UUID.randomUUID().toString()
+            pet.mAdoptionDate = Date()
+            return pet
+        }
     }
 }
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/AlbumNameAndBandName.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/AlbumNameAndBandName.kt
index 96dec5c..492913c 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/AlbumNameAndBandName.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/AlbumNameAndBandName.kt
@@ -15,4 +15,4 @@
  */
 package androidx.room.integration.kotlintestapp.vo
 
-data class AlbumNameAndBandName(val albumName: String?, val bandName: String?)
\ No newline at end of file
+data class AlbumNameAndBandName(val mAlbumName: String?, val mBandName: String?)
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Day.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Day.kt
new file mode 100644
index 0000000..d71dcc5
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Day.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.room.integration.kotlintestapp.vo
+
+enum class Day {
+    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Experiment.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Experiment.kt
new file mode 100644
index 0000000..99a7600
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Experiment.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.androidx.room.integration.kotlintestapp.vo
+
+@JvmInline
+value class Schrodinger(val experiment: Experiment)
+
+@JvmInline
+value class Cat(val catStatus: CatStatus)
+
+data class Experiment(val isCatAlive: String)
+
+data class CatStatus(val isCatAlive: String)
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Image.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Image.kt
new file mode 100644
index 0000000..d6493a4
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Image.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.room.integration.kotlintestapp.vo
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import java.util.Date
+
+@Entity
+class Image(
+    @field:PrimaryKey val mImageId: Int,
+    val mImageYear: Long,
+    val mArtistInImage: String,
+    val mAlbumCover: ByteArray,
+    val mDateReleased: Date,
+    format: ImageFormat
+) {
+    val mFormat: ImageFormat
+
+    init {
+        mFormat = format
+    }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/ImageFormat.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/ImageFormat.kt
new file mode 100644
index 0000000..9fcdb1c
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/ImageFormat.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.room.integration.kotlintestapp.vo
+
+enum class ImageFormat {
+    JPG, GIF, MPEG
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Pet.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Pet.kt
new file mode 100644
index 0000000..92d22dc
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Pet.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.room.integration.kotlintestapp.vo
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import androidx.room.TypeConverters
+import androidx.room.integration.kotlintestapp.TestDatabase
+import java.util.Date
+
+@Entity
+@TypeConverters(TestDatabase.Converters::class)
+class Pet {
+    @PrimaryKey
+    var mPetId = 0
+    var mUserId = 0
+
+    @ColumnInfo(name = "mPetName")
+    var mName: String? = null
+    var mAdoptionDate: Date? = null
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || javaClass != other.javaClass) return false
+        val pet = other as Pet
+        if (mPetId != pet.mPetId) return false
+        if (mUserId != pet.mUserId) return false
+        if (if (mName != null) mName != pet.mName else pet.mName != null) return false
+        return if (mAdoptionDate != null) mAdoptionDate == pet.mAdoptionDate else
+            pet.mAdoptionDate == null
+    }
+
+    override fun hashCode(): Int {
+        var result = mPetId
+        result = 31 * result + mUserId
+        result = 31 * result + if (mName != null) mName.hashCode() else 0
+        result = 31 * result + if (mAdoptionDate != null) mAdoptionDate.hashCode() else 0
+        return result
+    }
+
+    override fun toString(): String {
+        return ("Pet{" +
+            "mPetId=" + mPetId +
+            ", mUserId=" + mUserId +
+            ", mName='" + mName + '\'' +
+            ", mAdoptionDate=" + mAdoptionDate +
+            '}')
+    }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/PetAndOwner.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/PetAndOwner.kt
new file mode 100644
index 0000000..566ad1d
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/PetAndOwner.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.room.integration.kotlintestapp.vo
+
+import androidx.room.Embedded
+import androidx.room.Relation
+
+class PetAndOwner(
+    @field:Embedded val mPet: Pet,
+    @field:Relation(
+        parentColumn = "mUserId",
+        entityColumn = "mId"
+    ) val mUser: PetUser
+)
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/PetUser.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/PetUser.kt
new file mode 100644
index 0000000..d0e5261
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/PetUser.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.integration.kotlintestapp.vo
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import androidx.room.TypeConverters
+import androidx.room.integration.kotlintestapp.TestDatabase
+import java.lang.Float.floatToIntBits
+import java.util.Date
+
+@Entity
+@TypeConverters(TestDatabase.Converters::class)
+class PetUser {
+    @PrimaryKey
+    var mId = 0
+    var mName: String? = null
+    var mLastName: String? = null
+    var mAge = 0
+    var mAdmin = false
+    var mWeight = 0f
+    var mBirthday: Date? = null
+
+    @ColumnInfo(name = "custommm", collate = ColumnInfo.NOCASE)
+    var mCustomField: String? = null
+
+    // bit flags
+    lateinit var mWorkDays: Set<Day>
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || javaClass != other.javaClass) return false
+        val user = other as PetUser
+        if (mId != user.mId) return false
+        if (mAge != user.mAge) return false
+        if (mAdmin != user.mAdmin) return false
+        if (user.mWeight.compareTo(mWeight) != 0) return false
+        if (if (mName != null) mName != user.mName else user.mName != null) return false
+        if (if (mLastName != null) mLastName != user.mLastName else user.mLastName != null) {
+            return false
+        }
+        if (if (mBirthday != null) mBirthday != user.mBirthday else user.mBirthday != null) {
+            return false
+        }
+        if (if (mCustomField != null) mCustomField != user.mCustomField else
+            user.mCustomField != null) {
+            return false
+        }
+        return mWorkDays == user.mWorkDays
+    }
+
+    override fun hashCode(): Int {
+        var result = mId
+        result = 31 * result + if (mName != null) mName.hashCode() else 0
+        result = 31 * result + if (mLastName != null) mLastName.hashCode() else 0
+        result = 31 * result + mAge
+        result = 31 * result + if (mAdmin) 1 else 0
+        result = 31 * result + if (mWeight != +0.0f) floatToIntBits(mWeight) else 0
+        result = 31 * result + if (mBirthday != null) mBirthday.hashCode() else 0
+        result = 31 * result + if (mCustomField != null) mCustomField.hashCode() else 0
+        result = 31 * result + mWorkDays.hashCode()
+        return result
+    }
+
+    override fun toString(): String {
+        return ("User{" +
+            "mId=" + mId +
+            ", mName='" + mName + '\'' +
+            ", mLastName='" + mLastName + '\'' +
+            ", mAge=" + mAge +
+            ", mAdmin=" + mAdmin +
+            ", mWeight=" + mWeight +
+            ", mBirthday=" + mBirthday +
+            ", mCustomField='" + mCustomField + '\'' +
+            ", mWorkDays=" + mWorkDays +
+            '}')
+    }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/PetWithToyIds.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/PetWithToyIds.kt
new file mode 100644
index 0000000..f78770c
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/PetWithToyIds.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.room.integration.kotlintestapp.vo
+
+import androidx.room.Embedded
+import androidx.room.Ignore
+import androidx.room.Relation
+
+class PetWithToyIds {
+    @Embedded
+    val mPet: Pet?
+
+    @Relation(
+        parentColumn = "mPetId",
+        entityColumn = "mPetId",
+        projection = ["mId"],
+        entity = Toy::class
+    )
+    var mToyIds: List<Int>? = null
+
+    // for the relation
+    constructor(pet: Pet?) {
+        this.mPet = pet
+    }
+
+    @Ignore
+    constructor(pet: Pet?, toyIds: List<Int>?) {
+        this.mPet = pet
+        this.mToyIds = toyIds
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || javaClass != other.javaClass) return false
+        val that = other as PetWithToyIds
+        if (if (mPet != null) !mPet.equals(that.mPet) else that.mPet != null) return false
+        return if (mToyIds != null) mToyIds == that.mToyIds else that.mToyIds == null
+    }
+
+    override fun hashCode(): Int {
+        var result = mPet?.hashCode() ?: 0
+        result = 31 * result + if (mToyIds != null) mToyIds.hashCode() else 0
+        return result
+    }
+
+    override fun toString(): String {
+        return ("PetWithToyIds{" +
+            "pet=" + mPet +
+            ", toyIds=" + mToyIds +
+            '}')
+    }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/PetWithUser.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/PetWithUser.kt
new file mode 100644
index 0000000..91c347e
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/PetWithUser.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.room.integration.kotlintestapp.vo
+
+import androidx.room.DatabaseView
+import androidx.room.Embedded
+
+@DatabaseView(
+    "SELECT Pet.*, PetUser.* FROM Pet INNER JOIN PetUser ON Pet.mUserId = PetUser.mId"
+)
+data class PetWithUser(
+    @Embedded
+    var mPet: Pet,
+    @Embedded
+    var mUser: PetUser
+)
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/PlaylistMultiSongXRefView.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/PlaylistMultiSongXRefView.kt
new file mode 100644
index 0000000..770db72
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/PlaylistMultiSongXRefView.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.room.integration.kotlintestapp.vo
+
+import androidx.room.DatabaseView
+
+// View of join table with playlists with more than 1 song
+@DatabaseView(
+    "SELECT * FROM PlaylistSongXRef WHERE mPlaylistId IN (SELECT mPlaylistId FROM" +
+        " PlaylistSongXRef GROUP BY mPlaylistId HAVING COUNT(mSongId) > 1)"
+)
+class PlaylistMultiSongXRefView(val mPlaylistId: Int, val mSongId: Int)
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/PlaylistWithSongTitles.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/PlaylistWithSongTitles.kt
new file mode 100644
index 0000000..a0194e2
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/PlaylistWithSongTitles.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.room.integration.kotlintestapp.vo
+
+import androidx.room.Embedded
+import androidx.room.Junction
+import androidx.room.Relation
+
+data class PlaylistWithSongTitles(
+    @Embedded
+    var playlist: Playlist,
+    @Relation(
+        parentColumn = "mPlaylistId",
+        entity = Song::class,
+        entityColumn = "mSongId",
+        associateBy = Junction(
+            PlaylistSongXRef::class
+        ),
+        projection = ["mTitle"]
+    )
+    var titles: List<String>
+)
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/ReleasedAlbum.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/ReleasedAlbum.kt
index 811b5e315..0642cf6 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/ReleasedAlbum.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/ReleasedAlbum.kt
@@ -15,4 +15,4 @@
  */
 package androidx.room.integration.kotlintestapp.vo
 
-data class ReleasedAlbum(val releaseYear: Int, val albumName: String?)
\ No newline at end of file
+data class ReleasedAlbum(val mReleaseYear: Int, val mAlbumName: String?)
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/SchrodingerConverter.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/SchrodingerConverter.kt
new file mode 100755
index 0000000..edf961a
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/SchrodingerConverter.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.androidx.room.integration.kotlintestapp.vo
+
+import androidx.room.TypeConverter
+
+object SchrodingerConverter {
+    @TypeConverter
+    fun schrodingerToCat(schrodinger: Schrodinger): Cat {
+        return Cat(CatStatus(schrodinger.experiment.isCatAlive))
+    }
+
+    @TypeConverter
+    fun catToIsCatAlive(cat: Cat): String {
+        return cat.catStatus.isCatAlive
+    }
+
+    @TypeConverter
+    fun isCatAliveToCat(isCatAlive: String): Cat {
+        return Cat(CatStatus(isCatAlive))
+    }
+
+    @TypeConverter
+    fun catToSchrodinger(cat: Cat): Schrodinger {
+        return Schrodinger(Experiment(cat.catStatus.isCatAlive))
+    }
+}
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Toy.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Toy.kt
new file mode 100644
index 0000000..56cf4b7
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Toy.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.room.integration.kotlintestapp.vo
+
+import androidx.room.Entity
+import androidx.room.ForeignKey
+import androidx.room.Index
+import androidx.room.PrimaryKey
+
+/**
+ * The toys of a pet.
+ */
+@Entity(
+    indices = [Index(value = ["mName"], unique = true), Index(value = ["mPetId"])],
+    foreignKeys = [ForeignKey(
+        entity = Pet::class,
+        parentColumns = ["mPetId"],
+        childColumns = ["mPetId"],
+        deferred = true
+    )]
+)
+data class Toy(
+    @PrimaryKey(autoGenerate = true)
+    val mId: Int,
+    var mName: String?,
+    var mPetId: Int
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || javaClass != other.javaClass) return false
+        val toy = other as Toy
+        if (mId != toy.mId) return false
+        if (mPetId != toy.mPetId) return false
+        return if (mName != null) mName == toy.mName else toy.mName == null
+    }
+
+    override fun hashCode(): Int {
+        var result = mId
+        result = 31 * result + if (mName != null) mName.hashCode() else 0
+        result = 31 * result + mPetId
+        return result
+    }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTestWithKsp/java/androidx/room/integration/kotlintestapp/NullabilityAwareTypeConversionTest.kt b/room/integration-tests/kotlintestapp/src/androidTestWithKspGenJava/java/androidx/room/integration/kotlintestapp/NullabilityAwareTypeConversionTest.kt
similarity index 100%
rename from room/integration-tests/kotlintestapp/src/androidTestWithKsp/java/androidx/room/integration/kotlintestapp/NullabilityAwareTypeConversionTest.kt
rename to room/integration-tests/kotlintestapp/src/androidTestWithKspGenJava/java/androidx/room/integration/kotlintestapp/NullabilityAwareTypeConversionTest.kt
diff --git a/room/integration-tests/kotlintestapp/src/androidTestWithKspGenKotlin/java/androidx/room/integration/kotlintestapp/test/ValueClassConverterWrapperTest.kt b/room/integration-tests/kotlintestapp/src/androidTestWithKspGenKotlin/java/androidx/room/integration/kotlintestapp/test/ValueClassConverterWrapperTest.kt
new file mode 100644
index 0000000..673ebd7
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTestWithKspGenKotlin/java/androidx/room/integration/kotlintestapp/test/ValueClassConverterWrapperTest.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.integration.kotlintestapp.test
+
+import android.content.Context
+import androidx.room.Dao
+import androidx.room.Database
+import androidx.room.Entity
+import androidx.room.Insert
+import androidx.room.PrimaryKey
+import androidx.room.Query
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import androidx.room.TypeConverters
+import androidx.room.androidx.room.integration.kotlintestapp.vo.Experiment
+import androidx.room.androidx.room.integration.kotlintestapp.vo.Schrodinger
+import androidx.room.androidx.room.integration.kotlintestapp.vo.SchrodingerConverter
+import androidx.room.integration.kotlintestapp.vo.DateConverter
+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
+import java.util.Date
+import java.util.UUID
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ValueClassConverterWrapperTest {
+
+    @JvmInline
+    value class UserWithInt(val password: Int)
+
+    @JvmInline
+    value class UserWithString(val password: String)
+
+    @JvmInline
+    value class UserWithUUID(val password: UUID)
+
+    @JvmInline
+    value class UserWithByte(val password: Byte)
+
+    @JvmInline
+    value class UserWithDate(val password: Date)
+
+    @JvmInline
+    value class UserWithGeneric<T>(val password: T)
+
+    enum class Season {
+        WINTER, SUMMER, SPRING, FALL
+    }
+
+    @JvmInline
+    value class UserWithEnum(val password: Season)
+
+    @JvmInline
+    value class UserWithStringInternal(internal val password: String)
+
+    @JvmInline
+    value class UserWithByteArray(val password: ByteArray)
+
+    @Entity
+    @TypeConverters(DateConverter::class, SchrodingerConverter::class)
+    class UserInfo(
+        @PrimaryKey
+        val pk: Int,
+        val userIntPwd: UserWithInt,
+        val userStringPwd: UserWithString,
+        val userUUIDPwd: UserWithUUID,
+        val userBytePwd: UserWithByte,
+        val userEnumPwd: UserWithEnum,
+        val userDatePwd: UserWithDate,
+        val userStringInternalPwd: UserWithStringInternal,
+        val userGenericPwd: UserWithGeneric<String>,
+        val userByteArrayPwd: UserWithByteArray,
+        val schrodingerUser: Schrodinger
+    ) {
+        override fun equals(other: Any?): Boolean {
+            val otherEntity = other as UserInfo
+            return pk == otherEntity.pk &&
+                userIntPwd == otherEntity.userIntPwd &&
+                userStringPwd == otherEntity.userStringPwd &&
+                userBytePwd == otherEntity.userBytePwd &&
+                userUUIDPwd == otherEntity.userUUIDPwd &&
+                userEnumPwd == otherEntity.userEnumPwd &&
+                userDatePwd == otherEntity.userDatePwd &&
+                userStringInternalPwd == otherEntity.userStringInternalPwd &&
+                userGenericPwd == otherEntity.userGenericPwd &&
+                userByteArrayPwd.password.contentEquals(otherEntity.userByteArrayPwd.password) &&
+                schrodingerUser.experiment.isCatAlive ==
+                otherEntity.schrodingerUser.experiment.isCatAlive
+        }
+
+        override fun hashCode(): Int {
+            return 1
+        }
+    }
+
+    @Dao
+    interface SampleDao {
+        @Query("SELECT * FROM UserInfo")
+        fun getEntity(): UserInfo
+
+        @Insert
+        fun insert(item: UserInfo)
+    }
+
+    @Database(
+        entities = [UserInfo::class],
+        version = 1,
+        exportSchema = false
+    )
+    abstract class ValueClassConverterWrapperDatabase : RoomDatabase() {
+        abstract fun dao(): SampleDao
+    }
+
+    private lateinit var db: ValueClassConverterWrapperDatabase
+    private val pk = 0
+    private val intPwd = UserWithInt(123)
+    private val stringPwd = UserWithString("open_sesame")
+    private val uuidPwd = UserWithUUID(UUID.randomUUID())
+    private val bytePwd = UserWithByte(Byte.MIN_VALUE)
+    private val enumPwd = UserWithEnum(Season.SUMMER)
+    private val datePwd = UserWithDate(Date(2023L))
+    private val internalPwd = UserWithStringInternal("open_sesame")
+    private val genericPwd = UserWithGeneric("open_sesame")
+    private val byteArrayPwd = UserWithByteArray(byteArrayOf(Byte.MIN_VALUE))
+    private val shrodingerPwd = Schrodinger(Experiment("the cat is alive!"))
+
+    @Test
+    fun readAndWriteValueClassToDatabase() {
+        val customerInfo = UserInfo(
+            pk = pk,
+            userIntPwd = intPwd,
+            userStringPwd = stringPwd,
+            userUUIDPwd = uuidPwd,
+            userBytePwd = bytePwd,
+            userEnumPwd = enumPwd,
+            userDatePwd = datePwd,
+            userStringInternalPwd = internalPwd,
+            userGenericPwd = genericPwd,
+            userByteArrayPwd = byteArrayPwd,
+            schrodingerUser = shrodingerPwd
+        )
+
+        db.dao().insert(customerInfo)
+
+        val readEntity = db.dao().getEntity()
+
+        assertThat(readEntity).isEqualTo(customerInfo)
+    }
+
+    @Before
+    fun initDb() {
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        db = Room.inMemoryDatabaseBuilder(
+            context,
+            ValueClassConverterWrapperDatabase::class.java
+        ).build()
+    }
+
+    @After
+    fun teardown() {
+        db.close()
+    }
+}
diff --git a/room/integration-tests/testapp/build.gradle b/room/integration-tests/testapp/build.gradle
index 36725cf..6267016 100644
--- a/room/integration-tests/testapp/build.gradle
+++ b/room/integration-tests/testapp/build.gradle
@@ -40,13 +40,12 @@
     buildFeatures {
         aidl = true
     }
-    buildTypes {
-        debug {
-            // Need to make sure androidx.tracing.Trace gets put in primary dex for legacy multidex
-            // as it is needed by androidx.test.runner.AndroidJUnitRunner
-            multiDexKeepFile file('multidex-config.txt')
-        }
+    buildTypes.all {
+        // Need to make sure certain classes are put in primary dex as they're used early in
+        // process creation, and would fail to load on non-native-multidex API versions
+        multiDexKeepProguard file('multidex-rules.pro')
     }
+
     sourceSets {
         androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
         androidTest.assets.srcDirs += files("$projectDir/databases".toString())
@@ -127,9 +126,14 @@
     androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-runtime-testing"))
     androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-livedata"))
 
+    // guavaAndroid is a implementation dependency instead of androidTestImplementation because
+    // if it's in the androidTest apk it causes the test APK to be multidex in ways that break
+    // non-native multidex (< API 21). This should be changed back to androidTestImplementation
+    // once this app is minApi 21
+    implementation(libs.guavaAndroid)
+
     // libs.findbugs dependency resolves an app/testapp version conflict.
     androidTestImplementation(libs.findbugs)
-    androidTestImplementation(libs.guavaAndroid)
     androidTestImplementation(libs.rxjava2)
     androidTestImplementation(libs.rxjava3)
     androidTestImplementation(libs.testExtJunit)
diff --git a/room/integration-tests/testapp/multidex-config.txt b/room/integration-tests/testapp/multidex-config.txt
deleted file mode 100644
index c108ccb..0000000
--- a/room/integration-tests/testapp/multidex-config.txt
+++ /dev/null
@@ -1 +0,0 @@
-androidx/tracing/Trace.class
diff --git a/room/integration-tests/testapp/multidex-rules.pro b/room/integration-tests/testapp/multidex-rules.pro
new file mode 100644
index 0000000..b05591c
--- /dev/null
+++ b/room/integration-tests/testapp/multidex-rules.pro
@@ -0,0 +1 @@
+-keep class androidx.tracing.Trace
\ No newline at end of file
diff --git a/room/room-common/build.gradle b/room/room-common/build.gradle
index 8930841..151639e 100644
--- a/room/room-common/build.gradle
+++ b/room/room-common/build.gradle
@@ -28,7 +28,7 @@
     testImplementation(libs.junit)
     testImplementation(libs.mockitoCore4)
     testImplementation(libs.guava)
-    testImplementation(libs.truth)
+    testImplementation(project(":internal-testutils-kmp"))
 }
 
 androidx {
diff --git a/room/room-common/src/test/java/androidx/room/AmbiguousColumnResolverTest.kt b/room/room-common/src/test/java/androidx/room/AmbiguousColumnResolverTest.kt
index 79d4f9c..80885a53 100644
--- a/room/room-common/src/test/java/androidx/room/AmbiguousColumnResolverTest.kt
+++ b/room/room-common/src/test/java/androidx/room/AmbiguousColumnResolverTest.kt
@@ -16,7 +16,7 @@
 
 package androidx.room
 
-import com.google.common.truth.Truth.assertThat
+import androidx.kruth.assertThat
 import java.util.Locale
 import org.junit.Test
 import org.junit.Ignore
diff --git a/room/room-common/src/test/java/androidx/room/AnnotationRetentionPolicyTest.kt b/room/room-common/src/test/java/androidx/room/AnnotationRetentionPolicyTest.kt
index 55c1c81..43a60e5 100644
--- a/room/room-common/src/test/java/androidx/room/AnnotationRetentionPolicyTest.kt
+++ b/room/room-common/src/test/java/androidx/room/AnnotationRetentionPolicyTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.room
 
+import androidx.kruth.assertThat
 import com.google.common.reflect.ClassPath
 import org.junit.Test
 import java.lang.annotation.Retention
@@ -36,10 +37,7 @@
         // For Room to be incremental, all annotations need to have CLASS retention policy.
         annotations.forEach {
             val retentionPolicy = it.getAnnotation(Retention::class.java)?.value
-            assert(retentionPolicy == RetentionPolicy.CLASS) {
-                "Expected ${it.name} annotation to have retention policy CLASS" +
-                    " but it is $retentionPolicy"
-            }
+            assertThat(retentionPolicy).isEqualTo(RetentionPolicy.CLASS)
         }
     }
 }
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XExecutableParameterElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XExecutableParameterElement.kt
index b5148d4..8454bd9 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XExecutableParameterElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XExecutableParameterElement.kt
@@ -20,6 +20,21 @@
  * Parameter of a method.
  */
 interface XExecutableParameterElement : XVariableElement {
+    /**
+     * Returns `true` if this parameter is a synthetic Continuation parameter of a suspend function.
+     */
+    fun isContinuationParam(): Boolean
+
+    /**
+     * Returns `true` if this parameter represents the receiver of an extension function.
+     */
+    fun isReceiverParam(): Boolean
+
+    /**
+     * Returns `true` if this parameter represents the single parameter of a Kotlin property setter
+     * method.
+     */
+    fun isKotlinPropertyParam(): Boolean
 
     /**
      * The enclosing [XExecutableElement] this parameter belongs to.
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 efaa56e..4b1a7e9 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
@@ -19,9 +19,8 @@
 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.CodeGenerator
-import com.google.devtools.ksp.processing.KSPLogger
 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.KClassName
@@ -206,15 +205,11 @@
         @JvmStatic
         @JvmOverloads
         fun create(
-            options: Map<String, String>,
+            symbolProcessorEnvironment: SymbolProcessorEnvironment,
             resolver: Resolver,
-            codeGenerator: CodeGenerator,
-            logger: KSPLogger,
             config: XProcessingEnvConfig = XProcessingEnvConfig.DEFAULT
         ): XProcessingEnv = KspProcessingEnv(
-            options = options,
-            codeGenerator = codeGenerator,
-            logger = logger,
+            delegate = symbolProcessorEnvironment,
             config = config
         ).also { it.resolver = resolver }
     }
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt
index 317de2c..1874984 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt
@@ -56,6 +56,7 @@
 import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticContinuationParameterElement
 import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticPropertyMethodElement
 import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticReceiverParameterElement
+import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
 import com.google.devtools.ksp.symbol.KSAnnotated
 import com.google.devtools.ksp.symbol.KSAnnotation
 import com.google.devtools.ksp.symbol.KSClassDeclaration
@@ -169,6 +170,9 @@
         (env as JavacProcessingEnv).wrap(this, null, null)
 
     @JvmStatic
+    fun XProcessingEnv.toKS(): SymbolProcessorEnvironment = (this as KspProcessingEnv).delegate
+
+    @JvmStatic
     fun XTypeElement.toKS(): KSClassDeclaration = (this as KspTypeElement).declaration
 
     @JvmStatic
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacMethodParameter.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacMethodParameter.kt
index 27aeaa5..88fe3b7 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacMethodParameter.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacMethodParameter.kt
@@ -30,6 +30,19 @@
     kotlinMetadataFactory: () -> KmValueParameterContainer?,
     val argIndex: Int
 ) : JavacVariableElement(env, element), XExecutableParameterElement {
+    override fun isContinuationParam() =
+        enclosingElement is JavacMethodElement &&
+        enclosingElement.isSuspendFunction() &&
+        enclosingElement.parameters.last() == this
+
+    override fun isReceiverParam() =
+        enclosingElement is JavacMethodElement &&
+        enclosingElement.isExtensionFunction() &&
+        enclosingElement.parameters.first() == this
+
+    override fun isKotlinPropertyParam() =
+        enclosingElement is JavacMethodElement &&
+        enclosingElement.isKotlinPropertyMethod()
 
     override val kotlinMetadata by lazy { kotlinMetadataFactory() }
 
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspBasicAnnotationProcessor.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspBasicAnnotationProcessor.kt
index dade470..9a24b90 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspBasicAnnotationProcessor.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspBasicAnnotationProcessor.kt
@@ -38,9 +38,7 @@
     private val logger = DelegateLogger(symbolProcessorEnvironment.logger)
 
     private val xEnv = KspProcessingEnv(
-        options = symbolProcessorEnvironment.options,
-        codeGenerator = symbolProcessorEnvironment.codeGenerator,
-        logger = logger,
+        delegate = symbolProcessorEnvironment,
         config = config
     )
 
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableParameterElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableParameterElement.kt
index 8a6ed9a..fe11246 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableParameterElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableParameterElement.kt
@@ -36,6 +36,11 @@
 ) : KspElement(env, parameter),
     XExecutableParameterElement,
     XAnnotated by KspAnnotated.create(env, parameter, NO_USE_SITE_OR_METHOD_PARAMETER) {
+    override fun isContinuationParam() = false
+
+    override fun isReceiverParam() = false
+
+    override fun isKotlinPropertyParam() = false
 
     override val name: String
         get() = parameter.name?.asString() ?: "_no_param_name"
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt
index 75cd2c3..232bacd 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt
@@ -28,9 +28,8 @@
 import androidx.room.compiler.processing.javac.XTypeElementStore
 import com.google.devtools.ksp.KspExperimental
 import com.google.devtools.ksp.getClassDeclarationByName
-import com.google.devtools.ksp.processing.CodeGenerator
-import com.google.devtools.ksp.processing.KSPLogger
 import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
 import com.google.devtools.ksp.symbol.ClassKind
 import com.google.devtools.ksp.symbol.KSClassDeclaration
 import com.google.devtools.ksp.symbol.KSFile
@@ -43,12 +42,13 @@
 import com.google.devtools.ksp.symbol.Variance
 
 internal class KspProcessingEnv(
-    override val options: Map<String, String>,
-    codeGenerator: CodeGenerator,
-    logger: KSPLogger,
+    val delegate: SymbolProcessorEnvironment,
     override val config: XProcessingEnvConfig,
 ) : XProcessingEnv {
     override val backend: XProcessingEnv.Backend = XProcessingEnv.Backend.KSP
+    override val options = delegate.options
+    private val logger = delegate.logger
+    private val codeGenerator = delegate.codeGenerator
 
     // No API to get this but Kotlin's default is 8, so go with it for now.
     // TODO: https://github.com/google/ksp/issues/810
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
index a9d786c..8fb91f6 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
@@ -248,7 +248,9 @@
     }
 
     override fun isValueClass(): Boolean {
-        return Modifier.INLINE in declaration.modifiers
+        // The inline modifier for inline classes is deprecated in Kotlin but we still include it
+        // in this check.
+        return Modifier.VALUE in declaration.modifiers || Modifier.INLINE in declaration.modifiers
     }
 
     override fun isFunctionalInterface(): Boolean {
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticContinuationParameterElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticContinuationParameterElement.kt
index 620f91f..447fbf3 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticContinuationParameterElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticContinuationParameterElement.kt
@@ -48,6 +48,11 @@
         delegate = null, // does not matter, this is synthetic and has no annotations.
         filter = NO_USE_SITE
     ) {
+    override fun isContinuationParam() = true
+
+    override fun isReceiverParam() = false
+
+    override fun isKotlinPropertyParam() = false
 
     override val name: String by lazy {
         // KAPT uses `continuation` but it doesn't check for conflicts, we do.
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
index d6bf00e..ec5190d 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
@@ -232,6 +232,11 @@
                 delegate = enclosingElement.field.declaration.setter?.parameter,
                 filter = NO_USE_SITE_OR_SET_PARAM
             ) {
+            override fun isContinuationParam() = false
+
+            override fun isReceiverParam() = false
+
+            override fun isKotlinPropertyParam() = true
 
             private val jvmTypeResolutionScope = KspJvmTypeResolutionScope.PropertySetterParameter(
                 declaration = enclosingElement
@@ -323,4 +328,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticReceiverParameterElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticReceiverParameterElement.kt
index 1c7bf1e..acef3af 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticReceiverParameterElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticReceiverParameterElement.kt
@@ -42,6 +42,12 @@
         filter = KspAnnotated.UseSiteFilter.NO_USE_SITE
     ) {
 
+    override fun isContinuationParam() = false
+
+    override fun isReceiverParam() = true
+
+    override fun isKotlinPropertyParam() = false
+
     override val name: String by lazy {
         // KAPT uses `$this$<functionName>`
         "$" + "this" + "$" + enclosingElement.name
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 73c87b1..91ff9e3 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
@@ -445,6 +445,7 @@
                     assertThat(method.parameters.first().type.asTypeName()).isEqualTo(
                         String::class.asClassName()
                     )
+                    assertThat(method.parameters.first().isKotlinPropertyParam()).isTrue()
                     assertThat(method.isPublic()).isTrue()
                     assertThat(method.parameters.first().type.nullability).isEqualTo(
                         XNullability.NONNULL
@@ -986,6 +987,7 @@
                 element.getDeclaredMethodByJvmName("ext1").let { method ->
                     assertThat(method.isExtensionFunction()).isTrue()
                     assertThat(method.parameters.size).isEqualTo(1)
+                    assertThat(method.parameters[0].isReceiverParam()).isTrue()
                     assertThat(method.parameters[0].name).isEqualTo("\$this\$ext1")
                     assertThat(method.parameters[0].type.asTypeName())
                         .isEqualTo(String::class.asClassName())
@@ -1028,6 +1030,7 @@
                     assertThat(method.parameters.size).isEqualTo(2)
                     assertThat(method.parameters[0].type.asTypeName())
                         .isEqualTo(String::class.asClassName())
+                    assertThat(method.parameters[1].isContinuationParam()).isTrue()
                     assertThat(method.parameters[1].type.typeName).isEqualTo(
                         ParameterizedTypeName.get(
                             ClassName.get("kotlin.coroutines", "Continuation"),
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 330d1b4..24449d4 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
@@ -408,11 +408,7 @@
             assertThat(getModifiers("DataClass"))
                 .containsExactly("public", "final", "class", "data")
             assertThat(getModifiers("InlineClass")).apply {
-                if (isPreCompiled && invocation.isKsp) {
-                    containsExactly("public", "final", "class")
-                } else {
-                    containsExactly("public", "final", "class", "value")
-                }
+                containsExactly("public", "final", "class", "value")
             }
             assertThat(getModifiers("FunInterface"))
                 .containsExactly("public", "abstract", "interface", "fun")
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 2676100..472f46e 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/DatabaseProcessingStep.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/DatabaseProcessingStep.kt
@@ -23,6 +23,7 @@
 import androidx.room.compiler.processing.XTypeElement
 import androidx.room.log.RLog
 import androidx.room.processor.Context
+import androidx.room.processor.Context.BooleanProcessorOptions.GENERATE_KOTLIN
 import androidx.room.processor.DatabaseProcessor
 import androidx.room.processor.ProcessorErrors
 import androidx.room.util.SchemaFileResolver
@@ -45,9 +46,10 @@
         elementsByAnnotation: Map<String, Set<XElement>>,
         isLastRound: Boolean
     ): Set<XTypeElement> {
-        check(env.config == ENV_CONFIG) {
-            "Room Processor expected $ENV_CONFIG but was invoked with a different configuration:" +
-                "${env.config}"
+        check(env.config == getEnvConfig(env.options)) {
+            "Room Processor expected ${getEnvConfig(env.options)} " +
+                "but was invoked with a different " +
+                "configuration: ${env.config}"
         }
         val context = Context(env)
 
@@ -175,8 +177,9 @@
     }
 
     companion object {
-        internal val ENV_CONFIG = XProcessingEnvConfig.DEFAULT.copy(
-            excludeMethodsWithInvalidJvmSourceNames = true
-        )
+        internal fun getEnvConfig(options: Map<String, String>) =
+            XProcessingEnvConfig.DEFAULT.copy(
+                excludeMethodsWithInvalidJvmSourceNames = !GENERATE_KOTLIN.getValue(options)
+            )
     }
 }
\ No newline at end of file
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/RoomKspProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/RoomKspProcessor.kt
index 0a37134..e2f9e3f 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/RoomKspProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/RoomKspProcessor.kt
@@ -16,7 +16,6 @@
 
 package androidx.room
 
-import androidx.room.DatabaseProcessingStep.Companion.ENV_CONFIG
 import androidx.room.compiler.processing.XProcessingEnv
 import androidx.room.compiler.processing.XRoundEnv
 import androidx.room.compiler.processing.ksp.KspBasicAnnotationProcessor
@@ -33,7 +32,10 @@
  */
 class RoomKspProcessor(
     environment: SymbolProcessorEnvironment
-) : KspBasicAnnotationProcessor(environment, ENV_CONFIG) {
+) : KspBasicAnnotationProcessor(
+    symbolProcessorEnvironment = environment,
+    config = DatabaseProcessingStep.getEnvConfig(environment.options)
+) {
     init {
         // print a warning if null aware converter is disabled because we'll remove that ability
         // soon.
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/RoomProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/RoomProcessor.kt
index 5c7daee..00d1e5a 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/RoomProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/RoomProcessor.kt
@@ -16,7 +16,6 @@
 
 package androidx.room
 
-import androidx.room.DatabaseProcessingStep.Companion.ENV_CONFIG
 import androidx.room.compiler.processing.XProcessingEnv
 import androidx.room.compiler.processing.XRoundEnv
 import androidx.room.compiler.processing.javac.JavacBasicAnnotationProcessor
@@ -36,9 +35,11 @@
 /**
  * The annotation processor for Room.
  */
-class RoomProcessor : JavacBasicAnnotationProcessor({
-    ENV_CONFIG
-}) {
+class RoomProcessor : JavacBasicAnnotationProcessor(
+    configureEnv = { options ->
+        DatabaseProcessingStep.getEnvConfig(options)
+    }
+) {
 
     /** Helper variable to avoid reporting the warning twice. */
     private var jdkVersionHasBugReported = false
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/ext/xelement_ext.kt b/room/room-compiler/src/main/kotlin/androidx/room/ext/xelement_ext.kt
index 487bafc..5c36768 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/ext/xelement_ext.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/ext/xelement_ext.kt
@@ -18,6 +18,7 @@
 
 import kotlin.contracts.contract
 import androidx.room.compiler.processing.XElement
+import androidx.room.compiler.processing.XFieldElement
 import androidx.room.compiler.processing.XTypeElement
 
 fun XElement.isEntityElement(): Boolean {
@@ -27,6 +28,13 @@
     return this.hasAnnotation(androidx.room.Entity::class)
 }
 
+fun XTypeElement.getValueClassUnderlyingProperty(): XFieldElement {
+    check(this.isValueClass()) {
+        "Can't get value class property, type element '$this' is not a value class"
+    }
+    return this.getDeclaredFields().single()
+}
+
 /**
  * Suffix of the Kotlin synthetic class created interface method implementations.
  */
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 3221bd5..07ee499 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
@@ -274,8 +274,16 @@
             return getInputValue(processingEnv) ?: defaultValue
         }
 
+        fun getValue(options: Map<String, String>): Boolean {
+            return getInputValue(options) ?: defaultValue
+        }
+
         fun getInputValue(processingEnv: XProcessingEnv): Boolean? {
-            return processingEnv.options[argName]?.takeIf {
+            return getInputValue(processingEnv.options)
+        }
+
+        private fun getInputValue(options: Map<String, String>): Boolean? {
+            return options[argName]?.takeIf {
                 it.isNotBlank()
             }?.toBoolean()
         }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/FieldProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/FieldProcessor.kt
index 1645d65..5c72f35 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/FieldProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/FieldProcessor.kt
@@ -17,6 +17,7 @@
 package androidx.room.processor
 
 import androidx.room.ColumnInfo
+import androidx.room.compiler.codegen.CodeLanguage
 import androidx.room.compiler.processing.XFieldElement
 import androidx.room.compiler.processing.XType
 import androidx.room.parser.Collate
@@ -82,6 +83,13 @@
             nonNull = nonNull
         )
 
+        // TODO(b/273592453): Figure out a way to detect value classes in KAPT and guard against it.
+        if (member.typeElement?.isValueClass() == true &&
+            context.codeLanguage != CodeLanguage.KOTLIN
+        ) {
+            onBindingError(field, ProcessorErrors.VALUE_CLASS_ONLY_SUPPORTED_IN_KSP)
+        }
+
         when (bindingScope) {
             BindingScope.TWO_WAY -> {
                 field.statementBinder = adapter
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt
index 0f67cea..05c36d0 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt
@@ -742,8 +742,7 @@
                     fieldName = field.name,
                     jvmName = field.name,
                     type = field.type,
-                    callType = CallType.FIELD,
-                    isMutableField = !field.element.isFinal()
+                    callType = CallType.FIELD
                 )
             },
             assignFromMethod = { match ->
@@ -756,8 +755,7 @@
                             CallType.SYNTHETIC_METHOD
                         } else {
                             CallType.METHOD
-                        },
-                    isMutableField = !field.element.isFinal()
+                        }
                 )
             },
             reportAmbiguity = { matching ->
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 414e98f..0367eeb 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
@@ -255,6 +255,9 @@
     val CANNOT_FIND_COLUMN_TYPE_ADAPTER = "Cannot figure out how to save this field into" +
         " database. You can consider adding a type converter for it."
 
+    val VALUE_CLASS_ONLY_SUPPORTED_IN_KSP = "Kotlin value classes are only supported " +
+        "in Room using KSP and generating Kotlin (room.generateKotlin=true)."
+
     val CANNOT_FIND_STMT_BINDER = "Cannot figure out how to bind this field into a statement."
 
     val CANNOT_FIND_CURSOR_READER = "Cannot figure out how to read this field from a cursor."
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 329e023..030d092 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
@@ -28,6 +28,7 @@
 import androidx.room.ext.CollectionTypeNames.LONG_SPARSE_ARRAY
 import androidx.room.ext.CommonTypeNames
 import androidx.room.ext.GuavaTypeNames
+import androidx.room.ext.getValueClassUnderlyingProperty
 import androidx.room.ext.isByteBuffer
 import androidx.room.ext.isEntityElement
 import androidx.room.ext.isNotByte
@@ -115,6 +116,7 @@
 import androidx.room.solver.types.StringColumnTypeAdapter
 import androidx.room.solver.types.TypeConverter
 import androidx.room.solver.types.UuidColumnTypeAdapter
+import androidx.room.solver.types.ValueClassConverterWrapper
 import androidx.room.vo.BuiltInConverterFlags
 import androidx.room.vo.MapInfo
 import androidx.room.vo.ShortcutQueryParameter
@@ -278,7 +280,7 @@
         if (adapterByTypeConverter != null) {
             return adapterByTypeConverter
         }
-        val defaultAdapter = createDefaultTypeAdapter(input)
+        val defaultAdapter = createDefaultTypeAdapter(input, affinity)
         if (defaultAdapter != null) {
             return defaultAdapter
         }
@@ -316,7 +318,7 @@
             return typeConverterAdapter
         }
 
-        val defaultAdapter = createDefaultTypeAdapter(output)
+        val defaultAdapter = createDefaultTypeAdapter(output, affinity)
         if (defaultAdapter != null) {
             return defaultAdapter
         }
@@ -361,7 +363,7 @@
         }
 
         if (!skipDefaultConverter) {
-            val defaultAdapter = createDefaultTypeAdapter(out)
+            val defaultAdapter = createDefaultTypeAdapter(out, affinity)
             if (defaultAdapter != null) {
                 return defaultAdapter
             }
@@ -369,8 +371,27 @@
         return null
     }
 
-    private fun createDefaultTypeAdapter(type: XType): ColumnTypeAdapter? {
+    private fun createDefaultTypeAdapter(
+        type: XType,
+        affinity: SQLTypeAffinity?
+    ): ColumnTypeAdapter? {
         val typeElement = type.typeElement
+        if (typeElement?.isValueClass() == true) {
+            // Extract the type value of the Value class element
+            val underlyingProperty = typeElement.getValueClassUnderlyingProperty()
+            val underlyingTypeColumnAdapter = findColumnTypeAdapter(
+                out = underlyingProperty.asMemberOf(type),
+                affinity = affinity,
+                skipDefaultConverter = false
+            ) ?: return null
+
+            return ValueClassConverterWrapper(
+                valueTypeColumnAdapter = underlyingTypeColumnAdapter,
+                affinity = underlyingTypeColumnAdapter.typeAffinity,
+                out = type,
+                valuePropertyName = underlyingProperty.name
+            )
+        }
         return when {
             builtInConverterFlags.enums.isEnabled() &&
                 typeElement?.isEnum() == true -> EnumColumnTypeAdapter(typeElement, type)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/ValueClassConverterWrapper.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/ValueClassConverterWrapper.kt
new file mode 100644
index 0000000..57a2247
--- /dev/null
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/ValueClassConverterWrapper.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.solver.types
+
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.processing.XNullability
+import androidx.room.compiler.processing.XType
+import androidx.room.parser.SQLTypeAffinity
+import androidx.room.solver.CodeGenScope
+
+/**
+ * ColumnTypeAdapter for Kotlin value classes that simply wraps and forwards calls to a found
+ * adapter for the underlying type.
+ */
+class ValueClassConverterWrapper(
+    val valueTypeColumnAdapter: ColumnTypeAdapter,
+    val affinity: SQLTypeAffinity,
+    out: XType,
+    val valuePropertyName: String
+) : ColumnTypeAdapter(out, affinity) {
+    override fun readFromCursor(
+        outVarName: String,
+        cursorVarName: String,
+        indexVarName: String,
+        scope: CodeGenScope
+    ) {
+        scope.builder.apply {
+            fun XCodeBlock.Builder.addTypeToValueClassStatement() {
+                val propertyValueVarName = scope.getTmpVar("_$valuePropertyName")
+                addLocalVariable(propertyValueVarName, valueTypeColumnAdapter.outTypeName)
+                valueTypeColumnAdapter.readFromCursor(
+                    propertyValueVarName,
+                    cursorVarName,
+                    indexVarName,
+                    scope
+                )
+                addStatement(
+                    format = "%L = %L",
+                    outVarName,
+                    XCodeBlock.ofNewInstance(
+                        language,
+                        out.asTypeName(),
+                        "%N",
+                        propertyValueVarName
+                    )
+                )
+            }
+            if (out.nullability == XNullability.NONNULL) {
+                addTypeToValueClassStatement()
+            } else {
+                beginControlFlow("if (%L.isNull(%L))", cursorVarName, indexVarName)
+                    .addStatement("%L = null", outVarName)
+                nextControlFlow("else")
+                    .addTypeToValueClassStatement()
+                endControlFlow()
+            }
+        }
+    }
+
+    override fun bindToStmt(
+        stmtName: String,
+        indexVarName: String,
+        valueVarName: String,
+        scope: CodeGenScope
+    ) {
+        scope.builder.apply {
+            val propertyName = scope.getTmpVar("_$valuePropertyName")
+            addLocalVariable(
+                name = propertyName,
+                typeName = valueTypeColumnAdapter.outTypeName,
+                assignExpr = XCodeBlock.of(
+                    scope.language,
+                    "%L.%L",
+                    valueVarName,
+                    valuePropertyName
+                )
+            )
+
+            if (out.nullability == XNullability.NONNULL) {
+                valueTypeColumnAdapter.bindToStmt(
+                    stmtName,
+                    indexVarName,
+                    propertyName,
+                    scope
+                )
+            } else {
+                beginControlFlow(
+                    "if (%L == null)",
+                    propertyName
+                ).addStatement("%L.bindNull(%L)", stmtName, indexVarName)
+                nextControlFlow("else")
+                valueTypeColumnAdapter.bindToStmt(
+                    stmtName,
+                    indexVarName,
+                    propertyName,
+                    scope
+                )
+                endControlFlow()
+            }
+        }
+    }
+}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/FieldGetter.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/FieldGetter.kt
index 05165a4..28cd4c9 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/FieldGetter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/FieldGetter.kt
@@ -30,7 +30,6 @@
     val jvmName: String,
     val type: XType,
     val callType: CallType,
-    val isMutableField: Boolean
 ) {
     fun writeGet(ownerVar: String, outVar: String, builder: XCodeBlock.Builder) {
         builder.addLocalVariable(
@@ -48,14 +47,11 @@
         scope: CodeGenScope
     ) {
         val varExpr = getterExpression(ownerVar, scope.language)
-        // A temporary local val is needed in Kotlin if the field or property is mutable (var)
-        // and is nullable since otherwise smart cast will fail indicating that the property
-        // might have changed when binding to statement.
-        val needTempVal = scope.language == CodeLanguage.KOTLIN &&
-            (callType == CallType.FIELD || callType == CallType.SYNTHETIC_METHOD) &&
-            type.nullability != XNullability.NONNULL &&
-            isMutableField
-        if (needTempVal) {
+        // A temporary local val is needed in Kotlin whenever the getter method returns nullable or
+        // the field / property is nullable such that a smart cast can be properly performed. Even
+        // if the field / property are immutable (val), we still use a local val in case the
+        // property is declared in another module, which would make the smart cast impossible.
+        if (scope.language == CodeLanguage.KOTLIN && type.nullability != XNullability.NONNULL) {
             val tmpField = scope.getTmpVar("_tmp${fieldName.capitalize(Locale.US)}")
             scope.builder.addLocalVariable(
                 name = tmpField,
@@ -81,7 +77,7 @@
                 CallType.FIELD, CallType.SYNTHETIC_METHOD ->
                     XCodeBlock.of(codeLanguage, "%L.%L", ownerVar, fieldName)
                 CallType.METHOD ->
-                    XCodeBlock.of(codeLanguage, "%L.%L", ownerVar, jvmName)
+                    XCodeBlock.of(codeLanguage, "%L.%L()", ownerVar, jvmName)
                 CallType.CONSTRUCTOR -> error("Getters should never be of type 'constructor'!")
             }
         }
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/RoomTestEnvConfigProvider.kt b/room/room-compiler/src/test/kotlin/androidx/room/RoomTestEnvConfigProvider.kt
index addefbc..761159f 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/RoomTestEnvConfigProvider.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/RoomTestEnvConfigProvider.kt
@@ -21,6 +21,6 @@
 
 class RoomTestEnvConfigProvider : XProcessingEnvironmentTestConfigProvider {
     override fun configure(options: Map<String, String>): XProcessingEnvConfig {
-        return DatabaseProcessingStep.ENV_CONFIG
+        return DatabaseProcessingStep.getEnvConfig(options)
     }
 }
\ No newline at end of file
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/EntityNameMatchingVariationsTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/EntityNameMatchingVariationsTest.kt
index 170837e..c71cc23 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/EntityNameMatchingVariationsTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/EntityNameMatchingVariationsTest.kt
@@ -78,7 +78,7 @@
             assertThat(field.setter)
                 .isEqualTo(FieldSetter(field.name, setterName, intType, CallType.METHOD))
             assertThat(field.getter)
-                .isEqualTo(FieldGetter(field.name, getterName, intType, CallType.METHOD, true))
+                .isEqualTo(FieldGetter(field.name, getterName, intType, CallType.METHOD))
         }
     }
 }
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/Fts3TableEntityProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/Fts3TableEntityProcessorTest.kt
index ec64f94..e959e20 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/Fts3TableEntityProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/Fts3TableEntityProcessorTest.kt
@@ -84,7 +84,7 @@
             assertThat(field.setter,
                 `is`(FieldSetter("rowId", "setRowId", intType, CallType.METHOD)))
             assertThat(field.getter,
-                `is`(FieldGetter("rowId", "getRowId", intType, CallType.METHOD, true)))
+                `is`(FieldGetter("rowId", "getRowId", intType, CallType.METHOD)))
             assertThat(entity.primaryKey.fields, `is`(Fields(field)))
             assertThat(entity.shadowTableName, `is`("MyEntity_content"))
             assertThat(entity.ftsVersion, `is`(FtsVersion.FTS3))
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/Fts4TableEntityProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/Fts4TableEntityProcessorTest.kt
index fef806c..178c2938 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/Fts4TableEntityProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/Fts4TableEntityProcessorTest.kt
@@ -71,7 +71,7 @@
             assertThat(field.setter,
                 `is`(FieldSetter("rowId", "setRowId", intType, CallType.METHOD)))
             assertThat(field.getter,
-                `is`(FieldGetter("rowId", "getRowId", intType, CallType.METHOD, true)))
+                `is`(FieldGetter("rowId", "getRowId", intType, CallType.METHOD)))
             assertThat(entity.primaryKey.fields, `is`(Fields(field)))
             assertThat(entity.shadowTableName, `is`("MyEntity_content"))
             assertThat(entity.ftsVersion, `is`(FtsVersion.FTS4))
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
index 7623237..b345b6f 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
@@ -2099,8 +2099,7 @@
                     fieldName = "isbn",
                     jvmName = "getIsbn",
                     type = stringType,
-                    callType = CallType.SYNTHETIC_METHOD,
-                    isMutableField = true
+                    callType = CallType.SYNTHETIC_METHOD
                 )
             )
             Truth.assertThat(
@@ -2121,8 +2120,7 @@
                     fieldName = "isbn2",
                     jvmName = "getIsbn2",
                     type = stringType.makeNullable(),
-                    callType = CallType.SYNTHETIC_METHOD,
-                    isMutableField = true
+                    callType = CallType.SYNTHETIC_METHOD
                 )
             )
             Truth.assertThat(
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt
index a7291ea..e7d8207 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt
@@ -77,7 +77,7 @@
             )
             assertThat(field.setter, `is`(FieldSetter("id", "setId", intType, CallType.METHOD)))
             assertThat(field.getter,
-                `is`(FieldGetter("id", "getId", intType, CallType.METHOD, true)))
+                `is`(FieldGetter("id", "getId", intType, CallType.METHOD)))
             assertThat(entity.primaryKey.fields, `is`(Fields(field)))
         }
     }
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
index 558e6c0..f5c3ac3 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
@@ -22,6 +22,7 @@
 import androidx.room.Dao
 import androidx.room.compiler.codegen.XCodeBlock
 import androidx.room.compiler.codegen.XTypeName
+import androidx.room.compiler.codegen.XTypeName.Companion.PRIMITIVE_INT
 import androidx.room.compiler.processing.XProcessingEnv
 import androidx.room.compiler.processing.XRawType
 import androidx.room.compiler.processing.isTypeElement
@@ -62,18 +63,20 @@
 import androidx.room.solver.shortcut.binderprovider.RxCallableUpsertMethodBinderProvider
 import androidx.room.solver.types.BoxedPrimitiveColumnTypeAdapter
 import androidx.room.solver.types.ByteBufferColumnTypeAdapter
+import androidx.room.solver.types.ColumnTypeAdapter
 import androidx.room.solver.types.CompositeAdapter
 import androidx.room.solver.types.CustomTypeConverterWrapper
 import androidx.room.solver.types.EnumColumnTypeAdapter
 import androidx.room.solver.types.PrimitiveColumnTypeAdapter
 import androidx.room.solver.types.SingleStatementTypeConverter
+import androidx.room.solver.types.StringColumnTypeAdapter
 import androidx.room.solver.types.TypeConverter
 import androidx.room.solver.types.UuidColumnTypeAdapter
+import androidx.room.solver.types.ValueClassConverterWrapper
 import androidx.room.testing.context
 import androidx.room.vo.BuiltInConverterFlags
 import androidx.room.vo.ReadQueryMethod
 import com.google.common.truth.Truth.assertThat
-import java.util.UUID
 import org.hamcrest.CoreMatchers.instanceOf
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.CoreMatchers.notNullValue
@@ -221,6 +224,99 @@
     }
 
     @Test
+    fun testKotlinLangValueClassCompilesWithoutError() {
+        val source = Source.kotlin(
+            "Foo.kt",
+            """
+            import androidx.room.*
+            @JvmInline
+            value class IntValueClass(val data: Int)
+            @JvmInline
+            value class StringValueClass(val data: String)
+            class EntityWithValueClass {
+                val intData = IntValueClass(123)
+                val stringData = StringValueClass("bla")
+            }
+            """.trimIndent()
+        )
+        var results: Map<String, String?> = mutableMapOf()
+
+        runProcessorTest(
+            sources = listOf(source)
+        ) { invocation ->
+            val typeAdapterStore = TypeAdapterStore.create(
+                context = invocation.context,
+                builtInConverterFlags = BuiltInConverterFlags.DEFAULT
+            )
+            val subject = invocation.processingEnv.requireTypeElement("EntityWithValueClass")
+            results = subject.getAllFieldsIncludingPrivateSupers().associate { field ->
+                val columnAdapter = typeAdapterStore.findColumnTypeAdapter(
+                    out = field.type,
+                    affinity = null,
+                    false
+                )
+
+                val typeElementColumnAdapter: ColumnTypeAdapter? =
+                    if (columnAdapter is ValueClassConverterWrapper) {
+                        columnAdapter.valueTypeColumnAdapter
+                    } else {
+                        columnAdapter
+                    }
+
+                when (typeElementColumnAdapter) {
+                    is PrimitiveColumnTypeAdapter -> {
+                        field.name to "primitive"
+                    }
+
+                    is StringColumnTypeAdapter -> {
+                        field.name to "string"
+                    }
+
+                    else -> {
+                        field.name to null
+                    }
+                }
+            }
+        }
+        assertThat(results).containsExactlyEntriesIn(
+            mapOf(
+                "intData" to "primitive",
+                "stringData" to "string"
+            )
+        )
+    }
+
+    @Test
+    fun testValueClassWithDifferentTypeVal() {
+        val source = Source.kotlin(
+            "Foo.kt",
+            """
+            import androidx.room.*
+            @JvmInline
+            value class Foo(val value : Int) {
+                val double
+                    get() = value * 2
+            }
+            """.trimIndent()
+        )
+
+        runProcessorTest(
+            sources = listOf(source)
+        ) { invocation ->
+            TypeAdapterStore.create(
+                context = invocation.context,
+                builtInConverterFlags = BuiltInConverterFlags.DEFAULT
+            )
+            val typeElement = invocation
+                .processingEnv
+                .requireTypeElement("Foo")
+            assertThat(typeElement.getDeclaredFields()).hasSize(1)
+            assertThat(typeElement.getDeclaredFields().single().type.asTypeName())
+                .isEqualTo(PRIMITIVE_INT)
+        }
+    }
+
+    @Test
     fun testJavaLangByteBufferCompilesWithoutError() {
         runProcessorTest { invocation ->
             val store = TypeAdapterStore.create(
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 dd79611f..0abf406 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
@@ -438,7 +438,7 @@
     }
 
     private fun assignGetterSetter(f: Field, name: String, type: XType) {
-        f.getter = FieldGetter(f.name, name, type, CallType.FIELD, true)
+        f.getter = FieldGetter(f.name, name, type, CallType.FIELD)
         f.setter = FieldSetter(f.name, name, type, CallType.FIELD)
     }
 
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 15f4065..11f0ed7 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
@@ -86,21 +86,34 @@
             interface MyDao {
               @Query("SELECT * FROM MyEntity")
               fun getEntity(): MyEntity
+
+              @Insert
+              fun addEntity(item: MyEntity)
             }
             """.trimIndent()
         )
         val javaEntity = Source.java(
             "MyEntity",
             """
+            import androidx.annotation.Nullable;
             import androidx.room.*;
             
             @Entity
             public class MyEntity {
               @PrimaryKey
               private long mValue;
-              
+
+              @Nullable
+              private String mNullableValue;
+
               public long getValue() { return mValue; }
               public void setValue(long value) { mValue = value; }
+
+              @Nullable
+              public String getNullableValue() { return mNullableValue; }
+              public void setNullableValue(@Nullable String nullableValue) {
+                mNullableValue = nullableValue;
+              }
             }
             """.trimIndent()
         )
@@ -110,6 +123,59 @@
         )
     }
 
+    // b/274760383
+    @Test
+    fun pojoRowAdapter_otherModule() {
+        val testName = object {}.javaClass.enclosingMethod!!.name
+        val lib = compileFiles(
+            sources = listOf(
+                Source.kotlin(
+                    "MyEntity.kt",
+                """
+                import androidx.room.*
+
+                @Entity
+                class MyEntity(
+                    @PrimaryKey
+                    val pk: Int,
+                    val primitive: Long = 0,
+                    val string: String = "",
+                    val nullableString: String? = null,
+                    @JvmField val fieldString: String = "",
+                    @JvmField val nullableFieldString: String? = null
+                ) {
+                    var variablePrimitive: Long = 0
+                    var variableString: String = ""
+                    var variableNullableString: String? = null
+                    @JvmField var variableFieldString: String = ""
+                    @JvmField var variableNullableFieldString: String? = null
+                }
+                """.trimIndent()
+                )
+            )
+        )
+        val src = Source.kotlin(
+            "MyDao.kt",
+            """
+            import androidx.room.*
+
+            @Dao
+            interface MyDao {
+              @Query("SELECT * FROM MyEntity")
+              fun getEntity(): MyEntity
+
+              @Insert
+              fun addEntity(item: MyEntity)
+            }
+            """.trimIndent()
+        )
+        runTest(
+            sources = listOf(src, databaseSrc),
+            expectedFilePath = getTestGoldenPath(testName),
+            compiledFiles = lib
+        )
+    }
+
     @Test
     fun pojoRowAdapter_internalVisibility() {
         val testName = object {}.javaClass.enclosingMethod!!.name
@@ -1310,6 +1376,7 @@
             expectedFilePath = getTestGoldenPath(testName)
         )
     }
+
     @Test
     fun queryResultAdapter_optional() {
         val testName = object {}.javaClass.enclosingMethod!!.name
@@ -2145,4 +2212,46 @@
             expectedFilePath = getTestGoldenPath(testName)
         )
     }
+
+    @Test
+    fun valueClassConverter() {
+        val testName = object {}.javaClass.enclosingMethod!!.name
+        val src = Source.kotlin(
+            "MyDao.kt",
+            """
+            import androidx.room.*
+            import java.util.UUID
+
+            @Dao
+            interface MyDao {
+              @Query("SELECT * FROM MyEntity")
+              fun getEntity(): MyEntity
+
+              @Insert
+              fun addEntity(item: MyEntity)
+            }
+
+            @JvmInline
+            value class LongValueClass(val data: Long)
+
+            @JvmInline
+            value class UUIDValueClass(val data: UUID)
+
+            @JvmInline
+            value class GenericValueClass<T>(val password: T)
+
+            @Entity
+            data class MyEntity (
+                @PrimaryKey
+                val pk: LongValueClass,
+                val uuidData: UUIDValueClass,
+                val genericData: GenericValueClass<String>
+            )
+            """.trimIndent()
+        )
+        runTest(
+            sources = listOf(src, databaseSrc),
+            expectedFilePath = getTestGoldenPath(testName)
+        )
+    }
 }
\ 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 e42eb9f..e823e0a 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/entityRowAdapter.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/entityRowAdapter.kt
@@ -36,10 +36,11 @@
                 val _tmp: Int = if (entity.valueBoolean) 1 else 0
                 statement.bindLong(2, _tmp.toLong())
                 statement.bindString(3, entity.valueString)
-                if (entity.valueNullableString == null) {
+                val _tmpValueNullableString: String? = entity.valueNullableString
+                if (_tmpValueNullableString == null) {
                     statement.bindNull(4)
                 } else {
-                    statement.bindString(4, entity.valueNullableString)
+                    statement.bindString(4, _tmpValueNullableString)
                 }
                 statement.bindLong(5, entity.variablePrimitive)
                 val _tmpVariableNullableBoolean: Boolean? = entity.variableNullableBoolean
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_boolean.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_boolean.kt
index ab6a26f..e4aa4f3 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_boolean.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_boolean.kt
@@ -34,7 +34,8 @@
                 statement.bindLong(1, entity.pk.toLong())
                 val _tmp: Int = if (entity.boolean) 1 else 0
                 statement.bindLong(2, _tmp.toLong())
-                val _tmp_1: Int? = entity.nullableBoolean?.let { if (it) 1 else 0 }
+                val _tmpNullableBoolean: Boolean? = entity.nullableBoolean
+                val _tmp_1: Int? = _tmpNullableBoolean?.let { if (it) 1 else 0 }
                 if (_tmp_1 == null) {
                     statement.bindNull(3)
                 } else {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_byteArray.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_byteArray.kt
index 53dfdd2..284798e 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_byteArray.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_byteArray.kt
@@ -33,10 +33,11 @@
             public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindBlob(2, entity.byteArray)
-                if (entity.nullableByteArray == null) {
+                val _tmpNullableByteArray: ByteArray? = entity.nullableByteArray
+                if (_tmpNullableByteArray == null) {
                     statement.bindNull(3)
                 } else {
-                    statement.bindBlob(3, entity.nullableByteArray)
+                    statement.bindBlob(3, _tmpNullableByteArray)
                 }
             }
         }
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_enum.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_enum.kt
index 480173c..25aa37e 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_enum.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_enum.kt
@@ -33,10 +33,11 @@
             public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindString(2, __Fruit_enumToString(entity.enum))
-                if (entity.nullableEnum == null) {
+                val _tmpNullableEnum: Fruit? = entity.nullableEnum
+                if (_tmpNullableEnum == null) {
                     statement.bindNull(3)
                 } else {
-                    statement.bindString(3, __Fruit_enumToString(entity.nullableEnum))
+                    statement.bindString(3, __Fruit_enumToString(_tmpNullableEnum))
                 }
             }
         }
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_otherModule.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_otherModule.kt
new file mode 100644
index 0000000..8b4945d
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_otherModule.kt
@@ -0,0 +1,153 @@
+import android.database.Cursor
+import androidx.room.EntityInsertionAdapter
+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.sqlite.db.SupportSQLiteStatement
+import java.lang.Class
+import javax.`annotation`.processing.Generated
+import kotlin.Int
+import kotlin.Long
+import kotlin.String
+import kotlin.Suppress
+import kotlin.Unit
+import kotlin.collections.List
+import kotlin.jvm.JvmStatic
+
+@Generated(value = ["androidx.room.RoomProcessor"])
+@Suppress(names = ["UNCHECKED_CAST", "DEPRECATION", "REDUNDANT_PROJECTION"])
+public class MyDao_Impl(
+    __db: RoomDatabase,
+) : MyDao {
+    private val __db: RoomDatabase
+
+    private val __insertionAdapterOfMyEntity: EntityInsertionAdapter<MyEntity>
+    init {
+        this.__db = __db
+        this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
+            public override fun createQuery(): String =
+                "INSERT OR ABORT INTO `MyEntity` (`pk`,`primitive`,`string`,`nullableString`,`fieldString`,`nullableFieldString`,`variablePrimitive`,`variableString`,`variableNullableString`,`variableFieldString`,`variableNullableFieldString`) VALUES (?,?,?,?,?,?,?,?,?,?,?)"
+
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+                statement.bindLong(1, entity.pk.toLong())
+                statement.bindLong(2, entity.primitive)
+                statement.bindString(3, entity.string)
+                val _tmpNullableString: String? = entity.nullableString
+                if (_tmpNullableString == null) {
+                    statement.bindNull(4)
+                } else {
+                    statement.bindString(4, _tmpNullableString)
+                }
+                statement.bindString(5, entity.fieldString)
+                val _tmpNullableFieldString: String? = entity.nullableFieldString
+                if (_tmpNullableFieldString == null) {
+                    statement.bindNull(6)
+                } else {
+                    statement.bindString(6, _tmpNullableFieldString)
+                }
+                statement.bindLong(7, entity.variablePrimitive)
+                statement.bindString(8, entity.variableString)
+                val _tmpVariableNullableString: String? = entity.variableNullableString
+                if (_tmpVariableNullableString == null) {
+                    statement.bindNull(9)
+                } else {
+                    statement.bindString(9, _tmpVariableNullableString)
+                }
+                statement.bindString(10, entity.variableFieldString)
+                val _tmpVariableNullableFieldString: String? = entity.variableNullableFieldString
+                if (_tmpVariableNullableFieldString == null) {
+                    statement.bindNull(11)
+                } else {
+                    statement.bindString(11, _tmpVariableNullableFieldString)
+                }
+            }
+        }
+    }
+
+    public override fun addEntity(item: MyEntity): Unit {
+        __db.assertNotSuspendingTransaction()
+        __db.beginTransaction()
+        try {
+            __insertionAdapterOfMyEntity.insert(item)
+            __db.setTransactionSuccessful()
+        } finally {
+            __db.endTransaction()
+        }
+    }
+
+    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 _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
+            val _cursorIndexOfPrimitive: Int = getColumnIndexOrThrow(_cursor, "primitive")
+            val _cursorIndexOfString: Int = getColumnIndexOrThrow(_cursor, "string")
+            val _cursorIndexOfNullableString: Int = getColumnIndexOrThrow(_cursor, "nullableString")
+            val _cursorIndexOfFieldString: Int = getColumnIndexOrThrow(_cursor, "fieldString")
+            val _cursorIndexOfNullableFieldString: Int = getColumnIndexOrThrow(_cursor,
+                "nullableFieldString")
+            val _cursorIndexOfVariablePrimitive: Int = getColumnIndexOrThrow(_cursor, "variablePrimitive")
+            val _cursorIndexOfVariableString: Int = getColumnIndexOrThrow(_cursor, "variableString")
+            val _cursorIndexOfVariableNullableString: Int = getColumnIndexOrThrow(_cursor,
+                "variableNullableString")
+            val _cursorIndexOfVariableFieldString: Int = getColumnIndexOrThrow(_cursor,
+                "variableFieldString")
+            val _cursorIndexOfVariableNullableFieldString: Int = getColumnIndexOrThrow(_cursor,
+                "variableNullableFieldString")
+            val _result: MyEntity
+            if (_cursor.moveToFirst()) {
+                val _tmpPk: Int
+                _tmpPk = _cursor.getInt(_cursorIndexOfPk)
+                val _tmpPrimitive: Long
+                _tmpPrimitive = _cursor.getLong(_cursorIndexOfPrimitive)
+                val _tmpString: String
+                _tmpString = _cursor.getString(_cursorIndexOfString)
+                val _tmpNullableString: String?
+                if (_cursor.isNull(_cursorIndexOfNullableString)) {
+                    _tmpNullableString = null
+                } else {
+                    _tmpNullableString = _cursor.getString(_cursorIndexOfNullableString)
+                }
+                val _tmpFieldString: String
+                _tmpFieldString = _cursor.getString(_cursorIndexOfFieldString)
+                val _tmpNullableFieldString: String?
+                if (_cursor.isNull(_cursorIndexOfNullableFieldString)) {
+                    _tmpNullableFieldString = null
+                } else {
+                    _tmpNullableFieldString = _cursor.getString(_cursorIndexOfNullableFieldString)
+                }
+                _result =
+                    MyEntity(_tmpPk,_tmpPrimitive,_tmpString,_tmpNullableString,_tmpFieldString,_tmpNullableFieldString)
+                _result.variablePrimitive = _cursor.getLong(_cursorIndexOfVariablePrimitive)
+                _result.variableString = _cursor.getString(_cursorIndexOfVariableString)
+                if (_cursor.isNull(_cursorIndexOfVariableNullableString)) {
+                    _result.variableNullableString = null
+                } else {
+                    _result.variableNullableString = _cursor.getString(_cursorIndexOfVariableNullableString)
+                }
+                _result.variableFieldString = _cursor.getString(_cursorIndexOfVariableFieldString)
+                if (_cursor.isNull(_cursorIndexOfVariableNullableFieldString)) {
+                    _result.variableNullableFieldString = null
+                } else {
+                    _result.variableNullableFieldString =
+                        _cursor.getString(_cursorIndexOfVariableNullableFieldString)
+                }
+            } else {
+                error("Cursor was empty, but expected a single item.")
+            }
+            return _result
+        } finally {
+            _cursor.close()
+            _statement.release()
+        }
+    }
+
+    public companion object {
+        @JvmStatic
+        public fun getRequiredConverters(): List<Class<*>> = emptyList()
+    }
+}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives_nullable.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives_nullable.kt
index 19411e3..3ec68d0 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives_nullable.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives_nullable.kt
@@ -36,40 +36,47 @@
                 "INSERT OR ABORT INTO `MyEntity` (`int`,`short`,`byte`,`long`,`char`,`float`,`double`) VALUES (?,?,?,?,?,?,?)"
 
             public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
-                if (entity.int == null) {
+                val _tmpInt: Int? = entity.int
+                if (_tmpInt == null) {
                     statement.bindNull(1)
                 } else {
-                    statement.bindLong(1, entity.int.toLong())
+                    statement.bindLong(1, _tmpInt.toLong())
                 }
-                if (entity.short == null) {
+                val _tmpShort: Short? = entity.short
+                if (_tmpShort == null) {
                     statement.bindNull(2)
                 } else {
-                    statement.bindLong(2, entity.short.toLong())
+                    statement.bindLong(2, _tmpShort.toLong())
                 }
-                if (entity.byte == null) {
+                val _tmpByte: Byte? = entity.byte
+                if (_tmpByte == null) {
                     statement.bindNull(3)
                 } else {
-                    statement.bindLong(3, entity.byte.toLong())
+                    statement.bindLong(3, _tmpByte.toLong())
                 }
-                if (entity.long == null) {
+                val _tmpLong: Long? = entity.long
+                if (_tmpLong == null) {
                     statement.bindNull(4)
                 } else {
-                    statement.bindLong(4, entity.long)
+                    statement.bindLong(4, _tmpLong)
                 }
-                if (entity.char == null) {
+                val _tmpChar: Char? = entity.char
+                if (_tmpChar == null) {
                     statement.bindNull(5)
                 } else {
-                    statement.bindLong(5, entity.char.toLong())
+                    statement.bindLong(5, _tmpChar.toLong())
                 }
-                if (entity.float == null) {
+                val _tmpFloat: Float? = entity.float
+                if (_tmpFloat == null) {
                     statement.bindNull(6)
                 } else {
-                    statement.bindDouble(6, entity.float.toDouble())
+                    statement.bindDouble(6, _tmpFloat.toDouble())
                 }
-                if (entity.double == null) {
+                val _tmpDouble: Double? = entity.double
+                if (_tmpDouble == null) {
                     statement.bindNull(7)
                 } else {
-                    statement.bindDouble(7, entity.double)
+                    statement.bindDouble(7, _tmpDouble)
                 }
             }
         }
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_string.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_string.kt
index 534cef8..0a4b23b 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_string.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_string.kt
@@ -31,10 +31,11 @@
 
             public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
                 statement.bindString(1, entity.string)
-                if (entity.nullableString == null) {
+                val _tmpNullableString: String? = entity.nullableString
+                if (_tmpNullableString == null) {
                     statement.bindNull(2)
                 } else {
-                    statement.bindString(2, entity.nullableString)
+                    statement.bindString(2, _tmpNullableString)
                 }
             }
         }
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_uuid.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_uuid.kt
index 884c2a9..dd7f1df 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_uuid.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_uuid.kt
@@ -35,10 +35,11 @@
             public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindBlob(2, convertUUIDToByte(entity.uuid))
-                if (entity.nullableUuid == null) {
+                val _tmpNullableUuid: UUID? = entity.nullableUuid
+                if (_tmpNullableUuid == null) {
                     statement.bindNull(3)
                 } else {
-                    statement.bindBlob(3, convertUUIDToByte(entity.nullableUuid))
+                    statement.bindBlob(3, convertUUIDToByte(_tmpNullableUuid))
                 }
             }
         }
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty_java.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty_java.kt
index 6a0ead8..9ffae96 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty_java.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty_java.kt
@@ -1,15 +1,18 @@
 import android.database.Cursor
+import androidx.room.EntityInsertionAdapter
 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.sqlite.db.SupportSQLiteStatement
 import java.lang.Class
 import javax.`annotation`.processing.Generated
 import kotlin.Int
 import kotlin.Long
 import kotlin.String
 import kotlin.Suppress
+import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -19,8 +22,35 @@
     __db: RoomDatabase,
 ) : MyDao {
     private val __db: RoomDatabase
+
+    private val __insertionAdapterOfMyEntity: EntityInsertionAdapter<MyEntity>
     init {
         this.__db = __db
+        this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
+            public override fun createQuery(): String =
+                "INSERT OR ABORT INTO `MyEntity` (`mValue`,`mNullableValue`) VALUES (?,?)"
+
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+                statement.bindLong(1, entity.getValue())
+                val _tmpMNullableValue: String? = entity.getNullableValue()
+                if (_tmpMNullableValue == null) {
+                    statement.bindNull(2)
+                } else {
+                    statement.bindString(2, _tmpMNullableValue)
+                }
+            }
+        }
+    }
+
+    public override fun addEntity(item: MyEntity): Unit {
+        __db.assertNotSuspendingTransaction()
+        __db.beginTransaction()
+        try {
+            __insertionAdapterOfMyEntity.insert(item)
+            __db.setTransactionSuccessful()
+        } finally {
+            __db.endTransaction()
+        }
     }
 
     public override fun getEntity(): MyEntity {
@@ -30,12 +60,20 @@
         val _cursor: Cursor = query(__db, _statement, false, null)
         try {
             val _cursorIndexOfMValue: Int = getColumnIndexOrThrow(_cursor, "mValue")
+            val _cursorIndexOfMNullableValue: Int = getColumnIndexOrThrow(_cursor, "mNullableValue")
             val _result: MyEntity
             if (_cursor.moveToFirst()) {
                 _result = MyEntity()
                 val _tmpMValue: Long
                 _tmpMValue = _cursor.getLong(_cursorIndexOfMValue)
                 _result.setValue(_tmpMValue)
+                val _tmpMNullableValue: String?
+                if (_cursor.isNull(_cursorIndexOfMNullableValue)) {
+                    _tmpMNullableValue = null
+                } else {
+                    _tmpMNullableValue = _cursor.getString(_cursorIndexOfMNullableValue)
+                }
+                _result.setNullableValue(_tmpMNullableValue)
             } else {
                 error("Cursor was empty, but expected a single item.")
             }
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/valueClassConverter.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/valueClassConverter.kt
new file mode 100644
index 0000000..0d3b34d
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/valueClassConverter.kt
@@ -0,0 +1,96 @@
+import android.database.Cursor
+import androidx.room.EntityInsertionAdapter
+import androidx.room.RoomDatabase
+import androidx.room.RoomSQLiteQuery
+import androidx.room.RoomSQLiteQuery.Companion.acquire
+import androidx.room.util.convertByteToUUID
+import androidx.room.util.convertUUIDToByte
+import androidx.room.util.getColumnIndexOrThrow
+import androidx.room.util.query
+import androidx.sqlite.db.SupportSQLiteStatement
+import java.lang.Class
+import java.util.UUID
+import javax.`annotation`.processing.Generated
+import kotlin.Int
+import kotlin.Long
+import kotlin.String
+import kotlin.Suppress
+import kotlin.Unit
+import kotlin.collections.List
+import kotlin.jvm.JvmStatic
+
+@Generated(value = ["androidx.room.RoomProcessor"])
+@Suppress(names = ["UNCHECKED_CAST", "DEPRECATION", "REDUNDANT_PROJECTION"])
+public class MyDao_Impl(
+    __db: RoomDatabase,
+) : MyDao {
+    private val __db: RoomDatabase
+
+    private val __insertionAdapterOfMyEntity: EntityInsertionAdapter<MyEntity>
+    init {
+        this.__db = __db
+        this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
+            public override fun createQuery(): String =
+                "INSERT OR ABORT INTO `MyEntity` (`pk`,`uuidData`,`genericData`) VALUES (?,?,?)"
+
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+                val _data: Long = entity.pk.data
+                statement.bindLong(1, _data)
+                val _data_1: UUID = entity.uuidData.data
+                statement.bindBlob(2, convertUUIDToByte(_data_1))
+                val _password: String = entity.genericData.password
+                statement.bindString(3, _password)
+            }
+        }
+    }
+
+    public override fun addEntity(item: MyEntity): Unit {
+        __db.assertNotSuspendingTransaction()
+        __db.beginTransaction()
+        try {
+            __insertionAdapterOfMyEntity.insert(item)
+            __db.setTransactionSuccessful()
+        } finally {
+            __db.endTransaction()
+        }
+    }
+
+    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 _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
+            val _cursorIndexOfUuidData: Int = getColumnIndexOrThrow(_cursor, "uuidData")
+            val _cursorIndexOfGenericData: Int = getColumnIndexOrThrow(_cursor, "genericData")
+            val _result: MyEntity
+            if (_cursor.moveToFirst()) {
+                val _tmpPk: LongValueClass
+                val _data: Long
+                _data = _cursor.getLong(_cursorIndexOfPk)
+                _tmpPk = LongValueClass(_data)
+                val _tmpUuidData: UUIDValueClass
+                val _data_1: UUID
+                _data_1 = convertByteToUUID(_cursor.getBlob(_cursorIndexOfUuidData))
+                _tmpUuidData = UUIDValueClass(_data_1)
+                val _tmpGenericData: GenericValueClass<String>
+                val _password: String
+                _password = _cursor.getString(_cursorIndexOfGenericData)
+                _tmpGenericData = GenericValueClass<String>(_password)
+                _result = MyEntity(_tmpPk,_tmpUuidData,_tmpGenericData)
+            } else {
+                error("Cursor was empty, but expected a single item.")
+            }
+            return _result
+        } finally {
+            _cursor.close()
+            _statement.release()
+        }
+    }
+
+    public companion object {
+        @JvmStatic
+        public fun getRequiredConverters(): List<Class<*>> = emptyList()
+    }
+}
\ No newline at end of file
diff --git a/room/room-ktx/build.gradle b/room/room-ktx/build.gradle
index 466e365..5e4a868 100644
--- a/room/room-ktx/build.gradle
+++ b/room/room-ktx/build.gradle
@@ -30,12 +30,12 @@
     api(libs.kotlinCoroutinesAndroid)
     testImplementation(libs.junit)
     testImplementation(libs.mockitoCore4)
-    testImplementation(libs.truth)
+    testImplementation(project(":internal-testutils-kmp"))
     testImplementation("androidx.lifecycle:lifecycle-livedata-core:2.0.0")
     testImplementation(libs.testRunner)
     testImplementation(libs.kotlinCoroutinesTest)
 
-    androidTestImplementation(libs.truth)
+    androidTestImplementation(project(":internal-testutils-kmp"))
     androidTestImplementation(libs.testRunner)
     androidTestImplementation(libs.kotlinCoroutinesTest)
 }
diff --git a/room/room-ktx/src/androidTest/java/androidx/room/CoroutineRoomCancellationTest.kt b/room/room-ktx/src/androidTest/java/androidx/room/CoroutineRoomCancellationTest.kt
index aaa8ba9..97dcc0d 100644
--- a/room/room-ktx/src/androidTest/java/androidx/room/CoroutineRoomCancellationTest.kt
+++ b/room/room-ktx/src/androidTest/java/androidx/room/CoroutineRoomCancellationTest.kt
@@ -18,10 +18,10 @@
 
 import android.database.sqlite.SQLiteException
 import android.os.CancellationSignal
+import androidx.kruth.assertThat
 import androidx.sqlite.db.SupportSQLiteOpenHelper
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
-import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.DelicateCoroutinesApi
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -32,7 +32,6 @@
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
-import org.junit.Assert.assertTrue
 import org.junit.Assert.fail
 import org.junit.Test
 import java.util.concurrent.Callable
@@ -135,7 +134,7 @@
                     }
                 )
             } catch (exception: Throwable) {
-                assertTrue(exception is SQLiteException)
+                assertThat(exception).isInstanceOf<SQLiteException>()
             }
         }
 
diff --git a/room/room-ktx/src/test/java/androidx/room/CoroutinesRoomTest.kt b/room/room-ktx/src/test/java/androidx/room/CoroutinesRoomTest.kt
index 46e43ec..08dc8c5 100644
--- a/room/room-ktx/src/test/java/androidx/room/CoroutinesRoomTest.kt
+++ b/room/room-ktx/src/test/java/androidx/room/CoroutinesRoomTest.kt
@@ -16,8 +16,8 @@
 
 package androidx.room
 
+import androidx.kruth.assertThat
 import androidx.sqlite.db.SupportSQLiteOpenHelper
-import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.async
 import kotlinx.coroutines.flow.first
@@ -56,11 +56,11 @@
         }
         yield(); yield() // yield for async and flow
 
-        assertThat(invalidationTracker.observers.size).isEqualTo(1)
+        assertThat(invalidationTracker.observers).hasSize(1)
         assertThat(callableExecuted).isTrue()
 
         assertThat(job.await()).isEqualTo(expectedResult)
-        assertThat(invalidationTracker.observers.isEmpty()).isTrue()
+        assertThat(invalidationTracker.observers).isEmpty()
     }
 
     // Use runBlocking dispatcher as query dispatchers, keeps the tests consistent.
diff --git a/room/room-ktx/src/test/java/androidx/room/MigrationTest.kt b/room/room-ktx/src/test/java/androidx/room/MigrationTest.kt
index 162f50b..f49e8ce 100644
--- a/room/room-ktx/src/test/java/androidx/room/MigrationTest.kt
+++ b/room/room-ktx/src/test/java/androidx/room/MigrationTest.kt
@@ -21,11 +21,11 @@
 import android.database.sqlite.SQLiteTransactionListener
 import android.os.CancellationSignal
 import android.util.Pair
+import androidx.kruth.assertThat
 import androidx.room.migration.Migration
 import androidx.sqlite.db.SupportSQLiteDatabase
 import androidx.sqlite.db.SupportSQLiteQuery
 import androidx.sqlite.db.SupportSQLiteStatement
-import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
diff --git a/room/room-runtime/src/main/java/androidx/room/RoomDatabase.kt b/room/room-runtime/src/main/java/androidx/room/RoomDatabase.kt
index a455992..798a2fbc 100644
--- a/room/room-runtime/src/main/java/androidx/room/RoomDatabase.kt
+++ b/room/room-runtime/src/main/java/androidx/room/RoomDatabase.kt
@@ -671,8 +671,9 @@
                 return this
             }
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
-                val manager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
-                if (!isLowRamDevice(manager)) {
+                val manager =
+                    context.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager
+                if (manager != null && !isLowRamDevice(manager)) {
                     return WRITE_AHEAD_LOGGING
                 }
             }
diff --git a/room/settings.gradle b/room/settings.gradle
index cde30ee..b253b61 100644
--- a/room/settings.gradle
+++ b/room/settings.gradle
@@ -33,6 +33,7 @@
         if (name == ":annotation:annotation-experimental-lint") return true
         if (name == ":annotation:annotation-experimental-lint-integration-tests") return true
         if (name == ":internal-testutils-truth") return true
+        if (name == ":internal-testutils-kmp") return true
         return false
     })
 }
diff --git a/savedstate/savedstate-ktx/build.gradle b/savedstate/savedstate-ktx/build.gradle
index da99fd8..0f3f346 100644
--- a/savedstate/savedstate-ktx/build.gradle
+++ b/savedstate/savedstate-ktx/build.gradle
@@ -23,11 +23,6 @@
 }
 
 dependencies {
-    // Atomic group
-    constraints {
-        implementation(project(":savedstate:savedstate"))
-    }
-
     api(project(":savedstate:savedstate"))
     api(libs.kotlinStdlib)
 
diff --git a/savedstate/savedstate/build.gradle b/savedstate/savedstate/build.gradle
index 23eb488..a102643 100644
--- a/savedstate/savedstate/build.gradle
+++ b/savedstate/savedstate/build.gradle
@@ -14,11 +14,6 @@
 }
 
 dependencies {
-    // Atomic group
-    constraints {
-        implementation(project(":savedstate:savedstate-ktx"))
-    }
-
     api("androidx.annotation:annotation:1.1.0")
     implementation("androidx.arch.core:core-common:2.1.0")
     implementation("androidx.lifecycle:lifecycle-common:2.6.1")
diff --git a/settings.gradle b/settings.gradle
index b2f892c..4a28986 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -23,8 +23,8 @@
     repos.addMavenRepositories(repositories)
 
     dependencies {
-        classpath("com.gradle:gradle-enterprise-gradle-plugin:3.12.2")
-        classpath("com.gradle:common-custom-user-data-gradle-plugin:1.7.2")
+        classpath("com.gradle:gradle-enterprise-gradle-plugin:3.12.4")
+        classpath("com.gradle:common-custom-user-data-gradle-plugin:1.9")
         classpath("androidx.build.gradle.gcpbuildcache:gcpbuildcache:1.0.0-alpha06")
     }
 }
@@ -353,13 +353,6 @@
 includeProject(":activity:activity-ktx", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE, BuildType.WEAR])
 includeProject(":activity:activity-lint", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE, BuildType.WEAR])
 includeProject(":activity:integration-tests:testapp", [BuildType.MAIN, BuildType.FLAN])
-includeProject(":ads:ads-identifier", [BuildType.MAIN])
-includeProject(":ads:ads-identifier-benchmark", [BuildType.MAIN])
-includeProject(":ads:ads-identifier-common", [BuildType.MAIN])
-includeProject(":ads:ads-identifier-provider", [BuildType.MAIN])
-includeProject(":ads:ads-identifier-provider:integration-tests:testapp", [BuildType.MAIN])
-includeProject(":ads:ads-identifier-testing", [BuildType.MAIN])
-includeProject(":ads:ads-identifier:integration-tests:testapp", [BuildType.MAIN])
 includeProject(":annotation:annotation")
 includeProject(":annotation:annotation-experimental")
 includeProject(":annotation:annotation-experimental-lint")
@@ -370,6 +363,7 @@
 includeProject(":appactions:interaction:interaction-capabilities-fitness", [BuildType.MAIN])
 includeProject(":appactions:interaction:interaction-capabilities-productivity", [BuildType.MAIN])
 includeProject(":appactions:interaction:interaction-capabilities-safety", [BuildType.MAIN])
+includeProject(":appactions:interaction:interaction-capabilities-testing", [BuildType.MAIN])
 includeProject(":appactions:interaction:interaction-proto", [BuildType.MAIN])
 includeProject(":appactions:interaction:interaction-service", [BuildType.MAIN])
 includeProject(":appactions:interaction:interaction-service-proto", [BuildType.MAIN])
@@ -510,7 +504,6 @@
 includeProject(":compose:foundation:foundation-lint", [BuildType.COMPOSE])
 includeProject(":compose:foundation:foundation:integration-tests:foundation-demos", [BuildType.COMPOSE])
 includeProject(":compose:foundation:foundation:foundation-samples", "compose/foundation/foundation/samples", [BuildType.COMPOSE])
-includeProject(":compose:foundation:foundation-do-not-ship-newtext", "compose/foundation/foundation-newtext", [BuildType.COMPOSE])
 includeProject(":compose:integration-tests", [BuildType.COMPOSE])
 includeProject(":compose:integration-tests:demos", [BuildType.COMPOSE])
 includeProject(":compose:integration-tests:demos:common", [BuildType.COMPOSE])
@@ -607,6 +600,7 @@
 includeProject(":contentpager:contentpager", [BuildType.MAIN])
 includeProject(":coordinatorlayout:coordinatorlayout", [BuildType.MAIN])
 includeProject(":core:core", [BuildType.MAIN, BuildType.GLANCE, BuildType.MEDIA, BuildType.FLAN, BuildType.COMPOSE, BuildType.WEAR])
+includeProject(":core:core-testing", [BuildType.MAIN, BuildType.GLANCE, BuildType.MEDIA, BuildType.FLAN, BuildType.COMPOSE, BuildType.WEAR])
 includeProject(":core:core:integration-tests:publishing", [BuildType.MAIN])
 includeProject(":core:core-animation", [BuildType.MAIN])
 includeProject(":core:core-animation-integration-tests:testapp", [BuildType.MAIN])
@@ -696,8 +690,9 @@
 includeProject(":glance:glance-wear-tiles-preview", [BuildType.GLANCE])
 includeProject(":graphics:filters:filters", [BuildType.MAIN])
 includeProject(":graphics:graphics-core", [BuildType.MAIN])
+includeProject(":graphics:graphics-shapes", [BuildType.MAIN, BuildType.COMPOSE])
 includeProject(":graphics:integration-tests:testapp", [BuildType.MAIN])
-includeProject(":graphics:graphics-shapes", [BuildType.MAIN])
+includeProject(":graphics:integration-tests:testapp-compose", [BuildType.COMPOSE])
 includeProject(":gridlayout:gridlayout", [BuildType.MAIN])
 includeProject(":health:connect:connect-client", [BuildType.MAIN])
 includeProject(":health:connect:connect-client-proto", [BuildType.MAIN])
@@ -913,7 +908,8 @@
 includeProject(":transition:transition-ktx", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":tv:tv-foundation", [BuildType.COMPOSE])
 includeProject(":tv:tv-material", [BuildType.COMPOSE])
-includeProject(":tv:integration-tests:demos", [BuildType.COMPOSE])
+includeProject(":tv:integration-tests:playground", [BuildType.COMPOSE])
+includeProject(":tv:integration-tests:presentation", [BuildType.COMPOSE])
 includeProject(":tv:tv-samples", "tv/samples", [BuildType.COMPOSE])
 includeProject(":tvprovider:tvprovider", [BuildType.MAIN])
 includeProject(":vectordrawable:integration-tests:testapp", [BuildType.MAIN])
@@ -1062,7 +1058,7 @@
 includeProject(":internal-testutils-fonts", "testutils/testutils-fonts", [BuildType.MAIN, BuildType.GLANCE, BuildType.MEDIA, BuildType.FLAN, BuildType.COMPOSE, BuildType.WEAR])
 includeProject(":internal-testutils-truth", "testutils/testutils-truth")
 includeProject(":internal-testutils-ktx", "testutils/testutils-ktx")
-includeProject(":internal-testutils-kmp", "testutils/testutils-kmp", [BuildType.MAIN, BuildType.KMP])
+includeProject(":internal-testutils-kmp", "testutils/testutils-kmp", [BuildType.MAIN, BuildType.KMP, BuildType.COMPOSE])
 includeProject(":internal-testutils-macrobenchmark", "testutils/testutils-macrobenchmark", [BuildType.MAIN, BuildType.COMPOSE])
 includeProject(":internal-testutils-navigation", "testutils/testutils-navigation", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN])
 includeProject(":internal-testutils-paging", "testutils/testutils-paging", [BuildType.MAIN, BuildType.COMPOSE])
diff --git a/slidingpanelayout/slidingpanelayout/build.gradle b/slidingpanelayout/slidingpanelayout/build.gradle
index c1e7fc2..6a29dca 100644
--- a/slidingpanelayout/slidingpanelayout/build.gradle
+++ b/slidingpanelayout/slidingpanelayout/build.gradle
@@ -19,7 +19,7 @@
     androidTestImplementation(libs.kotlinStdlib)
     androidTestImplementation(libs.truth)
     androidTestImplementation(project(':internal-testutils-runtime'))
-    androidTestImplementation(project(":window:window-testing"))
+    androidTestImplementation("androidx.window:window-testing:1.0.0")
 }
 
 androidx {
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Test.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Test.java
index 7b87886..cf32f66 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Test.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Test.java
@@ -48,7 +48,6 @@
 public class UiObject2Test extends BaseTest {
     private static final int TIMEOUT_MS = 10_000;
     private static final int SPEED_MS = 100;
-    private static final int SCROLL_MARGIN = 50;
 
     @Test
     public void testClear() {
@@ -160,7 +159,6 @@
                 TIMEOUT_MS));
     }
 
-    @Ignore // b/266617335
     @Test
     @SdkSuppress(minSdkVersion = 24)
     public void testDrag_dest() {
@@ -175,7 +173,6 @@
         assertEquals("drag_received", dragDestination.getText());
     }
 
-    @Ignore // b/270210522
     @Test
     @SdkSuppress(minSdkVersion = 24)
     public void testDrag_destAndSpeed() {
@@ -509,7 +506,7 @@
 
         UiObject2 pinchArea = mDevice.findObject(By.res(TEST_APP, "pinch_area"));
         UiObject2 scaleText = pinchArea.findObject(By.res(TEST_APP, "scale_factor"));
-        pinchArea.pinchClose(1f);
+        pinchArea.pinchClose(0.75f);
         scaleText.wait(Until.textNotEquals("1.0f"), TIMEOUT_MS);
         float scaleValueAfterPinch = Float.parseFloat(scaleText.getText());
         assertTrue(String.format("Expected scale value to be less than 1f after pinchClose(), "
@@ -562,7 +559,6 @@
         launchTestActivity(SwipeTestActivity.class);
 
         UiObject2 swipeRegion = mDevice.findObject(By.res(TEST_APP, "swipe_region"));
-        swipeRegion.setGestureMargin(SCROLL_MARGIN);
 
         swipeRegion.swipe(Direction.LEFT, 0.9f);
         assertTrue(swipeRegion.wait(Until.textEquals("swipe_left"), TIMEOUT_MS));
@@ -613,11 +609,10 @@
 
         // Scroll down to bottom where is two-screen-height distant from the top.
         UiObject2 scrollView = mDevice.findObject(By.res(TEST_APP, "scroll_view"));
-        scrollView.setGestureMargin(SCROLL_MARGIN); // Avoid touching too close to the edges.
 
         Rect bounds = scrollView.getVisibleBounds();
         float percent =
-                (float) (mDevice.getDisplayHeight() * 2 / (bounds.height() - 2 * SCROLL_MARGIN));
+                (float) (mDevice.getDisplayHeight() * 2 / (bounds.height() - 100));
         scrollView.scroll(Direction.DOWN, percent);
 
         assertTrue(mDevice.hasObject(By.res(TEST_APP, "bottom_text")));
@@ -630,7 +625,6 @@
 
         // Scroll until end (scroll method returns false).
         UiObject2 scrollView = mDevice.findObject(By.res(TEST_APP, "scroll_view"));
-        scrollView.setGestureMargin(SCROLL_MARGIN); // Avoid touching too close to the edges.
         while (scrollView.scroll(Direction.DOWN, 1.0f)) {
             // Continue until bottom.
         }
@@ -645,7 +639,6 @@
 
         // Scroll until end
         UiObject2 scrollView = mDevice.findObject(By.res(TEST_APP, "scroll_view"));
-        scrollView.setGestureMargin(SCROLL_MARGIN); // Avoid touching too close to the edges.
         assertNotNull(scrollView.scrollUntil(Direction.DOWN,
                 Until.findObject(By.res(TEST_APP, "bottom_text"))));
         assertTrue(mDevice.hasObject(By.res(TEST_APP, "bottom_text")));
@@ -658,7 +651,6 @@
         assertFalse(mDevice.hasObject(By.res(TEST_APP, "bottom_text")));
 
         UiObject2 scrollView = mDevice.findObject(By.res(TEST_APP, "scroll_view"));
-        scrollView.setGestureMargin(SCROLL_MARGIN); // Avoid touching too close to the edges.
         // fail to find text that doesn't exist.
         assertNull(scrollView.scrollUntil(Direction.DOWN,
                 Until.findObject(By.res(TEST_APP, "nonexistent_text"))));
@@ -673,7 +665,6 @@
         assertFalse(mDevice.hasObject(By.res(TEST_APP, "bottom_text")));
 
         UiObject2 scrollView = mDevice.findObject(By.res(TEST_APP, "scroll_view"));
-        scrollView.setGestureMargin(SCROLL_MARGIN); // Avoid touching too close to the edges.
         // Scroll for the event condition that occurs early before scrolling to the end.
         Integer result = scrollView.scrollUntil(Direction.DOWN,
                 new EventCondition<Integer>() {
@@ -704,7 +695,6 @@
         assertFalse(mDevice.hasObject(By.res(TEST_APP, "bottom_text")));
 
         UiObject2 scrollView = mDevice.findObject(By.res(TEST_APP, "scroll_view"));
-        scrollView.setGestureMargin(SCROLL_MARGIN); // Avoid touching too close to the edges.
         // Scroll for the event condition that doesn't occur.
         Integer result = scrollView.scrollUntil(Direction.DOWN,
                 new EventCondition<Integer>() {
@@ -733,9 +723,7 @@
     public void testFling_direction() {
         launchTestActivity(FlingTestActivity.class);
 
-        // Avoid touching too close to the edges.
         UiObject2 flingRegion = mDevice.findObject(By.res(TEST_APP, "fling_region"));
-        flingRegion.setGestureMargin(SCROLL_MARGIN);
 
         // No fling yet.
         assertEquals("no_fling", flingRegion.getText());
@@ -750,9 +738,7 @@
     public void testFling_directionAndSpeed() {
         launchTestActivity(FlingTestActivity.class);
 
-        // Avoid touching too close to the edges.
         UiObject2 flingRegion = mDevice.findObject(By.res(TEST_APP, "fling_region"));
-        flingRegion.setGestureMargin(SCROLL_MARGIN);
 
         // No fling yet.
         assertEquals("no_fling", flingRegion.getText());
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java
index 3a5945f..36aaeb6 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java
@@ -501,15 +501,15 @@
 
     @Test
     public void testIsLongClickable() throws Exception {
-        launchTestActivity(LongClickTestActivity.class);
+        launchTestActivity(IsLongClickableTestActivity.class);
 
-        UiObject button = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id/button"));
-        UiObject expectedButton = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
-                + "/button").text("I've been long clicked!"));
+        UiObject longClickableButton = mDevice.findObject(
+                new UiSelector().resourceId(TEST_APP + ":id/long_clickable_button"));
+        UiObject nonLongClickableButton = mDevice.findObject(
+                new UiSelector().resourceId(TEST_APP + ":id/non_long_clickable_button"));
 
-        assertEquals("Long Click Me!", button.getText());
-        button.longClick();
-        assertTrue(expectedButton.waitForExists(TIMEOUT_MS));
+        assertTrue(longClickableButton.isLongClickable());
+        assertFalse(nonLongClickableButton.isLongClickable());
     }
 
     @Test
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/LongClickTestActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/LongClickTestActivity.java
index b4b9e84..f0137a3 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/LongClickTestActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/LongClickTestActivity.java
@@ -18,10 +18,8 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.View;
 import android.widget.Button;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 public class LongClickTestActivity extends Activity {
@@ -29,14 +27,12 @@
     @Override
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
         setContentView(R.layout.long_click_test_activity);
 
-        Button button = (Button) findViewById(R.id.button);
-        button.setOnClickListener(this::onButtonLongClick);
-    }
-
-    public void onButtonLongClick(@NonNull View v) {
-        ((Button) v).setText("I've been long clicked!");
+        Button button = findViewById(R.id.button);
+        button.setOnLongClickListener(v -> {
+            button.setText("I've been long clicked!");
+            return true;
+        });
     }
 }
diff --git a/test/uiautomator/uiautomator/api/current.txt b/test/uiautomator/uiautomator/api/current.txt
index 6c6f650..2948288 100644
--- a/test/uiautomator/uiautomator/api/current.txt
+++ b/test/uiautomator/uiautomator/api/current.txt
@@ -313,6 +313,8 @@
     method public <U> U! scrollUntil(androidx.test.uiautomator.Direction, androidx.test.uiautomator.Condition<? super androidx.test.uiautomator.UiObject2,U!>);
     method public <U> U! scrollUntil(androidx.test.uiautomator.Direction, androidx.test.uiautomator.EventCondition<U!>);
     method public void setGestureMargin(int);
+    method public void setGestureMarginPercent(@FloatRange(from=0.0f, to=0.5f) float);
+    method public void setGestureMarginPercent(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
     method public void setGestureMargins(int, int, int, int);
     method public void setText(String?);
     method public void swipe(androidx.test.uiautomator.Direction, float);
diff --git a/test/uiautomator/uiautomator/api/public_plus_experimental_current.txt b/test/uiautomator/uiautomator/api/public_plus_experimental_current.txt
index 6c6f650..2948288 100644
--- a/test/uiautomator/uiautomator/api/public_plus_experimental_current.txt
+++ b/test/uiautomator/uiautomator/api/public_plus_experimental_current.txt
@@ -313,6 +313,8 @@
     method public <U> U! scrollUntil(androidx.test.uiautomator.Direction, androidx.test.uiautomator.Condition<? super androidx.test.uiautomator.UiObject2,U!>);
     method public <U> U! scrollUntil(androidx.test.uiautomator.Direction, androidx.test.uiautomator.EventCondition<U!>);
     method public void setGestureMargin(int);
+    method public void setGestureMarginPercent(@FloatRange(from=0.0f, to=0.5f) float);
+    method public void setGestureMarginPercent(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
     method public void setGestureMargins(int, int, int, int);
     method public void setText(String?);
     method public void swipe(androidx.test.uiautomator.Direction, float);
diff --git a/test/uiautomator/uiautomator/api/restricted_current.txt b/test/uiautomator/uiautomator/api/restricted_current.txt
index 6c6f650..2948288 100644
--- a/test/uiautomator/uiautomator/api/restricted_current.txt
+++ b/test/uiautomator/uiautomator/api/restricted_current.txt
@@ -313,6 +313,8 @@
     method public <U> U! scrollUntil(androidx.test.uiautomator.Direction, androidx.test.uiautomator.Condition<? super androidx.test.uiautomator.UiObject2,U!>);
     method public <U> U! scrollUntil(androidx.test.uiautomator.Direction, androidx.test.uiautomator.EventCondition<U!>);
     method public void setGestureMargin(int);
+    method public void setGestureMarginPercent(@FloatRange(from=0.0f, to=0.5f) float);
+    method public void setGestureMarginPercent(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
     method public void setGestureMargins(int, int, int, int);
     method public void setText(String?);
     method public void swipe(androidx.test.uiautomator.Direction, float);
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Gestures.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Gestures.java
index 4468216..c0436c5 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Gestures.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Gestures.java
@@ -23,10 +23,13 @@
 /** Factory methods for constructing {@link PointerGesture}s. */
 class Gestures {
 
+    // Duration of a long press (with multiplier to ensure detection).
+    private static final long LONG_PRESS_DURATION_MS =
+            (long) (ViewConfiguration.getLongPressTimeout() * 1.5f);
+
     // Constants used by pinch gestures
     private static final int INNER = 0;
     private static final int OUTER = 1;
-    private static final int INNER_MARGIN = 5;
 
     private Gestures() {
     }
@@ -68,7 +71,7 @@
      */
     public static PointerGesture longClick(Point point, int displayId) {
         // A long click is a click with a duration that exceeds a certain threshold.
-        return click(point, ViewConfiguration.getLongPressTimeout(), displayId);
+        return click(point, LONG_PRESS_DURATION_MS, displayId);
     }
 
     /**
@@ -185,12 +188,12 @@
     private static void calcPinchCoordinates(Rect area, float percent,
             Point[] bottomLeft, Point[] topRight) {
 
-        int offsetX = (int)((area.width() - 2 * INNER_MARGIN) / 2 * percent);
-        int offsetY = (int)((area.height() - 2 * INNER_MARGIN) / 2 * percent);
+        int offsetX = (int) (area.width() / 2 * percent);
+        int offsetY = (int) (area.height() / 2 * percent);
 
         // Outer set of pinch coordinates
-        bottomLeft[OUTER] = new Point(area.left + INNER_MARGIN, area.bottom - INNER_MARGIN);
-        topRight[OUTER]   = new Point(area.right - INNER_MARGIN, area.top + INNER_MARGIN);
+        bottomLeft[OUTER] = new Point(area.left, area.bottom);
+        topRight[OUTER]   = new Point(area.right, area.top);
 
         // Inner set of pinch coordinates
         bottomLeft[INNER] = new Point(bottomLeft[OUTER]);
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/InteractionController.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/InteractionController.java
index 379d940..490209f 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/InteractionController.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/InteractionController.java
@@ -50,6 +50,10 @@
 
     private static final String TAG = InteractionController.class.getSimpleName();
 
+    // Duration of a long press (with multiplier to ensure detection).
+    private static final long LONG_PRESS_DURATION_MS =
+            (long) (ViewConfiguration.getLongPressTimeout() * 1.5f);
+
     private final KeyCharacterMap mKeyCharacterMap =
             KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
 
@@ -187,7 +191,7 @@
      * @return true if events are received, else false if timeout.
      */
     public boolean clickAndSync(final int x, final int y, long timeout) {
-        return runAndWaitForEvents(clickRunnable(x, y), new WaitForAnyEventPredicate(
+        return runAndWaitForEvents(() -> clickNoSync(x, y), new WaitForAnyEventPredicate(
                 AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED |
                 AccessibilityEvent.TYPE_VIEW_SELECTED), timeout) != null;
     }
@@ -202,46 +206,12 @@
      * @return true if both events occurred in the expected order
      */
     public boolean clickAndWaitForNewWindow(final int x, final int y, long timeout) {
-        return runAndWaitForEvents(clickRunnable(x, y), new WaitForAllEventPredicate(
+        return runAndWaitForEvents(() -> clickNoSync(x, y), new WaitForAllEventPredicate(
                 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED |
                 AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED), timeout) != null;
     }
 
     /**
-     * Returns a Runnable for use in
-     * {@link #runAndWaitForEvents(Runnable, AccessibilityEventFilter, long) to perform a click.
-     *
-     * @param x coordinate
-     * @param y coordinate
-     * @return Runnable
-     */
-    private Runnable clickRunnable(final int x, final int y) {
-        return () -> {
-            if (touchDown(x, y)) {
-                SystemClock.sleep(REGULAR_CLICK_LENGTH);
-                touchUp(x, y);
-            }
-        };
-    }
-
-    /**
-     * Returns a Runnable for use in
-     * {@link #runAndWaitForEvents(Runnable, AccessibilityEventFilter, long) to perform a long tap.
-     *
-     * @param x coordinate
-     * @param y coordinate
-     * @return Runnable
-     */
-    private Runnable longTapRunnable(final int x, final int y) {
-        return () -> {
-            if (touchDown(x, y)) {
-                SystemClock.sleep(ViewConfiguration.getLongPressTimeout());
-                touchUp(x, y);
-            }
-        };
-    }
-
-    /**
      * Touches down for a long press at the specified coordinates.
      *
      * @param x
@@ -250,7 +220,7 @@
      */
     public boolean longTapNoSync(int x, int y) {
         if (touchDown(x, y)) {
-            SystemClock.sleep(ViewConfiguration.getLongPressTimeout());
+            SystemClock.sleep(LONG_PRESS_DURATION_MS);
             return touchUp(x, y);
         }
         return false;
@@ -266,7 +236,7 @@
      * @return true if events are received, else false if timeout.
      */
     public boolean longTapAndSync(final int x, final int y, long timeout) {
-        return runAndWaitForEvents(longTapRunnable(x, y), new WaitForAnyEventPredicate(
+        return runAndWaitForEvents(() -> longTapNoSync(x, y), new WaitForAnyEventPredicate(
                 AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED |
                 AccessibilityEvent.TYPE_VIEW_SELECTED), timeout) != null;
     }
@@ -360,7 +330,7 @@
         ret = touchDown(downX, downY);
         SystemClock.sleep(MOTION_EVENT_INJECTION_DELAY_MILLIS);
         if (drag)
-            SystemClock.sleep(ViewConfiguration.getLongPressTimeout());
+            SystemClock.sleep(LONG_PRESS_DURATION_MS);
         for(int i = 1; i < swipeSteps; i++) {
             ret &= touchMove(downX + (int)(xStep * i), downY + (int)(yStep * i));
             if (!ret) {
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
index 61c44be..90573b1 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
@@ -38,6 +38,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.DoNotInline;
+import androidx.annotation.FloatRange;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -59,6 +60,9 @@
 
     private static final String TAG = UiObject2.class.getSimpleName();
 
+    // default percentage of margins for gestures.
+    private static final float DEFAULT_GESTURE_MARGIN_PERCENT = 0.1f;
+
     // default percentage of each scroll in scrollUntil().
     private static final float DEFAULT_SCROLL_UNTIL_PERCENT = 0.8f;
 
@@ -78,12 +82,10 @@
     private final int mDisplayId;
     private final float mDisplayDensity;
     private AccessibilityNodeInfo mCachedNode;
-
-    // Margins used for gestures (avoids touching too close to the object's edge).
-    private int mMarginLeft = 5;
-    private int mMarginTop = 5;
-    private int mMarginRight = 5;
-    private int mMarginBottom = 5;
+    private Margins mMargins = new PercentMargins(DEFAULT_GESTURE_MARGIN_PERCENT,
+            DEFAULT_GESTURE_MARGIN_PERCENT,
+            DEFAULT_GESTURE_MARGIN_PERCENT,
+            DEFAULT_GESTURE_MARGIN_PERCENT);
 
     /** Package-private constructor. Used by {@link UiDevice#findObject(BySelector)}. */
     UiObject2(UiDevice device, BySelector selector, AccessibilityNodeInfo cachedNode) {
@@ -134,6 +136,36 @@
 
     // Settings
 
+    /**
+     * Sets the percentage of gestures' margins to avoid touching too close to the edges, e.g.
+     * when scrolling up, phone open quick settings instead if gesture is close to the top.
+     * The percentage is based on the object's visible size, e.g. to set 20% margins:
+     * <pre>mUiObject2.setGestureMarginPercent(0.2f);</pre>
+     *
+     * @Param percent Float between [0, 0.5] for four margins: left, top, right, and bottom.
+     */
+    public void setGestureMarginPercent(@FloatRange(from = 0f, to = 0.5f) float percent) {
+        setGestureMarginPercent(percent, percent, percent, percent);
+    }
+
+    /**
+     * Sets the percentage of gestures' margins to avoid touching too close to the edges, e.g.
+     * when scrolling up, phone open quick settings instead if gesture is close to the top.
+     * The percentage is based on the object's visible size, e.g. to set 20% bottom margin only:
+     * <pre>mUiObject2.setGestureMarginPercent(0f, 0f, 0f, 0.2f);</pre>
+     *
+     * @Param left Float between [0, 1] for left margin
+     * @Param top Float between [0, 1] for top margin
+     * @Param right Float between [0, 1] for right margin
+     * @Param bottom Float between [0, 1] for bottom margin
+     */
+    public void setGestureMarginPercent(@FloatRange(from = 0f, to = 1f) float left,
+            @FloatRange(from = 0f, to = 1f) float top,
+            @FloatRange(from = 0f, to = 1f) float right,
+            @FloatRange(from = 0f, to = 1f) float bottom) {
+        mMargins = new PercentMargins(left, top, right, bottom);
+    }
+
     /** Sets the margins used for gestures in pixels. */
     public void setGestureMargin(int margin) {
         setGestureMargins(margin, margin, margin, margin);
@@ -141,10 +173,7 @@
 
     /** Sets the margins used for gestures in pixels. */
     public void setGestureMargins(int left, int top, int right, int bottom) {
-        mMarginLeft = left;
-        mMarginTop = top;
-        mMarginRight = right;
-        mMarginBottom = bottom;
+        mMargins = new SimpleMargins(left, top, right, bottom);
     }
 
     // Wait functions
@@ -240,11 +269,7 @@
     /** Returns this object's visible bounds with the margins removed. */
     private Rect getVisibleBoundsForGestures() {
         Rect ret = getVisibleBounds();
-        ret.left = ret.left + mMarginLeft;
-        ret.top = ret.top + mMarginTop;
-        ret.right = ret.right - mMarginRight;
-        ret.bottom = ret.bottom - mMarginBottom;
-        return ret;
+        return mMargins.apply(ret);
     }
 
     /** Updates a {@code point} to ensure it is within this object's visible bounds. */
@@ -937,4 +962,44 @@
             return accessibilityWindowInfo.getDisplayId();
         }
     }
+
+    private interface Margins {
+        Rect apply(Rect bounds);
+    }
+
+    private static class SimpleMargins implements Margins {
+        int mLeft, mTop, mRight, mBottom;
+        SimpleMargins(int left, int top, int right, int bottom) {
+            mLeft = left;
+            mTop = top;
+            mRight = right;
+            mBottom = bottom;
+        }
+
+        @Override
+        public Rect apply(Rect bounds) {
+            return new Rect(bounds.left + mLeft,
+                    bounds.top + mTop,
+                    bounds.right - mRight,
+                    bounds.bottom - mBottom);
+        }
+    }
+
+    private static class PercentMargins implements Margins {
+        float mLeft, mTop, mRight, mBottom;
+        PercentMargins(float left, float top, float right, float bottom) {
+            mLeft = left;
+            mTop = top;
+            mRight = right;
+            mBottom = bottom;
+        }
+
+        @Override
+        public Rect apply(Rect bounds) {
+            return new Rect(bounds.left + (int) (bounds.width() * mLeft),
+                    bounds.top + (int) (bounds.height() * mTop),
+                    bounds.right - (int) (bounds.width() * mRight),
+                    bounds.bottom - (int) (bounds.height() * mBottom));
+        }
+    }
 }
diff --git a/testutils/testutils-kmp/src/commonMain/kotlin/androidx/kruth/Subject.kt b/testutils/testutils-kmp/src/commonMain/kotlin/androidx/kruth/Subject.kt
index 87cf1b2..5ef04c5 100644
--- a/testutils/testutils-kmp/src/commonMain/kotlin/androidx/kruth/Subject.kt
+++ b/testutils/testutils-kmp/src/commonMain/kotlin/androidx/kruth/Subject.kt
@@ -163,7 +163,7 @@
         this == null && expected == null -> true
         this == null || expected == null -> false
         this is ByteArray && expected is ByteArray -> contentEquals(expected)
-        this is Array<*> && expected is Array<*> -> contentEquals(expected)
+        this is Array<*> && expected is Array<*> -> contentDeepEquals(expected)
         isIntegralBoxedPrimitive() && expected.isIntegralBoxedPrimitive() -> {
             integralValue() == expected.integralValue()
         }
diff --git a/testutils/testutils-kmp/src/commonTest/kotlin/androidx/kruth/SubjectTest.kt b/testutils/testutils-kmp/src/commonTest/kotlin/androidx/kruth/SubjectTest.kt
index 2f7b4fc..8cb19f9 100644
--- a/testutils/testutils-kmp/src/commonTest/kotlin/androidx/kruth/SubjectTest.kt
+++ b/testutils/testutils-kmp/src/commonTest/kotlin/androidx/kruth/SubjectTest.kt
@@ -176,6 +176,40 @@
     }
 
     @Test
+    fun isEqualToNestedArrays() {
+        fun getArray(): Array<*> =
+            arrayOf(
+                intArrayOf(1, 2, 3),
+                arrayOf(
+                    intArrayOf(1, 2, 3),
+                    arrayOf("a", null, "b"),
+                ),
+                listOf(1, 2, 3),
+                "a",
+            )
+
+        assertThat(getArray()).isEqualTo(getArray())
+    }
+
+    @Test
+    fun isEqualToNestedArraysFailsNotEqual() {
+        fun getArray(arg: Int): Array<*> =
+            arrayOf(
+                intArrayOf(1, 2, 3),
+                arrayOf(
+                    intArrayOf(1, arg, 3),
+                    arrayOf("a", null, "b"),
+                ),
+                listOf(1, 2, 3),
+                "a",
+            )
+
+        assertFailsWith<AssertionError> {
+            assertThat(getArray(arg = 10)).isEqualTo(getArray(arg = 20))
+        }
+    }
+
+    @Test
     fun isNotEqualToWithNulls() {
         val o: Any? = null
         assertThat(o).isNotEqualTo("a")
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/FontPaddingTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/FontPaddingTest.kt
index f7e466f..d84f481 100644
--- a/text/text/src/androidTest/java/androidx/compose/ui/text/android/FontPaddingTest.kt
+++ b/text/text/src/androidTest/java/androidx/compose/ui/text/android/FontPaddingTest.kt
@@ -18,10 +18,8 @@
 
 import android.graphics.Typeface
 import android.text.TextPaint
-import android.os.Build
 import androidx.core.content.res.ResourcesCompat
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.testutils.fonts.R
@@ -181,12 +179,7 @@
     }
 
     @Test
-    @SdkSuppress(maxSdkVersion = 33) // b/262909049: Failing on SDK 34
     fun tallTypefaceTextIsTwiceTheHeightOfLatinTypefaceTextMultiLine() {
-        if (Build.VERSION.SDK_INT == 33 && Build.VERSION.CODENAME != "REL") {
-            return // b/262909049: Do not run this test on pre-release Android U.
-        }
-
         val latinLayout = TextLayout(latinTextMultiLine, typeface = latinTypeface)
         val tallLayout = TextLayout(tallTextMultiLine, typeface = tallTypeface)
 
@@ -212,7 +205,8 @@
             charSequence = text,
             textPaint = textPaint,
             includePadding = includePadding,
-            fallbackLineSpacing = false
+            fallbackLineSpacing = false,
+            width = fontSize * 1.5f
         )
     }
-}
\ No newline at end of file
+}
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/StaticLayoutFactoryTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/StaticLayoutFactoryTest.kt
index f5879547..e67a9d2 100644
--- a/text/text/src/androidTest/java/androidx/compose/ui/text/android/StaticLayoutFactoryTest.kt
+++ b/text/text/src/androidTest/java/androidx/compose/ui/text/android/StaticLayoutFactoryTest.kt
@@ -34,6 +34,7 @@
 import kotlin.math.floor
 
 @RunWith(AndroidJUnit4::class)
+@OptIn(InternalPlatformTextApi::class)
 @SmallTest
 class StaticLayoutFactoryTest {
     private var sampleFont: Typeface? = null
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutIntrinsicWidthTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutIntrinsicWidthTest.kt
index d8cf4f8..009740b 100644
--- a/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutIntrinsicWidthTest.kt
+++ b/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutIntrinsicWidthTest.kt
@@ -22,7 +22,7 @@
 import android.text.TextPaint
 import androidx.compose.ui.text.android.style.LetterSpacingSpanEm
 import androidx.compose.ui.text.android.style.LetterSpacingSpanPx
-import androidx.compose.ui.text.android.style.LineHeightSpan
+import androidx.compose.ui.text.android.style.LineHeightStyleSpan
 import androidx.core.content.res.ResourcesCompat
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -60,7 +60,7 @@
     @Test
     fun intrinsicWidth_with_letterSpacing_and_lineHeight_createsOneLine() {
         val text = defaultText.apply {
-            setSpan(LineHeightSpan(lineHeight))
+            setLineHeight(lineHeight)
             setSpan(LetterSpacingSpanPx(letterSpacingPx))
         }
 
@@ -71,8 +71,9 @@
     fun intrinsicWidth_with_letterSpacing_and_lineHeight_createsOneLine_multipleSpans() {
         val text = defaultText.apply {
             for (i in 0..8) {
-                setSpan(LineHeightSpan(lineHeight), i, i + 1)
-                setSpan(LetterSpacingSpanPx(letterSpacingPx), i, i + 1)
+                val end = i + 1
+                setLineHeight(lineHeight, i, end)
+                setSpan(LetterSpacingSpanPx(letterSpacingPx), i, end)
             }
         }
 
@@ -82,7 +83,7 @@
     @Test
     fun intrinsicWidth_with_letterSpacingEm_and_lineHeight_createsOneLine() {
         val text = defaultText.apply {
-            setSpan(LineHeightSpan(lineHeight))
+            setLineHeight(lineHeight)
             setSpan(LetterSpacingSpanEm(letterSpacingEm))
         }
 
@@ -92,7 +93,7 @@
     @Test
     fun intrinsicWidth_with_paintLetterSpacing_and_lineHeight_createsOneLine() {
         val text = defaultText.apply {
-            setSpan(LineHeightSpan(lineHeight))
+            setLineHeight(lineHeight)
         }
 
         val paint = defaultPaint.apply {
@@ -114,7 +115,7 @@
     @Test
     fun intrinsicWidth_with_noLetterSpacing_and_withLineHeight_createsOneLine() {
         val text = defaultText.apply {
-            setSpan(LineHeightSpan(lineHeight))
+            setLineHeight(lineHeight)
         }
 
         assertLineCount(text)
@@ -158,9 +159,21 @@
         ).isEqualTo(1)
     }
 
-    fun Spannable.setSpan(span: Any, start: Int = 0, end: Int = length) {
+    private fun Spannable.setSpan(span: Any, start: Int = 0, end: Int = length) {
         this.setSpan(span, start, end, SPAN_INCLUSIVE_INCLUSIVE)
     }
 
-    fun Float.spToPx(): Float = this * fontScale * density
+    private fun Float.spToPx(): Float = this * fontScale * density
+
+    private fun Spannable.setLineHeight(lineHeight: Float, start: Int = 0, end: Int = length) {
+        val span = LineHeightStyleSpan(
+            lineHeight = lineHeight,
+            startIndex = start,
+            endIndex = end,
+            trimFirstLineTop = true,
+            trimLastLineBottom = true,
+            topRatio = -1f /* default: proportional */
+        )
+        setSpan(span, start, end)
+    }
 }
\ No newline at end of file
diff --git a/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutTest.kt b/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutTest.kt
index 2621b1e..4de574f 100644
--- a/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutTest.kt
+++ b/text/text/src/androidTest/java/androidx/compose/ui/text/android/TextLayoutTest.kt
@@ -53,7 +53,7 @@
 
     @Test
     fun constructor_default_values() {
-        val textLayout = TextLayout(charSequence = "", textPaint = TextPaint())
+        val textLayout = TextLayout(charSequence = "", textPaint = TextPaint(), width = 0f)
         val frameworkLayout = textLayout.layout
 
         assertThat(frameworkLayout.width).isEqualTo(0)
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/BoringLayoutConstructor33.java b/text/text/src/main/java/androidx/compose/ui/text/android/BoringLayoutConstructor33.java
deleted file mode 100644
index b1379e8..0000000
--- a/text/text/src/main/java/androidx/compose/ui/text/android/BoringLayoutConstructor33.java
+++ /dev/null
@@ -1,73 +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.android;
-
-import android.text.BoringLayout;
-import android.text.Layout;
-import android.text.TextPaint;
-import android.text.TextUtils;
-
-import androidx.annotation.IntRange;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-
-/**
- * Platform BoringLayout constructor has marked TextUtils.TruncateAt as NonNull even though it is
- * nullable, and have to be nullable.
- *
- * This class has the same signature of the BoringLayout constructor with only difference of
- * ellipsize is marked as Nullable.
- *
- * This was the only way to prevent compilation failure for nullability of TruncateAt ellipsize.
- *
- * See b/225695033
- */
-@RequiresApi(33)
-class BoringLayoutConstructor33 {
-
-    private BoringLayoutConstructor33() {}
-
-    @NonNull
-    public static BoringLayout create(
-            @NonNull CharSequence text,
-            @NonNull TextPaint paint,
-            @IntRange(from = 0) int width,
-            @NonNull Layout.Alignment alignment,
-            float lineSpacingMultiplier,
-            float lineSpacingExtra,
-            @NonNull BoringLayout.Metrics metrics,
-            boolean includePadding,
-            @Nullable TextUtils.TruncateAt ellipsize,
-            @IntRange(from = 0) int ellipsizedWidth,
-            boolean useFallbackLineSpacing
-    ) {
-        return new BoringLayout(
-                text,
-                paint,
-                width,
-                alignment,
-                lineSpacingMultiplier,
-                lineSpacingExtra,
-                metrics,
-                includePadding,
-                ellipsize,
-                ellipsizedWidth,
-                useFallbackLineSpacing
-        );
-    }
-}
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/BoringLayoutFactory.kt b/text/text/src/main/java/androidx/compose/ui/text/android/BoringLayoutFactory.kt
index f9116ac..35b37ea4 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/BoringLayoutFactory.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/BoringLayoutFactory.kt
@@ -15,6 +15,7 @@
  */
 package androidx.compose.ui.text.android
 
+import android.os.Build
 import android.text.BoringLayout
 import android.text.BoringLayout.Metrics
 import android.text.Layout.Alignment
@@ -88,7 +89,7 @@
         require(width >= 0)
         require(ellipsizedWidth >= 0)
 
-        return if (BuildCompat.isAtLeastT()) {
+        return if (Build.VERSION.SDK_INT >= 33) {
             BoringLayoutFactory33.create(
                 text,
                 paint,
@@ -162,7 +163,7 @@
         ellipsize: TruncateAt? = null,
         ellipsizedWidth: Int = width
     ): BoringLayout {
-        return BoringLayoutConstructor33.create(
+        return BoringLayout(
             text,
             paint,
             width,
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/LayoutCompat.kt b/text/text/src/main/java/androidx/compose/ui/text/android/LayoutCompat.kt
index 97d9027a..02496f3 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/LayoutCompat.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/LayoutCompat.kt
@@ -32,7 +32,7 @@
  * @suppress
  */
 @InternalPlatformTextApi
-object LayoutCompat {
+internal object LayoutCompat {
     const val ALIGN_NORMAL = 0
     const val ALIGN_OPPOSITE = 1
     const val ALIGN_CENTER = 2
@@ -170,7 +170,7 @@
  * @suppress
  */
 @InternalPlatformTextApi
-fun Layout.getLineForOffset(@IntRange(from = 0) offset: Int, upstream: Boolean): Int {
+internal fun Layout.getLineForOffset(@IntRange(from = 0) offset: Int, upstream: Boolean): Int {
     if (offset <= 0) return 0
     if (offset >= text.length) return lineCount - 1
     val downstreamLineNo = getLineForOffset(offset)
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/LayoutHelper.kt b/text/text/src/main/java/androidx/compose/ui/text/android/LayoutHelper.kt
index 20f6ff4..e2b2cdc 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/LayoutHelper.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/LayoutHelper.kt
@@ -31,7 +31,7 @@
  * @suppress
  */
 @InternalPlatformTextApi
-class LayoutHelper(val layout: Layout) {
+internal class LayoutHelper(val layout: Layout) {
 
     private val paragraphEnds: List<Int>
 
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/LayoutIntrinsics.kt b/text/text/src/main/java/androidx/compose/ui/text/android/LayoutIntrinsics.kt
index bab2459..c26ca204 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/LayoutIntrinsics.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/LayoutIntrinsics.kt
@@ -32,7 +32,7 @@
  * @suppress
  */
 @InternalPlatformTextApi
-class LayoutIntrinsics(
+internal class LayoutIntrinsics(
     private val charSequence: CharSequence,
     private val textPaint: TextPaint,
     @LayoutCompat.TextDirection private val textDirectionHeuristic: Int
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt b/text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt
index 821635c..c1e5fed 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/StaticLayoutFactory.kt
@@ -39,8 +39,12 @@
 
 private const val TAG = "StaticLayoutFactory"
 
+/**
+* @suppress
+*/
 @OptIn(InternalPlatformTextApi::class)
-internal object StaticLayoutFactory {
+@InternalPlatformTextApi
+object StaticLayoutFactory {
 
     private val delegate: StaticLayoutFactoryImpl = if (Build.VERSION.SDK_INT >= 23) {
         StaticLayoutFactory23()
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/TextLayout.kt b/text/text/src/main/java/androidx/compose/ui/text/android/TextLayout.kt
index cb51b971..0290795 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/TextLayout.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/TextLayout.kt
@@ -112,9 +112,9 @@
  */
 @OptIn(InternalPlatformTextApi::class)
 @InternalPlatformTextApi
-class TextLayout constructor(
+internal class TextLayout constructor(
     charSequence: CharSequence,
-    width: Float = 0.0f,
+    width: Float,
     textPaint: TextPaint,
     @TextLayoutAlignment alignment: Int = DEFAULT_ALIGNMENT,
     ellipsize: TextUtils.TruncateAt? = null,
@@ -137,31 +137,6 @@
         textDirectionHeuristic
     )
 ) {
-    companion object {
-        // This is used for benchmarks in HyphensLineBreakBenchmark.kt
-        @VisibleForTesting
-        fun constructStaticLayout(
-            charSequence: CharSequence,
-            width: Int,
-            textPaint: TextPaint,
-            hyphenationFrequency: Int,
-            lineBreakStyle: Int,
-            breakStrategy: Int,
-            lineBreakWordStyle: Int
-        ): StaticLayout {
-            val layout = StaticLayoutFactory.create(
-                text = charSequence,
-                paint = textPaint,
-                width = width,
-                hyphenationFrequency = hyphenationFrequency,
-                lineBreakStyle = lineBreakStyle,
-                breakStrategy = breakStrategy,
-                lineBreakWordStyle = lineBreakWordStyle
-            )
-            return layout
-        }
-    }
-
     val maxIntrinsicWidth: Float
         get() = layoutIntrinsics.maxIntrinsicWidth
 
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/animation/SegmentBreaker.kt b/text/text/src/main/java/androidx/compose/ui/text/android/animation/SegmentBreaker.kt
index e5a5e76..4d24152 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/animation/SegmentBreaker.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/animation/SegmentBreaker.kt
@@ -43,7 +43,7 @@
  * @suppress
  */
 @InternalPlatformTextApi
-data class Segment(
+internal data class Segment(
     val startOffset: Int,
     val endOffset: Int,
     val left: Int,
@@ -57,7 +57,7 @@
  * @suppress
  */
 @InternalPlatformTextApi
-object SegmentBreaker {
+internal object SegmentBreaker {
     private fun breakInWords(layoutHelper: LayoutHelper): List<Int> {
         val text = layoutHelper.layout.text
         val words = breakWithBreakIterator(text, BreakIterator.getLineInstance(Locale.getDefault()))
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/selection/WordBoundary.kt b/text/text/src/main/java/androidx/compose/ui/text/android/selection/WordBoundary.kt
index 563844b..877adc2 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/selection/WordBoundary.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/selection/WordBoundary.kt
@@ -36,7 +36,7 @@
  * @suppress
  */
 @InternalPlatformTextApi
-class WordBoundary(
+internal class WordBoundary(
     locale: Locale,
     text: CharSequence
 ) {
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/BaselineShiftSpan.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/BaselineShiftSpan.kt
index f4b6c09..676c6d8 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/style/BaselineShiftSpan.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/style/BaselineShiftSpan.kt
@@ -27,7 +27,7 @@
  * @suppress
  */
 @InternalPlatformTextApi
-open class BaselineShiftSpan(val multiplier: Float) : MetricAffectingSpan() {
+internal open class BaselineShiftSpan(val multiplier: Float) : MetricAffectingSpan() {
 
     override fun updateMeasureState(textPaint: TextPaint) {
         textPaint.baselineShift += ceil(textPaint.ascent() * multiplier).toInt()
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/FontFeatureSpan.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/FontFeatureSpan.kt
index 71a0288..0b3cd18 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/style/FontFeatureSpan.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/style/FontFeatureSpan.kt
@@ -26,7 +26,7 @@
  * @suppress
  */
 @InternalPlatformTextApi
-class FontFeatureSpan(val fontFeatureSettings: String) : MetricAffectingSpan() {
+internal class FontFeatureSpan(val fontFeatureSettings: String) : MetricAffectingSpan() {
     override fun updateMeasureState(textPaint: TextPaint) {
         textPaint.fontFeatureSettings = fontFeatureSettings
     }
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/IndentationFixSpan.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/IndentationFixSpan.kt
index 4e38948..e90ec5b 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/style/IndentationFixSpan.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/style/IndentationFixSpan.kt
@@ -38,7 +38,7 @@
  * @suppress
  */
 @InternalPlatformTextApi
-class IndentationFixSpan : LeadingMarginSpan {
+internal class IndentationFixSpan : LeadingMarginSpan {
     override fun getLeadingMargin(firstLine: Boolean): Int {
         return 0
     }
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanEm.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanEm.kt
index aae4ecd..9e149f3 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanEm.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanEm.kt
@@ -25,7 +25,7 @@
  * @suppress
  */
 @InternalPlatformTextApi
-class LetterSpacingSpanEm(val letterSpacing: Float) : MetricAffectingSpan() {
+internal class LetterSpacingSpanEm(val letterSpacing: Float) : MetricAffectingSpan() {
     override fun updateDrawState(textPaint: TextPaint) {
         textPaint.letterSpacing = letterSpacing
     }
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanPx.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanPx.kt
index b6caa39..8c074a2 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanPx.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/style/LetterSpacingSpanPx.kt
@@ -11,7 +11,7 @@
  * @suppress
  */
 @InternalPlatformTextApi
-class LetterSpacingSpanPx(@Px val letterSpacing: Float) : MetricAffectingSpan() {
+internal class LetterSpacingSpanPx(@Px val letterSpacing: Float) : MetricAffectingSpan() {
     private fun TextPaint.updatePaint() {
         // In framework, 1em letterSpacing equals to textSize * textScaleX pixels.
         val emWidth = textSize * textScaleX
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/LineHeightSpan.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/LineHeightSpan.kt
index 9fe46043..9e0c6e7 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/style/LineHeightSpan.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/style/LineHeightSpan.kt
@@ -30,7 +30,7 @@
  * @suppress
  */
 @InternalPlatformTextApi
-class LineHeightSpan(
+internal class LineHeightSpan(
     val lineHeight: Float
 ) : android.text.style.LineHeightSpan {
 
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/LineHeightStyleSpan.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/LineHeightStyleSpan.kt
index d0a755a..9f61a01 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/style/LineHeightStyleSpan.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/style/LineHeightStyleSpan.kt
@@ -44,19 +44,19 @@
  * @suppress
  */
 @InternalPlatformTextApi
-class LineHeightStyleSpan(
+internal class LineHeightStyleSpan(
     val lineHeight: Float,
     private val startIndex: Int,
     private val endIndex: Int,
     private val trimFirstLineTop: Boolean,
     val trimLastLineBottom: Boolean,
-    @FloatRange(from = 0.0, to = 1.0) private val topRatio: Float
+    @FloatRange(from = -1.0, to = 1.0) private val topRatio: Float
 ) : android.text.style.LineHeightSpan {
 
-    private var firstAscent: Int = 0
-    private var ascent: Int = 0
-    private var descent: Int = 0
-    private var lastDescent: Int = 0
+    private var firstAscent: Int = Int.MIN_VALUE
+    private var ascent: Int = Int.MIN_VALUE
+    private var descent: Int = Int.MIN_VALUE
+    private var lastDescent: Int = Int.MIN_VALUE
 
     /** Holds the firstAscent - fontMetricsInt.ascent */
     var firstAscentDiff = 0
@@ -90,7 +90,9 @@
         // if single line and should not apply, return
         if (isFirstLine && isLastLine && trimFirstLineTop && trimLastLineBottom) return
 
-        if (isFirstLine) calculateTargetMetrics(fontMetricsInt)
+        if (firstAscent == Int.MIN_VALUE) {
+            calculateTargetMetrics(fontMetricsInt)
+        }
 
         fontMetricsInt.ascent = if (isFirstLine) firstAscent else ascent
         fontMetricsInt.descent = if (isLastLine) lastDescent else descent
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/PlaceholderSpan.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/PlaceholderSpan.kt
index 5e40ca0..6ea2ff3 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/style/PlaceholderSpan.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/style/PlaceholderSpan.kt
@@ -43,7 +43,7 @@
  * @suppress
  */
 @InternalPlatformTextApi
-class PlaceholderSpan(
+internal class PlaceholderSpan(
     private val width: Float,
     @Unit
     private val widthUnit: Int,
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/ShadowSpan.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/ShadowSpan.kt
index 2f65ef0..c29e527 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/style/ShadowSpan.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/style/ShadowSpan.kt
@@ -25,7 +25,7 @@
  * @suppress
  */
 @InternalPlatformTextApi
-class ShadowSpan(
+internal class ShadowSpan(
     val color: Int,
     val offsetX: Float,
     val offsetY: Float,
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/SkewXSpan.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/SkewXSpan.kt
index e2b3da0..983b4c7 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/style/SkewXSpan.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/style/SkewXSpan.kt
@@ -26,7 +26,7 @@
  * @suppress
  */
 @InternalPlatformTextApi
-open class SkewXSpan(val skewX: Float) : MetricAffectingSpan() {
+internal open class SkewXSpan(val skewX: Float) : MetricAffectingSpan() {
     override fun updateDrawState(textPaint: TextPaint) {
         textPaint.textSkewX = skewX + textPaint.textSkewX
     }
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/TextDecorationSpan.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/TextDecorationSpan.kt
index afa6802..aadc1a1 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/style/TextDecorationSpan.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/style/TextDecorationSpan.kt
@@ -28,7 +28,7 @@
  * @suppress
  */
 @InternalPlatformTextApi
-class TextDecorationSpan(
+internal class TextDecorationSpan(
     val isUnderlineText: Boolean,
     val isStrikethroughText: Boolean
 ) : CharacterStyle() {
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/style/TypefaceSpan.kt b/text/text/src/main/java/androidx/compose/ui/text/android/style/TypefaceSpan.kt
index 5448b81..05002a6 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/style/TypefaceSpan.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/style/TypefaceSpan.kt
@@ -32,7 +32,7 @@
  * @suppress
  */
 @InternalPlatformTextApi
-class TypefaceSpan(val typeface: Typeface) : MetricAffectingSpan() {
+internal class TypefaceSpan(val typeface: Typeface) : MetricAffectingSpan() {
     override fun updateDrawState(ds: TextPaint) {
         updateTypeface(ds)
     }
diff --git a/tv/integration-tests/demos/build.gradle b/tv/integration-tests/demos/build.gradle
deleted file mode 100644
index 8d0133f..0000000
--- a/tv/integration-tests/demos/build.gradle
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import androidx.build.LibraryType
-
-plugins {
-    id("AndroidXPlugin")
-    id("com.android.application")
-    id("AndroidXComposePlugin")
-    id("org.jetbrains.kotlin.android")
-}
-
-dependencies {
-    implementation(libs.kotlinStdlib)
-
-    implementation("androidx.activity:activity-compose:1.7.0-rc01")
-
-    implementation(project(":compose:material3:material3"))
-    implementation(project(":navigation:navigation-runtime"))
-
-    implementation(project(":tv:tv-foundation"))
-    implementation(project(":tv:tv-material"))
-    implementation "androidx.profileinstaller:profileinstaller:1.3.0-alpha03"
-
-    implementation("androidx.appcompat:appcompat:1.6.0-alpha05")
-}
-
-androidx {
-    name = "TV-Compose Test App"
-    type = LibraryType.SAMPLES
-    inceptionYear = "2022"
-    description = "Test application for TV libraries"
-}
-
-android {
-    defaultConfig {
-        minSdkVersion 28
-    }
-
-    buildTypes {
-        release {
-            minifyEnabled true
-            shrinkResources true
-            proguardFiles getDefaultProguardFile('proguard-android.txt')
-            signingConfig signingConfigs.debug
-        }
-    }
-    namespace "androidx.tv.integration.demos"
-}
-
-// Workaround for https://github.com/gradle/gradle/issues/19882
-configurations.all {
-    resolutionStrategy.dependencySubstitution {
-        substitute(module("androidx.lifecycle:lifecycle-common-java8:")).
-                using project(":lifecycle:lifecycle-common-java8")
-        substitute(module("androidx.lifecycle:lifecycle-livedata-core:")).
-                using project(":lifecycle:lifecycle-livedata-core")
-        substitute(module("androidx.lifecycle:lifecycle-runtime:")).
-                using project(":lifecycle:lifecycle-runtime")
-        substitute(module("androidx.lifecycle:lifecycle-viewmodel:")).
-                using project(":lifecycle:lifecycle-viewmodel")
-        substitute(module("androidx.lifecycle:lifecycle-viewmodel-savedstate:")).
-                using project(":lifecycle:lifecycle-viewmodel-savedstate")
-    }
-}
diff --git a/tv/integration-tests/demos/src/main/AndroidManifest.xml b/tv/integration-tests/demos/src/main/AndroidManifest.xml
deleted file mode 100644
index 4c801cb..0000000
--- a/tv/integration-tests/demos/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2022 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools">
-
-    <application
-        android:allowBackup="true"
-        android:icon="@mipmap/ic_launcher"
-        android:label="@string/app_name"
-        android:supportsRtl="true"
-        android:theme="@style/Theme.Androidx">
-        <activity
-            android:name=".MainActivity"
-            android:banner="@drawable/app_icon_your_company"
-            android:exported="true"
-            android:icon="@drawable/app_icon_your_company"
-            android:label="@string/app_name"
-            android:logo="@drawable/app_icon_your_company"
-            android:screenOrientation="landscape">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-
-                <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
-            </intent-filter>
-        </activity>
-
-    </application>
-
-    <uses-feature
-        android:name="android.software.leanback"
-        android:required="true" />
-    <uses-feature
-        android:name="android.hardware.touchscreen"
-        android:required="false" />
-
-    <uses-permission android:name="android.permission.INTERNET" />
-
-</manifest>
\ No newline at end of file
diff --git a/tv/integration-tests/demos/src/main/baseline-prof.txt b/tv/integration-tests/demos/src/main/baseline-prof.txt
deleted file mode 100644
index 23fe742..0000000
--- a/tv/integration-tests/demos/src/main/baseline-prof.txt
+++ /dev/null
@@ -1,7782 +0,0 @@
-HPLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$5$1$1$1;-><init>(Landroidx/compose/ui/layout/Placeable;Landroidx/compose/animation/ContentTransform;)V
-HPLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$5$1$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$5$1$1;-><init>(Landroidx/compose/animation/ContentTransform;)V
-HPLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$5$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$5$1$3;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$5$1$4$1;-><init>(Landroidx/compose/runtime/snapshots/SnapshotStateList;Ljava/lang/Object;Landroidx/compose/animation/AnimatedContentScope;)V
-HPLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$5$1$4;-><init>(Landroidx/compose/animation/AnimatedContentScope;Ljava/lang/Object;Lkotlin/jvm/functions/Function4;ILandroidx/compose/runtime/snapshots/SnapshotStateList;)V
-HPLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$5$1$4;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$5$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$8;-><init>(Landroidx/compose/animation/core/Transition;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Alignment;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function4;II)V
-HPLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$8;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/AnimatedContentKt$SizeTransform$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/AnimatedContentKt;->AnimatedContent(Landroidx/compose/animation/core/Transition;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Alignment;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function4;Landroidx/compose/runtime/Composer;II)V
-HPLandroidx/compose/animation/AnimatedContentKt;->AnimatedContent(Ljava/lang/Object;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Alignment;Lkotlin/jvm/functions/Function4;Landroidx/compose/runtime/Composer;II)V
-HPLandroidx/compose/animation/AnimatedContentKt;->with(Landroidx/compose/animation/EnterTransition;Landroidx/compose/animation/ExitTransition;)Landroidx/compose/animation/ContentTransform;
-HPLandroidx/compose/animation/AnimatedContentMeasurePolicy$measure$3;-><init>([Landroidx/compose/ui/layout/Placeable;Landroidx/compose/animation/AnimatedContentMeasurePolicy;II)V
-HPLandroidx/compose/animation/AnimatedContentMeasurePolicy$measure$3;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/AnimatedContentMeasurePolicy;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Ljava/util/List;J)Landroidx/compose/ui/layout/MeasureResult;
-HPLandroidx/compose/animation/AnimatedContentScope$SizeModifier$measure$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/AnimatedContentScope$SizeModifier$measure$size$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/AnimatedContentScope$SizeModifier$measure$size$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/AnimatedContentScope$SizeModifier;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Landroidx/compose/ui/layout/Measurable;J)Landroidx/compose/ui/layout/MeasureResult;
-HPLandroidx/compose/animation/AnimatedEnterExitMeasurePolicy$measure$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/AnimatedEnterExitMeasurePolicy;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Ljava/util/List;J)Landroidx/compose/ui/layout/MeasureResult;
-HPLandroidx/compose/animation/AnimatedVisibilityKt$AnimatedEnterExitImpl$1$1$1;->invoke()Ljava/lang/Object;
-HPLandroidx/compose/animation/AnimatedVisibilityKt$AnimatedEnterExitImpl$1$1$2;->emit(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HPLandroidx/compose/animation/AnimatedVisibilityKt$AnimatedEnterExitImpl$1$1;-><init>(Landroidx/compose/animation/core/Transition;Landroidx/compose/runtime/MutableState;Lkotlin/coroutines/Continuation;)V
-HPLandroidx/compose/animation/AnimatedVisibilityKt$AnimatedEnterExitImpl$1$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/AnimatedVisibilityKt$AnimatedEnterExitImpl$2;-><init>(Landroidx/compose/animation/core/Transition;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Landroidx/compose/animation/EnterTransition;Landroidx/compose/animation/ExitTransition;Lkotlin/jvm/functions/Function3;I)V
-HPLandroidx/compose/animation/AnimatedVisibilityKt$AnimatedEnterExitImpl$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/AnimatedVisibilityKt$AnimatedVisibility$8;-><init>(Landroidx/compose/animation/core/MutableTransitionState;Landroidx/compose/ui/Modifier;Landroidx/compose/animation/EnterTransition;Landroidx/compose/animation/ExitTransition;Ljava/lang/String;Lkotlin/jvm/functions/Function3;II)V
-HPLandroidx/compose/animation/AnimatedVisibilityKt$AnimatedVisibility$8;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/AnimatedVisibilityKt;->AnimatedEnterExitImpl(Landroidx/compose/animation/core/Transition;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Landroidx/compose/animation/EnterTransition;Landroidx/compose/animation/ExitTransition;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;I)V
-HPLandroidx/compose/animation/AnimatedVisibilityKt;->AnimatedVisibility(Landroidx/compose/animation/core/MutableTransitionState;Landroidx/compose/ui/Modifier;Landroidx/compose/animation/EnterTransition;Landroidx/compose/animation/ExitTransition;Ljava/lang/String;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V
-HPLandroidx/compose/animation/AnimatedVisibilityKt;->AnimatedVisibility(Landroidx/compose/animation/core/Transition;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Landroidx/compose/animation/EnterTransition;Landroidx/compose/animation/ExitTransition;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V
-HPLandroidx/compose/animation/AnimatedVisibilityKt;->targetEnterExit(Landroidx/compose/animation/core/Transition;Lkotlin/jvm/functions/Function1;Ljava/lang/Object;Landroidx/compose/runtime/Composer;)Landroidx/compose/animation/EnterExitState;
-HPLandroidx/compose/animation/AnimatedVisibilityScopeImpl;-><init>(Landroidx/compose/animation/core/Transition;)V
-HPLandroidx/compose/animation/ContentTransform;-><init>(Landroidx/compose/animation/EnterTransition;Landroidx/compose/animation/ExitTransition;)V
-HPLandroidx/compose/animation/EnterExitTransitionKt$createModifier$2$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/EnterExitTransitionKt$createModifier$alpha$2;-><init>(Landroidx/compose/animation/EnterTransition;Landroidx/compose/animation/ExitTransition;)V
-HPLandroidx/compose/animation/EnterExitTransitionKt$createModifier$alpha$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/EnterExitTransitionKt$shrinkExpand$1;-><init>(Landroidx/compose/animation/core/Transition;Landroidx/compose/runtime/MutableState;Landroidx/compose/runtime/MutableState;)V
-HPLandroidx/compose/animation/EnterExitTransitionKt$shrinkExpand$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/EnterExitTransitionKt$slideInHorizontally$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/EnterExitTransitionKt$slideInOut$1;-><init>(Landroidx/compose/animation/core/Transition;Landroidx/compose/runtime/MutableState;Landroidx/compose/runtime/MutableState;)V
-HPLandroidx/compose/animation/EnterExitTransitionKt$slideInOut$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/EnterExitTransitionKt$slideOutHorizontally$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/EnterExitTransitionKt$slideOutHorizontally$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/EnterTransition;->equals(Ljava/lang/Object;)Z
-HPLandroidx/compose/animation/EnterTransitionImpl;->getData$animation_release()Landroidx/compose/animation/TransitionData;
-HPLandroidx/compose/animation/ExitTransition;->equals(Ljava/lang/Object;)Z
-HPLandroidx/compose/animation/ExitTransitionImpl;->getData$animation_release()Landroidx/compose/animation/TransitionData;
-HPLandroidx/compose/animation/SizeTransformImpl;->createAnimationSpec-TemP2vQ(JJ)Landroidx/compose/animation/core/FiniteAnimationSpec;
-HPLandroidx/compose/animation/SlideModifier$measure$1$slideOffset$1;-><init>(Landroidx/compose/animation/SlideModifier;J)V
-HPLandroidx/compose/animation/SlideModifier$measure$1$slideOffset$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/SlideModifier$measure$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/SlideModifier$transitionSpec$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/SlideModifier;-><init>(Landroidx/compose/animation/core/Transition$DeferredAnimation;Landroidx/compose/runtime/State;Landroidx/compose/runtime/State;)V
-HPLandroidx/compose/animation/SlideModifier;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Landroidx/compose/ui/layout/Measurable;J)Landroidx/compose/ui/layout/MeasureResult;
-HPLandroidx/compose/animation/core/AnimationVector2D;-><init>(FF)V
-HPLandroidx/compose/animation/core/AnimationVector2D;->get$animation_core_release(I)F
-HPLandroidx/compose/animation/core/AnimationVector2D;->getSize$animation_core_release()I
-HPLandroidx/compose/animation/core/AnimationVector2D;->set$animation_core_release(FI)V
-HPLandroidx/compose/animation/core/CubicBezierEasing;->transform(F)F
-HPLandroidx/compose/animation/core/FloatTweenSpec;-><init>(IILandroidx/compose/animation/core/Easing;)V
-HPLandroidx/compose/animation/core/FloatTweenSpec;->clampPlayTime(J)J
-HPLandroidx/compose/animation/core/FloatTweenSpec;->getValueFromNanos(JFFF)F
-HPLandroidx/compose/animation/core/FloatTweenSpec;->getVelocityFromNanos(JFFF)F
-HPLandroidx/compose/animation/core/MutableTransitionState;-><init>(Ljava/lang/Object;)V
-HPLandroidx/compose/animation/core/MutableTransitionState;->isIdle()Z
-HPLandroidx/compose/animation/core/Transition$DeferredAnimation$DeferredAnimationData;->getValue()Ljava/lang/Object;
-HPLandroidx/compose/animation/core/Transition$DeferredAnimation$DeferredAnimationData;->updateAnimationStates(Landroidx/compose/animation/core/Transition$Segment;)V
-HPLandroidx/compose/animation/core/Transition$DeferredAnimation;-><init>(Landroidx/compose/animation/core/Transition;Landroidx/compose/animation/core/TwoWayConverterImpl;Ljava/lang/String;)V
-HPLandroidx/compose/animation/core/Transition$DeferredAnimation;->animate(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Landroidx/compose/animation/core/Transition$DeferredAnimation$DeferredAnimationData;
-HPLandroidx/compose/animation/core/Transition$Segment;->isTransitioningTo(Landroidx/compose/animation/EnterExitState;Landroidx/compose/animation/EnterExitState;)Z
-HPLandroidx/compose/animation/core/Transition$SegmentImpl;-><init>(Ljava/lang/Object;Ljava/lang/Object;)V
-HPLandroidx/compose/animation/core/Transition$SegmentImpl;->equals(Ljava/lang/Object;)Z
-HPLandroidx/compose/animation/core/Transition$SegmentImpl;->getInitialState()Ljava/lang/Object;
-HPLandroidx/compose/animation/core/Transition$SegmentImpl;->getTargetState()Ljava/lang/Object;
-HPLandroidx/compose/animation/core/Transition$TransitionAnimationState;-><init>(Landroidx/compose/animation/core/Transition;Ljava/lang/Object;Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/TwoWayConverter;Ljava/lang/String;)V
-HPLandroidx/compose/animation/core/Transition$TransitionAnimationState;->getAnimation()Landroidx/compose/animation/core/TargetBasedAnimation;
-HPLandroidx/compose/animation/core/Transition$TransitionAnimationState;->getAnimationSpec()Landroidx/compose/animation/core/FiniteAnimationSpec;
-HPLandroidx/compose/animation/core/Transition$TransitionAnimationState;->getValue()Ljava/lang/Object;
-HPLandroidx/compose/animation/core/Transition$TransitionAnimationState;->updateAnimation$default(Landroidx/compose/animation/core/Transition$TransitionAnimationState;Ljava/lang/Object;ZI)V
-HPLandroidx/compose/animation/core/Transition$TransitionAnimationState;->updateTargetValue$animation_core_release(Ljava/lang/Object;Landroidx/compose/animation/core/FiniteAnimationSpec;)V
-HPLandroidx/compose/animation/core/Transition$animateTo$1$1$1;-><init>(Landroidx/compose/animation/core/Transition;F)V
-HPLandroidx/compose/animation/core/Transition$animateTo$1$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/core/Transition$animateTo$1$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/core/Transition$animateTo$2;-><init>(Landroidx/compose/animation/core/Transition;Ljava/lang/Object;I)V
-HPLandroidx/compose/animation/core/Transition$updateTarget$2;-><init>(Landroidx/compose/animation/core/Transition;Ljava/lang/Object;I)V
-HPLandroidx/compose/animation/core/Transition;-><init>(Landroidx/compose/animation/core/MutableTransitionState;Ljava/lang/String;)V
-HPLandroidx/compose/animation/core/Transition;->animateTo$animation_core_release(Ljava/lang/Object;Landroidx/compose/runtime/Composer;I)V
-HPLandroidx/compose/animation/core/Transition;->getCurrentState()Ljava/lang/Object;
-HPLandroidx/compose/animation/core/Transition;->getSegment()Landroidx/compose/animation/core/Transition$Segment;
-HPLandroidx/compose/animation/core/Transition;->getTargetState()Ljava/lang/Object;
-HPLandroidx/compose/animation/core/Transition;->isSeeking()Z
-HPLandroidx/compose/animation/core/Transition;->onFrame$animation_core_release(FJ)V
-HPLandroidx/compose/animation/core/Transition;->onTransitionEnd$animation_core_release()V
-HPLandroidx/compose/animation/core/Transition;->updateTarget$animation_core_release(Ljava/lang/Object;Landroidx/compose/runtime/Composer;I)V
-HPLandroidx/compose/animation/core/TransitionKt$createChildTransitionInternal$1$1$invoke$$inlined$onDispose$1;->dispose()V
-HPLandroidx/compose/animation/core/TransitionKt$createChildTransitionInternal$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/core/TransitionKt$createDeferredAnimation$1$invoke$$inlined$onDispose$1;->dispose()V
-HPLandroidx/compose/animation/core/TransitionKt$createTransitionAnimation$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/core/TransitionKt;->createDeferredAnimation(Landroidx/compose/animation/core/Transition;Landroidx/compose/animation/core/TwoWayConverterImpl;Ljava/lang/String;Landroidx/compose/runtime/Composer;I)Landroidx/compose/animation/core/Transition$DeferredAnimation;
-HPLandroidx/compose/animation/core/TransitionKt;->createTransitionAnimation(Landroidx/compose/animation/core/Transition;Ljava/lang/Object;Ljava/lang/Object;Landroidx/compose/animation/core/FiniteAnimationSpec;Landroidx/compose/animation/core/TwoWayConverterImpl;Ljava/lang/String;Landroidx/compose/runtime/Composer;)Landroidx/compose/animation/core/Transition$TransitionAnimationState;
-HPLandroidx/compose/animation/core/TweenSpec;->vectorize(Landroidx/compose/animation/core/TwoWayConverter;)Landroidx/compose/animation/core/VectorizedAnimationSpec;
-HPLandroidx/compose/animation/core/VectorConvertersKt$DpToVector$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/core/VectorConvertersKt$IntOffsetToVector$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/animation/core/VectorizedAnimationSpec;->getEndVelocity(Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;)Landroidx/compose/animation/core/AnimationVector;
-HPLandroidx/compose/animation/core/VectorizedDurationBasedAnimationSpec;->getDurationNanos(Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;)J
-HPLandroidx/compose/animation/core/VectorizedFloatAnimationSpec$1;->get(I)Landroidx/compose/animation/core/FloatAnimationSpec;
-HPLandroidx/compose/animation/core/VectorizedFloatAnimationSpec;-><init>(Landroidx/compose/animation/core/FloatAnimationSpec;)V
-HPLandroidx/compose/animation/core/VectorizedTweenSpec;-><init>(IILandroidx/compose/animation/core/Easing;)V
-HPLandroidx/compose/animation/core/VectorizedTweenSpec;->getValueFromNanos(JLandroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;)Landroidx/compose/animation/core/AnimationVector;
-HPLandroidx/compose/animation/core/VectorizedTweenSpec;->getVelocityFromNanos(JLandroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;)Landroidx/compose/animation/core/AnimationVector;
-HPLandroidx/compose/foundation/BorderKt$drawRoundRectBorder$1;-><init>(ZLandroidx/compose/ui/graphics/Brush;JFFJJLandroidx/compose/ui/graphics/drawscope/Stroke;)V
-HPLandroidx/compose/foundation/BorderKt$drawRoundRectBorder$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/foundation/ClipScrollableContainerKt$VerticalScrollableClipModifier$1;->createOutline-Pq9zytI(JLandroidx/compose/ui/unit/LayoutDirection;Landroidx/compose/ui/unit/Density;)Landroidx/compose/ui/graphics/Outline;
-HPLandroidx/compose/foundation/layout/BoxKt$boxMeasurePolicy$1$measure$5;-><init>([Landroidx/compose/ui/layout/Placeable;Ljava/util/List;Landroidx/compose/ui/layout/MeasureScope;Lkotlin/jvm/internal/Ref$IntRef;Lkotlin/jvm/internal/Ref$IntRef;Landroidx/compose/ui/Alignment;)V
-HPLandroidx/compose/foundation/layout/BoxKt$boxMeasurePolicy$1$measure$5;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/foundation/layout/PaddingValuesModifier$measure$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/foundation/layout/PaddingValuesModifier;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Landroidx/compose/ui/layout/Measurable;J)Landroidx/compose/ui/layout/MeasureResult;
-HPLandroidx/compose/foundation/layout/UnspecifiedConstraintsModifier;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Landroidx/compose/ui/layout/Measurable;J)Landroidx/compose/ui/layout/MeasureResult;
-HPLandroidx/compose/foundation/lazy/layout/LazyLayoutPrefetcher;->onForgotten()V
-HPLandroidx/compose/material/ripple/AndroidRippleIndicationInstance;-><init>(ZFLandroidx/compose/runtime/MutableState;Landroidx/compose/runtime/MutableState;Landroidx/compose/material/ripple/RippleContainer;)V
-HPLandroidx/compose/material/ripple/AndroidRippleIndicationInstance;->drawIndication(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)V
-HPLandroidx/compose/material/ripple/PlatformRipple;->rememberUpdatedRippleInstance-942rkJo(Landroidx/compose/foundation/interaction/InteractionSource;ZFLandroidx/compose/runtime/MutableState;Landroidx/compose/runtime/MutableState;Landroidx/compose/runtime/Composer;)Landroidx/compose/material/ripple/RippleIndicationInstance;
-HPLandroidx/compose/material/ripple/Ripple$rememberUpdatedInstance$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/material/ripple/Ripple;->rememberUpdatedInstance(Landroidx/compose/foundation/interaction/InteractionSource;Landroidx/compose/runtime/Composer;)Landroidx/compose/foundation/IndicationInstance;
-HPLandroidx/compose/material/ripple/RippleIndicationInstance;->drawStateLayer-H2RKhps(Landroidx/compose/ui/graphics/drawscope/DrawScope;FJ)V
-HPLandroidx/compose/material3/ButtonElevation$animateElevation$1$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/material3/ButtonElevation$animateElevation$3;-><init>(Landroidx/compose/animation/core/Animatable;Landroidx/compose/material3/ButtonElevation;FLandroidx/compose/foundation/interaction/Interaction;Lkotlin/coroutines/Continuation;)V
-HPLandroidx/compose/material3/ButtonElevation$animateElevation$3;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/material3/ButtonElevation;->animateElevation(ZLandroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/runtime/Composer;I)Landroidx/compose/animation/core/AnimationState;
-HPLandroidx/compose/material3/ButtonKt$Button$2$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/material3/ButtonKt$Button$2$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/material3/ButtonKt$Button$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/material3/ButtonKt$Button$3;-><init>(Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;ZLandroidx/compose/ui/graphics/Shape;Landroidx/compose/material3/ButtonColors;Landroidx/compose/material3/ButtonElevation;Landroidx/compose/foundation/layout/PaddingValues;Landroidx/compose/foundation/interaction/MutableInteractionSource;Lkotlin/jvm/functions/Function3;II)V
-HPLandroidx/compose/material3/ButtonKt;->Button(Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;ZLandroidx/compose/ui/graphics/Shape;Landroidx/compose/material3/ButtonColors;Landroidx/compose/material3/ButtonElevation;Landroidx/compose/foundation/layout/PaddingValues;Landroidx/compose/foundation/interaction/MutableInteractionSource;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V
-HPLandroidx/compose/material3/ColorSchemeKt;->toColor(ILandroidx/compose/runtime/Composer;)J
-HPLandroidx/compose/material3/MinimumTouchTargetModifier$measure$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/material3/MinimumTouchTargetModifier;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Landroidx/compose/ui/layout/Measurable;J)Landroidx/compose/ui/layout/MeasureResult;
-HPLandroidx/compose/material3/SurfaceKt$Surface$3;-><init>(Landroidx/compose/ui/Modifier;Landroidx/compose/ui/graphics/Shape;JFIFLandroidx/compose/foundation/interaction/MutableInteractionSource;ZLkotlin/jvm/functions/Function0;Landroidx/compose/runtime/internal/ComposableLambdaImpl;)V
-HPLandroidx/compose/material3/SurfaceKt$Surface$3;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/material3/TextKt$Text$2;-><init>(Ljava/lang/String;Landroidx/compose/ui/Modifier;JJLandroidx/compose/ui/text/font/FontStyle;Landroidx/compose/ui/text/font/FontWeight;Landroidx/compose/ui/text/font/FontFamily;JLandroidx/compose/ui/text/style/TextDecoration;Landroidx/compose/ui/text/style/TextAlign;JIZILkotlin/jvm/functions/Function1;Landroidx/compose/ui/text/TextStyle;III)V
-HPLandroidx/compose/material3/TextKt;->ProvideTextStyle(Landroidx/compose/ui/text/TextStyle;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;I)V
-HPLandroidx/compose/material3/TouchTargetKt$minimumTouchTargetSize$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/runtime/AbstractApplier;->clear()V
-HPLandroidx/compose/runtime/ComposerImpl$CompositionContextImpl;->unregisterComposer$runtime_release(Landroidx/compose/runtime/Composer;)V
-HPLandroidx/compose/runtime/ComposerImpl$CompositionContextImpl;->unregisterComposition$runtime_release(Landroidx/compose/runtime/ControlledComposition;)V
-HPLandroidx/compose/runtime/ComposerImpl$endRestartGroup$1$1;-><init>(Landroidx/compose/runtime/RecomposeScopeImpl$end$1$2;Landroidx/compose/runtime/ComposerImpl;)V
-HPLandroidx/compose/runtime/ComposerImpl$endRestartGroup$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/runtime/ComposerImpl$realizeMovement$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/runtime/ComposerImpl$start$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/runtime/ComposerImpl;->dispose$runtime_release()V
-HPLandroidx/compose/runtime/ComposerImpl;->endMovableGroup()V
-HPLandroidx/compose/runtime/ComposerImpl;->startMovableGroup(ILjava/lang/Object;)V
-HPLandroidx/compose/runtime/CompositionImpl;->dispose()V
-HPLandroidx/compose/runtime/JoinedKey;->hashCode()I
-HPLandroidx/compose/runtime/Pending;->nodePositionOf(Landroidx/compose/runtime/KeyInfo;)I
-HPLandroidx/compose/runtime/Pending;->updateNodeCount(II)Z
-HPLandroidx/compose/runtime/RecomposeScopeImpl$end$1$2;-><init>(Landroidx/compose/runtime/RecomposeScopeImpl;ILandroidx/compose/runtime/collection/IdentityArrayIntMap;)V
-HPLandroidx/compose/runtime/RecomposeScopeImpl$end$1$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/runtime/Recomposer;->unregisterComposition$runtime_release(Landroidx/compose/runtime/ControlledComposition;)V
-HPLandroidx/compose/runtime/SlotWriter;->fixParentAnchorsFor(III)V
-HPLandroidx/compose/runtime/saveable/SaveableStateHolderImpl$RegistryHolder$registry$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/runtime/saveable/SaveableStateRegistryImpl$registerProvider$3;->unregister()V
-HPLandroidx/compose/runtime/snapshots/Snapshot$Companion$registerApplyObserver$2;->dispose()V
-HPLandroidx/compose/runtime/snapshots/Snapshot;->getInvalid$runtime_release()Landroidx/compose/runtime/snapshots/SnapshotIdSet;
-HPLandroidx/compose/runtime/snapshots/SnapshotIdSet$iterator$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/runtime/snapshots/SnapshotStateList;->getModification$runtime_release()I
-HPLandroidx/compose/runtime/snapshots/SnapshotStateList;->listIterator()Ljava/util/ListIterator;
-HPLandroidx/compose/runtime/snapshots/StateListIterator;-><init>(Landroidx/compose/runtime/snapshots/SnapshotStateList;I)V
-HPLandroidx/compose/runtime/snapshots/StateListIterator;->hasNext()Z
-HPLandroidx/compose/runtime/snapshots/StateListIterator;->next()Ljava/lang/Object;
-HPLandroidx/compose/runtime/snapshots/StateListIterator;->validateModification()V
-HPLandroidx/compose/ui/focus/FocusRequesterModifierNodeImpl;->onDetach()V
-HPLandroidx/compose/ui/geometry/RoundRectKt;->isSimple(Landroidx/compose/ui/geometry/RoundRect;)Z
-HPLandroidx/compose/ui/graphics/AndroidCanvas;->clipRect-N_I0leg(FFFFI)V
-HPLandroidx/compose/ui/graphics/BlockGraphicsLayerModifier$measure$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/ui/graphics/BlockGraphicsLayerModifier;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Landroidx/compose/ui/layout/Measurable;J)Landroidx/compose/ui/layout/MeasureResult;
-HPLandroidx/compose/ui/graphics/Canvas;->clipRect-mtrdD-E(Landroidx/compose/ui/geometry/Rect;I)V
-HPLandroidx/compose/ui/graphics/ColorKt;->luminance-8_81llA(J)F
-HPLandroidx/compose/ui/graphics/GraphicsLayerModifierKt$graphicsLayer$$inlined$modifierElementOf$1;-><init>(Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
-HPLandroidx/compose/ui/graphics/GraphicsLayerModifierKt;->graphicsLayer(Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;)Landroidx/compose/ui/Modifier;
-HPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScope;->drawRoundRect-ZuiqVtQ(Landroidx/compose/ui/graphics/Brush;JJJFLandroidx/arch/core/executor/TaskExecutor;Landroidx/compose/ui/graphics/ColorFilter;I)V
-HPLandroidx/compose/ui/layout/Placeable$PlacementScope;->placeWithLayer-aW-9-wM$default(Landroidx/compose/ui/layout/Placeable$PlacementScope;Landroidx/compose/ui/layout/Placeable;J)V
-HPLandroidx/compose/ui/layout/SubcomposeLayoutKt$SubcomposeLayout$5$1$invoke$$inlined$onDispose$1;->dispose()V
-HPLandroidx/compose/ui/modifier/ModifierLocalManager$invalidate$1;->invoke()Ljava/lang/Object;
-HPLandroidx/compose/ui/node/LayoutNode;->detach$ui_release()V
-HPLandroidx/compose/ui/node/LayoutNode;->onChildRemoved(Landroidx/compose/ui/node/LayoutNode;)V
-HPLandroidx/compose/ui/node/LayoutNode;->removeAll$ui_release()V
-HPLandroidx/compose/ui/node/LayoutNode;->removeAt$ui_release(II)V
-HPLandroidx/compose/ui/node/NodeCoordinator$Companion$onCommitAffectingLayerParams$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/ui/node/OwnerSnapshotObserver$onCommitAffectingLayoutModifier$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/compose/ui/node/UiApplier;->onClear()V
-HPLandroidx/compose/ui/platform/AndroidComposeView;->getFocusedRect(Landroid/graphics/Rect;)V
-HPLandroidx/compose/ui/platform/AndroidComposeView;->onDetach(Landroidx/compose/ui/node/LayoutNode;)V
-HPLandroidx/compose/ui/unit/Constraints$Companion;->fixedWidth-OenEA2s(I)J
-HPLandroidx/compose/ui/unit/IntOffset;->equals(Ljava/lang/Object;)Z
-HPLandroidx/compose/ui/unit/IntSize;->equals(Ljava/lang/Object;)Z
-HPLandroidx/tv/foundation/lazy/grid/ComposableSingletons$LazyGridItemProviderKt$lambda-1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridDslKt$rememberColumnWidthSums$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridDslKt$rememberRowHeightSums$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridIntervalContent;->getKey()Lkotlin/jvm/functions/Function1;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridIntervalContent;->getType()Lkotlin/jvm/functions/Function1;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderImpl;->Item(ILandroidx/compose/runtime/Composer;I)V
-HPLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderImpl;->getContentType(I)Ljava/lang/Object;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderImpl;->getHasCustomSpans()Z
-HPLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderImpl;->getItemCount()I
-HPLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderImpl;->getKey(I)Ljava/lang/Object;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderImpl;->getKeyToIndexMap()Ljava/util/Map;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderKt$rememberLazyGridItemProvider$1$1;->Item(ILandroidx/compose/runtime/Composer;I)V
-HPLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderKt$rememberLazyGridItemProvider$1$1;->getContentType(I)Ljava/lang/Object;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderKt$rememberLazyGridItemProvider$1$1;->getItemCount()I
-HPLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderKt$rememberLazyGridItemProvider$1$1;->getKey(I)Ljava/lang/Object;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderKt$rememberLazyGridItemProvider$1$1;->getKeyToIndexMap()Ljava/util/Map;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderKt$rememberLazyGridItemProvider$1$1;->getSpanLayoutProvider()Landroidx/tv/foundation/lazy/grid/LazyGridSpanLayoutProvider;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridKt$rememberLazyGridMeasurePolicy$1$1$1;-><init>(Landroidx/tv/foundation/lazy/grid/LazyGridSpanLayoutProvider;Landroidx/tv/foundation/lazy/grid/LazyMeasuredLineProvider;)V
-HPLandroidx/tv/foundation/lazy/grid/LazyGridKt$rememberLazyGridMeasurePolicy$1$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridKt$rememberLazyGridMeasurePolicy$1$1$3;-><init>(Landroidx/compose/foundation/lazy/layout/LazyLayoutMeasureScope;JII)V
-HPLandroidx/tv/foundation/lazy/grid/LazyGridKt$rememberLazyGridMeasurePolicy$1$1$3;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridKt$rememberLazyGridMeasurePolicy$1$1$measuredItemProvider$1;-><init>(Landroidx/compose/foundation/lazy/layout/LazyLayoutMeasureScope;ZZIILandroidx/tv/foundation/lazy/grid/LazyGridItemPlacementAnimator;J)V
-HPLandroidx/tv/foundation/lazy/grid/LazyGridKt$rememberLazyGridMeasurePolicy$1$1$measuredItemProvider$1;->createItem-ejjdvUs(ILjava/lang/Object;IILjava/util/List;)Landroidx/tv/foundation/lazy/grid/LazyMeasuredItem;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridKt$rememberLazyGridMeasurePolicy$1$1$measuredLineProvider$1;-><init>(ZLjava/util/List;Landroidx/compose/foundation/lazy/layout/LazyLayoutMeasureScope;I)V
-HPLandroidx/tv/foundation/lazy/grid/LazyGridKt$rememberLazyGridMeasurePolicy$1$1$measuredLineProvider$1;->createLine-62kVtck(I[Landroidx/tv/foundation/lazy/grid/LazyMeasuredItem;Ljava/util/List;I)Landroidx/tv/foundation/lazy/grid/LazyMeasuredLine;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridKt$rememberLazyGridMeasurePolicy$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridKt;->LazyGrid(Landroidx/compose/ui/Modifier;Landroidx/tv/foundation/lazy/grid/TvLazyGridState;Lkotlin/jvm/functions/Function2;Landroidx/compose/foundation/layout/PaddingValues;ZZZLandroidx/compose/foundation/layout/Arrangement$Vertical;Landroidx/compose/foundation/layout/Arrangement$Horizontal;Landroidx/tv/foundation/PivotOffsets;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;III)V
-HPLandroidx/tv/foundation/lazy/grid/LazyGridMeasureKt$measureLazyGrid$3;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridPositionedItem;-><init>(JILjava/lang/Object;IIJIIZLjava/util/List;Landroidx/tv/foundation/lazy/grid/LazyGridItemPlacementAnimator;JIZ)V
-HPLandroidx/tv/foundation/lazy/grid/LazyGridPositionedItem;->getAnimationSpec(I)Landroidx/compose/animation/core/FiniteAnimationSpec;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridScrollPosition;->update-cXEmwJU(II)V
-HPLandroidx/tv/foundation/lazy/grid/LazyGridSpanLayoutProvider$LineConfiguration;-><init>(ILjava/util/List;)V
-HPLandroidx/tv/foundation/lazy/grid/LazyGridSpanLayoutProvider;->getLineConfiguration(I)Landroidx/tv/foundation/lazy/grid/LazyGridSpanLayoutProvider$LineConfiguration;
-HPLandroidx/tv/foundation/lazy/grid/LazyGridSpanLayoutProvider;->getLineIndexOfItem-lRO02eA(I)I
-HPLandroidx/tv/foundation/lazy/grid/LazyGridSpanLayoutProvider;->getTotalSize()I
-HPLandroidx/tv/foundation/lazy/grid/LazyMeasuredItem;-><init>(ILjava/lang/Object;ZIIZLandroidx/compose/ui/unit/LayoutDirection;IILjava/util/List;Landroidx/tv/foundation/lazy/grid/LazyGridItemPlacementAnimator;J)V
-HPLandroidx/tv/foundation/lazy/grid/LazyMeasuredItem;->position(IIIIII)Landroidx/tv/foundation/lazy/grid/LazyGridPositionedItem;
-HPLandroidx/tv/foundation/lazy/grid/LazyMeasuredItemProvider;-><init>(Landroidx/tv/foundation/lazy/grid/LazyGridItemProvider;Landroidx/compose/foundation/lazy/layout/LazyLayoutMeasureScope;ILandroidx/tv/foundation/lazy/grid/LazyGridKt$rememberLazyGridMeasurePolicy$1$1$measuredItemProvider$1;)V
-HPLandroidx/tv/foundation/lazy/grid/LazyMeasuredItemProvider;->getAndMeasure-ndEz314(IIJ)Landroidx/tv/foundation/lazy/grid/LazyMeasuredItem;
-HPLandroidx/tv/foundation/lazy/grid/LazyMeasuredLine;-><init>(I[Landroidx/tv/foundation/lazy/grid/LazyMeasuredItem;Ljava/util/List;ZILandroidx/compose/ui/unit/LayoutDirection;II)V
-HPLandroidx/tv/foundation/lazy/grid/LazyMeasuredLine;->position(III)Ljava/util/ArrayList;
-HPLandroidx/tv/foundation/lazy/grid/LazyMeasuredLineProvider;-><init>(ZLjava/util/List;IIILandroidx/tv/foundation/lazy/grid/LazyMeasuredItemProvider;Landroidx/tv/foundation/lazy/grid/LazyGridSpanLayoutProvider;Landroidx/tv/foundation/lazy/grid/LazyGridKt$rememberLazyGridMeasurePolicy$1$1$measuredLineProvider$1;)V
-HPLandroidx/tv/foundation/lazy/grid/LazyMeasuredLineProvider;->childConstraints-JhjzzOo$tv_foundation_release(II)J
-HPLandroidx/tv/foundation/lazy/grid/LazyMeasuredLineProvider;->getAndMeasure-MDRrEZU(I)Landroidx/tv/foundation/lazy/grid/LazyMeasuredLine;
-HPLandroidx/tv/foundation/lazy/grid/TvGridCells$Fixed;->calculateCrossAxisCellSizes(Landroidx/compose/ui/unit/Density;II)Ljava/util/ArrayList;
-HPLandroidx/tv/foundation/lazy/grid/TvLazyGridMeasureResult;-><init>(Landroidx/tv/foundation/lazy/grid/LazyMeasuredLine;IZFLandroidx/compose/ui/layout/MeasureResult;Ljava/util/List;ILandroidx/compose/foundation/gestures/Orientation;)V
-HPLandroidx/tv/foundation/lazy/grid/TvLazyGridScope$items$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/foundation/lazy/grid/TvLazyGridState$scrollableState$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/foundation/lazy/grid/TvLazyGridState;-><init>(II)V
-HPLandroidx/tv/foundation/lazy/grid/TvLazyGridState;->getFirstVisibleItemIndex()I
-HPLandroidx/tv/foundation/lazy/grid/TvLazyGridState;->isVertical$tv_foundation_release()Z
-HPLandroidx/tv/foundation/lazy/grid/TvLazyGridState;->scroll(Landroidx/compose/foundation/MutatePriority;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HPLandroidx/tv/foundation/lazy/grid/TvLazyGridState;->updateScrollPositionIfTheFirstItemWasMoved$tv_foundation_release(Landroidx/tv/foundation/lazy/grid/LazyGridItemProvider;)V
-HPLandroidx/tv/foundation/lazy/list/LazyListMeasureKt;->createItemsBeforeList_kVT7Qgw$addItem$4(Lkotlin/jvm/internal/Ref$ObjectRef;Landroidx/tv/foundation/lazy/list/LazyMeasuredItemProvider;I)V
-HPLandroidx/tv/foundation/lazy/list/LazyListPinnableContainerProviderKt$LazyListPinnableContainerProvider$2;-><init>(Landroidx/tv/foundation/lazy/list/TvLazyListState;ILkotlin/jvm/functions/Function2;I)V
-HPLandroidx/tv/material3/BringIntoViewIfChildrenAreFocusedKt$bringIntoViewIfChildrenAreFocused$2$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/material3/BringIntoViewIfChildrenAreFocusedKt$bringIntoViewIfChildrenAreFocused$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/material3/CarouselDefaults;->IndicatorRow-hGBTI10(IILandroidx/compose/ui/Modifier;FLkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V
-HPLandroidx/tv/material3/CarouselItemDefaults$OverlayEnterTransition$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/material3/CarouselItemKt$CarouselItem$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/material3/CarouselItemKt$CarouselItem$3;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/material3/CarouselItemKt$CarouselItem$4$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/material3/CarouselItemKt$CarouselItem$4$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/material3/CarouselItemKt$onAnimationCompletion$2;->invoke()Ljava/lang/Object;
-HPLandroidx/tv/material3/CarouselItemKt;->CarouselItem(Lkotlin/jvm/functions/Function2;Landroidx/compose/ui/Modifier;JLandroidx/compose/animation/EnterTransition;Landroidx/compose/animation/ExitTransition;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V
-HPLandroidx/tv/material3/CarouselItemKt;->access$onAnimationCompletion(Landroidx/compose/animation/core/MutableTransitionState;Landroidx/tv/material3/CarouselItemKt$CarouselItem$1$1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HPLandroidx/tv/material3/CarouselKt$Carousel$5$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/material3/CarouselKt$Carousel$5$2$1;-><init>(Landroidx/compose/animation/AnimatedVisibilityScope;Landroidx/compose/ui/focus/FocusRequester;Landroidx/compose/ui/focus/FocusManager;Landroidx/compose/runtime/MutableState;Landroidx/compose/runtime/MutableState;Lkotlin/coroutines/Continuation;)V
-HPLandroidx/tv/material3/CarouselKt$Carousel$5$2$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/material3/CarouselKt$Carousel$5$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/material3/CarouselKt$Carousel$6;-><init>(ILandroidx/compose/ui/Modifier;Landroidx/tv/material3/CarouselState;JLandroidx/compose/animation/EnterTransition;Landroidx/compose/animation/ExitTransition;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;II)V
-HPLandroidx/tv/material3/CarouselKt$Carousel$6;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/material3/CarouselKt$handleKeyEvents$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HPLandroidx/tv/material3/CarouselKt$onAnimationCompletion$2;->invoke()Ljava/lang/Object;
-HPLandroidx/tv/material3/CarouselKt;->Carousel(ILandroidx/compose/ui/Modifier;Landroidx/tv/material3/CarouselState;JLandroidx/compose/animation/EnterTransition;Landroidx/compose/animation/ExitTransition;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V
-HPLandroidx/tv/material3/CarouselKt;->access$onAnimationCompletion(Landroidx/compose/animation/AnimatedVisibilityScope;Landroidx/tv/material3/CarouselKt$Carousel$5$2$1$1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HPLandroidx/tv/material3/CarouselState;->getActiveSlideIndex()I
-HPLandroidx/tv/material3/ComposableSingletons$CarouselKt$lambda-1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLcom/example/tvcomposebasedtests/MainActivity$jankReportListener$1;->onJankReport(Ljava/lang/String;ILjava/util/ArrayList;)V
-HPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$FeaturedCarouselKt$lambda-2$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$FeaturedCarouselKt$lambda-2$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$FeaturedCarouselKt$lambda-3$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$LazyContainersKt$lambda-1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$LazyContainersKt$lambda-2$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$LazyContainersKt$lambda-4$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLcom/example/tvcomposebasedtests/tvComponents/FeaturedCarouselKt$FeaturedCarousel$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLcom/example/tvcomposebasedtests/tvComponents/FeaturedCarouselKt;->access$OverlayButton(Landroidx/compose/runtime/Composer;I)V
-HPLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$TvLazyRowsAndColumn$1$1$1$1$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$TvLazyRowsAndColumn$1$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLcom/google/gson/Gson;->getAdapter(Lcom/google/gson/reflect/TypeToken;)Lcom/google/gson/TypeAdapter;
-HPLcom/google/gson/JsonArray;-><init>()V
-HPLcom/google/gson/JsonElement;-><init>()V
-HPLcom/google/gson/JsonObject;-><init>()V
-HPLcom/google/gson/JsonPrimitive;-><init>(Ljava/lang/Boolean;)V
-HPLcom/google/gson/JsonPrimitive;-><init>(Ljava/lang/Number;)V
-HPLcom/google/gson/JsonPrimitive;->getAsNumber()Ljava/lang/Number;
-HPLcom/google/gson/TypeAdapter;-><init>()V
-HPLcom/google/gson/internal/$Gson$Types;->canonicalize(Ljava/lang/reflect/Type;)Ljava/lang/reflect/Type;
-HPLcom/google/gson/internal/LinkedTreeMap$EntrySet;-><init>(Lcom/google/gson/internal/LinkedTreeMap;)V
-HPLcom/google/gson/internal/LinkedTreeMap$Node;-><init>(ZLcom/google/gson/internal/LinkedTreeMap$Node;Ljava/lang/Object;Lcom/google/gson/internal/LinkedTreeMap$Node;Lcom/google/gson/internal/LinkedTreeMap$Node;)V
-HPLcom/google/gson/internal/LinkedTreeMap;-><init>(Z)V
-HPLcom/google/gson/internal/LinkedTreeMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLcom/google/gson/internal/LinkedTreeMap;->rebalance(Lcom/google/gson/internal/LinkedTreeMap$Node;Z)V
-HPLcom/google/gson/internal/LinkedTreeMap;->replaceInParent(Lcom/google/gson/internal/LinkedTreeMap$Node;Lcom/google/gson/internal/LinkedTreeMap$Node;)V
-HPLcom/google/gson/internal/LinkedTreeMap;->rotateLeft(Lcom/google/gson/internal/LinkedTreeMap$Node;)V
-HPLcom/google/gson/internal/LinkedTreeMap;->rotateRight(Lcom/google/gson/internal/LinkedTreeMap$Node;)V
-HPLcom/google/gson/internal/bind/CollectionTypeAdapterFactory$Adapter;->write(Lcom/google/gson/stream/JsonWriter;Ljava/lang/Object;)V
-HPLcom/google/gson/internal/bind/JsonTreeWriter;-><init>()V
-HPLcom/google/gson/internal/bind/JsonTreeWriter;->beginArray()V
-HPLcom/google/gson/internal/bind/JsonTreeWriter;->beginObject()V
-HPLcom/google/gson/internal/bind/JsonTreeWriter;->endArray()V
-HPLcom/google/gson/internal/bind/JsonTreeWriter;->endObject()V
-HPLcom/google/gson/internal/bind/JsonTreeWriter;->get()Lcom/google/gson/JsonElement;
-HPLcom/google/gson/internal/bind/JsonTreeWriter;->peek()Lcom/google/gson/JsonElement;
-HPLcom/google/gson/internal/bind/JsonTreeWriter;->value(J)V
-HPLcom/google/gson/internal/bind/JsonTreeWriter;->value(Ljava/lang/Boolean;)V
-HPLcom/google/gson/internal/bind/ReflectiveTypeAdapterFactory$Adapter;->write(Lcom/google/gson/stream/JsonWriter;Ljava/lang/Object;)V
-HPLcom/google/gson/internal/bind/TypeAdapterRuntimeTypeWrapper;-><init>(Lcom/google/gson/Gson;Lcom/google/gson/TypeAdapter;Ljava/lang/reflect/Type;)V
-HPLcom/google/gson/internal/bind/TypeAdapters$11;->write(Lcom/google/gson/stream/JsonWriter;Ljava/lang/Object;)V
-HPLcom/google/gson/internal/bind/TypeAdapters$3;->write(Lcom/google/gson/stream/JsonWriter;Ljava/lang/Object;)V
-HPLcom/google/gson/reflect/TypeToken;->equals(Ljava/lang/Object;)Z
-HPLcom/google/gson/reflect/TypeToken;->hashCode()I
-HPLcom/google/gson/stream/JsonWriter;-><init>(Ljava/io/Writer;)V
-HPLcom/google/gson/stream/JsonWriter;->beginArray()V
-HPLcom/google/gson/stream/JsonWriter;->beginObject()V
-HPLcom/google/gson/stream/JsonWriter;->close(IIC)V
-HPLcom/google/gson/stream/JsonWriter;->name(Ljava/lang/String;)V
-HPLcom/google/gson/stream/JsonWriter;->peek()I
-HPLcom/google/gson/stream/JsonWriter;->string(Ljava/lang/String;)V
-HPLcom/google/gson/stream/JsonWriter;->value(Ljava/lang/Number;)V
-HPLcom/google/gson/stream/JsonWriter;->writeDeferredName()V
-HPLkotlinx/coroutines/CancellableContinuationImpl;->cancelCompletedResult$kotlinx_coroutines_core(Ljava/lang/Object;Ljava/util/concurrent/CancellationException;)V
-HPLkotlinx/coroutines/JobSupport$Finishing;->isActive()Z
-HPLkotlinx/coroutines/flow/AbstractFlow$collect$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HPLkotlinx/coroutines/internal/DispatchedContinuation;->getContext()Lkotlin/coroutines/CoroutineContext;
-HPLkotlinx/coroutines/scheduling/Task;-><init>(JLkotlinx/coroutines/scheduling/TaskContext;)V
-HSPLandroidx/activity/ComponentActivity$$ExternalSyntheticLambda1;-><init>(Landroidx/activity/ComponentActivity;)V
-HSPLandroidx/activity/ComponentActivity$$ExternalSyntheticLambda2;-><init>(Landroidx/activity/ComponentActivity;)V
-HSPLandroidx/activity/ComponentActivity$$ExternalSyntheticLambda2;->onContextAvailable()V
-HSPLandroidx/activity/ComponentActivity$$ExternalSyntheticOutline0;->m(Ljava/lang/String;)Ljava/lang/StringBuilder;
-HSPLandroidx/activity/ComponentActivity$1;-><init>(Landroidx/activity/ComponentActivity;)V
-HSPLandroidx/activity/ComponentActivity$2;-><init>()V
-HSPLandroidx/activity/ComponentActivity$3;-><init>(Landroidx/activity/ComponentActivity;)V
-HSPLandroidx/activity/ComponentActivity$3;->onStateChanged(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle$Event;)V
-HSPLandroidx/activity/ComponentActivity$4;-><init>(Landroidx/activity/ComponentActivity;)V
-HSPLandroidx/activity/ComponentActivity$4;->onStateChanged(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle$Event;)V
-HSPLandroidx/activity/ComponentActivity$5;-><init>(Landroidx/activity/ComponentActivity;)V
-HSPLandroidx/activity/ComponentActivity$5;->onStateChanged(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle$Event;)V
-HSPLandroidx/activity/ComponentActivity;-><init>()V
-HSPLandroidx/activity/ComponentActivity;->getDefaultViewModelCreationExtras()Landroidx/lifecycle/viewmodel/CreationExtras;
-HSPLandroidx/activity/ComponentActivity;->getLifecycle()Landroidx/lifecycle/LifecycleRegistry;
-HSPLandroidx/activity/ComponentActivity;->getSavedStateRegistry()Landroidx/savedstate/SavedStateRegistry;
-HSPLandroidx/activity/ComponentActivity;->getViewModelStore()Landroidx/lifecycle/ViewModelStore;
-HSPLandroidx/activity/ComponentActivity;->initViewTreeOwners()V
-HSPLandroidx/activity/ComponentActivity;->onCreate(Landroid/os/Bundle;)V
-HSPLandroidx/activity/ComponentActivity;->setContentView(Landroid/view/View;Landroid/view/ViewGroup$LayoutParams;)V
-HSPLandroidx/activity/OnBackPressedDispatcher;-><init>(Landroidx/activity/ComponentActivity$1;)V
-HSPLandroidx/activity/compose/ComponentActivityKt;-><clinit>()V
-HSPLandroidx/activity/contextaware/ContextAwareHelper;-><init>()V
-HSPLandroidx/activity/result/ActivityResultRegistry;-><init>()V
-HSPLandroidx/arch/core/executor/ArchTaskExecutor;-><init>()V
-HSPLandroidx/arch/core/executor/ArchTaskExecutor;->getInstance()Landroidx/arch/core/executor/ArchTaskExecutor;
-HSPLandroidx/arch/core/executor/DefaultTaskExecutor$1;-><init>()V
-HSPLandroidx/arch/core/executor/DefaultTaskExecutor;-><init>()V
-HSPLandroidx/arch/core/executor/TaskExecutor;-><init>()V
-HSPLandroidx/arch/core/internal/FastSafeIterableMap;-><init>()V
-HSPLandroidx/arch/core/internal/FastSafeIterableMap;->get(Ljava/lang/Object;)Landroidx/arch/core/internal/SafeIterableMap$Entry;
-HSPLandroidx/arch/core/internal/FastSafeIterableMap;->remove(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/arch/core/internal/SafeIterableMap$AscendingIterator;-><init>(Landroidx/arch/core/internal/SafeIterableMap$Entry;Landroidx/arch/core/internal/SafeIterableMap$Entry;)V
-HSPLandroidx/arch/core/internal/SafeIterableMap$Entry;-><init>(Ljava/lang/Object;Ljava/lang/Object;)V
-HSPLandroidx/arch/core/internal/SafeIterableMap$Entry;->getKey()Ljava/lang/Object;
-HSPLandroidx/arch/core/internal/SafeIterableMap$Entry;->getValue()Ljava/lang/Object;
-HSPLandroidx/arch/core/internal/SafeIterableMap$IteratorWithAdditions;-><init>(Landroidx/arch/core/internal/SafeIterableMap;)V
-HSPLandroidx/arch/core/internal/SafeIterableMap$IteratorWithAdditions;->hasNext()Z
-HSPLandroidx/arch/core/internal/SafeIterableMap$IteratorWithAdditions;->next()Ljava/lang/Object;
-HSPLandroidx/arch/core/internal/SafeIterableMap$IteratorWithAdditions;->supportRemove(Landroidx/arch/core/internal/SafeIterableMap$Entry;)V
-HSPLandroidx/arch/core/internal/SafeIterableMap$ListIterator;-><init>(Landroidx/arch/core/internal/SafeIterableMap$Entry;Landroidx/arch/core/internal/SafeIterableMap$Entry;)V
-HSPLandroidx/arch/core/internal/SafeIterableMap$ListIterator;->hasNext()Z
-HSPLandroidx/arch/core/internal/SafeIterableMap$SupportRemove;-><init>()V
-HSPLandroidx/arch/core/internal/SafeIterableMap;-><init>()V
-HSPLandroidx/arch/core/internal/SafeIterableMap;->get(Ljava/lang/Object;)Landroidx/arch/core/internal/SafeIterableMap$Entry;
-HSPLandroidx/arch/core/internal/SafeIterableMap;->iterator()Ljava/util/Iterator;
-HSPLandroidx/arch/core/internal/SafeIterableMap;->remove(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/collection/ArraySet;-><clinit>()V
-HSPLandroidx/collection/ArraySet;-><init>()V
-HSPLandroidx/collection/ArraySet;->add(Ljava/lang/Object;)Z
-HSPLandroidx/collection/ArraySet;->allocArrays(I)V
-HSPLandroidx/collection/ArraySet;->clear()V
-HSPLandroidx/collection/ArraySet;->freeArrays([I[Ljava/lang/Object;I)V
-HSPLandroidx/collection/ArraySet;->indexOf(ILjava/lang/Object;)I
-HSPLandroidx/collection/ArraySet;->toArray()[Ljava/lang/Object;
-HSPLandroidx/collection/ContainerHelpers;-><clinit>()V
-HSPLandroidx/collection/SimpleArrayMap;-><init>()V
-HSPLandroidx/collection/SparseArrayCompat;-><clinit>()V
-HSPLandroidx/collection/SparseArrayCompat;-><init>()V
-HSPLandroidx/compose/animation/ColorVectorConverterKt$ColorToVector$1$1;-><clinit>()V
-HSPLandroidx/compose/animation/ColorVectorConverterKt$ColorToVector$1$1;-><init>()V
-HSPLandroidx/compose/animation/ColorVectorConverterKt$ColorToVector$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/ColorVectorConverterKt$ColorToVector$1$2;-><init>(Landroidx/compose/ui/graphics/colorspace/ColorSpace;)V
-HSPLandroidx/compose/animation/ColorVectorConverterKt$ColorToVector$1$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/ColorVectorConverterKt;-><clinit>()V
-HSPLandroidx/compose/animation/ColorVectorConverterKt;->access$multiplyColumn(IFFF[F)F
-HSPLandroidx/compose/animation/FlingCalculator;-><init>(FLandroidx/compose/ui/unit/Density;)V
-HSPLandroidx/compose/animation/FlingCalculatorKt;-><clinit>()V
-HSPLandroidx/compose/animation/SingleValueAnimationKt;-><clinit>()V
-HSPLandroidx/compose/animation/SingleValueAnimationKt;->animateColorAsState-euL9pac(JLandroidx/compose/runtime/Composer;)Landroidx/compose/animation/core/AnimationState;
-HSPLandroidx/compose/animation/SplineBasedFloatDecayAnimationSpec;-><init>(Landroidx/compose/ui/unit/Density;)V
-HSPLandroidx/compose/animation/SplineBasedFloatDecayAnimationSpec_androidKt;-><clinit>()V
-HSPLandroidx/compose/animation/core/Animatable$runAnimation$2$1;-><init>(Landroidx/compose/animation/core/Animatable;Landroidx/compose/animation/core/AnimationState;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/Ref$BooleanRef;)V
-HSPLandroidx/compose/animation/core/Animatable$runAnimation$2$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/Animatable$runAnimation$2;-><init>(Landroidx/compose/animation/core/Animatable;Ljava/lang/Object;Landroidx/compose/animation/core/Animation;JLkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/animation/core/Animatable$runAnimation$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/Animatable$runAnimation$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/Animatable;-><init>(Ljava/lang/Object;Landroidx/compose/animation/core/TwoWayConverter;Ljava/lang/Object;Ljava/lang/String;)V
-HSPLandroidx/compose/animation/core/Animatable;->access$clampToBounds(Landroidx/compose/animation/core/Animatable;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/Animatable;->animateTo$default(Landroidx/compose/animation/core/Animatable;Ljava/lang/Object;Landroidx/compose/animation/core/AnimationSpec;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/Animatable;->getValue()Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/AnimateAsStateKt$animateValueAsState$2;-><init>(Lkotlinx/coroutines/channels/Channel;Ljava/lang/Object;)V
-HSPLandroidx/compose/animation/core/AnimateAsStateKt$animateValueAsState$2;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/AnimateAsStateKt$animateValueAsState$3$1;-><init>(Ljava/lang/Object;Landroidx/compose/animation/core/Animatable;Landroidx/compose/runtime/State;Landroidx/compose/runtime/State;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/animation/core/AnimateAsStateKt$animateValueAsState$3$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/animation/core/AnimateAsStateKt$animateValueAsState$3$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/AnimateAsStateKt$animateValueAsState$3;-><init>(Lkotlinx/coroutines/channels/Channel;Landroidx/compose/animation/core/Animatable;Landroidx/compose/runtime/State;Landroidx/compose/runtime/State;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/animation/core/AnimateAsStateKt$animateValueAsState$3;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/animation/core/AnimateAsStateKt$animateValueAsState$3;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/AnimateAsStateKt;-><clinit>()V
-HSPLandroidx/compose/animation/core/AnimateAsStateKt;->animateDpAsState-AjpBEmI(FLandroidx/compose/runtime/Composer;)Landroidx/compose/animation/core/AnimationState;
-HSPLandroidx/compose/animation/core/AnimateAsStateKt;->animateValueAsState(Ljava/lang/Object;Landroidx/compose/animation/core/TwoWayConverter;Landroidx/compose/animation/core/AnimationSpec;Ljava/lang/Float;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)Landroidx/compose/animation/core/AnimationState;
-HSPLandroidx/compose/animation/core/Animation;->isFinishedFromNanos(J)Z
-HSPLandroidx/compose/animation/core/AnimationEndReason$EnumUnboxingSharedUtility;-><clinit>()V
-HSPLandroidx/compose/animation/core/AnimationEndReason$EnumUnboxingSharedUtility;->compareTo(II)I
-HSPLandroidx/compose/animation/core/AnimationEndReason$EnumUnboxingSharedUtility;->ordinal(I)I
-HSPLandroidx/compose/animation/core/AnimationEndReason$EnumUnboxingSharedUtility;->values(I)[I
-HSPLandroidx/compose/animation/core/AnimationResult;-><init>(Landroidx/compose/animation/core/AnimationState;I)V
-HSPLandroidx/compose/animation/core/AnimationScope;-><init>(Ljava/lang/Object;Landroidx/compose/animation/core/TwoWayConverter;Landroidx/compose/animation/core/AnimationVector;JLjava/lang/Object;JLkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/animation/core/AnimationScope;->getValue()Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/AnimationState;-><init>(Landroidx/compose/animation/core/TwoWayConverter;Ljava/lang/Object;Landroidx/compose/animation/core/AnimationVector;I)V
-HSPLandroidx/compose/animation/core/AnimationState;-><init>(Landroidx/compose/animation/core/TwoWayConverter;Ljava/lang/Object;Landroidx/compose/animation/core/AnimationVector;JJZ)V
-HSPLandroidx/compose/animation/core/AnimationState;->getValue()Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/AnimationVector1D;-><init>(F)V
-HSPLandroidx/compose/animation/core/AnimationVector1D;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/animation/core/AnimationVector1D;->get$animation_core_release(I)F
-HSPLandroidx/compose/animation/core/AnimationVector1D;->getSize$animation_core_release()I
-HSPLandroidx/compose/animation/core/AnimationVector1D;->newVector$animation_core_release()Landroidx/compose/animation/core/AnimationVector;
-HSPLandroidx/compose/animation/core/AnimationVector1D;->reset$animation_core_release()V
-HSPLandroidx/compose/animation/core/AnimationVector1D;->set$animation_core_release(FI)V
-HSPLandroidx/compose/animation/core/AnimationVector4D;-><init>(FFFF)V
-HSPLandroidx/compose/animation/core/AnimationVector4D;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/animation/core/AnimationVector4D;->get$animation_core_release(I)F
-HSPLandroidx/compose/animation/core/AnimationVector4D;->getSize$animation_core_release()I
-HSPLandroidx/compose/animation/core/AnimationVector4D;->newVector$animation_core_release()Landroidx/compose/animation/core/AnimationVector;
-HSPLandroidx/compose/animation/core/AnimationVector4D;->reset$animation_core_release()V
-HSPLandroidx/compose/animation/core/AnimationVector4D;->set$animation_core_release(FI)V
-HSPLandroidx/compose/animation/core/AnimationVector;-><init>()V
-HSPLandroidx/compose/animation/core/ComplexDouble;-><init>(DD)V
-HSPLandroidx/compose/animation/core/ComplexDoubleKt;->complexSqrt(D)Landroidx/compose/animation/core/ComplexDouble;
-HSPLandroidx/compose/animation/core/DecayAnimationSpecImpl;-><init>(Landroidx/compose/animation/SplineBasedFloatDecayAnimationSpec;)V
-HSPLandroidx/compose/animation/core/FloatSpringSpec;-><init>(FFF)V
-HSPLandroidx/compose/animation/core/FloatSpringSpec;-><init>(FFI)V
-HSPLandroidx/compose/animation/core/FloatSpringSpec;->getDurationNanos(FFF)J
-HSPLandroidx/compose/animation/core/FloatSpringSpec;->getEndVelocity(FFF)F
-HSPLandroidx/compose/animation/core/FloatSpringSpec;->getValueFromNanos(JFFF)F
-HSPLandroidx/compose/animation/core/FloatSpringSpec;->getVelocityFromNanos(JFFF)F
-HSPLandroidx/compose/animation/core/MutatorMutex$Mutator;-><init>(ILkotlinx/coroutines/Job;)V
-HSPLandroidx/compose/animation/core/MutatorMutex$mutate$2;-><init>(ILandroidx/compose/animation/core/MutatorMutex;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/animation/core/MutatorMutex$mutate$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/animation/core/MutatorMutex$mutate$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/MutatorMutex$mutate$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/MutatorMutex;-><init>()V
-HSPLandroidx/compose/animation/core/SpringSimulation;-><init>()V
-HSPLandroidx/compose/animation/core/SpringSimulation;->updateValues-IJZedt4$animation_core_release(FFJ)J
-HSPLandroidx/compose/animation/core/SpringSpec;-><init>(FFLjava/lang/Object;)V
-HSPLandroidx/compose/animation/core/SpringSpec;-><init>(Ljava/lang/Object;I)V
-HSPLandroidx/compose/animation/core/SpringSpec;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/animation/core/SpringSpec;->vectorize(Landroidx/compose/animation/core/TwoWayConverter;)Landroidx/compose/animation/core/VectorizedAnimationSpec;
-HSPLandroidx/compose/animation/core/SpringSpec;->vectorize(Landroidx/compose/animation/core/TwoWayConverter;)Landroidx/compose/animation/core/VectorizedSpringSpec;
-HSPLandroidx/compose/animation/core/SuspendAnimationKt$animate$3;-><init>(Landroidx/compose/foundation/gestures/ScrollExtensionsKt$animateScrollBy$2$1;Landroidx/compose/animation/core/TwoWayConverterImpl;)V
-HSPLandroidx/compose/animation/core/SuspendAnimationKt$animate$3;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/SuspendAnimationKt$animate$4;-><init>(Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/animation/core/SuspendAnimationKt$animate$4;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/SuspendAnimationKt$animate$6$1;-><init>(Landroidx/compose/animation/core/AnimationState;)V
-HSPLandroidx/compose/animation/core/SuspendAnimationKt$animate$6;-><init>(Lkotlin/jvm/internal/Ref$ObjectRef;Ljava/lang/Object;Landroidx/compose/animation/core/Animation;Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationState;FLkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/animation/core/SuspendAnimationKt$animate$6;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/SuspendAnimationKt$animate$7;-><init>(Landroidx/compose/animation/core/AnimationState;)V
-HSPLandroidx/compose/animation/core/SuspendAnimationKt$animate$9;-><init>(Lkotlin/jvm/internal/Ref$ObjectRef;FLandroidx/compose/animation/core/Animation;Landroidx/compose/animation/core/AnimationState;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/animation/core/SuspendAnimationKt$animate$9;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/SuspendAnimationKt$callWithFrameNanos$2;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/animation/core/SuspendAnimationKt$callWithFrameNanos$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/SuspendAnimationKt;->animate(Landroidx/compose/animation/core/AnimationState;Landroidx/compose/animation/core/Animation;JLkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/SuspendAnimationKt;->callWithFrameNanos(Landroidx/compose/animation/core/Animation;Lkotlin/jvm/functions/Function1;Landroidx/compose/animation/core/SuspendAnimationKt$animate$4;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/SuspendAnimationKt;->doAnimationFrameWithScale(Landroidx/compose/animation/core/AnimationScope;JFLandroidx/compose/animation/core/Animation;Landroidx/compose/animation/core/AnimationState;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/animation/core/SuspendAnimationKt;->getDurationScale(Lkotlin/coroutines/CoroutineContext;)F
-HSPLandroidx/compose/animation/core/SuspendAnimationKt;->updateState(Landroidx/compose/animation/core/AnimationScope;Landroidx/compose/animation/core/AnimationState;)V
-HSPLandroidx/compose/animation/core/TargetBasedAnimation;-><init>(Landroidx/compose/animation/core/AnimationSpec;Landroidx/compose/animation/core/TwoWayConverter;Ljava/lang/Object;Ljava/lang/Object;Landroidx/compose/animation/core/AnimationVector;)V
-HSPLandroidx/compose/animation/core/TargetBasedAnimation;->getDurationNanos()J
-HSPLandroidx/compose/animation/core/TargetBasedAnimation;->getTargetValue()Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/TargetBasedAnimation;->getTypeConverter()Landroidx/compose/animation/core/TwoWayConverter;
-HSPLandroidx/compose/animation/core/TargetBasedAnimation;->getValueFromNanos(J)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/TargetBasedAnimation;->getVelocityVectorFromNanos(J)Landroidx/compose/animation/core/AnimationVector;
-HSPLandroidx/compose/animation/core/TargetBasedAnimation;->isInfinite()Z
-HSPLandroidx/compose/animation/core/TwoWayConverterImpl;-><init>(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/animation/core/TwoWayConverterImpl;->getConvertFromVector()Lkotlin/jvm/functions/Function1;
-HSPLandroidx/compose/animation/core/TwoWayConverterImpl;->getConvertToVector()Lkotlin/jvm/functions/Function1;
-HSPLandroidx/compose/animation/core/VectorConvertersKt$DpOffsetToVector$1;-><clinit>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$DpOffsetToVector$1;-><init>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$DpOffsetToVector$2;-><clinit>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$DpOffsetToVector$2;-><init>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$DpToVector$1;-><clinit>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$DpToVector$1;-><init>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$DpToVector$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/VectorConvertersKt$DpToVector$2;-><clinit>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$DpToVector$2;-><init>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$FloatToVector$1;-><clinit>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$FloatToVector$1;-><init>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$FloatToVector$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/VectorConvertersKt$FloatToVector$2;-><clinit>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$FloatToVector$2;-><init>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$FloatToVector$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/animation/core/VectorConvertersKt$IntOffsetToVector$1;-><clinit>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$IntOffsetToVector$1;-><init>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$IntOffsetToVector$2;-><clinit>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$IntOffsetToVector$2;-><init>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$IntSizeToVector$1;-><clinit>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$IntSizeToVector$1;-><init>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$IntSizeToVector$2;-><clinit>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$IntSizeToVector$2;-><init>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$IntToVector$1;-><clinit>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$IntToVector$1;-><init>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$IntToVector$2;-><clinit>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$IntToVector$2;-><init>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$OffsetToVector$1;-><clinit>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$OffsetToVector$1;-><init>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$OffsetToVector$2;-><clinit>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$OffsetToVector$2;-><init>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$RectToVector$1;-><clinit>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$RectToVector$1;-><init>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$RectToVector$2;-><clinit>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$RectToVector$2;-><init>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$SizeToVector$1;-><clinit>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$SizeToVector$1;-><init>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$SizeToVector$2;-><clinit>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt$SizeToVector$2;-><init>()V
-HSPLandroidx/compose/animation/core/VectorConvertersKt;-><clinit>()V
-HSPLandroidx/compose/animation/core/VectorizedAnimationSpecKt$createSpringAnimations$1;-><init>(FFLandroidx/compose/animation/core/AnimationVector;)V
-HSPLandroidx/compose/animation/core/VectorizedAnimationSpecKt$createSpringAnimations$1;->get(I)Landroidx/compose/animation/core/FloatAnimationSpec;
-HSPLandroidx/compose/animation/core/VectorizedAnimationSpecKt$createSpringAnimations$2;-><init>(FF)V
-HSPLandroidx/compose/animation/core/VectorizedAnimationSpecKt$createSpringAnimations$2;->get(I)Landroidx/compose/animation/core/FloatAnimationSpec;
-HSPLandroidx/compose/animation/core/VectorizedFloatAnimationSpec;-><init>(Landroidx/compose/animation/core/Animations;)V
-HSPLandroidx/compose/animation/core/VectorizedFloatAnimationSpec;->getDurationNanos(Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;)J
-HSPLandroidx/compose/animation/core/VectorizedFloatAnimationSpec;->getEndVelocity(Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;)Landroidx/compose/animation/core/AnimationVector;
-HSPLandroidx/compose/animation/core/VectorizedFloatAnimationSpec;->getValueFromNanos(JLandroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;)Landroidx/compose/animation/core/AnimationVector;
-HSPLandroidx/compose/animation/core/VectorizedFloatAnimationSpec;->getVelocityFromNanos(JLandroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;)Landroidx/compose/animation/core/AnimationVector;
-HSPLandroidx/compose/animation/core/VectorizedSpringSpec;-><init>(FFLandroidx/compose/animation/core/AnimationVector;)V
-HSPLandroidx/compose/animation/core/VectorizedSpringSpec;->getDurationNanos(Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;)J
-HSPLandroidx/compose/animation/core/VectorizedSpringSpec;->getEndVelocity(Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;)Landroidx/compose/animation/core/AnimationVector;
-HSPLandroidx/compose/animation/core/VectorizedSpringSpec;->getValueFromNanos(JLandroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;)Landroidx/compose/animation/core/AnimationVector;
-HSPLandroidx/compose/animation/core/VectorizedSpringSpec;->getVelocityFromNanos(JLandroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;Landroidx/compose/animation/core/AnimationVector;)Landroidx/compose/animation/core/AnimationVector;
-HSPLandroidx/compose/animation/core/VectorizedSpringSpec;->isInfinite()V
-HSPLandroidx/compose/animation/core/VisibilityThresholdsKt;-><clinit>()V
-HSPLandroidx/compose/animation/core/VisibilityThresholdsKt;->getVisibilityThreshold()J
-HSPLandroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect$effectModifier$1$1;-><init>(Landroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect$effectModifier$1$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect$effectModifier$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect$effectModifier$1$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect$effectModifier$1;-><init>(Landroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect$effectModifier$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect$effectModifier$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect$effectModifier$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect$onNewSize$1;-><init>(Landroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect;)V
-HSPLandroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect$onNewSize$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect;-><init>(Landroid/content/Context;Landroidx/compose/foundation/OverscrollConfiguration;)V
-HSPLandroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect;->animateToRelease()V
-HSPLandroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect;->getEffectModifier()Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect;->invalidateOverscroll()V
-HSPLandroidx/compose/foundation/AndroidOverscrollKt$StretchOverscrollNonClippingLayer$1$1;-><init>(Landroidx/compose/ui/layout/Placeable;I)V
-HSPLandroidx/compose/foundation/AndroidOverscrollKt$StretchOverscrollNonClippingLayer$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/AndroidOverscrollKt$StretchOverscrollNonClippingLayer$1;-><clinit>()V
-HSPLandroidx/compose/foundation/AndroidOverscrollKt$StretchOverscrollNonClippingLayer$1;-><init>()V
-HSPLandroidx/compose/foundation/AndroidOverscrollKt$StretchOverscrollNonClippingLayer$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/AndroidOverscrollKt$StretchOverscrollNonClippingLayer$2$1;-><init>(Landroidx/compose/ui/layout/Placeable;I)V
-HSPLandroidx/compose/foundation/AndroidOverscrollKt$StretchOverscrollNonClippingLayer$2$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/AndroidOverscrollKt$StretchOverscrollNonClippingLayer$2;-><clinit>()V
-HSPLandroidx/compose/foundation/AndroidOverscrollKt$StretchOverscrollNonClippingLayer$2;-><init>()V
-HSPLandroidx/compose/foundation/AndroidOverscrollKt$StretchOverscrollNonClippingLayer$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/AndroidOverscrollKt;-><clinit>()V
-HSPLandroidx/compose/foundation/Api31Impl$$ExternalSyntheticApiModelOutline1;->m(Landroid/widget/EdgeEffect;)F
-HSPLandroidx/compose/foundation/Api31Impl;-><clinit>()V
-HSPLandroidx/compose/foundation/Api31Impl;-><init>()V
-HSPLandroidx/compose/foundation/Api31Impl;->create(Landroid/content/Context;Landroid/util/AttributeSet;)Landroid/widget/EdgeEffect;
-HSPLandroidx/compose/foundation/Api31Impl;->getDistance(Landroid/widget/EdgeEffect;)F
-HSPLandroidx/compose/foundation/Background;-><init>(Landroidx/compose/ui/graphics/Color;Landroidx/compose/ui/graphics/Shape;)V
-HSPLandroidx/compose/foundation/Background;->draw(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)V
-HSPLandroidx/compose/foundation/Background;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/foundation/BackgroundKt;->background-bw27NRU(Landroidx/compose/ui/Modifier;JLandroidx/compose/ui/graphics/Shape;)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/foundation/BorderKt$border$2$1;-><init>(FLandroidx/compose/ui/graphics/Shape;Landroidx/compose/ui/node/Ref;Landroidx/compose/ui/graphics/Brush;)V
-HSPLandroidx/compose/foundation/BorderKt$border$2$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/BorderKt$border$2;-><init>(FLandroidx/compose/ui/graphics/SolidColor;Landroidx/compose/ui/graphics/Shape;)V
-HSPLandroidx/compose/foundation/BorderKt$border$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/BorderKt$drawRectBorder$1;-><init>(Landroidx/compose/ui/graphics/Brush;JJLandroidx/arch/core/executor/TaskExecutor;)V
-HSPLandroidx/compose/foundation/BorderKt$drawRectBorder$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/ClickableKt$PressedInteractionSourceDisposableEffect$1$invoke$$inlined$onDispose$1;-><init>(Landroidx/compose/runtime/MutableState;Ljava/util/Map;Landroidx/compose/foundation/interaction/MutableInteractionSource;)V
-HSPLandroidx/compose/foundation/ClickableKt$PressedInteractionSourceDisposableEffect$1;-><init>(Landroidx/compose/runtime/MutableState;Ljava/util/Map;Landroidx/compose/foundation/interaction/MutableInteractionSource;)V
-HSPLandroidx/compose/foundation/ClickableKt$PressedInteractionSourceDisposableEffect$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/ClickableKt$clickable$2;-><init>(ZLjava/lang/String;Landroidx/compose/ui/semantics/Role;Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/foundation/ClickableKt$clickable$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/ClickableKt$clickable$4$1$1;-><init>(Landroidx/compose/runtime/MutableState;)V
-HSPLandroidx/compose/foundation/ClickableKt$clickable$4$1$1;->onModifierLocalsUpdated(Landroidx/compose/ui/modifier/ModifierLocalReadScope;)V
-HSPLandroidx/compose/foundation/ClickableKt$clickable$4$delayPressInteraction$1$1;-><init>(Landroidx/compose/runtime/MutableState;Landroidx/compose/foundation/Clickable_androidKt$isComposeRootInScrollableContainer$1;)V
-HSPLandroidx/compose/foundation/ClickableKt$clickable$4$gesture$1$1$1;-><init>(ZLandroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/runtime/MutableState;Landroidx/compose/runtime/State;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/ClickableKt$clickable$4$gesture$1$1$2;-><init>(Landroidx/compose/runtime/State;Z)V
-HSPLandroidx/compose/foundation/ClickableKt$clickable$4$gesture$1$1;-><init>(Landroidx/compose/runtime/MutableState;ZLandroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/runtime/MutableState;Landroidx/compose/runtime/State;Landroidx/compose/runtime/State;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/ClickableKt$clickable$4$gesture$1$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/ClickableKt$clickable$4$gesture$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/ClickableKt$clickable$4$gesture$1$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/ClickableKt$clickable$4;-><init>(Landroidx/compose/foundation/Indication;Landroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/ui/semantics/Role;Ljava/lang/String;Lkotlin/jvm/functions/Function0;Z)V
-HSPLandroidx/compose/foundation/ClickableKt$genericClickableWithoutGesture$clickSemantics$1$1;-><init>(Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/foundation/ClickableKt$genericClickableWithoutGesture$clickSemantics$1;-><init>(Landroidx/compose/ui/semantics/Role;Ljava/lang/String;ZLkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/foundation/ClickableKt$genericClickableWithoutGesture$clickSemantics$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/ClickableKt$genericClickableWithoutGesture$detectPressAndClickFromKey$1;-><init>(ZLjava/util/Map;Landroidx/compose/runtime/MutableState;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function0;Landroidx/compose/foundation/interaction/MutableInteractionSource;)V
-HSPLandroidx/compose/foundation/ClickableKt$genericClickableWithoutGesture$detectPressAndClickFromKey$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/ClickableKt;->PressedInteractionSourceDisposableEffect(Landroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/runtime/MutableState;Ljava/util/Map;Landroidx/compose/runtime/Composer;I)V
-HSPLandroidx/compose/foundation/ClickableKt;->clickable-O2vRcR0(Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/foundation/Indication;ZLjava/lang/String;Landroidx/compose/ui/semantics/Role;Lkotlin/jvm/functions/Function0;)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/foundation/Clickable_androidKt$isComposeRootInScrollableContainer$1;-><init>(Landroid/view/View;)V
-HSPLandroidx/compose/foundation/Clickable_androidKt;-><clinit>()V
-HSPLandroidx/compose/foundation/ClipScrollableContainerKt$HorizontalScrollableClipModifier$1;-><init>()V
-HSPLandroidx/compose/foundation/ClipScrollableContainerKt$HorizontalScrollableClipModifier$1;->createOutline-Pq9zytI(JLandroidx/compose/ui/unit/LayoutDirection;Landroidx/compose/ui/unit/Density;)Landroidx/compose/ui/graphics/Outline;
-HSPLandroidx/compose/foundation/ClipScrollableContainerKt$VerticalScrollableClipModifier$1;-><init>()V
-HSPLandroidx/compose/foundation/ClipScrollableContainerKt;-><clinit>()V
-HSPLandroidx/compose/foundation/ClipScrollableContainerKt;->clipScrollableContainer(Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/gestures/Orientation;)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/foundation/DefaultDebugIndication$DefaultDebugIndicationInstance;-><init>(Landroidx/compose/runtime/MutableState;Landroidx/compose/runtime/MutableState;Landroidx/compose/runtime/MutableState;)V
-HSPLandroidx/compose/foundation/DefaultDebugIndication$DefaultDebugIndicationInstance;->drawIndication(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)V
-HSPLandroidx/compose/foundation/DefaultDebugIndication;-><clinit>()V
-HSPLandroidx/compose/foundation/DefaultDebugIndication;-><init>()V
-HSPLandroidx/compose/foundation/DefaultDebugIndication;->rememberUpdatedInstance(Landroidx/compose/foundation/interaction/InteractionSource;Landroidx/compose/runtime/Composer;)Landroidx/compose/foundation/IndicationInstance;
-HSPLandroidx/compose/foundation/DrawOverscrollModifier;-><init>(Landroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect;)V
-HSPLandroidx/compose/foundation/DrawOverscrollModifier;->draw(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)V
-HSPLandroidx/compose/foundation/DrawOverscrollModifier;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/foundation/EdgeEffectCompat;->create(Landroid/content/Context;)Landroid/widget/EdgeEffect;
-HSPLandroidx/compose/foundation/EdgeEffectCompat;->getDistanceCompat(Landroid/widget/EdgeEffect;)F
-HSPLandroidx/compose/foundation/FocusableKt$focusGroup$1;-><clinit>()V
-HSPLandroidx/compose/foundation/FocusableKt$focusGroup$1;-><init>()V
-HSPLandroidx/compose/foundation/FocusableKt$focusGroup$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$1$1$invoke$$inlined$onDispose$1;-><init>(Landroidx/compose/runtime/MutableState;Landroidx/compose/foundation/interaction/MutableInteractionSource;)V
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$1$1$invoke$$inlined$onDispose$1;->dispose()V
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$1$1;-><init>(Landroidx/compose/runtime/MutableState;Landroidx/compose/foundation/interaction/MutableInteractionSource;)V
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$2$invoke$$inlined$onDispose$1;-><init>()V
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$2$invoke$$inlined$onDispose$1;->dispose()V
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$2;-><init>(ZLkotlinx/coroutines/CoroutineScope;Landroidx/compose/runtime/MutableState;Landroidx/compose/foundation/interaction/MutableInteractionSource;)V
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$3$1$invoke$$inlined$onDispose$1;-><init>(Landroidx/compose/runtime/MutableState;)V
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$3$1$invoke$$inlined$onDispose$1;->dispose()V
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$3$1;-><init>(Landroidx/compose/ui/layout/PinnableContainer;Landroidx/compose/runtime/MutableState;Landroidx/compose/runtime/MutableState;)V
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$3$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$4$1$1;-><init>(Landroidx/compose/runtime/MutableState;Landroidx/compose/ui/focus/FocusRequester;)V
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$4$1;-><init>(Landroidx/compose/runtime/MutableState;Landroidx/compose/ui/focus/FocusRequester;)V
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$4$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$5$1;-><init>(Landroidx/compose/runtime/MutableState;Landroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/foundation/relocation/BringIntoViewRequester;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$5$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$5$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$5$2;-><init>(Landroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/runtime/MutableState;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$5$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$5$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$5;-><init>(Landroidx/compose/ui/layout/PinnableContainer;Lkotlinx/coroutines/CoroutineScope;Landroidx/compose/runtime/MutableState;Landroidx/compose/runtime/MutableState;Landroidx/compose/runtime/MutableState;Landroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/foundation/relocation/BringIntoViewRequester;)V
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2$5;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2;-><init>(Landroidx/compose/foundation/interaction/MutableInteractionSource;Z)V
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2;->invoke$lambda$2(Landroidx/compose/runtime/MutableState;)Z
-HSPLandroidx/compose/foundation/FocusableKt$focusable$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/FocusableKt$focusableInNonTouchMode$2$1;-><init>(Landroidx/compose/ui/input/InputModeManager;)V
-HSPLandroidx/compose/foundation/FocusableKt$focusableInNonTouchMode$2$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/FocusableKt$focusableInNonTouchMode$2;-><init>(Landroidx/compose/foundation/interaction/MutableInteractionSource;Z)V
-HSPLandroidx/compose/foundation/FocusableKt$focusableInNonTouchMode$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/FocusableKt;-><clinit>()V
-HSPLandroidx/compose/foundation/FocusableKt;->focusable(Landroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/ui/Modifier;Z)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/foundation/FocusedBoundsKt$ModifierLocalFocusedBoundsObserver$1;-><clinit>()V
-HSPLandroidx/compose/foundation/FocusedBoundsKt$ModifierLocalFocusedBoundsObserver$1;-><init>()V
-HSPLandroidx/compose/foundation/FocusedBoundsKt$ModifierLocalFocusedBoundsObserver$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/foundation/FocusedBoundsKt$onFocusedBoundsChanged$2;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/foundation/FocusedBoundsKt$onFocusedBoundsChanged$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/FocusedBoundsKt;-><clinit>()V
-HSPLandroidx/compose/foundation/FocusedBoundsModifier;-><init>()V
-HSPLandroidx/compose/foundation/FocusedBoundsModifier;->onGloballyPositioned(Landroidx/compose/ui/node/NodeCoordinator;)V
-HSPLandroidx/compose/foundation/FocusedBoundsModifier;->onModifierLocalsUpdated(Landroidx/compose/ui/modifier/ModifierLocalReadScope;)V
-HSPLandroidx/compose/foundation/FocusedBoundsObserverModifier;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/foundation/FocusedBoundsObserverModifier;->getKey()Landroidx/compose/ui/modifier/ProvidableModifierLocal;
-HSPLandroidx/compose/foundation/FocusedBoundsObserverModifier;->getValue()Ljava/lang/Object;
-HSPLandroidx/compose/foundation/FocusedBoundsObserverModifier;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/FocusedBoundsObserverModifier;->onModifierLocalsUpdated(Landroidx/compose/ui/modifier/ModifierLocalReadScope;)V
-HSPLandroidx/compose/foundation/HoverableKt$hoverable$2$1$1$invoke$$inlined$onDispose$1;-><init>(Landroidx/compose/runtime/MutableState;Landroidx/compose/foundation/interaction/MutableInteractionSource;)V
-HSPLandroidx/compose/foundation/HoverableKt$hoverable$2$1$1;-><init>(Landroidx/compose/runtime/MutableState;Landroidx/compose/foundation/interaction/MutableInteractionSource;)V
-HSPLandroidx/compose/foundation/HoverableKt$hoverable$2$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/HoverableKt$hoverable$2$2$1;-><init>(ZLandroidx/compose/runtime/MutableState;Landroidx/compose/foundation/interaction/MutableInteractionSource;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/HoverableKt$hoverable$2$2$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/HoverableKt$hoverable$2$2$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/HoverableKt$hoverable$2$3$1;-><init>(Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/CoroutineScope;Landroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/runtime/MutableState;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/HoverableKt$hoverable$2$3$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/HoverableKt$hoverable$2$3$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/HoverableKt$hoverable$2$3;-><init>(Lkotlinx/coroutines/CoroutineScope;Landroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/runtime/MutableState;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/HoverableKt$hoverable$2$3;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/HoverableKt$hoverable$2$3;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/HoverableKt$hoverable$2$3;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/HoverableKt$hoverable$2;-><init>(Landroidx/compose/foundation/interaction/MutableInteractionSource;Z)V
-HSPLandroidx/compose/foundation/HoverableKt$hoverable$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/ImageKt$Image$2$measure$1;-><clinit>()V
-HSPLandroidx/compose/foundation/ImageKt$Image$2$measure$1;-><init>()V
-HSPLandroidx/compose/foundation/ImageKt$Image$2$measure$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/ImageKt$Image$2;-><clinit>()V
-HSPLandroidx/compose/foundation/ImageKt$Image$2;-><init>()V
-HSPLandroidx/compose/foundation/ImageKt$Image$2;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Ljava/util/List;J)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/foundation/ImageKt$Image$semantics$1$1;-><init>(Ljava/lang/String;)V
-HSPLandroidx/compose/foundation/ImageKt$Image$semantics$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/ImageKt;->Image(Landroidx/compose/ui/graphics/painter/Painter;Ljava/lang/String;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Alignment;Landroidx/compose/ui/layout/ContentScale;FLandroidx/compose/ui/graphics/ColorFilter;Landroidx/compose/runtime/Composer;II)V
-HSPLandroidx/compose/foundation/IndicationKt$LocalIndication$1;-><clinit>()V
-HSPLandroidx/compose/foundation/IndicationKt$LocalIndication$1;-><init>()V
-HSPLandroidx/compose/foundation/IndicationKt$LocalIndication$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/foundation/IndicationKt$indication$2;-><init>(Landroidx/compose/foundation/Indication;Landroidx/compose/foundation/interaction/MutableInteractionSource;)V
-HSPLandroidx/compose/foundation/IndicationKt$indication$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/IndicationKt;-><clinit>()V
-HSPLandroidx/compose/foundation/IndicationModifier;-><init>(Landroidx/compose/foundation/IndicationInstance;)V
-HSPLandroidx/compose/foundation/IndicationModifier;->draw(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)V
-HSPLandroidx/compose/foundation/MutatePriority;-><clinit>()V
-HSPLandroidx/compose/foundation/MutatePriority;-><init>(ILjava/lang/String;)V
-HSPLandroidx/compose/foundation/MutatorMutex$Mutator;-><init>(Landroidx/compose/foundation/MutatePriority;Lkotlinx/coroutines/Job;)V
-HSPLandroidx/compose/foundation/MutatorMutex$mutateWith$2;-><init>(Landroidx/compose/foundation/MutatePriority;Landroidx/compose/foundation/MutatorMutex;Lkotlin/jvm/functions/Function2;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/MutatorMutex$mutateWith$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/MutatorMutex$mutateWith$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/MutatorMutex$mutateWith$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/MutatorMutex;-><init>()V
-HSPLandroidx/compose/foundation/OverscrollConfiguration;-><init>()V
-HSPLandroidx/compose/foundation/OverscrollConfiguration;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/foundation/OverscrollConfigurationKt$LocalOverscrollConfiguration$1;-><clinit>()V
-HSPLandroidx/compose/foundation/OverscrollConfigurationKt$LocalOverscrollConfiguration$1;-><init>()V
-HSPLandroidx/compose/foundation/OverscrollConfigurationKt$LocalOverscrollConfiguration$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/foundation/OverscrollConfigurationKt;-><clinit>()V
-HSPLandroidx/compose/foundation/ScrollKt$rememberScrollState$1$1;-><init>(I)V
-HSPLandroidx/compose/foundation/ScrollKt$rememberScrollState$1$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/foundation/ScrollKt$scroll$2$semantics$1$1;-><init>(Lkotlinx/coroutines/CoroutineScope;ZLandroidx/compose/foundation/ScrollState;)V
-HSPLandroidx/compose/foundation/ScrollKt$scroll$2$semantics$1$accessibilityScrollState$1;-><init>(Landroidx/compose/foundation/ScrollState;)V
-HSPLandroidx/compose/foundation/ScrollKt$scroll$2$semantics$1$accessibilityScrollState$2;-><init>(Landroidx/compose/foundation/ScrollState;)V
-HSPLandroidx/compose/foundation/ScrollKt$scroll$2$semantics$1;-><init>(ZZZLandroidx/compose/foundation/ScrollState;Lkotlinx/coroutines/CoroutineScope;)V
-HSPLandroidx/compose/foundation/ScrollKt$scroll$2$semantics$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/ScrollKt$scroll$2;-><init>(Landroidx/compose/foundation/ScrollState;Landroidx/compose/foundation/gestures/FlingBehavior;ZZ)V
-HSPLandroidx/compose/foundation/ScrollKt$scroll$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/ScrollState$Companion$Saver$1;-><clinit>()V
-HSPLandroidx/compose/foundation/ScrollState$Companion$Saver$1;-><init>()V
-HSPLandroidx/compose/foundation/ScrollState$Companion$Saver$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/ScrollState$Companion$Saver$2;-><clinit>()V
-HSPLandroidx/compose/foundation/ScrollState$Companion$Saver$2;-><init>()V
-HSPLandroidx/compose/foundation/ScrollState$canScrollBackward$2;-><init>(Landroidx/compose/foundation/ScrollState;)V
-HSPLandroidx/compose/foundation/ScrollState$canScrollForward$2;-><init>(Landroidx/compose/foundation/ScrollState;)V
-HSPLandroidx/compose/foundation/ScrollState$scrollableState$1;-><init>(Landroidx/compose/foundation/ScrollState;)V
-HSPLandroidx/compose/foundation/ScrollState;-><clinit>()V
-HSPLandroidx/compose/foundation/ScrollState;-><init>(I)V
-HSPLandroidx/compose/foundation/ScrollState;->getValue()I
-HSPLandroidx/compose/foundation/ScrollingLayoutModifier$measure$1;-><init>(Landroidx/compose/foundation/ScrollingLayoutModifier;ILandroidx/compose/ui/layout/Placeable;)V
-HSPLandroidx/compose/foundation/ScrollingLayoutModifier$measure$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/ScrollingLayoutModifier;-><init>(Landroidx/compose/foundation/ScrollState;ZZ)V
-HSPLandroidx/compose/foundation/ScrollingLayoutModifier;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/foundation/ScrollingLayoutModifier;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Landroidx/compose/ui/layout/Measurable;J)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/foundation/gestures/AndroidConfig;-><clinit>()V
-HSPLandroidx/compose/foundation/gestures/AndroidConfig;-><init>()V
-HSPLandroidx/compose/foundation/gestures/BringIntoViewRequestPriorityQueue;-><init>()V
-HSPLandroidx/compose/foundation/gestures/ContentInViewModifier$modifier$1;-><init>(Landroidx/compose/foundation/gestures/ContentInViewModifier;)V
-HSPLandroidx/compose/foundation/gestures/ContentInViewModifier$modifier$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/ContentInViewModifier;-><init>(Lkotlinx/coroutines/CoroutineScope;Landroidx/compose/foundation/gestures/Orientation;Landroidx/compose/foundation/gestures/ScrollableState;Z)V
-HSPLandroidx/compose/foundation/gestures/ContentInViewModifier;->bringChildIntoView(Landroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$2$1$1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/ContentInViewModifier;->calculateRectForParent(Landroidx/compose/ui/geometry/Rect;)Landroidx/compose/ui/geometry/Rect;
-HSPLandroidx/compose/foundation/gestures/ContentInViewModifier;->onPlaced(Landroidx/compose/ui/node/NodeCoordinator;)V
-HSPLandroidx/compose/foundation/gestures/ContentInViewModifier;->onRemeasured-ozmzZPI(J)V
-HSPLandroidx/compose/foundation/gestures/ContentInViewModifier;->relocationDistance(FFF)F
-HSPLandroidx/compose/foundation/gestures/ContentInViewModifier;->relocationOffset-BMxPBkI(Landroidx/compose/ui/geometry/Rect;J)J
-HSPLandroidx/compose/foundation/gestures/DefaultFlingBehavior;-><init>(Landroidx/compose/animation/core/DecayAnimationSpec;)V
-HSPLandroidx/compose/foundation/gestures/DefaultScrollableState$scroll$2$1;-><init>(Landroidx/compose/foundation/gestures/DefaultScrollableState;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/DefaultScrollableState$scroll$2$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/gestures/DefaultScrollableState$scroll$2$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/DefaultScrollableState$scroll$2$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/DefaultScrollableState$scroll$2;-><init>(Landroidx/compose/foundation/gestures/DefaultScrollableState;Landroidx/compose/foundation/MutatePriority;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/DefaultScrollableState$scroll$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/gestures/DefaultScrollableState$scroll$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/DefaultScrollableState$scroll$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/DefaultScrollableState$scrollScope$1;-><init>(Landroidx/compose/foundation/gestures/DefaultScrollableState;)V
-HSPLandroidx/compose/foundation/gestures/DefaultScrollableState$scrollScope$1;->scrollBy(F)F
-HSPLandroidx/compose/foundation/gestures/DefaultScrollableState;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/foundation/gestures/DefaultScrollableState;->scroll(Landroidx/compose/foundation/MutatePriority;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/DragLogic;-><init>(Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/MutableState;Landroidx/compose/foundation/interaction/MutableInteractionSource;)V
-HSPLandroidx/compose/foundation/gestures/DraggableKt$awaitDownAndSlop$1;-><init>(Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$6;-><init>(Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$9$1$1$invoke$$inlined$onDispose$1;-><init>(Landroidx/compose/runtime/MutableState;Landroidx/compose/foundation/interaction/MutableInteractionSource;)V
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$9$1$1;-><init>(Landroidx/compose/runtime/MutableState;Landroidx/compose/foundation/interaction/MutableInteractionSource;)V
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$9$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$9$2;-><init>(Lkotlinx/coroutines/channels/Channel;Landroidx/compose/foundation/gestures/DraggableState;Landroidx/compose/runtime/State;Landroidx/compose/foundation/gestures/Orientation;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$9$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$9$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$9$3$1$1;-><init>(Lkotlinx/coroutines/CoroutineScope;Landroidx/compose/runtime/State;Landroidx/compose/runtime/State;Landroidx/compose/foundation/gestures/Orientation;Lkotlinx/coroutines/channels/Channel;ZLkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$9$3$1$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$9$3$1$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$9$3$1;-><init>(Landroidx/compose/ui/input/pointer/PointerInputScope;Landroidx/compose/runtime/State;Landroidx/compose/runtime/State;Landroidx/compose/foundation/gestures/Orientation;Lkotlinx/coroutines/channels/Channel;ZLkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$9$3$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$9$3$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$9$3$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$9$3;-><init>(ZLandroidx/compose/runtime/State;Landroidx/compose/runtime/State;Landroidx/compose/foundation/gestures/Orientation;Lkotlinx/coroutines/channels/Channel;ZLkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$9$3;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$9$3;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$9$3;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$9;-><init>(Landroidx/compose/foundation/gestures/ScrollDraggableState;Landroidx/compose/foundation/gestures/Orientation;Landroidx/compose/foundation/interaction/MutableInteractionSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;ZZ)V
-HSPLandroidx/compose/foundation/gestures/DraggableKt$draggable$9;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/DraggableKt;->access$awaitDownAndSlop(Landroidx/compose/ui/input/pointer/AwaitPointerEventScope;Landroidx/compose/runtime/State;Landroidx/compose/runtime/State;Landroidx/compose/ui/input/pointer/util/VelocityTracker;Landroidx/compose/foundation/gestures/Orientation;Lkotlin/coroutines/Continuation;)Ljava/io/Serializable;
-HSPLandroidx/compose/foundation/gestures/ForEachGestureKt$awaitEachGesture$2;-><init>(Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/ForEachGestureKt$awaitEachGesture$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/gestures/ForEachGestureKt$awaitEachGesture$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/ForEachGestureKt;->awaitEachGesture(Landroidx/compose/ui/input/pointer/PointerInputScope;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/ModifierLocalScrollableContainerProvider;-><clinit>()V
-HSPLandroidx/compose/foundation/gestures/ModifierLocalScrollableContainerProvider;-><init>()V
-HSPLandroidx/compose/foundation/gestures/ModifierLocalScrollableContainerProvider;->getKey()Landroidx/compose/ui/modifier/ProvidableModifierLocal;
-HSPLandroidx/compose/foundation/gestures/ModifierLocalScrollableContainerProvider;->getValue()Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/Orientation;-><clinit>()V
-HSPLandroidx/compose/foundation/gestures/Orientation;-><init>(ILjava/lang/String;)V
-HSPLandroidx/compose/foundation/gestures/PressGestureScopeImpl$reset$1;-><init>(Landroidx/compose/foundation/gestures/PressGestureScopeImpl;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/PressGestureScopeImpl;-><init>(Landroidx/compose/ui/input/pointer/PointerInputScope;)V
-HSPLandroidx/compose/foundation/gestures/PressGestureScopeImpl;->reset(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/ScrollDraggableState;-><init>(Landroidx/compose/runtime/MutableState;)V
-HSPLandroidx/compose/foundation/gestures/ScrollExtensionsKt$animateScrollBy$1;-><init>(Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/ScrollExtensionsKt$animateScrollBy$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/ScrollExtensionsKt$animateScrollBy$2$1;-><init>(Lkotlin/jvm/internal/Ref$FloatRef;Landroidx/compose/foundation/gestures/ScrollScope;)V
-HSPLandroidx/compose/foundation/gestures/ScrollExtensionsKt$animateScrollBy$2$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/ScrollExtensionsKt$animateScrollBy$2;-><init>(FLandroidx/compose/animation/core/AnimationSpec;Lkotlin/jvm/internal/Ref$FloatRef;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/ScrollExtensionsKt$animateScrollBy$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/gestures/ScrollExtensionsKt$animateScrollBy$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/ScrollExtensionsKt$animateScrollBy$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/ScrollExtensionsKt;->animateScrollBy$default(Landroidx/compose/foundation/gestures/ScrollableState;FLkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/ScrollExtensionsKt;->animateScrollBy(Landroidx/compose/foundation/gestures/ScrollableState;FLandroidx/compose/animation/core/AnimationSpec;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/ScrollableKt$DefaultScrollMotionDurationScale$1;-><init>()V
-HSPLandroidx/compose/foundation/gestures/ScrollableKt$ModifierLocalScrollableContainer$1;-><clinit>()V
-HSPLandroidx/compose/foundation/gestures/ScrollableKt$ModifierLocalScrollableContainer$1;-><init>()V
-HSPLandroidx/compose/foundation/gestures/ScrollableKt$NoOpScrollScope$1;-><init>()V
-HSPLandroidx/compose/foundation/gestures/ScrollableKt$awaitScrollEvent$1;-><init>(Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/ScrollableKt$mouseWheelScroll$1$1;-><init>(Landroidx/compose/foundation/gestures/ScrollConfig;Landroidx/compose/runtime/State;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/ScrollableKt$mouseWheelScroll$1$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/gestures/ScrollableKt$mouseWheelScroll$1$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/ScrollableKt$mouseWheelScroll$1;-><init>(Landroidx/compose/foundation/gestures/ScrollConfig;Landroidx/compose/runtime/State;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/ScrollableKt$mouseWheelScroll$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/gestures/ScrollableKt$mouseWheelScroll$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/ScrollableKt$mouseWheelScroll$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/ScrollableKt$pointerScrollable$1;-><clinit>()V
-HSPLandroidx/compose/foundation/gestures/ScrollableKt$pointerScrollable$1;-><init>()V
-HSPLandroidx/compose/foundation/gestures/ScrollableKt$pointerScrollable$2$1;-><init>(Landroidx/compose/runtime/MutableState;)V
-HSPLandroidx/compose/foundation/gestures/ScrollableKt$pointerScrollable$3$1;-><init>(Landroidx/compose/runtime/MutableState;Landroidx/compose/runtime/State;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/ScrollableKt$scrollable$2;-><init>(Landroidx/compose/foundation/OverscrollEffect;Landroidx/compose/foundation/gestures/FlingBehavior;Landroidx/compose/foundation/gestures/Orientation;Landroidx/compose/foundation/ScrollState;Landroidx/compose/foundation/interaction/MutableInteractionSourceImpl;ZZ)V
-HSPLandroidx/compose/foundation/gestures/ScrollableKt$scrollable$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/ScrollableKt$scrollableNestedScrollConnection$1;-><init>(Landroidx/compose/runtime/MutableState;Z)V
-HSPLandroidx/compose/foundation/gestures/ScrollableKt;-><clinit>()V
-HSPLandroidx/compose/foundation/gestures/ScrollableKt;->access$awaitScrollEvent(Landroidx/compose/ui/input/pointer/AwaitPointerEventScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/ScrollingLogic;-><init>(Landroidx/compose/foundation/gestures/Orientation;ZLandroidx/compose/runtime/MutableState;Landroidx/compose/foundation/gestures/ScrollableState;Landroidx/compose/foundation/gestures/FlingBehavior;Landroidx/compose/foundation/OverscrollEffect;)V
-HSPLandroidx/compose/foundation/gestures/TapGestureDetectorKt$NoPressGesture$1;-><init>(Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/TapGestureDetectorKt$awaitFirstDown$2;-><init>(Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/TapGestureDetectorKt$detectTapAndPress$2$1$1;-><init>(Landroidx/compose/foundation/gestures/PressGestureScopeImpl;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/TapGestureDetectorKt$detectTapAndPress$2$1$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/gestures/TapGestureDetectorKt$detectTapAndPress$2$1$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/TapGestureDetectorKt$detectTapAndPress$2$1;-><init>(Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Landroidx/compose/foundation/gestures/PressGestureScopeImpl;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/TapGestureDetectorKt$detectTapAndPress$2$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/gestures/TapGestureDetectorKt$detectTapAndPress$2$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/TapGestureDetectorKt$detectTapAndPress$2$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/TapGestureDetectorKt$detectTapAndPress$2;-><init>(Landroidx/compose/ui/input/pointer/PointerInputScope;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Landroidx/compose/foundation/gestures/PressGestureScopeImpl;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/gestures/TapGestureDetectorKt$detectTapAndPress$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/gestures/TapGestureDetectorKt$detectTapAndPress$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/TapGestureDetectorKt$detectTapAndPress$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/TapGestureDetectorKt;-><clinit>()V
-HSPLandroidx/compose/foundation/gestures/TapGestureDetectorKt;->awaitFirstDown$default(Landroidx/compose/ui/input/pointer/AwaitPointerEventScope;Lkotlin/coroutines/Continuation;I)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/TapGestureDetectorKt;->awaitFirstDown(Landroidx/compose/ui/input/pointer/AwaitPointerEventScope;ZLandroidx/compose/ui/input/pointer/PointerEventPass;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/gestures/UpdatableAnimationState;-><clinit>()V
-HSPLandroidx/compose/foundation/gestures/UpdatableAnimationState;-><init>()V
-HSPLandroidx/compose/foundation/interaction/FocusInteraction$Focus;-><init>()V
-HSPLandroidx/compose/foundation/interaction/FocusInteraction$Unfocus;-><init>(Landroidx/compose/foundation/interaction/FocusInteraction$Focus;)V
-HSPLandroidx/compose/foundation/interaction/FocusInteractionKt$collectIsFocusedAsState$1$1$1;-><init>(Ljava/util/ArrayList;Landroidx/compose/runtime/MutableState;)V
-HSPLandroidx/compose/foundation/interaction/FocusInteractionKt$collectIsFocusedAsState$1$1;-><init>(Landroidx/compose/foundation/interaction/InteractionSource;Landroidx/compose/runtime/MutableState;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/interaction/FocusInteractionKt$collectIsFocusedAsState$1$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/interaction/FocusInteractionKt$collectIsFocusedAsState$1$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/interaction/HoverInteractionKt$collectIsHoveredAsState$1$1$1;-><init>(Ljava/util/ArrayList;Landroidx/compose/runtime/MutableState;)V
-HSPLandroidx/compose/foundation/interaction/HoverInteractionKt$collectIsHoveredAsState$1$1;-><init>(Landroidx/compose/foundation/interaction/InteractionSource;Landroidx/compose/runtime/MutableState;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/interaction/HoverInteractionKt$collectIsHoveredAsState$1$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/interaction/HoverInteractionKt$collectIsHoveredAsState$1$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/interaction/MutableInteractionSourceImpl;-><init>()V
-HSPLandroidx/compose/foundation/interaction/MutableInteractionSourceImpl;->emit(Landroidx/compose/foundation/interaction/Interaction;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/interaction/MutableInteractionSourceImpl;->getInteractions()Lkotlinx/coroutines/flow/SharedFlowImpl;
-HSPLandroidx/compose/foundation/interaction/PressInteractionKt$collectIsPressedAsState$1$1$1;-><init>(Ljava/util/ArrayList;Landroidx/compose/runtime/MutableState;)V
-HSPLandroidx/compose/foundation/interaction/PressInteractionKt$collectIsPressedAsState$1$1;-><init>(Landroidx/compose/foundation/interaction/InteractionSource;Landroidx/compose/runtime/MutableState;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/interaction/PressInteractionKt$collectIsPressedAsState$1$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/interaction/PressInteractionKt$collectIsPressedAsState$1$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/layout/Arrangement$Bottom$1;-><init>()V
-HSPLandroidx/compose/foundation/layout/Arrangement$Center$1;-><init>()V
-HSPLandroidx/compose/foundation/layout/Arrangement$Center$1;->arrange(ILandroidx/compose/ui/unit/Density;Landroidx/compose/ui/unit/LayoutDirection;[I[I)V
-HSPLandroidx/compose/foundation/layout/Arrangement$Center$1;->getSpacing-D9Ej5fM()F
-HSPLandroidx/compose/foundation/layout/Arrangement$End$1;-><init>()V
-HSPLandroidx/compose/foundation/layout/Arrangement$SpaceAround$1;-><init>()V
-HSPLandroidx/compose/foundation/layout/Arrangement$SpaceBetween$1;-><init>()V
-HSPLandroidx/compose/foundation/layout/Arrangement$SpaceEvenly$1;-><init>()V
-HSPLandroidx/compose/foundation/layout/Arrangement$SpacedAligned;-><init>(F)V
-HSPLandroidx/compose/foundation/layout/Arrangement$SpacedAligned;->arrange(ILandroidx/compose/ui/unit/Density;Landroidx/compose/ui/unit/LayoutDirection;[I[I)V
-HSPLandroidx/compose/foundation/layout/Arrangement$SpacedAligned;->arrange(Landroidx/compose/ui/unit/Density;I[I[I)V
-HSPLandroidx/compose/foundation/layout/Arrangement$SpacedAligned;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/foundation/layout/Arrangement$SpacedAligned;->getSpacing-D9Ej5fM()F
-HSPLandroidx/compose/foundation/layout/Arrangement$Start$1;-><init>()V
-HSPLandroidx/compose/foundation/layout/Arrangement$Top$1;-><init>()V
-HSPLandroidx/compose/foundation/layout/Arrangement$Top$1;->arrange(Landroidx/compose/ui/unit/Density;I[I[I)V
-HSPLandroidx/compose/foundation/layout/Arrangement$spacedBy$1;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/Arrangement$spacedBy$1;-><init>()V
-HSPLandroidx/compose/foundation/layout/Arrangement;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/Arrangement;->placeCenter$foundation_layout_release(I[I[IZ)V
-HSPLandroidx/compose/foundation/layout/Arrangement;->placeLeftOrTop$foundation_layout_release([I[IZ)V
-HSPLandroidx/compose/foundation/layout/BoxKt$EmptyBoxMeasurePolicy$1$measure$1;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/BoxKt$EmptyBoxMeasurePolicy$1$measure$1;-><init>()V
-HSPLandroidx/compose/foundation/layout/BoxKt$EmptyBoxMeasurePolicy$1$measure$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/layout/BoxKt$EmptyBoxMeasurePolicy$1;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/BoxKt$EmptyBoxMeasurePolicy$1;-><init>()V
-HSPLandroidx/compose/foundation/layout/BoxKt$EmptyBoxMeasurePolicy$1;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Ljava/util/List;J)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/foundation/layout/BoxKt$boxMeasurePolicy$1$measure$2;-><init>(Landroidx/compose/ui/layout/Placeable;Landroidx/compose/ui/layout/Measurable;Landroidx/compose/ui/layout/MeasureScope;IILandroidx/compose/ui/Alignment;)V
-HSPLandroidx/compose/foundation/layout/BoxKt$boxMeasurePolicy$1$measure$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/layout/BoxKt$boxMeasurePolicy$1;-><init>(Z)V
-HSPLandroidx/compose/foundation/layout/BoxKt$boxMeasurePolicy$1;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Ljava/util/List;J)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/foundation/layout/BoxKt;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/BoxKt;->Box(Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;I)V
-HSPLandroidx/compose/foundation/layout/BoxKt;->access$placeInBox(Landroidx/compose/ui/layout/Placeable$PlacementScope;Landroidx/compose/ui/layout/Placeable;Landroidx/compose/ui/layout/Measurable;Landroidx/compose/ui/unit/LayoutDirection;IILandroidx/compose/ui/Alignment;)V
-HSPLandroidx/compose/foundation/layout/BoxKt;->rememberBoxMeasurePolicy(ZLandroidx/compose/runtime/Composer;)Landroidx/compose/ui/layout/MeasurePolicy;
-HSPLandroidx/compose/foundation/layout/ColumnKt$DefaultColumnMeasurePolicy$1;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/ColumnKt$DefaultColumnMeasurePolicy$1;-><init>()V
-HSPLandroidx/compose/foundation/layout/ColumnKt$DefaultColumnMeasurePolicy$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/io/Serializable;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/layout/ColumnKt$columnMeasurePolicy$1$1;-><init>(Landroidx/compose/foundation/layout/Arrangement$Vertical;)V
-HSPLandroidx/compose/foundation/layout/ColumnKt$columnMeasurePolicy$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/io/Serializable;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/layout/ColumnKt;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/ColumnKt;->columnMeasurePolicy(Landroidx/compose/foundation/layout/Arrangement$Vertical;Landroidx/compose/runtime/Composer;)Landroidx/compose/ui/layout/MeasurePolicy;
-HSPLandroidx/compose/foundation/layout/CrossAxisAlignment$CenterCrossAxisAlignment;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/CrossAxisAlignment$CenterCrossAxisAlignment;-><init>()V
-HSPLandroidx/compose/foundation/layout/CrossAxisAlignment$EndCrossAxisAlignment;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/CrossAxisAlignment$EndCrossAxisAlignment;-><init>()V
-HSPLandroidx/compose/foundation/layout/CrossAxisAlignment$HorizontalCrossAxisAlignment;-><init>(Landroidx/compose/ui/Alignment$Horizontal;)V
-HSPLandroidx/compose/foundation/layout/CrossAxisAlignment$HorizontalCrossAxisAlignment;->align$foundation_layout_release(ILandroidx/compose/ui/unit/LayoutDirection;Landroidx/compose/ui/layout/Placeable;)I
-HSPLandroidx/compose/foundation/layout/CrossAxisAlignment$StartCrossAxisAlignment;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/CrossAxisAlignment$StartCrossAxisAlignment;-><init>()V
-HSPLandroidx/compose/foundation/layout/CrossAxisAlignment$VerticalCrossAxisAlignment;-><init>(Landroidx/compose/ui/BiasAlignment$Vertical;)V
-HSPLandroidx/compose/foundation/layout/CrossAxisAlignment$VerticalCrossAxisAlignment;->align$foundation_layout_release(ILandroidx/compose/ui/unit/LayoutDirection;Landroidx/compose/ui/layout/Placeable;)I
-HSPLandroidx/compose/foundation/layout/CrossAxisAlignment;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/CrossAxisAlignment;-><init>()V
-HSPLandroidx/compose/foundation/layout/FillModifier$measure$1;-><init>(Landroidx/compose/ui/layout/Placeable;)V
-HSPLandroidx/compose/foundation/layout/FillModifier$measure$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/layout/FillModifier;-><init>(IFLkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/foundation/layout/FillModifier;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/foundation/layout/FillModifier;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Landroidx/compose/ui/layout/Measurable;J)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/foundation/layout/HorizontalAlignModifier;-><init>()V
-HSPLandroidx/compose/foundation/layout/HorizontalAlignModifier;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/foundation/layout/HorizontalAlignModifier;->modifyParentData(Landroidx/compose/ui/unit/Density;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/layout/IntrinsicMeasureBlocks$HorizontalMaxHeight$1$1;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/IntrinsicMeasureBlocks$HorizontalMaxHeight$1$1;-><init>()V
-HSPLandroidx/compose/foundation/layout/IntrinsicMeasureBlocks$HorizontalMaxHeight$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/layout/IntrinsicMeasureBlocks$HorizontalMaxHeight$1$2;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/IntrinsicMeasureBlocks$HorizontalMaxHeight$1$2;-><init>()V
-HSPLandroidx/compose/foundation/layout/IntrinsicMeasureBlocks$HorizontalMaxHeight$1$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/layout/IntrinsicMeasureBlocks$HorizontalMaxHeight$1;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/IntrinsicMeasureBlocks$HorizontalMaxHeight$1;-><init>()V
-HSPLandroidx/compose/foundation/layout/IntrinsicMeasureBlocks$HorizontalMaxHeight$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/layout/OffsetModifier$measure$1;-><init>(Landroidx/compose/foundation/layout/OffsetModifier;Landroidx/compose/ui/layout/Placeable;Landroidx/compose/ui/layout/MeasureScope;)V
-HSPLandroidx/compose/foundation/layout/OffsetModifier$measure$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/layout/OffsetModifier;-><init>(FF)V
-HSPLandroidx/compose/foundation/layout/OffsetModifier;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/foundation/layout/OffsetModifier;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Landroidx/compose/ui/layout/Measurable;J)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/foundation/layout/OrientationIndependentConstraints$$ExternalSyntheticOutline0;->m(III)I
-HSPLandroidx/compose/foundation/layout/PaddingKt;->calculateEndPadding(Landroidx/compose/foundation/layout/PaddingValues;Landroidx/compose/ui/unit/LayoutDirection;)F
-HSPLandroidx/compose/foundation/layout/PaddingKt;->calculateStartPadding(Landroidx/compose/foundation/layout/PaddingValues;Landroidx/compose/ui/unit/LayoutDirection;)F
-HSPLandroidx/compose/foundation/layout/PaddingKt;->padding-3ABfNKs(Landroidx/compose/ui/Modifier;F)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/foundation/layout/PaddingModifier$measure$1;-><init>(Landroidx/compose/foundation/layout/PaddingModifier;Landroidx/compose/ui/layout/Placeable;Landroidx/compose/ui/layout/MeasureScope;)V
-HSPLandroidx/compose/foundation/layout/PaddingModifier$measure$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/layout/PaddingModifier;-><init>(FFFF)V
-HSPLandroidx/compose/foundation/layout/PaddingModifier;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/foundation/layout/PaddingModifier;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Landroidx/compose/ui/layout/Measurable;J)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/foundation/layout/PaddingValuesImpl;-><init>(FFFF)V
-HSPLandroidx/compose/foundation/layout/PaddingValuesImpl;->calculateBottomPadding-D9Ej5fM()F
-HSPLandroidx/compose/foundation/layout/PaddingValuesImpl;->calculateLeftPadding-u2uoSUM(Landroidx/compose/ui/unit/LayoutDirection;)F
-HSPLandroidx/compose/foundation/layout/PaddingValuesImpl;->calculateRightPadding-u2uoSUM(Landroidx/compose/ui/unit/LayoutDirection;)F
-HSPLandroidx/compose/foundation/layout/PaddingValuesImpl;->calculateTopPadding-D9Ej5fM()F
-HSPLandroidx/compose/foundation/layout/RowColumnImplKt$rowColumnMeasurePolicy$1$measure$1;-><init>(Landroidx/compose/foundation/layout/RowColumnMeasurementHelper;Landroidx/compose/foundation/layout/RowColumnMeasureHelperResult;Landroidx/compose/ui/layout/MeasureScope;)V
-HSPLandroidx/compose/foundation/layout/RowColumnImplKt$rowColumnMeasurePolicy$1$measure$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/layout/RowColumnImplKt$rowColumnMeasurePolicy$1;-><init>(ILkotlin/jvm/functions/Function5;FLandroidx/compose/foundation/layout/CrossAxisAlignment;)V
-HSPLandroidx/compose/foundation/layout/RowColumnImplKt$rowColumnMeasurePolicy$1;->maxIntrinsicHeight(Landroidx/compose/ui/node/NodeCoordinator;Ljava/util/List;I)I
-HSPLandroidx/compose/foundation/layout/RowColumnImplKt$rowColumnMeasurePolicy$1;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Ljava/util/List;J)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/foundation/layout/RowColumnImplKt;->access$intrinsicSize(Ljava/util/List;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;IIII)I
-HSPLandroidx/compose/foundation/layout/RowColumnImplKt;->getRowColumnParentData(Landroidx/compose/ui/layout/IntrinsicMeasurable;)Landroidx/compose/foundation/layout/RowColumnParentData;
-HSPLandroidx/compose/foundation/layout/RowColumnImplKt;->getWeight(Landroidx/compose/foundation/layout/RowColumnParentData;)F
-HSPLandroidx/compose/foundation/layout/RowColumnImplKt;->rowColumnMeasurePolicy-TDGSqEk(ILkotlin/jvm/functions/Function5;FLandroidx/compose/foundation/layout/CrossAxisAlignment;)Landroidx/compose/foundation/layout/RowColumnImplKt$rowColumnMeasurePolicy$1;
-HSPLandroidx/compose/foundation/layout/RowColumnMeasureHelperResult;-><init>(III[I)V
-HSPLandroidx/compose/foundation/layout/RowColumnMeasurementHelper;-><init>(ILkotlin/jvm/functions/Function5;FILandroidx/compose/foundation/layout/CrossAxisAlignment;Ljava/util/List;[Landroidx/compose/ui/layout/Placeable;)V
-HSPLandroidx/compose/foundation/layout/RowColumnMeasurementHelper;->crossAxisSize(Landroidx/compose/ui/layout/Placeable;)I
-HSPLandroidx/compose/foundation/layout/RowColumnMeasurementHelper;->mainAxisSize(Landroidx/compose/ui/layout/Placeable;)I
-HSPLandroidx/compose/foundation/layout/RowColumnParentData;-><init>(I)V
-HSPLandroidx/compose/foundation/layout/RowKt$DefaultRowMeasurePolicy$1;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/RowKt$DefaultRowMeasurePolicy$1;-><init>()V
-HSPLandroidx/compose/foundation/layout/RowKt$rowMeasurePolicy$1$1;-><init>(Landroidx/compose/foundation/layout/Arrangement$HorizontalOrVertical;)V
-HSPLandroidx/compose/foundation/layout/RowKt$rowMeasurePolicy$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/io/Serializable;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/layout/RowKt;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/RowKt;->rowMeasurePolicy(Landroidx/compose/foundation/layout/Arrangement$HorizontalOrVertical;Landroidx/compose/runtime/Composer;)Landroidx/compose/ui/layout/MeasurePolicy;
-HSPLandroidx/compose/foundation/layout/RowScopeInstance;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/RowScopeInstance;-><init>()V
-HSPLandroidx/compose/foundation/layout/SizeKt$createFillSizeModifier$1;-><init>(F)V
-HSPLandroidx/compose/foundation/layout/SizeKt$createFillWidthModifier$1;-><init>(F)V
-HSPLandroidx/compose/foundation/layout/SizeKt$createWrapContentSizeModifier$1;-><init>(Landroidx/compose/ui/Alignment;)V
-HSPLandroidx/compose/foundation/layout/SizeKt$createWrapContentSizeModifier$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/layout/SizeKt$createWrapContentSizeModifier$2;-><init>(Landroidx/compose/ui/Alignment;Z)V
-HSPLandroidx/compose/foundation/layout/SizeKt;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/SizeKt;->createWrapContentSizeModifier(Landroidx/compose/ui/Alignment;Z)Landroidx/compose/foundation/layout/WrapContentModifier;
-HSPLandroidx/compose/foundation/layout/SizeKt;->fillMaxWidth$default(Landroidx/compose/ui/Modifier;)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/foundation/layout/SizeKt;->height-3ABfNKs(Landroidx/compose/ui/Modifier;F)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/foundation/layout/SizeKt;->width-3ABfNKs(Landroidx/compose/ui/Modifier;F)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/foundation/layout/SizeModifier$measure$1;-><init>(Landroidx/compose/ui/layout/Placeable;)V
-HSPLandroidx/compose/foundation/layout/SizeModifier$measure$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/layout/SizeModifier;-><init>(FFFF)V
-HSPLandroidx/compose/foundation/layout/SizeModifier;-><init>(FFFFI)V
-HSPLandroidx/compose/foundation/layout/SizeModifier;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/foundation/layout/SizeModifier;->getTargetConstraints-OenEA2s(Landroidx/compose/ui/unit/Density;)J
-HSPLandroidx/compose/foundation/layout/SizeModifier;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Landroidx/compose/ui/layout/Measurable;J)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/foundation/layout/SpacerKt;->Spacer(Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;)V
-HSPLandroidx/compose/foundation/layout/SpacerMeasurePolicy$measure$1$1;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/SpacerMeasurePolicy$measure$1$1;-><init>()V
-HSPLandroidx/compose/foundation/layout/SpacerMeasurePolicy$measure$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/layout/SpacerMeasurePolicy;-><clinit>()V
-HSPLandroidx/compose/foundation/layout/SpacerMeasurePolicy;-><init>()V
-HSPLandroidx/compose/foundation/layout/SpacerMeasurePolicy;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Ljava/util/List;J)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/foundation/layout/WrapContentModifier$measure$1;-><init>(Landroidx/compose/foundation/layout/WrapContentModifier;ILandroidx/compose/ui/layout/Placeable;ILandroidx/compose/ui/layout/MeasureScope;)V
-HSPLandroidx/compose/foundation/layout/WrapContentModifier$measure$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/layout/WrapContentModifier;-><init>(IZLkotlin/jvm/functions/Function2;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/foundation/layout/WrapContentModifier;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/foundation/layout/WrapContentModifier;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Landroidx/compose/ui/layout/Measurable;J)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/foundation/lazy/layout/DefaultDelegatingLazyLayoutItemProvider$Item$1;-><init>(Landroidx/compose/foundation/lazy/layout/DefaultDelegatingLazyLayoutItemProvider;II)V
-HSPLandroidx/compose/foundation/lazy/layout/DefaultDelegatingLazyLayoutItemProvider;-><init>(Landroidx/compose/runtime/DerivedSnapshotState;)V
-HSPLandroidx/compose/foundation/lazy/layout/DefaultDelegatingLazyLayoutItemProvider;->Item(ILandroidx/compose/runtime/Composer;I)V
-HSPLandroidx/compose/foundation/lazy/layout/DefaultDelegatingLazyLayoutItemProvider;->getContentType(I)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/DefaultDelegatingLazyLayoutItemProvider;->getItemCount()I
-HSPLandroidx/compose/foundation/lazy/layout/DefaultDelegatingLazyLayoutItemProvider;->getKey(I)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/DefaultDelegatingLazyLayoutItemProvider;->getKeyToIndexMap()Ljava/util/Map;
-HSPLandroidx/compose/foundation/lazy/layout/DefaultLazyKey$Companion$CREATOR$1;-><init>()V
-HSPLandroidx/compose/foundation/lazy/layout/DefaultLazyKey;-><clinit>()V
-HSPLandroidx/compose/foundation/lazy/layout/DefaultLazyKey;-><init>(I)V
-HSPLandroidx/compose/foundation/lazy/layout/DefaultLazyKey;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/foundation/lazy/layout/DefaultLazyKey;->hashCode()I
-HSPLandroidx/compose/foundation/lazy/layout/DefaultLazyLayoutItemsProvider$generateKeyToIndexMap$1$1;-><init>(IILjava/util/HashMap;)V
-HSPLandroidx/compose/foundation/lazy/layout/DefaultLazyLayoutItemsProvider$generateKeyToIndexMap$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/DefaultLazyLayoutItemsProvider;-><init>(Landroidx/compose/foundation/lazy/layout/MutableIntervalList;Landroidx/compose/runtime/internal/ComposableLambdaImpl;Lkotlin/ranges/IntRange;)V
-HSPLandroidx/compose/foundation/lazy/layout/DefaultLazyLayoutItemsProvider;->Item(ILandroidx/compose/runtime/Composer;I)V
-HSPLandroidx/compose/foundation/lazy/layout/DefaultLazyLayoutItemsProvider;->getContentType(I)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/DefaultLazyLayoutItemsProvider;->getItemCount()I
-HSPLandroidx/compose/foundation/lazy/layout/DefaultLazyLayoutItemsProvider;->getKey(I)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/IntervalList$Interval;-><init>(IILandroidx/compose/foundation/lazy/layout/LazyLayoutIntervalContent;)V
-HSPLandroidx/compose/foundation/lazy/layout/IntervalListKt;->access$binarySearch(ILandroidx/compose/runtime/collection/MutableVector;)I
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory$CachedItemContent$createContentLambda$1$1;-><init>(Landroidx/compose/foundation/lazy/layout/LazyLayoutItemProvider;I)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory$CachedItemContent$createContentLambda$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory$CachedItemContent$createContentLambda$1$2$invoke$$inlined$onDispose$1;-><init>(Landroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory$CachedItemContent;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory$CachedItemContent$createContentLambda$1$2$invoke$$inlined$onDispose$1;->dispose()V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory$CachedItemContent$createContentLambda$1$2;-><init>(Landroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory$CachedItemContent;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory$CachedItemContent$createContentLambda$1$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory$CachedItemContent$createContentLambda$1;-><init>(Landroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory;Landroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory$CachedItemContent;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory$CachedItemContent$createContentLambda$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory$CachedItemContent;-><init>(Landroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory;ILjava/lang/Object;Ljava/lang/Object;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory;-><init>(Landroidx/compose/runtime/saveable/SaveableStateHolder;Landroidx/compose/foundation/lazy/layout/LazyLayoutKt$LazyLayout$1$itemContentFactory$1$1;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory;->getContent(ILjava/lang/Object;)Lkotlin/jvm/functions/Function2;
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory;->getContentType(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutItemReusePolicy;-><init>(Landroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutItemReusePolicy;->areCompatible(Ljava/lang/Object;Ljava/lang/Object;)Z
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutItemReusePolicy;->getSlotsToRetain(Landroidx/compose/ui/layout/SubcomposeSlotReusePolicy$SlotIdsSet;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutKt$LazyLayout$1$2$1;-><init>(Landroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory;Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutKt$LazyLayout$1$2$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutKt$LazyLayout$1$itemContentFactory$1$1;-><init>(Landroidx/compose/runtime/State;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutKt$LazyLayout$1$itemContentFactory$1$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutKt$LazyLayout$1;-><init>(Landroidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function2;ILandroidx/compose/runtime/MutableState;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutKt$LazyLayout$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutKt;->LazyLayout(Landroidx/compose/foundation/lazy/layout/LazyLayoutItemProvider;Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutMeasureScopeImpl;-><init>(Landroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory;Landroidx/compose/ui/layout/SubcomposeMeasureScope;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutMeasureScopeImpl;->getLayoutDirection()Landroidx/compose/ui/unit/LayoutDirection;
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutMeasureScopeImpl;->layout(IILjava/util/Map;Lkotlin/jvm/functions/Function1;)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutMeasureScopeImpl;->measure-0kLqBqw(JI)Ljava/util/List;
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutMeasureScopeImpl;->roundToPx-0680j_4(F)I
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState;-><init>()V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutPrefetcher$PrefetchRequest;-><init>(IJ)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutPrefetcher$PrefetchRequest;->cancel()V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutPrefetcher;-><init>(Landroidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState;Landroidx/compose/ui/layout/SubcomposeLayoutState;Landroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory;Landroid/view/View;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutPrefetcher;->doFrame(J)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutPrefetcher;->onRemembered()V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutPrefetcher;->run()V
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutPrefetcher;->schedulePrefetch-0kLqBqw(JI)Landroidx/compose/foundation/lazy/layout/LazyLayoutPrefetcher$PrefetchRequest;
-HSPLandroidx/compose/foundation/lazy/layout/LazyLayoutPrefetcher_androidKt;->LazyLayoutPrefetcher(Landroidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState;Landroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory;Landroidx/compose/ui/layout/SubcomposeLayoutState;Landroidx/compose/runtime/Composer;I)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyNearestItemsRangeKt$rememberLazyNearestItemsRangeState$1$1$1;-><init>(Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyNearestItemsRangeKt$rememberLazyNearestItemsRangeState$1$1$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/LazyNearestItemsRangeKt$rememberLazyNearestItemsRangeState$1$1$2;-><init>(Landroidx/compose/runtime/MutableState;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyNearestItemsRangeKt$rememberLazyNearestItemsRangeState$1$1$2;->emit(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/LazyNearestItemsRangeKt$rememberLazyNearestItemsRangeState$1$1;-><init>(Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/MutableState;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazyNearestItemsRangeKt$rememberLazyNearestItemsRangeState$1$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/lazy/layout/LazyNearestItemsRangeKt$rememberLazyNearestItemsRangeState$1$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/LazyNearestItemsRangeKt;->rememberLazyNearestItemsRangeState(Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;)Landroidx/compose/runtime/MutableState;
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolder$1;-><init>(Landroidx/compose/runtime/saveable/SaveableStateRegistry;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolder$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolder$Companion$saver$1;-><clinit>()V
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolder$Companion$saver$1;-><init>()V
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolder$Companion$saver$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolder$Companion$saver$2;-><init>(Landroidx/compose/runtime/saveable/SaveableStateRegistry;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolder$SaveableStateProvider$1$invoke$$inlined$onDispose$1;-><init>(Landroidx/compose/foundation/lazy/layout/LazySaveableStateHolder;Ljava/lang/Object;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolder$SaveableStateProvider$1$invoke$$inlined$onDispose$1;->dispose()V
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolder$SaveableStateProvider$1;-><init>(Landroidx/compose/foundation/lazy/layout/LazySaveableStateHolder;Ljava/lang/Object;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolder$SaveableStateProvider$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolder$SaveableStateProvider$2;-><init>(Landroidx/compose/foundation/lazy/layout/LazySaveableStateHolder;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;I)V
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolder;-><init>(Landroidx/compose/runtime/saveable/SaveableStateRegistry;Ljava/util/Map;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolder;->SaveableStateProvider(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;I)V
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolder;->canBeSaved(Ljava/lang/Object;)Z
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolder;->consumeRestored(Ljava/lang/String;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolder;->performSave()Ljava/util/Map;
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolder;->registerProvider(Ljava/lang/String;Landroidx/compose/runtime/saveable/RememberSaveableKt$rememberSaveable$1$valueProvider$1;)Landroidx/compose/runtime/saveable/SaveableStateRegistry$Entry;
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolderKt$LazySaveableStateHolderProvider$1;-><init>(Landroidx/compose/foundation/lazy/layout/LazySaveableStateHolder;Lkotlin/jvm/functions/Function3;I)V
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolderKt$LazySaveableStateHolderProvider$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolderKt$LazySaveableStateHolderProvider$holder$1;-><init>(Landroidx/compose/runtime/saveable/SaveableStateRegistry;)V
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolderKt$LazySaveableStateHolderProvider$holder$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/foundation/lazy/layout/LazySaveableStateHolderKt;->LazySaveableStateHolderProvider(Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;I)V
-HSPLandroidx/compose/foundation/lazy/layout/MutableIntervalList;-><init>()V
-HSPLandroidx/compose/foundation/lazy/layout/MutableIntervalList;->addInterval(ILandroidx/compose/foundation/lazy/layout/LazyLayoutIntervalContent;)V
-HSPLandroidx/compose/foundation/lazy/layout/MutableIntervalList;->checkIndexBounds(I)V
-HSPLandroidx/compose/foundation/lazy/layout/MutableIntervalList;->forEach(IILandroidx/compose/foundation/lazy/layout/DefaultLazyLayoutItemsProvider$generateKeyToIndexMap$1$1;)V
-HSPLandroidx/compose/foundation/lazy/layout/MutableIntervalList;->getSize()I
-HSPLandroidx/compose/foundation/relocation/AndroidBringIntoViewParent;-><init>(Landroid/view/View;)V
-HSPLandroidx/compose/foundation/relocation/AndroidBringIntoViewParent;->bringChildIntoView(Landroidx/compose/ui/layout/LayoutCoordinates;Lkotlin/jvm/functions/Function0;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewChildModifier;-><init>(Landroidx/compose/foundation/relocation/AndroidBringIntoViewParent;)V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewChildModifier;->getLayoutCoordinates()Landroidx/compose/ui/layout/LayoutCoordinates;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewChildModifier;->onModifierLocalsUpdated(Landroidx/compose/ui/modifier/ModifierLocalReadScope;)V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewChildModifier;->onPlaced(Landroidx/compose/ui/node/NodeCoordinator;)V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewKt$ModifierLocalBringIntoViewParent$1;-><clinit>()V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewKt$ModifierLocalBringIntoViewParent$1;-><init>()V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewKt$ModifierLocalBringIntoViewParent$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewKt;-><clinit>()V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewRequesterImpl$bringIntoView$1;-><init>(Landroidx/compose/foundation/relocation/BringIntoViewRequesterImpl;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewRequesterImpl$bringIntoView$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewRequesterImpl;-><init>()V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewRequesterImpl;->bringIntoView(Landroidx/compose/ui/geometry/Rect;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewRequesterKt$bringIntoViewRequester$2$1$invoke$$inlined$onDispose$1;-><init>(Landroidx/compose/foundation/relocation/BringIntoViewRequester;Landroidx/compose/foundation/relocation/BringIntoViewRequesterModifier;)V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewRequesterKt$bringIntoViewRequester$2$1$invoke$$inlined$onDispose$1;->dispose()V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewRequesterKt$bringIntoViewRequester$2$1;-><init>(Landroidx/compose/foundation/relocation/BringIntoViewRequester;Landroidx/compose/foundation/relocation/BringIntoViewRequesterModifier;)V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewRequesterKt$bringIntoViewRequester$2$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewRequesterKt$bringIntoViewRequester$2;-><init>(Landroidx/compose/foundation/relocation/BringIntoViewRequester;)V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewRequesterKt$bringIntoViewRequester$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewRequesterModifier$bringIntoView$2;-><init>(Landroidx/compose/ui/geometry/Rect;Landroidx/compose/foundation/relocation/BringIntoViewRequesterModifier;)V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewRequesterModifier$bringIntoView$2;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewRequesterModifier;-><init>(Landroidx/compose/foundation/relocation/AndroidBringIntoViewParent;)V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderKt$bringIntoViewResponder$2;-><init>(Landroidx/compose/foundation/relocation/BringIntoViewResponder;)V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderKt$bringIntoViewResponder$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderKt;->bringIntoViewResponder(Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/relocation/BringIntoViewResponder;)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$2$1$1;-><init>(Landroidx/compose/foundation/relocation/BringIntoViewResponderModifier;Landroidx/compose/ui/layout/LayoutCoordinates;Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$2$1$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$2$1;-><init>(Landroidx/compose/foundation/relocation/BringIntoViewResponderModifier;Landroidx/compose/ui/layout/LayoutCoordinates;Lkotlin/jvm/functions/Function0;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$2$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$2$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$2$2;-><init>(Landroidx/compose/foundation/relocation/BringIntoViewResponderModifier;Lkotlin/jvm/functions/Function0;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$2$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$2$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$2;-><init>(Landroidx/compose/foundation/relocation/BringIntoViewResponderModifier;Landroidx/compose/ui/layout/LayoutCoordinates;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$parentRect$1;-><init>(Landroidx/compose/foundation/relocation/BringIntoViewResponderModifier;Landroidx/compose/ui/layout/LayoutCoordinates;Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$parentRect$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderModifier;-><init>(Landroidx/compose/foundation/relocation/AndroidBringIntoViewParent;)V
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderModifier;->access$bringChildIntoView$localRect(Landroidx/compose/foundation/relocation/BringIntoViewResponderModifier;Landroidx/compose/ui/layout/LayoutCoordinates;Lkotlin/jvm/functions/Function0;)Landroidx/compose/ui/geometry/Rect;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderModifier;->bringChildIntoView(Landroidx/compose/ui/layout/LayoutCoordinates;Lkotlin/jvm/functions/Function0;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderModifier;->getKey()Landroidx/compose/ui/modifier/ProvidableModifierLocal;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponderModifier;->getValue()Ljava/lang/Object;
-HSPLandroidx/compose/foundation/relocation/BringIntoViewResponder_androidKt;->rememberDefaultBringIntoViewParent(Landroidx/compose/runtime/Composer;)Landroidx/compose/foundation/relocation/AndroidBringIntoViewParent;
-HSPLandroidx/compose/foundation/selection/SelectableGroupKt$selectableGroup$1;-><clinit>()V
-HSPLandroidx/compose/foundation/selection/SelectableGroupKt$selectableGroup$1;-><init>()V
-HSPLandroidx/compose/foundation/selection/SelectableGroupKt$selectableGroup$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/shape/CornerBasedShape;-><init>(Landroidx/compose/foundation/shape/CornerSize;Landroidx/compose/foundation/shape/CornerSize;Landroidx/compose/foundation/shape/CornerSize;Landroidx/compose/foundation/shape/CornerSize;)V
-HSPLandroidx/compose/foundation/shape/CornerBasedShape;->createOutline-Pq9zytI(JLandroidx/compose/ui/unit/LayoutDirection;Landroidx/compose/ui/unit/Density;)Landroidx/compose/ui/graphics/Outline;
-HSPLandroidx/compose/foundation/shape/DpCornerSize;-><init>(F)V
-HSPLandroidx/compose/foundation/shape/PercentCornerSize;-><init>(F)V
-HSPLandroidx/compose/foundation/shape/PercentCornerSize;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/foundation/shape/PercentCornerSize;->toPx-TmRCtEA(JLandroidx/compose/ui/unit/Density;)F
-HSPLandroidx/compose/foundation/shape/RoundedCornerShape;-><init>(Landroidx/compose/foundation/shape/CornerSize;Landroidx/compose/foundation/shape/CornerSize;Landroidx/compose/foundation/shape/CornerSize;Landroidx/compose/foundation/shape/CornerSize;)V
-HSPLandroidx/compose/foundation/shape/RoundedCornerShape;->createOutline-LjSzlW0(JFFFFLandroidx/compose/ui/unit/LayoutDirection;)Landroidx/compose/ui/graphics/Outline;
-HSPLandroidx/compose/foundation/shape/RoundedCornerShape;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/foundation/shape/RoundedCornerShapeKt;-><clinit>()V
-HSPLandroidx/compose/foundation/shape/RoundedCornerShapeKt;->RoundedCornerShape-0680j_4(F)Landroidx/compose/foundation/shape/RoundedCornerShape;
-HSPLandroidx/compose/foundation/text/BasicTextKt$BasicText-4YKlhWE$$inlined$Layout$1;-><init>(Landroidx/compose/ui/node/LayoutNode$Companion$Constructor$1;)V
-HSPLandroidx/compose/foundation/text/BasicTextKt$BasicText-4YKlhWE$$inlined$Layout$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/foundation/text/BasicTextKt;->BasicText-BpD7jsM(Ljava/lang/String;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/text/TextStyle;Lkotlin/jvm/functions/Function1;IZILandroidx/compose/runtime/Composer;II)V
-HSPLandroidx/compose/foundation/text/HeightInLinesModifierKt$heightInLines$2;-><init>(IILandroidx/compose/ui/text/TextStyle;)V
-HSPLandroidx/compose/foundation/text/HeightInLinesModifierKt$heightInLines$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/text/HeightInLinesModifierKt;->validateMinMaxLines(II)V
-HSPLandroidx/compose/foundation/text/TextController$coreModifiers$1;-><init>(Landroidx/compose/foundation/text/TextController;)V
-HSPLandroidx/compose/foundation/text/TextController$coreModifiers$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/text/TextController$createSemanticsModifierFor$1$1;-><init>(Landroidx/compose/foundation/text/TextController;)V
-HSPLandroidx/compose/foundation/text/TextController$createSemanticsModifierFor$1;-><init>(Landroidx/compose/ui/text/AnnotatedString;Landroidx/compose/foundation/text/TextController;)V
-HSPLandroidx/compose/foundation/text/TextController$createSemanticsModifierFor$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/text/TextController$drawTextAndSelectionBehind$1;-><init>(Landroidx/compose/foundation/text/TextController;)V
-HSPLandroidx/compose/foundation/text/TextController$drawTextAndSelectionBehind$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/text/TextController$measurePolicy$1$measure$2;-><init>(Ljava/util/ArrayList;)V
-HSPLandroidx/compose/foundation/text/TextController$measurePolicy$1$measure$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/foundation/text/TextController$measurePolicy$1;-><init>(Landroidx/compose/foundation/text/TextController;)V
-HSPLandroidx/compose/foundation/text/TextController$measurePolicy$1;->maxIntrinsicHeight(Landroidx/compose/ui/node/NodeCoordinator;Ljava/util/List;I)I
-HSPLandroidx/compose/foundation/text/TextController$measurePolicy$1;->maxIntrinsicWidth(Landroidx/compose/ui/node/NodeCoordinator;Ljava/util/List;I)I
-HSPLandroidx/compose/foundation/text/TextController$measurePolicy$1;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Ljava/util/List;J)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/foundation/text/TextController;-><init>(Landroidx/compose/foundation/text/TextState;)V
-HSPLandroidx/compose/foundation/text/TextController;->onForgotten()V
-HSPLandroidx/compose/foundation/text/TextController;->onRemembered()V
-HSPLandroidx/compose/foundation/text/TextDelegate;-><init>(Landroidx/compose/ui/text/AnnotatedString;Landroidx/compose/ui/text/TextStyle;IIZILandroidx/compose/ui/unit/Density;Landroidx/compose/ui/text/font/FontFamily$Resolver;)V
-HSPLandroidx/compose/foundation/text/TextDelegate;->layout-NN6Ew-U(JLandroidx/compose/ui/unit/LayoutDirection;Landroidx/compose/ui/text/TextLayoutResult;)Landroidx/compose/ui/text/TextLayoutResult;
-HSPLandroidx/compose/foundation/text/TextDelegate;->layoutIntrinsics(Landroidx/compose/ui/unit/LayoutDirection;)V
-HSPLandroidx/compose/foundation/text/TextDelegateKt;->ceilToIntPx(F)I
-HSPLandroidx/compose/foundation/text/TextState$onTextLayout$1;-><clinit>()V
-HSPLandroidx/compose/foundation/text/TextState$onTextLayout$1;-><init>()V
-HSPLandroidx/compose/foundation/text/TextState;-><init>(Landroidx/compose/foundation/text/TextDelegate;J)V
-HSPLandroidx/compose/foundation/text/selection/SelectionRegistrarKt$LocalSelectionRegistrar$1;-><clinit>()V
-HSPLandroidx/compose/foundation/text/selection/SelectionRegistrarKt$LocalSelectionRegistrar$1;-><init>()V
-HSPLandroidx/compose/foundation/text/selection/SelectionRegistrarKt$LocalSelectionRegistrar$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/foundation/text/selection/SelectionRegistrarKt;-><clinit>()V
-HSPLandroidx/compose/foundation/text/selection/SelectionRegistrarKt;->hasSelection(Landroidx/compose/foundation/text/selection/SelectionRegistrar;J)Z
-HSPLandroidx/compose/foundation/text/selection/TextSelectionColors;-><init>(JJ)V
-HSPLandroidx/compose/foundation/text/selection/TextSelectionColors;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/foundation/text/selection/TextSelectionColorsKt$LocalTextSelectionColors$1;-><clinit>()V
-HSPLandroidx/compose/foundation/text/selection/TextSelectionColorsKt$LocalTextSelectionColors$1;-><init>()V
-HSPLandroidx/compose/foundation/text/selection/TextSelectionColorsKt;-><clinit>()V
-HSPLandroidx/compose/material3/ColorScheme$$ExternalSyntheticOutline0;->m(JLandroidx/compose/runtime/StructuralEqualityPolicy;)Landroidx/compose/runtime/ParcelableSnapshotMutableState;
-HSPLandroidx/compose/material3/TextKt$LocalTextStyle$1;-><clinit>()V
-HSPLandroidx/compose/material3/TextKt$LocalTextStyle$1;-><init>()V
-HSPLandroidx/compose/material3/TextKt$LocalTextStyle$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/material3/TextKt$Text$1;-><clinit>()V
-HSPLandroidx/compose/material3/TextKt$Text$1;-><init>()V
-HSPLandroidx/compose/material3/TextKt$Text$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/material3/TextKt;-><clinit>()V
-HSPLandroidx/compose/material3/TextKt;->Text-fLXpl1I(Ljava/lang/String;Landroidx/compose/ui/Modifier;JJLandroidx/compose/ui/text/font/FontStyle;Landroidx/compose/ui/text/font/FontWeight;Landroidx/compose/ui/text/font/FontFamily;JLandroidx/compose/ui/text/style/TextDecoration;Landroidx/compose/ui/text/style/TextAlign;JIZILkotlin/jvm/functions/Function1;Landroidx/compose/ui/text/TextStyle;Landroidx/compose/runtime/Composer;III)V
-HSPLandroidx/compose/runtime/AbstractApplier;-><init>(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/AbstractApplier;->down(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/AbstractApplier;->getCurrent()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/AbstractApplier;->up()V
-HSPLandroidx/compose/runtime/ActualAndroid_androidKt$DefaultMonotonicFrameClock$2;-><clinit>()V
-HSPLandroidx/compose/runtime/ActualAndroid_androidKt$DefaultMonotonicFrameClock$2;-><init>()V
-HSPLandroidx/compose/runtime/ActualAndroid_androidKt;-><clinit>()V
-HSPLandroidx/compose/runtime/Anchor;-><init>(I)V
-HSPLandroidx/compose/runtime/Anchor;->getValid()Z
-HSPLandroidx/compose/runtime/BroadcastFrameClock$FrameAwaiter;-><init>(Lkotlin/jvm/functions/Function1;Lkotlinx/coroutines/CancellableContinuationImpl;)V
-HSPLandroidx/compose/runtime/BroadcastFrameClock$withFrameNanos$2$1;-><init>(Landroidx/compose/runtime/BroadcastFrameClock;Lkotlin/jvm/internal/Ref$ObjectRef;)V
-HSPLandroidx/compose/runtime/BroadcastFrameClock$withFrameNanos$2$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/BroadcastFrameClock;-><init>(Landroidx/compose/runtime/Recomposer$broadcastFrameClock$1;)V
-HSPLandroidx/compose/runtime/BroadcastFrameClock;->fold(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/BroadcastFrameClock;->get(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
-HSPLandroidx/compose/runtime/BroadcastFrameClock;->getHasAwaiters()Z
-HSPLandroidx/compose/runtime/BroadcastFrameClock;->sendFrame(J)V
-HSPLandroidx/compose/runtime/BroadcastFrameClock;->withFrameNanos(Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposableSingletons$CompositionKt$lambda-1$1;-><clinit>()V
-HSPLandroidx/compose/runtime/ComposableSingletons$CompositionKt$lambda-1$1;-><init>()V
-HSPLandroidx/compose/runtime/ComposableSingletons$CompositionKt$lambda-2$1;-><clinit>()V
-HSPLandroidx/compose/runtime/ComposableSingletons$CompositionKt$lambda-2$1;-><init>()V
-HSPLandroidx/compose/runtime/ComposableSingletons$CompositionKt;-><clinit>()V
-HSPLandroidx/compose/runtime/Composer$Companion$Empty$1;-><init>()V
-HSPLandroidx/compose/runtime/Composer$Companion;-><clinit>()V
-HSPLandroidx/compose/runtime/ComposerImpl$CompositionContextHolder;-><init>(Landroidx/compose/runtime/ComposerImpl$CompositionContextImpl;)V
-HSPLandroidx/compose/runtime/ComposerImpl$CompositionContextHolder;->onRemembered()V
-HSPLandroidx/compose/runtime/ComposerImpl$CompositionContextImpl;-><init>(Landroidx/compose/runtime/ComposerImpl;IZ)V
-HSPLandroidx/compose/runtime/ComposerImpl$CompositionContextImpl;->composeInitial$runtime_release(Landroidx/compose/runtime/ControlledComposition;Landroidx/compose/runtime/internal/ComposableLambdaImpl;)V
-HSPLandroidx/compose/runtime/ComposerImpl$CompositionContextImpl;->doneComposing$runtime_release()V
-HSPLandroidx/compose/runtime/ComposerImpl$CompositionContextImpl;->getCollectingParameterInformation$runtime_release()Z
-HSPLandroidx/compose/runtime/ComposerImpl$CompositionContextImpl;->getCompositionLocalScope$runtime_release()Landroidx/compose/runtime/external/kotlinx/collections/immutable/PersistentMap;
-HSPLandroidx/compose/runtime/ComposerImpl$CompositionContextImpl;->getCompoundHashKey$runtime_release()I
-HSPLandroidx/compose/runtime/ComposerImpl$CompositionContextImpl;->getEffectCoroutineContext$runtime_release()Lkotlin/coroutines/CoroutineContext;
-HSPLandroidx/compose/runtime/ComposerImpl$CompositionContextImpl;->invalidate$runtime_release(Landroidx/compose/runtime/ControlledComposition;)V
-HSPLandroidx/compose/runtime/ComposerImpl$CompositionContextImpl;->registerComposer$runtime_release(Landroidx/compose/runtime/ComposerImpl;)V
-HSPLandroidx/compose/runtime/ComposerImpl$CompositionContextImpl;->startComposing$runtime_release()V
-HSPLandroidx/compose/runtime/ComposerImpl$apply$operation$1$$ExternalSyntheticOutline0;->m(Landroidx/compose/runtime/Applier;Ljava/lang/String;Landroidx/compose/runtime/SlotWriter;Ljava/lang/String;Landroidx/compose/runtime/RememberManager;Ljava/lang/String;)V
-HSPLandroidx/compose/runtime/ComposerImpl$apply$operation$1;-><init>(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/runtime/ComposerImpl$apply$operation$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl$createNode$2;-><init>(Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Anchor;I)V
-HSPLandroidx/compose/runtime/ComposerImpl$createNode$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl$createNode$3;-><init>(ILandroidx/compose/runtime/Anchor;)V
-HSPLandroidx/compose/runtime/ComposerImpl$createNode$3;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl$deactivateToEndGroup$2$1;-><init>(IILjava/lang/Object;)V
-HSPLandroidx/compose/runtime/ComposerImpl$deactivateToEndGroup$2$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl$deactivateToEndGroup$2$2;-><init>(IILjava/lang/Object;)V
-HSPLandroidx/compose/runtime/ComposerImpl$deactivateToEndGroup$2$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl$doCompose$2$3;-><init>(Landroidx/compose/runtime/ComposerImpl;)V
-HSPLandroidx/compose/runtime/ComposerImpl$doCompose$2$3;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl$doCompose$2$4;-><init>(Landroidx/compose/runtime/ComposerImpl;)V
-HSPLandroidx/compose/runtime/ComposerImpl$doCompose$2$4;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl$doCompose$2$5;-><init>(Landroidx/compose/runtime/internal/ComposableLambdaImpl;Landroidx/compose/runtime/ComposerImpl;Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/ComposerImpl$doCompose$2$5;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl$doCompose$lambda$37$$inlined$sortBy$1;-><init>()V
-HSPLandroidx/compose/runtime/ComposerImpl$doCompose$lambda$37$$inlined$sortBy$1;->compare(Ljava/lang/Object;Ljava/lang/Object;)I
-HSPLandroidx/compose/runtime/ComposerImpl$realizeDowns$1;-><init>([Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/ComposerImpl$realizeDowns$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl$realizeOperationLocation$2;-><init>(I)V
-HSPLandroidx/compose/runtime/ComposerImpl$realizeOperationLocation$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl$realizeUps$1;-><init>(I)V
-HSPLandroidx/compose/runtime/ComposerImpl$realizeUps$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl$recordInsert$1;-><init>(Landroidx/compose/runtime/SlotTable;Landroidx/compose/runtime/Anchor;)V
-HSPLandroidx/compose/runtime/ComposerImpl$recordInsert$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl$recordInsert$2;-><init>(Landroidx/compose/runtime/SlotTable;Landroidx/compose/runtime/Anchor;Ljava/util/ArrayList;)V
-HSPLandroidx/compose/runtime/ComposerImpl$recordInsert$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl$recordSideEffect$1;-><init>(Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/runtime/ComposerImpl$recordSideEffect$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl$recordSlotEditing$1;-><init>(Landroidx/compose/runtime/Anchor;)V
-HSPLandroidx/compose/runtime/ComposerImpl$recordSlotEditing$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl$startProviders$currentProviders$1;-><init>([Landroidx/compose/runtime/ProvidedValue;Landroidx/compose/runtime/external/kotlinx/collections/immutable/PersistentMap;)V
-HSPLandroidx/compose/runtime/ComposerImpl$startProviders$currentProviders$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl$startReaderGroup$1;-><init>(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/ComposerImpl$startReaderGroup$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl$updateValue$1;-><init>(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/ComposerImpl$updateValue$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl$updateValue$2;-><init>(ILjava/lang/Object;)V
-HSPLandroidx/compose/runtime/ComposerImpl$updateValue$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl;-><init>(Landroidx/compose/runtime/AbstractApplier;Landroidx/compose/runtime/CompositionContext;Landroidx/compose/runtime/SlotTable;Ljava/util/HashSet;Ljava/util/ArrayList;Ljava/util/ArrayList;Landroidx/compose/runtime/ControlledComposition;)V
-HSPLandroidx/compose/runtime/ComposerImpl;->apply(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/runtime/ComposerImpl;->buildContext()Landroidx/compose/runtime/ComposerImpl$CompositionContextImpl;
-HSPLandroidx/compose/runtime/ComposerImpl;->changed(I)Z
-HSPLandroidx/compose/runtime/ComposerImpl;->changed(J)Z
-HSPLandroidx/compose/runtime/ComposerImpl;->changed(Ljava/lang/Object;)Z
-HSPLandroidx/compose/runtime/ComposerImpl;->changed(Z)Z
-HSPLandroidx/compose/runtime/ComposerImpl;->changedInstance(Ljava/lang/Object;)Z
-HSPLandroidx/compose/runtime/ComposerImpl;->cleanUpCompose()V
-HSPLandroidx/compose/runtime/ComposerImpl;->composeContent$runtime_release(Landroidx/compose/runtime/collection/IdentityArrayMap;Landroidx/compose/runtime/internal/ComposableLambdaImpl;)V
-HSPLandroidx/compose/runtime/ComposerImpl;->consume(Landroidx/compose/runtime/ProvidableCompositionLocal;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl;->createNode(Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/runtime/ComposerImpl;->currentCompositionLocalScope(Ljava/lang/Integer;)Landroidx/compose/runtime/external/kotlinx/collections/immutable/PersistentMap;
-HSPLandroidx/compose/runtime/ComposerImpl;->deactivateToEndGroup(Z)V
-HSPLandroidx/compose/runtime/ComposerImpl;->disableReusing()V
-HSPLandroidx/compose/runtime/ComposerImpl;->doCompose(Landroidx/compose/runtime/collection/IdentityArrayMap;Landroidx/compose/runtime/internal/ComposableLambdaImpl;)V
-HSPLandroidx/compose/runtime/ComposerImpl;->doRecordDownsFor(II)V
-HSPLandroidx/compose/runtime/ComposerImpl;->enableReusing()V
-HSPLandroidx/compose/runtime/ComposerImpl;->endDefaults()V
-HSPLandroidx/compose/runtime/ComposerImpl;->endNode()V
-HSPLandroidx/compose/runtime/ComposerImpl;->endProviders()V
-HSPLandroidx/compose/runtime/ComposerImpl;->endReplaceableGroup()V
-HSPLandroidx/compose/runtime/ComposerImpl;->endRestartGroup()Landroidx/compose/runtime/RecomposeScopeImpl;
-HSPLandroidx/compose/runtime/ComposerImpl;->endReusableGroup()V
-HSPLandroidx/compose/runtime/ComposerImpl;->endRoot()V
-HSPLandroidx/compose/runtime/ComposerImpl;->enterGroup(ZLandroidx/compose/runtime/Pending;)V
-HSPLandroidx/compose/runtime/ComposerImpl;->getApplier()Landroidx/compose/runtime/Applier;
-HSPLandroidx/compose/runtime/ComposerImpl;->getApplyCoroutineContext()Lkotlin/coroutines/CoroutineContext;
-HSPLandroidx/compose/runtime/ComposerImpl;->getCompoundKeyHash()I
-HSPLandroidx/compose/runtime/ComposerImpl;->getCurrentRecomposeScope$runtime_release()Landroidx/compose/runtime/RecomposeScopeImpl;
-HSPLandroidx/compose/runtime/ComposerImpl;->getDefaultsInvalid()Z
-HSPLandroidx/compose/runtime/ComposerImpl;->getInserting()Z
-HSPLandroidx/compose/runtime/ComposerImpl;->getRecomposeScope()Landroidx/compose/runtime/RecomposeScopeImpl;
-HSPLandroidx/compose/runtime/ComposerImpl;->getSkipping()Z
-HSPLandroidx/compose/runtime/ComposerImpl;->nextSlot()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl;->realizeDowns$1()V
-HSPLandroidx/compose/runtime/ComposerImpl;->realizeMovement()V
-HSPLandroidx/compose/runtime/ComposerImpl;->realizeOperationLocation(Z)V
-HSPLandroidx/compose/runtime/ComposerImpl;->realizeUps()V
-HSPLandroidx/compose/runtime/ComposerImpl;->recompose$runtime_release(Landroidx/compose/runtime/collection/IdentityArrayMap;)Z
-HSPLandroidx/compose/runtime/ComposerImpl;->recomposeToGroupEnd()V
-HSPLandroidx/compose/runtime/ComposerImpl;->record(Lkotlin/jvm/functions/Function3;)V
-HSPLandroidx/compose/runtime/ComposerImpl;->recordDelete()V
-HSPLandroidx/compose/runtime/ComposerImpl;->recordRemoveNode(II)V
-HSPLandroidx/compose/runtime/ComposerImpl;->recordSideEffect(Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/runtime/ComposerImpl;->recordSlotEditing()V
-HSPLandroidx/compose/runtime/ComposerImpl;->recordSlotTableOperation(ZLkotlin/jvm/functions/Function3;)V
-HSPLandroidx/compose/runtime/ComposerImpl;->recordUp()V
-HSPLandroidx/compose/runtime/ComposerImpl;->recordUpsAndDowns(III)V
-HSPLandroidx/compose/runtime/ComposerImpl;->recordUsed(Landroidx/compose/runtime/RecomposeScope;)V
-HSPLandroidx/compose/runtime/ComposerImpl;->rememberedValue()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerImpl;->reportFreeMovableContent$reportGroup(Landroidx/compose/runtime/ComposerImpl;IZI)I
-HSPLandroidx/compose/runtime/ComposerImpl;->skipReaderToGroupEnd()V
-HSPLandroidx/compose/runtime/ComposerImpl;->skipToGroupEnd()V
-HSPLandroidx/compose/runtime/ComposerImpl;->start-BaiHCIY(IILjava/lang/Object;Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/ComposerImpl;->startDefaults()V
-HSPLandroidx/compose/runtime/ComposerImpl;->startGroup(ILandroidx/compose/runtime/OpaqueKey;)V
-HSPLandroidx/compose/runtime/ComposerImpl;->startNode()V
-HSPLandroidx/compose/runtime/ComposerImpl;->startProviders([Landroidx/compose/runtime/ProvidedValue;)V
-HSPLandroidx/compose/runtime/ComposerImpl;->startReaderGroup(Ljava/lang/Object;Z)V
-HSPLandroidx/compose/runtime/ComposerImpl;->startReplaceableGroup(I)V
-HSPLandroidx/compose/runtime/ComposerImpl;->startRestartGroup(I)Landroidx/compose/runtime/ComposerImpl;
-HSPLandroidx/compose/runtime/ComposerImpl;->startReusableGroup(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/ComposerImpl;->startReusableNode()V
-HSPLandroidx/compose/runtime/ComposerImpl;->startRoot()V
-HSPLandroidx/compose/runtime/ComposerImpl;->tryImminentInvalidation$runtime_release(Landroidx/compose/runtime/RecomposeScopeImpl;Ljava/lang/Object;)Z
-HSPLandroidx/compose/runtime/ComposerImpl;->updateCompoundKeyWhenWeExitGroupKeyHash(I)V
-HSPLandroidx/compose/runtime/ComposerImpl;->updateNodeCount(II)V
-HSPLandroidx/compose/runtime/ComposerImpl;->updateNodeCountOverrides(II)V
-HSPLandroidx/compose/runtime/ComposerImpl;->updateProviderMapGroup(Landroidx/compose/runtime/external/kotlinx/collections/immutable/PersistentMap;Landroidx/compose/runtime/external/kotlinx/collections/immutable/PersistentMap;)Landroidx/compose/runtime/external/kotlinx/collections/immutable/PersistentMap;
-HSPLandroidx/compose/runtime/ComposerImpl;->updateRememberedValue(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I
-HSPLandroidx/compose/runtime/ComposerImpl;->useNode()V
-HSPLandroidx/compose/runtime/ComposerKt$endGroupInstance$1;-><clinit>()V
-HSPLandroidx/compose/runtime/ComposerKt$endGroupInstance$1;-><init>()V
-HSPLandroidx/compose/runtime/ComposerKt$endGroupInstance$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerKt$removeCurrentGroupInstance$1;-><clinit>()V
-HSPLandroidx/compose/runtime/ComposerKt$removeCurrentGroupInstance$1;-><init>()V
-HSPLandroidx/compose/runtime/ComposerKt$removeCurrentGroupInstance$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerKt$startRootGroup$1;-><clinit>()V
-HSPLandroidx/compose/runtime/ComposerKt$startRootGroup$1;-><init>()V
-HSPLandroidx/compose/runtime/ComposerKt$startRootGroup$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/ComposerKt;-><clinit>()V
-HSPLandroidx/compose/runtime/ComposerKt;->access$removeRange(Ljava/util/ArrayList;II)V
-HSPLandroidx/compose/runtime/ComposerKt;->findLocation(ILjava/util/List;)I
-HSPLandroidx/compose/runtime/ComposerKt;->runtimeCheck(Z)V
-HSPLandroidx/compose/runtime/CompositionContext;-><init>()V
-HSPLandroidx/compose/runtime/CompositionContext;->doneComposing$runtime_release()V
-HSPLandroidx/compose/runtime/CompositionContext;->getCompositionLocalScope$runtime_release()Landroidx/compose/runtime/external/kotlinx/collections/immutable/PersistentMap;
-HSPLandroidx/compose/runtime/CompositionContext;->registerComposer$runtime_release(Landroidx/compose/runtime/ComposerImpl;)V
-HSPLandroidx/compose/runtime/CompositionContext;->startComposing$runtime_release()V
-HSPLandroidx/compose/runtime/CompositionContextKt;-><clinit>()V
-HSPLandroidx/compose/runtime/CompositionImpl$RememberEventDispatcher;-><init>(Ljava/util/HashSet;)V
-HSPLandroidx/compose/runtime/CompositionImpl$RememberEventDispatcher;->dispatchAbandons()V
-HSPLandroidx/compose/runtime/CompositionImpl$RememberEventDispatcher;->dispatchRememberObservers()V
-HSPLandroidx/compose/runtime/CompositionImpl$RememberEventDispatcher;->dispatchSideEffects()V
-HSPLandroidx/compose/runtime/CompositionImpl$RememberEventDispatcher;->forgetting(Landroidx/compose/runtime/RememberObserver;)V
-HSPLandroidx/compose/runtime/CompositionImpl$RememberEventDispatcher;->remembering(Landroidx/compose/runtime/RememberObserver;)V
-HSPLandroidx/compose/runtime/CompositionImpl$RememberEventDispatcher;->sideEffect(Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/runtime/CompositionImpl;-><init>(Landroidx/compose/runtime/CompositionContext;Landroidx/compose/runtime/AbstractApplier;)V
-HSPLandroidx/compose/runtime/CompositionImpl;->addPendingInvalidationsLocked$invalidate(Landroidx/compose/runtime/CompositionImpl;ZLkotlin/jvm/internal/Ref$ObjectRef;Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/CompositionImpl;->addPendingInvalidationsLocked(Ljava/util/Set;Z)V
-HSPLandroidx/compose/runtime/CompositionImpl;->applyChanges()V
-HSPLandroidx/compose/runtime/CompositionImpl;->applyChangesInLocked(Ljava/util/ArrayList;)V
-HSPLandroidx/compose/runtime/CompositionImpl;->applyLateChanges()V
-HSPLandroidx/compose/runtime/CompositionImpl;->changesApplied()V
-HSPLandroidx/compose/runtime/CompositionImpl;->cleanUpDerivedStateObservations()V
-HSPLandroidx/compose/runtime/CompositionImpl;->composeContent(Landroidx/compose/runtime/internal/ComposableLambdaImpl;)V
-HSPLandroidx/compose/runtime/CompositionImpl;->drainPendingModificationsForCompositionLocked()V
-HSPLandroidx/compose/runtime/CompositionImpl;->drainPendingModificationsLocked()V
-HSPLandroidx/compose/runtime/CompositionImpl;->getHasInvalidations()Z
-HSPLandroidx/compose/runtime/CompositionImpl;->invalidate$enumunboxing$(Landroidx/compose/runtime/RecomposeScopeImpl;Ljava/lang/Object;)I
-HSPLandroidx/compose/runtime/CompositionImpl;->invalidateChecked$enumunboxing$(Landroidx/compose/runtime/RecomposeScopeImpl;Landroidx/compose/runtime/Anchor;Ljava/lang/Object;)I
-HSPLandroidx/compose/runtime/CompositionImpl;->invalidateScopeOfLocked(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/CompositionImpl;->isComposing()Z
-HSPLandroidx/compose/runtime/CompositionImpl;->isDisposed()Z
-HSPLandroidx/compose/runtime/CompositionImpl;->observesAnyOf(Landroidx/compose/runtime/collection/IdentityArraySet;)Z
-HSPLandroidx/compose/runtime/CompositionImpl;->prepareCompose(Landroidx/compose/runtime/Recomposer$performRecompose$1$1;)V
-HSPLandroidx/compose/runtime/CompositionImpl;->recompose()Z
-HSPLandroidx/compose/runtime/CompositionImpl;->recordModificationsOf(Ljava/util/Set;)V
-HSPLandroidx/compose/runtime/CompositionImpl;->recordReadOf(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/CompositionImpl;->recordWriteOf(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/CompositionImpl;->setContent(Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/runtime/CompositionKt;-><clinit>()V
-HSPLandroidx/compose/runtime/CompositionKt;->Composition(Landroidx/compose/runtime/AbstractApplier;Landroidx/compose/runtime/CompositionContext;)Landroidx/compose/runtime/CompositionImpl;
-HSPLandroidx/compose/runtime/CompositionLocal;-><init>(Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/runtime/CompositionLocalKt;->CompositionLocalProvider([Landroidx/compose/runtime/ProvidedValue;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;I)V
-HSPLandroidx/compose/runtime/CompositionLocalKt;->compositionLocalOf$default(Lkotlin/jvm/functions/Function0;)Landroidx/compose/runtime/DynamicProvidableCompositionLocal;
-HSPLandroidx/compose/runtime/CompositionLocalKt;->compositionLocalOf(Landroidx/compose/runtime/SnapshotMutationPolicy;Lkotlin/jvm/functions/Function0;)Landroidx/compose/runtime/DynamicProvidableCompositionLocal;
-HSPLandroidx/compose/runtime/CompositionScopedCoroutineScopeCanceller;-><init>(Lkotlinx/coroutines/internal/ContextScope;)V
-HSPLandroidx/compose/runtime/CompositionScopedCoroutineScopeCanceller;->onForgotten()V
-HSPLandroidx/compose/runtime/CompositionScopedCoroutineScopeCanceller;->onRemembered()V
-HSPLandroidx/compose/runtime/DerivedSnapshotState$ResultRecord;-><clinit>()V
-HSPLandroidx/compose/runtime/DerivedSnapshotState$ResultRecord;-><init>()V
-HSPLandroidx/compose/runtime/DerivedSnapshotState$ResultRecord;->assign(Landroidx/compose/runtime/snapshots/StateRecord;)V
-HSPLandroidx/compose/runtime/DerivedSnapshotState$ResultRecord;->create()Landroidx/compose/runtime/snapshots/StateRecord;
-HSPLandroidx/compose/runtime/DerivedSnapshotState$ResultRecord;->readableHash(Landroidx/compose/runtime/DerivedState;Landroidx/compose/runtime/snapshots/Snapshot;)I
-HSPLandroidx/compose/runtime/DerivedSnapshotState$currentRecord$result$1$result$1;-><init>(Landroidx/compose/runtime/DerivedSnapshotState;Landroidx/compose/runtime/collection/IdentityArrayMap;I)V
-HSPLandroidx/compose/runtime/DerivedSnapshotState$currentRecord$result$1$result$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/DerivedSnapshotState;-><init>(Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/runtime/DerivedSnapshotState;->getCurrentValue()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/DerivedSnapshotState;->getDependencies()[Ljava/lang/Object;
-HSPLandroidx/compose/runtime/DerivedSnapshotState;->getFirstStateRecord()Landroidx/compose/runtime/snapshots/StateRecord;
-HSPLandroidx/compose/runtime/DerivedSnapshotState;->getPolicy()Landroidx/compose/runtime/SnapshotMutationPolicy;
-HSPLandroidx/compose/runtime/DerivedSnapshotState;->getValue()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/DerivedSnapshotState;->prependStateRecord(Landroidx/compose/runtime/snapshots/StateRecord;)V
-HSPLandroidx/compose/runtime/DisposableEffectImpl;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/runtime/DisposableEffectImpl;->onForgotten()V
-HSPLandroidx/compose/runtime/DisposableEffectImpl;->onRemembered()V
-HSPLandroidx/compose/runtime/DisposableEffectScope;-><init>()V
-HSPLandroidx/compose/runtime/DynamicProvidableCompositionLocal;-><init>(Landroidx/compose/runtime/SnapshotMutationPolicy;Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/runtime/DynamicProvidableCompositionLocal;->provided$runtime_release(Ljava/lang/Object;Landroidx/compose/runtime/Composer;)Landroidx/compose/runtime/State;
-HSPLandroidx/compose/runtime/EffectsKt;-><clinit>()V
-HSPLandroidx/compose/runtime/EffectsKt;->DisposableEffect(Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;)V
-HSPLandroidx/compose/runtime/EffectsKt;->LaunchedEffect(Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;)V
-HSPLandroidx/compose/runtime/EffectsKt;->createCompositionCoroutineScope(Landroidx/compose/runtime/Composer;)Lkotlinx/coroutines/internal/ContextScope;
-HSPLandroidx/compose/runtime/GroupInfo;-><init>(III)V
-HSPLandroidx/compose/runtime/IntStack;-><init>()V
-HSPLandroidx/compose/runtime/IntStack;->pop()I
-HSPLandroidx/compose/runtime/IntStack;->push(I)V
-HSPLandroidx/compose/runtime/Invalidation;-><init>(Landroidx/compose/runtime/RecomposeScopeImpl;ILandroidx/compose/runtime/collection/IdentityArraySet;)V
-HSPLandroidx/compose/runtime/KeyInfo;-><init>(ILjava/lang/Object;II)V
-HSPLandroidx/compose/runtime/Latch;-><init>()V
-HSPLandroidx/compose/runtime/LaunchedEffectImpl;-><init>(Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/runtime/LaunchedEffectImpl;->onForgotten()V
-HSPLandroidx/compose/runtime/LaunchedEffectImpl;->onRemembered()V
-HSPLandroidx/compose/runtime/LazyValueHolder;-><init>(Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/runtime/LazyValueHolder;->getValue()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/MonotonicFrameClock$Key;-><clinit>()V
-HSPLandroidx/compose/runtime/MonotonicFrameClock$Key;-><init>()V
-HSPLandroidx/compose/runtime/MonotonicFrameClock;->getKey()Lkotlin/coroutines/CoroutineContext$Key;
-HSPLandroidx/compose/runtime/MonotonicFrameClockKt;->getMonotonicFrameClock(Lkotlin/coroutines/CoroutineContext;)Landroidx/compose/runtime/MonotonicFrameClock;
-HSPLandroidx/compose/runtime/MonotonicFrameClockKt;->withFrameNanos(Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/NeverEqualPolicy;-><clinit>()V
-HSPLandroidx/compose/runtime/NeverEqualPolicy;-><init>()V
-HSPLandroidx/compose/runtime/NeverEqualPolicy;->equivalent(Ljava/lang/Object;Ljava/lang/Object;)Z
-HSPLandroidx/compose/runtime/OpaqueKey;-><init>(Ljava/lang/String;)V
-HSPLandroidx/compose/runtime/OpaqueKey;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/runtime/OpaqueKey;->hashCode()I
-HSPLandroidx/compose/runtime/ParcelableSnapshotMutableState$Companion$CREATOR$1;-><init>()V
-HSPLandroidx/compose/runtime/ParcelableSnapshotMutableState;-><clinit>()V
-HSPLandroidx/compose/runtime/ParcelableSnapshotMutableState;-><init>(Ljava/lang/Object;Landroidx/compose/runtime/SnapshotMutationPolicy;)V
-HSPLandroidx/compose/runtime/PausableMonotonicFrameClock$withFrameNanos$1;-><init>(Landroidx/compose/runtime/PausableMonotonicFrameClock;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/runtime/PausableMonotonicFrameClock$withFrameNanos$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/PausableMonotonicFrameClock;-><init>(Landroidx/compose/runtime/MonotonicFrameClock;)V
-HSPLandroidx/compose/runtime/PausableMonotonicFrameClock;->fold(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/PausableMonotonicFrameClock;->get(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
-HSPLandroidx/compose/runtime/PausableMonotonicFrameClock;->minusKey(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
-HSPLandroidx/compose/runtime/PausableMonotonicFrameClock;->withFrameNanos(Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/Pending$keyMap$2;-><init>(Landroidx/compose/runtime/Pending;)V
-HSPLandroidx/compose/runtime/Pending$keyMap$2;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/Pending;-><init>(ILjava/util/ArrayList;)V
-HSPLandroidx/compose/runtime/PrioritySet;-><init>(I)V
-HSPLandroidx/compose/runtime/PrioritySet;->add(I)V
-HSPLandroidx/compose/runtime/PrioritySet;->takeMax()I
-HSPLandroidx/compose/runtime/ProvidableCompositionLocal;-><init>(Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/runtime/ProvidableCompositionLocal;->provides(Ljava/lang/Object;)Landroidx/compose/runtime/ProvidedValue;
-HSPLandroidx/compose/runtime/ProvidedValue;-><init>(Landroidx/compose/runtime/CompositionLocal;Ljava/lang/Object;Z)V
-HSPLandroidx/compose/runtime/RecomposeScopeImpl;-><init>(Landroidx/compose/runtime/CompositionImpl;)V
-HSPLandroidx/compose/runtime/RecomposeScopeImpl;->invalidate()V
-HSPLandroidx/compose/runtime/Recomposer$Companion;-><init>()V
-HSPLandroidx/compose/runtime/Recomposer$RecomposerInfoImpl;-><init>()V
-HSPLandroidx/compose/runtime/Recomposer$State;-><clinit>()V
-HSPLandroidx/compose/runtime/Recomposer$State;-><init>(ILjava/lang/String;)V
-HSPLandroidx/compose/runtime/Recomposer$broadcastFrameClock$1;-><init>(Landroidx/compose/runtime/Recomposer;)V
-HSPLandroidx/compose/runtime/Recomposer$broadcastFrameClock$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/Recomposer$effectJob$1$1;-><init>(Landroidx/compose/runtime/Recomposer;)V
-HSPLandroidx/compose/runtime/Recomposer$join$2;-><init>(Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/runtime/Recomposer$join$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/runtime/Recomposer$join$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/Recomposer$join$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/Recomposer$performRecompose$1$1;-><init>(Landroidx/compose/runtime/ControlledComposition;Landroidx/compose/runtime/collection/IdentityArraySet;)V
-HSPLandroidx/compose/runtime/Recomposer$performRecompose$1$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/Recomposer$readObserverOf$1;-><init>(Landroidx/compose/runtime/ControlledComposition;)V
-HSPLandroidx/compose/runtime/Recomposer$readObserverOf$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/Recomposer$recompositionRunner$2$2;-><init>(Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/MonotonicFrameClock;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/runtime/Recomposer$recompositionRunner$2$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/runtime/Recomposer$recompositionRunner$2$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/Recomposer$recompositionRunner$2$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/Recomposer$recompositionRunner$2$unregisterApplyObserver$1;-><init>(Landroidx/compose/runtime/Recomposer;)V
-HSPLandroidx/compose/runtime/Recomposer$recompositionRunner$2$unregisterApplyObserver$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/Recomposer$recompositionRunner$2;-><init>(Landroidx/compose/runtime/Recomposer;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/MonotonicFrameClock;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/runtime/Recomposer$recompositionRunner$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/runtime/Recomposer$recompositionRunner$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/Recomposer$recompositionRunner$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/Recomposer$runRecomposeAndApplyChanges$2$2;-><init>(Landroidx/compose/runtime/Recomposer;Ljava/util/List;Ljava/util/List;Ljava/util/Set;Ljava/util/List;Ljava/util/Set;)V
-HSPLandroidx/compose/runtime/Recomposer$runRecomposeAndApplyChanges$2$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/Recomposer$runRecomposeAndApplyChanges$2;-><init>(Landroidx/compose/runtime/Recomposer;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/runtime/Recomposer$runRecomposeAndApplyChanges$2;->access$invokeSuspend$fillToInsert(Ljava/util/List;Landroidx/compose/runtime/Recomposer;)V
-HSPLandroidx/compose/runtime/Recomposer$runRecomposeAndApplyChanges$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/Recomposer$runRecomposeAndApplyChanges$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/Recomposer$writeObserverOf$1;-><init>(Landroidx/compose/runtime/ControlledComposition;Landroidx/compose/runtime/collection/IdentityArraySet;)V
-HSPLandroidx/compose/runtime/Recomposer$writeObserverOf$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/Recomposer;-><clinit>()V
-HSPLandroidx/compose/runtime/Recomposer;-><init>(Lkotlin/coroutines/CoroutineContext;)V
-HSPLandroidx/compose/runtime/Recomposer;->access$performRecompose(Landroidx/compose/runtime/Recomposer;Landroidx/compose/runtime/ControlledComposition;Landroidx/compose/runtime/collection/IdentityArraySet;)Landroidx/compose/runtime/ControlledComposition;
-HSPLandroidx/compose/runtime/Recomposer;->access$recordComposerModificationsLocked(Landroidx/compose/runtime/Recomposer;)V
-HSPLandroidx/compose/runtime/Recomposer;->applyAndCheck(Landroidx/compose/runtime/snapshots/MutableSnapshot;)V
-HSPLandroidx/compose/runtime/Recomposer;->composeInitial$runtime_release(Landroidx/compose/runtime/ControlledComposition;Landroidx/compose/runtime/internal/ComposableLambdaImpl;)V
-HSPLandroidx/compose/runtime/Recomposer;->deriveStateLocked()Lkotlinx/coroutines/CancellableContinuation;
-HSPLandroidx/compose/runtime/Recomposer;->getCollectingParameterInformation$runtime_release()Z
-HSPLandroidx/compose/runtime/Recomposer;->getCompoundHashKey$runtime_release()I
-HSPLandroidx/compose/runtime/Recomposer;->getEffectCoroutineContext$runtime_release()Lkotlin/coroutines/CoroutineContext;
-HSPLandroidx/compose/runtime/Recomposer;->getHasSchedulingWork()Z
-HSPLandroidx/compose/runtime/Recomposer;->invalidate$runtime_release(Landroidx/compose/runtime/ControlledComposition;)V
-HSPLandroidx/compose/runtime/Recomposer;->performInitialMovableContentInserts(Landroidx/compose/runtime/ControlledComposition;)V
-HSPLandroidx/compose/runtime/ReferentialEqualityPolicy;-><clinit>()V
-HSPLandroidx/compose/runtime/ReferentialEqualityPolicy;-><init>()V
-HSPLandroidx/compose/runtime/SkippableUpdater;-><init>(Landroidx/compose/runtime/Composer;)V
-HSPLandroidx/compose/runtime/SlotReader;-><init>(Landroidx/compose/runtime/SlotTable;)V
-HSPLandroidx/compose/runtime/SlotReader;->anchor(I)Landroidx/compose/runtime/Anchor;
-HSPLandroidx/compose/runtime/SlotReader;->aux([II)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/SlotReader;->close()V
-HSPLandroidx/compose/runtime/SlotReader;->endGroup()V
-HSPLandroidx/compose/runtime/SlotReader;->getGroupAux()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/SlotReader;->getGroupKey()I
-HSPLandroidx/compose/runtime/SlotReader;->groupGet(II)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/SlotReader;->groupSize(I)I
-HSPLandroidx/compose/runtime/SlotReader;->isNode(I)Z
-HSPLandroidx/compose/runtime/SlotReader;->node(I)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/SlotReader;->nodeCount(I)I
-HSPLandroidx/compose/runtime/SlotReader;->objectKey([II)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/SlotReader;->parent(I)I
-HSPLandroidx/compose/runtime/SlotReader;->reposition(I)V
-HSPLandroidx/compose/runtime/SlotReader;->skipGroup()I
-HSPLandroidx/compose/runtime/SlotReader;->skipToGroupEnd()V
-HSPLandroidx/compose/runtime/SlotReader;->startGroup()V
-HSPLandroidx/compose/runtime/SlotTable;-><init>()V
-HSPLandroidx/compose/runtime/SlotTable;->anchorIndex(Landroidx/compose/runtime/Anchor;)I
-HSPLandroidx/compose/runtime/SlotTable;->openReader()Landroidx/compose/runtime/SlotReader;
-HSPLandroidx/compose/runtime/SlotTable;->openWriter()Landroidx/compose/runtime/SlotWriter;
-HSPLandroidx/compose/runtime/SlotTable;->ownsAnchor(Landroidx/compose/runtime/Anchor;)Z
-HSPLandroidx/compose/runtime/SlotTableKt;->access$containsMark([II)Z
-HSPLandroidx/compose/runtime/SlotTableKt;->access$groupSize([II)I
-HSPLandroidx/compose/runtime/SlotTableKt;->access$hasAux([II)Z
-HSPLandroidx/compose/runtime/SlotTableKt;->access$isNode([II)Z
-HSPLandroidx/compose/runtime/SlotTableKt;->access$locationOf(Ljava/util/ArrayList;II)I
-HSPLandroidx/compose/runtime/SlotTableKt;->access$nodeCount([II)I
-HSPLandroidx/compose/runtime/SlotTableKt;->access$slotAnchor([II)I
-HSPLandroidx/compose/runtime/SlotTableKt;->access$updateGroupSize([III)V
-HSPLandroidx/compose/runtime/SlotTableKt;->access$updateNodeCount([III)V
-HSPLandroidx/compose/runtime/SlotTableKt;->countOneBits(I)I
-HSPLandroidx/compose/runtime/SlotTableKt;->search(Ljava/util/ArrayList;II)I
-HSPLandroidx/compose/runtime/SlotWriter$Companion;-><init>()V
-HSPLandroidx/compose/runtime/SlotWriter$Companion;->access$moveGroup(Landroidx/compose/runtime/SlotWriter;ILandroidx/compose/runtime/SlotWriter;ZZ)Ljava/util/List;
-HSPLandroidx/compose/runtime/SlotWriter$groupSlots$1;-><init>(IILandroidx/compose/runtime/SlotWriter;)V
-HSPLandroidx/compose/runtime/SlotWriter$groupSlots$1;->hasNext()Z
-HSPLandroidx/compose/runtime/SlotWriter$groupSlots$1;->next()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/SlotWriter;-><clinit>()V
-HSPLandroidx/compose/runtime/SlotWriter;-><init>(Landroidx/compose/runtime/SlotTable;)V
-HSPLandroidx/compose/runtime/SlotWriter;->advanceBy(I)V
-HSPLandroidx/compose/runtime/SlotWriter;->anchor(I)Landroidx/compose/runtime/Anchor;
-HSPLandroidx/compose/runtime/SlotWriter;->anchorIndex(Landroidx/compose/runtime/Anchor;)I
-HSPLandroidx/compose/runtime/SlotWriter;->auxIndex([II)I
-HSPLandroidx/compose/runtime/SlotWriter;->beginInsert()V
-HSPLandroidx/compose/runtime/SlotWriter;->close()V
-HSPLandroidx/compose/runtime/SlotWriter;->dataIndex([II)I
-HSPLandroidx/compose/runtime/SlotWriter;->dataIndexToDataAddress(I)I
-HSPLandroidx/compose/runtime/SlotWriter;->endGroup()V
-HSPLandroidx/compose/runtime/SlotWriter;->endInsert()V
-HSPLandroidx/compose/runtime/SlotWriter;->ensureStarted(I)V
-HSPLandroidx/compose/runtime/SlotWriter;->getSize$runtime_release()I
-HSPLandroidx/compose/runtime/SlotWriter;->groupIndexToAddress(I)I
-HSPLandroidx/compose/runtime/SlotWriter;->groupSize(I)I
-HSPLandroidx/compose/runtime/SlotWriter;->insertGroups(I)V
-HSPLandroidx/compose/runtime/SlotWriter;->insertSlots(II)V
-HSPLandroidx/compose/runtime/SlotWriter;->markGroup$default(Landroidx/compose/runtime/SlotWriter;)V
-HSPLandroidx/compose/runtime/SlotWriter;->moveFrom(Landroidx/compose/runtime/SlotTable;I)V
-HSPLandroidx/compose/runtime/SlotWriter;->moveGroupGapTo(I)V
-HSPLandroidx/compose/runtime/SlotWriter;->moveSlotGapTo(II)V
-HSPLandroidx/compose/runtime/SlotWriter;->node(I)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/SlotWriter;->parent(I)I
-HSPLandroidx/compose/runtime/SlotWriter;->parent([II)I
-HSPLandroidx/compose/runtime/SlotWriter;->recalculateMarks()V
-HSPLandroidx/compose/runtime/SlotWriter;->removeGroup()Z
-HSPLandroidx/compose/runtime/SlotWriter;->removeGroups(II)Z
-HSPLandroidx/compose/runtime/SlotWriter;->removeSlots(III)V
-HSPLandroidx/compose/runtime/SlotWriter;->set(ILjava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/SlotWriter;->skipGroup()I
-HSPLandroidx/compose/runtime/SlotWriter;->skipToGroupEnd()V
-HSPLandroidx/compose/runtime/SlotWriter;->slot(II)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/SlotWriter;->slotIndex([II)I
-HSPLandroidx/compose/runtime/SlotWriter;->startGroup()V
-HSPLandroidx/compose/runtime/SlotWriter;->startGroup(ILjava/lang/Object;ZLjava/lang/Object;)V
-HSPLandroidx/compose/runtime/SlotWriter;->update(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/SlotWriter;->updateAux(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/SlotWriter;->updateContainsMark(I)V
-HSPLandroidx/compose/runtime/SlotWriter;->updateNodeOfGroup(ILjava/lang/Object;)V
-HSPLandroidx/compose/runtime/SnapshotMutableStateImpl$StateStateRecord;-><init>(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/SnapshotMutableStateImpl$StateStateRecord;->create()Landroidx/compose/runtime/snapshots/StateRecord;
-HSPLandroidx/compose/runtime/SnapshotMutableStateImpl;-><init>(Ljava/lang/Object;Landroidx/compose/runtime/SnapshotMutationPolicy;)V
-HSPLandroidx/compose/runtime/SnapshotMutableStateImpl;->getFirstStateRecord()Landroidx/compose/runtime/snapshots/StateRecord;
-HSPLandroidx/compose/runtime/SnapshotMutableStateImpl;->getValue()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/SnapshotMutableStateImpl;->prependStateRecord(Landroidx/compose/runtime/snapshots/StateRecord;)V
-HSPLandroidx/compose/runtime/SnapshotMutableStateImpl;->setValue(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/SnapshotStateKt__DerivedStateKt;-><clinit>()V
-HSPLandroidx/compose/runtime/SnapshotStateKt__SnapshotFlowKt$snapshotFlow$1$readObserver$1;-><init>(Ljava/util/LinkedHashSet;)V
-HSPLandroidx/compose/runtime/SnapshotStateKt__SnapshotFlowKt$snapshotFlow$1$readObserver$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/SnapshotStateKt__SnapshotFlowKt$snapshotFlow$1$unregisterApplyObserver$1;-><init>(Lkotlinx/coroutines/channels/AbstractChannel;)V
-HSPLandroidx/compose/runtime/SnapshotStateKt__SnapshotFlowKt$snapshotFlow$1$unregisterApplyObserver$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/SnapshotStateKt__SnapshotFlowKt$snapshotFlow$1;-><init>(Lkotlin/jvm/functions/Function0;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/runtime/SnapshotStateKt__SnapshotFlowKt$snapshotFlow$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/runtime/SnapshotStateKt__SnapshotFlowKt$snapshotFlow$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/SnapshotStateKt__SnapshotFlowKt$snapshotFlow$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/SnapshotThreadLocal;-><init>()V
-HSPLandroidx/compose/runtime/SnapshotThreadLocal;->get()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/SnapshotThreadLocal;->set(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/Stack;-><init>()V
-HSPLandroidx/compose/runtime/Stack;->pop()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/Stack;->push(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/StaticProvidableCompositionLocal;-><init>(Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/runtime/StaticProvidableCompositionLocal;->provided$runtime_release(Ljava/lang/Object;Landroidx/compose/runtime/Composer;)Landroidx/compose/runtime/State;
-HSPLandroidx/compose/runtime/StaticValueHolder;-><init>(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/StaticValueHolder;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/runtime/StaticValueHolder;->getValue()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/StructuralEqualityPolicy;-><clinit>()V
-HSPLandroidx/compose/runtime/StructuralEqualityPolicy;-><init>()V
-HSPLandroidx/compose/runtime/StructuralEqualityPolicy;->equivalent(Ljava/lang/Object;Ljava/lang/Object;)Z
-HSPLandroidx/compose/runtime/Updater;->set-impl(Landroidx/compose/runtime/Composer;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/runtime/collection/IdentityArrayIntMap;-><init>()V
-HSPLandroidx/compose/runtime/collection/IdentityArrayIntMap;->add(ILjava/lang/Object;)I
-HSPLandroidx/compose/runtime/collection/IdentityArrayMap;-><init>()V
-HSPLandroidx/compose/runtime/collection/IdentityArrayMap;->find(Ljava/lang/Object;)I
-HSPLandroidx/compose/runtime/collection/IdentityArrayMap;->get(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/collection/IdentityArrayMap;->set(Ljava/lang/Object;Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/collection/IdentityArraySet;-><init>()V
-HSPLandroidx/compose/runtime/collection/IdentityArraySet;->add(Ljava/lang/Object;)Z
-HSPLandroidx/compose/runtime/collection/IdentityArraySet;->clear()V
-HSPLandroidx/compose/runtime/collection/IdentityArraySet;->contains(Ljava/lang/Object;)Z
-HSPLandroidx/compose/runtime/collection/IdentityArraySet;->find(Ljava/lang/Object;)I
-HSPLandroidx/compose/runtime/collection/IdentityArraySet;->get(I)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/collection/IdentityArraySet;->isEmpty()Z
-HSPLandroidx/compose/runtime/collection/IdentityArraySet;->remove(Ljava/lang/Object;)Z
-HSPLandroidx/compose/runtime/collection/IdentityScopeMap;-><init>()V
-HSPLandroidx/compose/runtime/collection/IdentityScopeMap;->add(Ljava/lang/Object;Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/collection/IdentityScopeMap;->contains(Ljava/lang/Object;)Z
-HSPLandroidx/compose/runtime/collection/IdentityScopeMap;->find(Ljava/lang/Object;)I
-HSPLandroidx/compose/runtime/collection/IdentityScopeMap;->remove(Ljava/lang/Object;Ljava/lang/Object;)Z
-HSPLandroidx/compose/runtime/collection/IdentityScopeMap;->removeScope(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/collection/IdentityScopeMap;->scopeSetAt(I)Landroidx/compose/runtime/collection/IdentityArraySet;
-HSPLandroidx/compose/runtime/collection/MutableVector$MutableVectorList;-><init>(Landroidx/compose/runtime/collection/MutableVector;)V
-HSPLandroidx/compose/runtime/collection/MutableVector$MutableVectorList;->get(I)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/collection/MutableVector$MutableVectorList;->indexOf(Ljava/lang/Object;)I
-HSPLandroidx/compose/runtime/collection/MutableVector$MutableVectorList;->isEmpty()Z
-HSPLandroidx/compose/runtime/collection/MutableVector$MutableVectorList;->iterator()Ljava/util/Iterator;
-HSPLandroidx/compose/runtime/collection/MutableVector$MutableVectorList;->size()I
-HSPLandroidx/compose/runtime/collection/MutableVector$VectorListIterator;-><init>(ILjava/util/List;)V
-HSPLandroidx/compose/runtime/collection/MutableVector$VectorListIterator;->hasNext()Z
-HSPLandroidx/compose/runtime/collection/MutableVector$VectorListIterator;->next()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/collection/MutableVector;-><init>([Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/collection/MutableVector;->add(ILjava/lang/Object;)V
-HSPLandroidx/compose/runtime/collection/MutableVector;->add(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/collection/MutableVector;->addAll(ILandroidx/compose/runtime/collection/MutableVector;)V
-HSPLandroidx/compose/runtime/collection/MutableVector;->asMutableList()Ljava/util/List;
-HSPLandroidx/compose/runtime/collection/MutableVector;->clear()V
-HSPLandroidx/compose/runtime/collection/MutableVector;->contains(Ljava/lang/Object;)Z
-HSPLandroidx/compose/runtime/collection/MutableVector;->ensureCapacity(I)V
-HSPLandroidx/compose/runtime/collection/MutableVector;->isEmpty()Z
-HSPLandroidx/compose/runtime/collection/MutableVector;->isNotEmpty()Z
-HSPLandroidx/compose/runtime/collection/MutableVector;->remove(Ljava/lang/Object;)Z
-HSPLandroidx/compose/runtime/collection/MutableVector;->removeAt(I)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/collection/MutableVector;->removeRange(II)V
-HSPLandroidx/compose/runtime/collection/MutableVectorKt;->access$checkIndex(ILjava/util/List;)V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/ExtensionsKt;->persistentHashMapOf()Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMap;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableList/AbstractPersistentList;-><init>()V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableList/AbstractPersistentList;->remove(Ljava/lang/Object;)Landroidx/compose/runtime/external/kotlinx/collections/immutable/PersistentList;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableList/SmallPersistentVector;-><clinit>()V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableList/SmallPersistentVector;-><init>([Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableList/SmallPersistentVector;->add(Ljava/lang/Object;)Landroidx/compose/runtime/external/kotlinx/collections/immutable/PersistentList;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableList/SmallPersistentVector;->get(I)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableList/SmallPersistentVector;->getSize()I
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableList/SmallPersistentVector;->indexOf(Ljava/lang/Object;)I
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableList/SmallPersistentVector;->removeAt(I)Landroidx/compose/runtime/external/kotlinx/collections/immutable/PersistentList;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/MapEntry;-><init>(Ljava/lang/Object;Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/MapEntry;->getKey()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/MapEntry;->getValue()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMap;-><clinit>()V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMap;-><init>(Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;I)V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMap;->builder()Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapBuilder;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMap;->containsKey(Ljava/lang/Object;)Z
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMap;->get(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMap;->put(Ljava/lang/Object;Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/persistentOrderedSet/Links;)Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMap;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapBaseIterator;-><init>(Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;[Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNodeBaseIterator;)V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapBaseIterator;->ensureNextEntryIsReady()V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapBaseIterator;->hasNext()Z
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapBaseIterator;->moveToNextNodeWithData(I)I
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapBaseIterator;->next()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapBuilder;-><init>(Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMap;)V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapBuilder;->build$1()Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMap;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapBuilder;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapBuilder;->putAll(Ljava/util/Map;)V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapBuilder;->setSize(I)V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapEntries;-><init>(Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMap;)V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapEntries;->getSize()I
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapEntries;->iterator()Ljava/util/Iterator;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapEntriesIterator;-><init>(Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;)V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode$ModificationResult;-><init>(Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;I)V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;-><clinit>()V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;-><init>(II[Ljava/lang/Object;Landroidx/lifecycle/runtime/R$id;)V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;->bufferMoveEntryToNode(IIILjava/lang/Object;Ljava/lang/Object;ILandroidx/lifecycle/runtime/R$id;)[Ljava/lang/Object;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;->containsKey(IILjava/lang/Object;)Z
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;->elementsIdentityEquals(Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;)Z
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;->entryKeyIndex$runtime_release(I)I
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;->get(IILjava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;->hasEntryAt$runtime_release(I)Z
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;->hasNodeAt(I)Z
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;->makeNode(ILjava/lang/Object;Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/Object;ILandroidx/lifecycle/runtime/R$id;)Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;->mutablePut(ILjava/lang/Object;Ljava/lang/Object;ILandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapBuilder;)Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;->mutablePutAll(Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;ILandroidx/compose/runtime/external/kotlinx/collections/immutable/internal/DeltaCounter;Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapBuilder;)Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;->nodeAtIndex$runtime_release(I)Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;->nodeIndex$runtime_release(I)I
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;->put(IILjava/lang/Object;Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/persistentOrderedSet/Links;)Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode$ModificationResult;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;->valueAtKeyIndex(I)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNodeBaseIterator;-><init>()V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNodeEntriesIterator;-><init>()V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNodeEntriesIterator;->next()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/persistentOrderedSet/Links;-><init>()V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/persistentOrderedSet/Links;-><init>(Ljava/lang/Object;Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/persistentOrderedSet/PersistentOrderedSet;-><clinit>()V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/persistentOrderedSet/PersistentOrderedSet;-><init>(Ljava/lang/Object;Ljava/lang/Object;Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMap;)V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/persistentOrderedSet/PersistentOrderedSet;->add(Landroidx/compose/runtime/Recomposer$RecomposerInfoImpl;)Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/persistentOrderedSet/PersistentOrderedSet;
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/persistentOrderedSet/PersistentOrderedSet;->getSize()I
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/internal/DeltaCounter;-><init>(I)V
-HSPLandroidx/compose/runtime/external/kotlinx/collections/immutable/internal/ListImplementation;->checkElementIndex$runtime_release(II)V
-HSPLandroidx/compose/runtime/internal/ComposableLambdaImpl$invoke$1;-><init>(Landroidx/compose/runtime/internal/ComposableLambdaImpl;Ljava/lang/Object;I)V
-HSPLandroidx/compose/runtime/internal/ComposableLambdaImpl$invoke$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/internal/ComposableLambdaImpl$invoke$2;-><init>(Landroidx/compose/runtime/internal/ComposableLambdaImpl;Ljava/lang/Object;Ljava/lang/Object;I)V
-HSPLandroidx/compose/runtime/internal/ComposableLambdaImpl;-><init>(IZ)V
-HSPLandroidx/compose/runtime/internal/ComposableLambdaImpl;->invoke(Ljava/lang/Object;Landroidx/compose/runtime/Composer;I)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/internal/ComposableLambdaImpl;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/internal/ComposableLambdaImpl;->invoke(Ljava/lang/Object;Ljava/lang/Object;Landroidx/compose/runtime/Composer;I)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/internal/ComposableLambdaImpl;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/internal/ComposableLambdaImpl;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/internal/ComposableLambdaImpl;->trackRead(Landroidx/compose/runtime/Composer;)V
-HSPLandroidx/compose/runtime/internal/ComposableLambdaImpl;->update(Lkotlin/jvm/internal/Lambda;)V
-HSPLandroidx/compose/runtime/internal/ComposableLambdaKt;->bitsForSlot(II)I
-HSPLandroidx/compose/runtime/internal/ComposableLambdaKt;->composableLambda(Landroidx/compose/runtime/Composer;ILkotlin/jvm/internal/Lambda;)Landroidx/compose/runtime/internal/ComposableLambdaImpl;
-HSPLandroidx/compose/runtime/internal/ComposableLambdaKt;->composableLambdaInstance(ILkotlin/jvm/internal/Lambda;Z)Landroidx/compose/runtime/internal/ComposableLambdaImpl;
-HSPLandroidx/compose/runtime/internal/ComposableLambdaKt;->replacableWith(Landroidx/compose/runtime/RecomposeScope;Landroidx/compose/runtime/RecomposeScope;)Z
-HSPLandroidx/compose/runtime/internal/ThreadMap;-><init>(I[J[Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/internal/ThreadMap;->find(J)I
-HSPLandroidx/compose/runtime/internal/ThreadMap;->newWith(JLjava/lang/Object;)Landroidx/compose/runtime/internal/ThreadMap;
-HSPLandroidx/compose/runtime/internal/ThreadMapKt;-><clinit>()V
-HSPLandroidx/compose/runtime/saveable/ListSaverKt$listSaver$1;-><init>(Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/runtime/saveable/ListSaverKt$listSaver$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/saveable/RememberSaveableKt$rememberSaveable$1$invoke$$inlined$onDispose$1;-><init>(Landroidx/compose/runtime/saveable/SaveableStateRegistry$Entry;)V
-HSPLandroidx/compose/runtime/saveable/RememberSaveableKt$rememberSaveable$1$valueProvider$1$1$1;-><init>(Landroidx/compose/runtime/saveable/SaveableStateRegistry;)V
-HSPLandroidx/compose/runtime/saveable/RememberSaveableKt$rememberSaveable$1$valueProvider$1$1$1;->canBeSaved(Ljava/lang/Object;)Z
-HSPLandroidx/compose/runtime/saveable/RememberSaveableKt$rememberSaveable$1$valueProvider$1;-><init>(Landroidx/compose/runtime/State;Landroidx/compose/runtime/State;Landroidx/compose/runtime/saveable/SaveableStateRegistry;)V
-HSPLandroidx/compose/runtime/saveable/RememberSaveableKt$rememberSaveable$1$valueProvider$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/saveable/RememberSaveableKt$rememberSaveable$1;-><init>(Landroidx/compose/runtime/saveable/SaveableStateRegistry;Ljava/lang/String;Landroidx/compose/runtime/MutableState;Landroidx/compose/runtime/MutableState;)V
-HSPLandroidx/compose/runtime/saveable/RememberSaveableKt$rememberSaveable$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/saveable/RememberSaveableKt;->rememberSaveable([Ljava/lang/Object;Landroidx/compose/runtime/saveable/SaverKt$Saver$1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/saveable/SaveableStateHolderImpl$Companion$Saver$1;-><clinit>()V
-HSPLandroidx/compose/runtime/saveable/SaveableStateHolderImpl$Companion$Saver$1;-><init>()V
-HSPLandroidx/compose/runtime/saveable/SaveableStateHolderImpl$Companion$Saver$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/saveable/SaveableStateHolderImpl$Companion$Saver$2;-><clinit>()V
-HSPLandroidx/compose/runtime/saveable/SaveableStateHolderImpl$Companion$Saver$2;-><init>()V
-HSPLandroidx/compose/runtime/saveable/SaveableStateHolderImpl$RegistryHolder$registry$1;-><init>(Landroidx/compose/runtime/saveable/SaveableStateHolderImpl;)V
-HSPLandroidx/compose/runtime/saveable/SaveableStateHolderImpl$RegistryHolder;-><init>(Landroidx/compose/runtime/saveable/SaveableStateHolderImpl;Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/saveable/SaveableStateHolderImpl$RegistryHolder;->saveTo(Ljava/util/Map;)V
-HSPLandroidx/compose/runtime/saveable/SaveableStateHolderImpl$SaveableStateProvider$1$1$invoke$$inlined$onDispose$1;-><init>(Landroidx/compose/runtime/saveable/SaveableStateHolderImpl$RegistryHolder;Landroidx/compose/runtime/saveable/SaveableStateHolderImpl;Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/saveable/SaveableStateHolderImpl$SaveableStateProvider$1$1$invoke$$inlined$onDispose$1;->dispose()V
-HSPLandroidx/compose/runtime/saveable/SaveableStateHolderImpl$SaveableStateProvider$1$1;-><init>(Landroidx/compose/runtime/saveable/SaveableStateHolderImpl$RegistryHolder;Landroidx/compose/runtime/saveable/SaveableStateHolderImpl;Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/saveable/SaveableStateHolderImpl$SaveableStateProvider$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/saveable/SaveableStateHolderImpl;-><clinit>()V
-HSPLandroidx/compose/runtime/saveable/SaveableStateHolderImpl;-><init>(I)V
-HSPLandroidx/compose/runtime/saveable/SaveableStateHolderImpl;-><init>(Ljava/util/Map;)V
-HSPLandroidx/compose/runtime/saveable/SaveableStateHolderImpl;->SaveableStateProvider(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;I)V
-HSPLandroidx/compose/runtime/saveable/SaveableStateHolderKt$rememberSaveableStateHolder$1;-><clinit>()V
-HSPLandroidx/compose/runtime/saveable/SaveableStateHolderKt$rememberSaveableStateHolder$1;-><init>()V
-HSPLandroidx/compose/runtime/saveable/SaveableStateHolderKt$rememberSaveableStateHolder$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/saveable/SaveableStateRegistryImpl$registerProvider$3;-><init>(Landroidx/compose/runtime/saveable/SaveableStateRegistryImpl;Ljava/lang/String;Landroidx/compose/runtime/saveable/RememberSaveableKt$rememberSaveable$1$valueProvider$1;)V
-HSPLandroidx/compose/runtime/saveable/SaveableStateRegistryImpl;-><init>(Ljava/util/Map;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/runtime/saveable/SaveableStateRegistryImpl;->canBeSaved(Ljava/lang/Object;)Z
-HSPLandroidx/compose/runtime/saveable/SaveableStateRegistryImpl;->consumeRestored(Ljava/lang/String;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/saveable/SaveableStateRegistryImpl;->performSave()Ljava/util/Map;
-HSPLandroidx/compose/runtime/saveable/SaveableStateRegistryImpl;->registerProvider(Ljava/lang/String;Landroidx/compose/runtime/saveable/RememberSaveableKt$rememberSaveable$1$valueProvider$1;)Landroidx/compose/runtime/saveable/SaveableStateRegistry$Entry;
-HSPLandroidx/compose/runtime/saveable/SaveableStateRegistryKt$LocalSaveableStateRegistry$1;-><clinit>()V
-HSPLandroidx/compose/runtime/saveable/SaveableStateRegistryKt$LocalSaveableStateRegistry$1;-><init>()V
-HSPLandroidx/compose/runtime/saveable/SaveableStateRegistryKt;-><clinit>()V
-HSPLandroidx/compose/runtime/saveable/SaverKt$AutoSaver$1;-><clinit>()V
-HSPLandroidx/compose/runtime/saveable/SaverKt$AutoSaver$1;-><init>()V
-HSPLandroidx/compose/runtime/saveable/SaverKt$AutoSaver$2;-><clinit>()V
-HSPLandroidx/compose/runtime/saveable/SaverKt$AutoSaver$2;-><init>()V
-HSPLandroidx/compose/runtime/saveable/SaverKt$Saver$1;-><init>(Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/runtime/saveable/SaverKt$Saver$1;->save(Landroidx/compose/runtime/saveable/SaverScope;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/saveable/SaverKt;-><clinit>()V
-HSPLandroidx/compose/runtime/snapshots/GlobalSnapshot$takeNestedMutableSnapshot$1;-><init>(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/runtime/snapshots/GlobalSnapshot$takeNestedMutableSnapshot$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/snapshots/GlobalSnapshot$takeNestedSnapshot$1;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/runtime/snapshots/GlobalSnapshot$takeNestedSnapshot$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/snapshots/GlobalSnapshot;-><init>(ILandroidx/compose/runtime/snapshots/SnapshotIdSet;)V
-HSPLandroidx/compose/runtime/snapshots/GlobalSnapshot;->dispose()V
-HSPLandroidx/compose/runtime/snapshots/GlobalSnapshot;->notifyObjectsInitialized$runtime_release()V
-HSPLandroidx/compose/runtime/snapshots/GlobalSnapshot;->takeNestedMutableSnapshot(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Landroidx/compose/runtime/snapshots/MutableSnapshot;
-HSPLandroidx/compose/runtime/snapshots/GlobalSnapshot;->takeNestedSnapshot(Lkotlin/jvm/functions/Function1;)Landroidx/compose/runtime/snapshots/Snapshot;
-HSPLandroidx/compose/runtime/snapshots/MutableSnapshot;-><init>(ILandroidx/compose/runtime/snapshots/SnapshotIdSet;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/runtime/snapshots/MutableSnapshot;->advance$runtime_release()V
-HSPLandroidx/compose/runtime/snapshots/MutableSnapshot;->apply()Landroidx/compose/runtime/snapshots/SnapshotApplyResult;
-HSPLandroidx/compose/runtime/snapshots/MutableSnapshot;->closeLocked$runtime_release()V
-HSPLandroidx/compose/runtime/snapshots/MutableSnapshot;->dispose()V
-HSPLandroidx/compose/runtime/snapshots/MutableSnapshot;->getModified$runtime_release()Ljava/util/Set;
-HSPLandroidx/compose/runtime/snapshots/MutableSnapshot;->getReadObserver$runtime_release()Lkotlin/jvm/functions/Function1;
-HSPLandroidx/compose/runtime/snapshots/MutableSnapshot;->getReadOnly()Z
-HSPLandroidx/compose/runtime/snapshots/MutableSnapshot;->getWriteObserver$runtime_release()Lkotlin/jvm/functions/Function1;
-HSPLandroidx/compose/runtime/snapshots/MutableSnapshot;->innerApplyLocked$runtime_release(ILjava/util/HashMap;Landroidx/compose/runtime/snapshots/SnapshotIdSet;)Landroidx/compose/runtime/snapshots/SnapshotApplyResult;
-HSPLandroidx/compose/runtime/snapshots/MutableSnapshot;->nestedDeactivated$runtime_release(Landroidx/compose/runtime/snapshots/Snapshot;)V
-HSPLandroidx/compose/runtime/snapshots/MutableSnapshot;->notifyObjectsInitialized$runtime_release()V
-HSPLandroidx/compose/runtime/snapshots/MutableSnapshot;->recordModified$runtime_release(Landroidx/compose/runtime/snapshots/StateObject;)V
-HSPLandroidx/compose/runtime/snapshots/MutableSnapshot;->recordPrevious$runtime_release(I)V
-HSPLandroidx/compose/runtime/snapshots/MutableSnapshot;->releasePinnedSnapshotsForCloseLocked$runtime_release()V
-HSPLandroidx/compose/runtime/snapshots/MutableSnapshot;->setModified(Ljava/util/HashSet;)V
-HSPLandroidx/compose/runtime/snapshots/ReadonlySnapshot;-><init>(ILandroidx/compose/runtime/snapshots/SnapshotIdSet;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/runtime/snapshots/ReadonlySnapshot;->dispose()V
-HSPLandroidx/compose/runtime/snapshots/ReadonlySnapshot;->getReadObserver$runtime_release()Lkotlin/jvm/functions/Function1;
-HSPLandroidx/compose/runtime/snapshots/ReadonlySnapshot;->nestedDeactivated$runtime_release(Landroidx/compose/runtime/snapshots/Snapshot;)V
-HSPLandroidx/compose/runtime/snapshots/Snapshot$Companion$registerApplyObserver$2;-><init>(Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/runtime/snapshots/Snapshot$Companion;->observe(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/snapshots/Snapshot;-><init>(ILandroidx/compose/runtime/snapshots/SnapshotIdSet;)V
-HSPLandroidx/compose/runtime/snapshots/Snapshot;->closeAndReleasePinning$runtime_release()V
-HSPLandroidx/compose/runtime/snapshots/Snapshot;->closeLocked$runtime_release()V
-HSPLandroidx/compose/runtime/snapshots/Snapshot;->dispose()V
-HSPLandroidx/compose/runtime/snapshots/Snapshot;->getId()I
-HSPLandroidx/compose/runtime/snapshots/Snapshot;->makeCurrent()Landroidx/compose/runtime/snapshots/Snapshot;
-HSPLandroidx/compose/runtime/snapshots/Snapshot;->releasePinnedSnapshotsForCloseLocked$runtime_release()V
-HSPLandroidx/compose/runtime/snapshots/Snapshot;->restoreCurrent(Landroidx/compose/runtime/snapshots/Snapshot;)V
-HSPLandroidx/compose/runtime/snapshots/Snapshot;->setId$runtime_release(I)V
-HSPLandroidx/compose/runtime/snapshots/Snapshot;->setInvalid$runtime_release(Landroidx/compose/runtime/snapshots/SnapshotIdSet;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotApplyResult$Success;-><clinit>()V
-HSPLandroidx/compose/runtime/snapshots/SnapshotApplyResult$Success;-><init>()V
-HSPLandroidx/compose/runtime/snapshots/SnapshotApplyResult;-><init>()V
-HSPLandroidx/compose/runtime/snapshots/SnapshotDoubleIndexHeap;-><init>()V
-HSPLandroidx/compose/runtime/snapshots/SnapshotDoubleIndexHeap;->add(I)I
-HSPLandroidx/compose/runtime/snapshots/SnapshotDoubleIndexHeap;->swap(II)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotIdSet;-><clinit>()V
-HSPLandroidx/compose/runtime/snapshots/SnapshotIdSet;-><init>(JJI[I)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotIdSet;->andNot(Landroidx/compose/runtime/snapshots/SnapshotIdSet;)Landroidx/compose/runtime/snapshots/SnapshotIdSet;
-HSPLandroidx/compose/runtime/snapshots/SnapshotIdSet;->clear(I)Landroidx/compose/runtime/snapshots/SnapshotIdSet;
-HSPLandroidx/compose/runtime/snapshots/SnapshotIdSet;->get(I)Z
-HSPLandroidx/compose/runtime/snapshots/SnapshotIdSet;->or(Landroidx/compose/runtime/snapshots/SnapshotIdSet;)Landroidx/compose/runtime/snapshots/SnapshotIdSet;
-HSPLandroidx/compose/runtime/snapshots/SnapshotIdSet;->set(I)Landroidx/compose/runtime/snapshots/SnapshotIdSet;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt$advanceGlobalSnapshot$2;-><clinit>()V
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt$advanceGlobalSnapshot$2;-><init>()V
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt$advanceGlobalSnapshot$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt$emptyLambda$1;-><clinit>()V
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt$emptyLambda$1;-><init>()V
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt$emptyLambda$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt$mergedReadObserver$1;-><init>(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt$mergedReadObserver$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt$mergedWriteObserver$1;-><init>(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt$mergedWriteObserver$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt$takeNewSnapshot$1;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt$takeNewSnapshot$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;-><clinit>()V
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->access$advanceGlobalSnapshot()V
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->access$mergedWriteObserver(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lkotlin/jvm/functions/Function1;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->access$optimisticMerges(Landroidx/compose/runtime/snapshots/MutableSnapshot;Landroidx/compose/runtime/snapshots/MutableSnapshot;Landroidx/compose/runtime/snapshots/SnapshotIdSet;)Ljava/util/HashMap;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->access$validateOpen(Landroidx/compose/runtime/snapshots/Snapshot;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->addRange(IILandroidx/compose/runtime/snapshots/SnapshotIdSet;)Landroidx/compose/runtime/snapshots/SnapshotIdSet;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->advanceGlobalSnapshot(Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->createTransparentSnapshotWithNoParentReadObserver(Landroidx/compose/runtime/snapshots/Snapshot;Lkotlin/jvm/functions/Function1;Z)Landroidx/compose/runtime/snapshots/Snapshot;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->current(Landroidx/compose/runtime/snapshots/StateRecord;)Landroidx/compose/runtime/snapshots/StateRecord;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->current(Landroidx/compose/runtime/snapshots/StateRecord;Landroidx/compose/runtime/snapshots/Snapshot;)Landroidx/compose/runtime/snapshots/StateRecord;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->currentSnapshot()Landroidx/compose/runtime/snapshots/Snapshot;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->mergedReadObserver(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Z)Lkotlin/jvm/functions/Function1;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->newOverwritableRecord(Landroidx/compose/runtime/snapshots/StateRecord;Landroidx/compose/runtime/snapshots/StateObject;)Landroidx/compose/runtime/snapshots/StateRecord;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->newWritableRecord(Landroidx/compose/runtime/snapshots/StateRecord;Landroidx/compose/runtime/snapshots/StateObject;Landroidx/compose/runtime/snapshots/Snapshot;)Landroidx/compose/runtime/snapshots/StateRecord;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->notifyWrite(Landroidx/compose/runtime/snapshots/Snapshot;Landroidx/compose/runtime/snapshots/StateObject;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->overwritableRecord(Landroidx/compose/runtime/SnapshotMutableStateImpl$StateStateRecord;Landroidx/compose/runtime/snapshots/StateObject;Landroidx/compose/runtime/snapshots/Snapshot;Landroidx/compose/runtime/SnapshotMutableStateImpl$StateStateRecord;)Landroidx/compose/runtime/snapshots/StateRecord;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->readable(Landroidx/compose/runtime/snapshots/StateRecord;ILandroidx/compose/runtime/snapshots/SnapshotIdSet;)Landroidx/compose/runtime/snapshots/StateRecord;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->readable(Landroidx/compose/runtime/snapshots/StateRecord;Landroidx/compose/runtime/snapshots/StateObject;)Landroidx/compose/runtime/snapshots/StateRecord;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->releasePinningLocked(I)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->takeNewGlobalSnapshot(Landroidx/compose/runtime/snapshots/Snapshot;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/snapshots/SnapshotKt;->writableRecord(Landroidx/compose/runtime/snapshots/StateRecord;Landroidx/compose/runtime/snapshots/StateObject;Landroidx/compose/runtime/snapshots/Snapshot;)Landroidx/compose/runtime/snapshots/StateRecord;
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateList$StateListStateRecord;-><init>(Landroidx/compose/runtime/external/kotlinx/collections/immutable/PersistentList;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateList$StateListStateRecord;->assign(Landroidx/compose/runtime/snapshots/StateRecord;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateList$StateListStateRecord;->create()Landroidx/compose/runtime/snapshots/StateRecord;
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateList$StateListStateRecord;->setList$runtime_release(Landroidx/compose/runtime/external/kotlinx/collections/immutable/PersistentList;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateList;-><init>()V
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateList;->add(Ljava/lang/Object;)Z
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateList;->get(I)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateList;->getFirstStateRecord()Landroidx/compose/runtime/snapshots/StateRecord;
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateList;->getReadable$runtime_release()Landroidx/compose/runtime/snapshots/SnapshotStateList$StateListStateRecord;
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateList;->prependStateRecord(Landroidx/compose/runtime/snapshots/StateRecord;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateList;->remove(Ljava/lang/Object;)Z
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateList;->size()I
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateListKt;-><clinit>()V
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver$ObservedScopeMap$derivedStateEnterObserver$1;-><init>(Landroidx/compose/runtime/snapshots/SnapshotStateObserver$ObservedScopeMap;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver$ObservedScopeMap$derivedStateEnterObserver$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver$ObservedScopeMap$derivedStateExitObserver$1;-><init>(Landroidx/compose/runtime/snapshots/SnapshotStateObserver$ObservedScopeMap;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver$ObservedScopeMap$derivedStateExitObserver$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver$ObservedScopeMap;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver$ObservedScopeMap;->access$clearObsoleteStateReads(Landroidx/compose/runtime/snapshots/SnapshotStateObserver$ObservedScopeMap;Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver$ObservedScopeMap;->recordInvalidation(Ljava/util/Set;)Z
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver$ObservedScopeMap;->recordRead(Ljava/lang/Object;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver$ObservedScopeMap;->removeScopeIf()V
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver$applyObserver$1;-><init>(Landroidx/compose/runtime/snapshots/SnapshotStateObserver;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver$applyObserver$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver$observeReads$1$1;-><init>(Landroidx/compose/runtime/snapshots/SnapshotStateObserver;Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver$observeReads$1$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver$readObserver$1;-><init>(Landroidx/compose/runtime/snapshots/SnapshotStateObserver;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver$readObserver$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver$sendNotifications$1;-><init>(Landroidx/compose/runtime/snapshots/SnapshotStateObserver;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver$sendNotifications$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver;-><init>(Landroidx/compose/ui/platform/AndroidComposeView$snapshotObserver$1;)V
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver;->access$drainChanges(Landroidx/compose/runtime/snapshots/SnapshotStateObserver;)Z
-HSPLandroidx/compose/runtime/snapshots/SnapshotStateObserver;->ensureMap(Lkotlin/jvm/functions/Function1;)Landroidx/compose/runtime/snapshots/SnapshotStateObserver$ObservedScopeMap;
-HSPLandroidx/compose/runtime/snapshots/StateRecord;-><init>()V
-HSPLandroidx/compose/runtime/snapshots/TransparentObserverMutableSnapshot;-><init>(Landroidx/compose/runtime/snapshots/MutableSnapshot;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ZZ)V
-HSPLandroidx/compose/runtime/snapshots/TransparentObserverMutableSnapshot;->apply()Landroidx/compose/runtime/snapshots/SnapshotApplyResult;
-HSPLandroidx/compose/runtime/snapshots/TransparentObserverMutableSnapshot;->dispose()V
-HSPLandroidx/compose/runtime/snapshots/TransparentObserverMutableSnapshot;->getCurrentSnapshot()Landroidx/compose/runtime/snapshots/MutableSnapshot;
-HSPLandroidx/compose/runtime/snapshots/TransparentObserverMutableSnapshot;->getId()I
-HSPLandroidx/compose/runtime/snapshots/TransparentObserverMutableSnapshot;->getInvalid$runtime_release()Landroidx/compose/runtime/snapshots/SnapshotIdSet;
-HSPLandroidx/compose/runtime/snapshots/TransparentObserverMutableSnapshot;->getReadOnly()Z
-HSPLandroidx/compose/runtime/snapshots/TransparentObserverMutableSnapshot;->notifyObjectsInitialized$runtime_release()V
-HSPLandroidx/compose/runtime/snapshots/TransparentObserverMutableSnapshot;->recordModified$runtime_release(Landroidx/compose/runtime/snapshots/StateObject;)V
-HSPLandroidx/compose/runtime/snapshots/TransparentObserverMutableSnapshot;->takeNestedMutableSnapshot(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Landroidx/compose/runtime/snapshots/MutableSnapshot;
-HSPLandroidx/compose/runtime/tooling/InspectionTablesKt$LocalInspectionTables$1;-><clinit>()V
-HSPLandroidx/compose/runtime/tooling/InspectionTablesKt$LocalInspectionTables$1;-><init>()V
-HSPLandroidx/compose/runtime/tooling/InspectionTablesKt$LocalInspectionTables$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/runtime/tooling/InspectionTablesKt;-><clinit>()V
-HSPLandroidx/compose/ui/Alignment$Companion;-><clinit>()V
-HSPLandroidx/compose/ui/BiasAlignment$Horizontal;-><init>(F)V
-HSPLandroidx/compose/ui/BiasAlignment$Horizontal;->align(IILandroidx/compose/ui/unit/LayoutDirection;)I
-HSPLandroidx/compose/ui/BiasAlignment$Horizontal;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/BiasAlignment$Vertical;-><init>(F)V
-HSPLandroidx/compose/ui/BiasAlignment$Vertical;->align(II)I
-HSPLandroidx/compose/ui/BiasAlignment$Vertical;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/BiasAlignment;-><init>(FF)V
-HSPLandroidx/compose/ui/BiasAlignment;->align-KFBX0sM(JJLandroidx/compose/ui/unit/LayoutDirection;)J
-HSPLandroidx/compose/ui/BiasAlignment;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/CombinedModifier;-><init>(Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Modifier;)V
-HSPLandroidx/compose/ui/CombinedModifier;->all(Lkotlin/jvm/functions/Function1;)Z
-HSPLandroidx/compose/ui/CombinedModifier;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/CombinedModifier;->foldIn(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/ComposedModifier;-><init>(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;)V
-HSPLandroidx/compose/ui/ComposedModifierKt$materialize$1;-><clinit>()V
-HSPLandroidx/compose/ui/ComposedModifierKt$materialize$1;-><init>()V
-HSPLandroidx/compose/ui/ComposedModifierKt$materialize$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/ComposedModifierKt$materialize$result$1;-><init>(Landroidx/compose/runtime/Composer;)V
-HSPLandroidx/compose/ui/ComposedModifierKt$materialize$result$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/ComposedModifierKt;->composed(Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/ui/ComposedModifierKt;->materialize(Landroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/ui/Modifier$Companion;-><clinit>()V
-HSPLandroidx/compose/ui/Modifier$Companion;-><init>()V
-HSPLandroidx/compose/ui/Modifier$Companion;->all(Lkotlin/jvm/functions/Function1;)Z
-HSPLandroidx/compose/ui/Modifier$Companion;->then(Landroidx/compose/ui/Modifier;)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/ui/Modifier$Element;->all(Lkotlin/jvm/functions/Function1;)Z
-HSPLandroidx/compose/ui/Modifier$Element;->foldIn(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/Modifier$Node;-><init>()V
-HSPLandroidx/compose/ui/Modifier$Node;->detach$ui_release()V
-HSPLandroidx/compose/ui/Modifier$Node;->getNode()Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/Modifier$Node;->onAttach()V
-HSPLandroidx/compose/ui/Modifier$Node;->onDetach()V
-HSPLandroidx/compose/ui/Modifier;->then(Landroidx/compose/ui/Modifier;)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/ui/MotionDurationScale$Key;-><clinit>()V
-HSPLandroidx/compose/ui/MotionDurationScale$Key;-><init>()V
-HSPLandroidx/compose/ui/MotionDurationScale;->getKey()Lkotlin/coroutines/CoroutineContext$Key;
-HSPLandroidx/compose/ui/ZIndexModifier$measure$1;-><init>(Landroidx/compose/ui/layout/Placeable;Landroidx/compose/ui/ZIndexModifier;)V
-HSPLandroidx/compose/ui/ZIndexModifier$measure$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/ZIndexModifier;-><init>()V
-HSPLandroidx/compose/ui/ZIndexModifier;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/ZIndexModifier;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Landroidx/compose/ui/layout/Measurable;J)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/ui/autofill/AndroidAutofill;-><init>(Landroid/view/View;Landroidx/compose/ui/autofill/AutofillTree;)V
-HSPLandroidx/compose/ui/autofill/AutofillCallback;-><clinit>()V
-HSPLandroidx/compose/ui/autofill/AutofillCallback;-><init>()V
-HSPLandroidx/compose/ui/autofill/AutofillCallback;->register(Landroidx/compose/ui/autofill/AndroidAutofill;)V
-HSPLandroidx/compose/ui/autofill/AutofillTree;-><init>()V
-HSPLandroidx/compose/ui/draw/CacheDrawScope;-><init>()V
-HSPLandroidx/compose/ui/draw/CacheDrawScope;->getDensity()F
-HSPLandroidx/compose/ui/draw/CacheDrawScope;->getSize-NH-jbRc()J
-HSPLandroidx/compose/ui/draw/ClipKt;->clip(Landroidx/compose/ui/Modifier;Landroidx/compose/ui/graphics/Shape;)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/ui/draw/ClipKt;->clipToBounds(Landroidx/compose/ui/Modifier;)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/ui/draw/DrawBackgroundModifier;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/draw/DrawBackgroundModifier;->draw(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)V
-HSPLandroidx/compose/ui/draw/DrawContentCacheModifier;-><init>(Landroidx/compose/ui/draw/CacheDrawScope;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/draw/DrawContentCacheModifier;->draw(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)V
-HSPLandroidx/compose/ui/draw/DrawContentCacheModifier;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/draw/DrawContentCacheModifier;->onBuildCache(Landroidx/compose/ui/node/BackwardsCompatNode;)V
-HSPLandroidx/compose/ui/draw/DrawModifierKt$drawBehind$$inlined$modifierElementOf$1;-><init>(Landroidx/compose/foundation/text/TextController$drawTextAndSelectionBehind$1;Landroidx/compose/foundation/text/TextController$drawTextAndSelectionBehind$1;Landroidx/compose/foundation/text/TextController$drawTextAndSelectionBehind$1;)V
-HSPLandroidx/compose/ui/draw/DrawModifierKt$drawBehind$$inlined$modifierElementOf$1;->create()Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/draw/DrawModifierKt$drawBehind$$inlined$modifierElementOf$1;->update(Landroidx/compose/ui/Modifier$Node;)Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/draw/DrawModifierKt$drawWithCache$2;-><init>(Landroidx/compose/foundation/BorderKt$border$2$1;)V
-HSPLandroidx/compose/ui/draw/DrawModifierKt$drawWithCache$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/draw/DrawResult;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/draw/EmptyBuildDrawCacheParams;-><clinit>()V
-HSPLandroidx/compose/ui/draw/EmptyBuildDrawCacheParams;-><init>()V
-HSPLandroidx/compose/ui/draw/PainterModifier$measure$1;-><init>(Landroidx/compose/ui/layout/Placeable;)V
-HSPLandroidx/compose/ui/draw/PainterModifier$measure$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/draw/PainterModifier;-><init>(Landroidx/compose/ui/graphics/painter/Painter;ZLandroidx/compose/ui/Alignment;Landroidx/compose/ui/layout/ContentScale;FLandroidx/compose/ui/graphics/ColorFilter;)V
-HSPLandroidx/compose/ui/draw/PainterModifier;->draw(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)V
-HSPLandroidx/compose/ui/draw/PainterModifier;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/draw/PainterModifier;->getUseIntrinsicSize()Z
-HSPLandroidx/compose/ui/draw/PainterModifier;->hasSpecifiedAndFiniteHeight-uvyYCjk(J)Z
-HSPLandroidx/compose/ui/draw/PainterModifier;->hasSpecifiedAndFiniteWidth-uvyYCjk(J)Z
-HSPLandroidx/compose/ui/draw/PainterModifier;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Landroidx/compose/ui/layout/Measurable;J)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/ui/draw/PainterModifier;->modifyConstraints-ZezNO4M(J)J
-HSPLandroidx/compose/ui/focus/BeyondBoundsLayoutKt;->searchBeyondBounds--OM-vw8(Landroidx/compose/ui/focus/FocusTargetModifierNode;ILkotlin/jvm/functions/Function1;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/focus/FocusChangedModifierKt$onFocusChanged$$inlined$modifierElementOf$2;-><init>(Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/focus/FocusChangedModifierKt$onFocusChanged$$inlined$modifierElementOf$2;->create()Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/focus/FocusChangedModifierKt$onFocusChanged$$inlined$modifierElementOf$2;->update(Landroidx/compose/ui/Modifier$Node;)Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/focus/FocusChangedModifierKt;->onFocusChanged(Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/ui/focus/FocusChangedModifierNode;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/focus/FocusChangedModifierNode;->onFocusEvent(Landroidx/compose/ui/focus/FocusStateImpl;)V
-HSPLandroidx/compose/ui/focus/FocusDirection;-><init>(I)V
-HSPLandroidx/compose/ui/focus/FocusEventModifierNodeKt;->getFocusState(Landroidx/compose/ui/focus/FocusEventModifierNode;)Landroidx/compose/ui/focus/FocusStateImpl;
-HSPLandroidx/compose/ui/focus/FocusEventModifierNodeKt;->refreshFocusEventNodes(Landroidx/compose/ui/focus/FocusTargetModifierNode;)V
-HSPLandroidx/compose/ui/focus/FocusInvalidationManager$invalidateNodes$1;-><init>(Landroidx/compose/ui/focus/FocusInvalidationManager;)V
-HSPLandroidx/compose/ui/focus/FocusInvalidationManager$invalidateNodes$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/focus/FocusInvalidationManager;-><init>(Landroidx/compose/ui/platform/AndroidComposeView$focusOwner$1;)V
-HSPLandroidx/compose/ui/focus/FocusInvalidationManager;->scheduleInvalidation(Ljava/util/LinkedHashSet;Ljava/lang/Object;)V
-HSPLandroidx/compose/ui/focus/FocusOwnerImpl$moveFocus$foundNextItem$1;-><init>(Landroidx/compose/ui/focus/FocusTargetModifierNode;)V
-HSPLandroidx/compose/ui/focus/FocusOwnerImpl$moveFocus$foundNextItem$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/focus/FocusOwnerImpl$special$$inlined$modifierElementOf$2;-><init>(Landroidx/compose/ui/focus/FocusOwnerImpl;)V
-HSPLandroidx/compose/ui/focus/FocusOwnerImpl$special$$inlined$modifierElementOf$2;->create()Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/focus/FocusOwnerImpl;-><init>(Landroidx/compose/ui/platform/AndroidComposeView$focusOwner$1;)V
-HSPLandroidx/compose/ui/focus/FocusOwnerImpl;->dispatchKeyEvent-ZmokQxo(Landroid/view/KeyEvent;)Z
-HSPLandroidx/compose/ui/focus/FocusOwnerImpl;->getModifier()Landroidx/compose/ui/focus/FocusOwnerImpl$special$$inlined$modifierElementOf$2;
-HSPLandroidx/compose/ui/focus/FocusOwnerImpl;->moveFocus-3ESFkO8(I)Z
-HSPLandroidx/compose/ui/focus/FocusOwnerImpl;->scheduleInvalidation(Landroidx/compose/ui/focus/FocusEventModifierNode;)V
-HSPLandroidx/compose/ui/focus/FocusOwnerImpl;->scheduleInvalidation(Landroidx/compose/ui/focus/FocusPropertiesModifierNode;)V
-HSPLandroidx/compose/ui/focus/FocusOwnerImpl;->scheduleInvalidation(Landroidx/compose/ui/focus/FocusTargetModifierNode;)V
-HSPLandroidx/compose/ui/focus/FocusOwnerImpl;->setLayoutDirection(Landroidx/compose/ui/unit/LayoutDirection;)V
-HSPLandroidx/compose/ui/focus/FocusOwnerImpl;->takeFocus()V
-HSPLandroidx/compose/ui/focus/FocusPropertiesImpl$enter$1;-><clinit>()V
-HSPLandroidx/compose/ui/focus/FocusPropertiesImpl$enter$1;-><init>()V
-HSPLandroidx/compose/ui/focus/FocusPropertiesImpl$exit$1;-><clinit>()V
-HSPLandroidx/compose/ui/focus/FocusPropertiesImpl$exit$1;-><init>()V
-HSPLandroidx/compose/ui/focus/FocusPropertiesImpl;-><init>()V
-HSPLandroidx/compose/ui/focus/FocusPropertiesImpl;->setCanFocus(Z)V
-HSPLandroidx/compose/ui/focus/FocusPropertiesKt$focusProperties$$inlined$modifierElementOf$2;-><init>(Lkotlin/jvm/internal/Lambda;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/focus/FocusPropertiesKt$focusProperties$$inlined$modifierElementOf$2;->create()Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/focus/FocusPropertiesKt$focusProperties$$inlined$modifierElementOf$2;->update(Landroidx/compose/ui/Modifier$Node;)Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/focus/FocusPropertiesModifierNodeImpl;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/focus/FocusPropertiesModifierNodeImpl;->modifyFocusProperties(Landroidx/compose/ui/focus/FocusProperties;)V
-HSPLandroidx/compose/ui/focus/FocusRequester;-><clinit>()V
-HSPLandroidx/compose/ui/focus/FocusRequester;-><init>()V
-HSPLandroidx/compose/ui/focus/FocusRequester;->findFocusTarget$ui_release(Lkotlin/jvm/functions/Function1;)Ljava/lang/Boolean;
-HSPLandroidx/compose/ui/focus/FocusRequesterModifierKt$focusRequester$$inlined$modifierElementOf$2;-><init>(Ljava/lang/Object;Landroidx/compose/ui/focus/FocusRequester;Landroidx/compose/ui/focus/FocusRequester;)V
-HSPLandroidx/compose/ui/focus/FocusRequesterModifierKt$focusRequester$$inlined$modifierElementOf$2;->create()Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/focus/FocusRequesterModifierKt$focusRequester$$inlined$modifierElementOf$2;->update(Landroidx/compose/ui/Modifier$Node;)Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/focus/FocusRequesterModifierNodeImpl;-><init>(Landroidx/compose/ui/focus/FocusRequester;)V
-HSPLandroidx/compose/ui/focus/FocusRequesterModifierNodeImpl;->onAttach()V
-HSPLandroidx/compose/ui/focus/FocusStateImpl;-><clinit>()V
-HSPLandroidx/compose/ui/focus/FocusStateImpl;-><init>(ILjava/lang/String;)V
-HSPLandroidx/compose/ui/focus/FocusStateImpl;->getHasFocus()Z
-HSPLandroidx/compose/ui/focus/FocusStateImpl;->isFocused()Z
-HSPLandroidx/compose/ui/focus/FocusTargetModifierNode$special$$inlined$modifierElementOf$2;-><init>()V
-HSPLandroidx/compose/ui/focus/FocusTargetModifierNode$special$$inlined$modifierElementOf$2;->create()Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/focus/FocusTargetModifierNode;-><clinit>()V
-HSPLandroidx/compose/ui/focus/FocusTargetModifierNode;-><init>()V
-HSPLandroidx/compose/ui/focus/FocusTargetModifierNode;->fetchFocusProperties$ui_release()Landroidx/compose/ui/focus/FocusPropertiesImpl;
-HSPLandroidx/compose/ui/focus/FocusTargetModifierNode;->invalidateFocus$ui_release()V
-HSPLandroidx/compose/ui/focus/FocusTransactionsKt$grantFocus$1;-><init>(Landroidx/compose/ui/focus/FocusTargetModifierNode;)V
-HSPLandroidx/compose/ui/focus/FocusTransactionsKt$grantFocus$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/focus/FocusTransactionsKt;->clearFocus(Landroidx/compose/ui/focus/FocusTargetModifierNode;ZZ)Z
-HSPLandroidx/compose/ui/focus/FocusTransactionsKt;->grantFocus(Landroidx/compose/ui/focus/FocusTargetModifierNode;)V
-HSPLandroidx/compose/ui/focus/FocusTransactionsKt;->requestFocus(Landroidx/compose/ui/focus/FocusTargetModifierNode;)Z
-HSPLandroidx/compose/ui/focus/FocusTransactionsKt;->requestFocusForChild(Landroidx/compose/ui/focus/FocusTargetModifierNode;Landroidx/compose/ui/focus/FocusTargetModifierNode;)Z
-HSPLandroidx/compose/ui/focus/FocusTraversalKt;->findActiveFocusNode(Landroidx/compose/ui/focus/FocusTargetModifierNode;)Landroidx/compose/ui/focus/FocusTargetModifierNode;
-HSPLandroidx/compose/ui/focus/FocusTraversalKt;->focusRect(Landroidx/compose/ui/focus/FocusTargetModifierNode;)Landroidx/compose/ui/geometry/Rect;
-HSPLandroidx/compose/ui/focus/FocusTraversalKt;->getActiveChild(Landroidx/compose/ui/focus/FocusTargetModifierNode;)Landroidx/compose/ui/focus/FocusTargetModifierNode;
-HSPLandroidx/compose/ui/focus/FocusTraversalKt;->isEligibleForFocusSearch(Landroidx/compose/ui/focus/FocusTargetModifierNode;)Z
-HSPLandroidx/compose/ui/focus/TwoDimensionalFocusSearchKt$generateAndSearchChildren$1;-><init>(Landroidx/compose/ui/focus/FocusTargetModifierNode;Landroidx/compose/ui/focus/FocusTargetModifierNode;ILkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/focus/TwoDimensionalFocusSearchKt$generateAndSearchChildren$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/focus/TwoDimensionalFocusSearchKt;->beamBeats-I7lrPNg(Landroidx/compose/ui/geometry/Rect;Landroidx/compose/ui/geometry/Rect;Landroidx/compose/ui/geometry/Rect;I)Z
-HSPLandroidx/compose/ui/focus/TwoDimensionalFocusSearchKt;->beamBeats_I7lrPNg$inSourceBeam(ILandroidx/compose/ui/geometry/Rect;Landroidx/compose/ui/geometry/Rect;)Z
-HSPLandroidx/compose/ui/focus/TwoDimensionalFocusSearchKt;->collectAccessibleChildren(Landroidx/compose/ui/node/DelegatableNode;Landroidx/compose/runtime/collection/MutableVector;)V
-HSPLandroidx/compose/ui/focus/TwoDimensionalFocusSearchKt;->findBestCandidate-4WY_MpI(Landroidx/compose/runtime/collection/MutableVector;Landroidx/compose/ui/geometry/Rect;I)Landroidx/compose/ui/focus/FocusTargetModifierNode;
-HSPLandroidx/compose/ui/focus/TwoDimensionalFocusSearchKt;->findChildCorrespondingToFocusEnter--OM-vw8(Landroidx/compose/ui/focus/FocusTargetModifierNode;ILkotlin/jvm/functions/Function1;)Z
-HSPLandroidx/compose/ui/focus/TwoDimensionalFocusSearchKt;->generateAndSearchChildren-4C6V_qg(Landroidx/compose/ui/focus/FocusTargetModifierNode;Landroidx/compose/ui/focus/FocusTargetModifierNode;ILkotlin/jvm/functions/Function1;)Z
-HSPLandroidx/compose/ui/focus/TwoDimensionalFocusSearchKt;->isBetterCandidate_I7lrPNg$isCandidate(ILandroidx/compose/ui/geometry/Rect;Landroidx/compose/ui/geometry/Rect;)Z
-HSPLandroidx/compose/ui/focus/TwoDimensionalFocusSearchKt;->isBetterCandidate_I7lrPNg$weightedDistance(ILandroidx/compose/ui/geometry/Rect;Landroidx/compose/ui/geometry/Rect;)J
-HSPLandroidx/compose/ui/focus/TwoDimensionalFocusSearchKt;->searchChildren-4C6V_qg(Landroidx/compose/ui/focus/FocusTargetModifierNode;Landroidx/compose/ui/focus/FocusTargetModifierNode;ILkotlin/jvm/functions/Function1;)Z
-HSPLandroidx/compose/ui/focus/TwoDimensionalFocusSearchKt;->twoDimensionalFocusSearch--OM-vw8(Landroidx/compose/ui/focus/FocusTargetModifierNode;ILandroidx/compose/ui/focus/FocusOwnerImpl$moveFocus$foundNextItem$1;)Z
-HSPLandroidx/compose/ui/geometry/CornerRadius;-><clinit>()V
-HSPLandroidx/compose/ui/geometry/CornerRadius;->getX-impl(J)F
-HSPLandroidx/compose/ui/geometry/CornerRadius;->getY-impl(J)F
-HSPLandroidx/compose/ui/geometry/CornerRadiusKt;->CornerRadius(FF)J
-HSPLandroidx/compose/ui/geometry/MutableRect;-><init>()V
-HSPLandroidx/compose/ui/geometry/MutableRect;->isEmpty()Z
-HSPLandroidx/compose/ui/geometry/Offset;-><clinit>()V
-HSPLandroidx/compose/ui/geometry/Offset;-><init>(J)V
-HSPLandroidx/compose/ui/geometry/Offset;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/geometry/Offset;->equals-impl0(JJ)Z
-HSPLandroidx/compose/ui/geometry/Offset;->getX-impl(J)F
-HSPLandroidx/compose/ui/geometry/Offset;->getY-impl(J)F
-HSPLandroidx/compose/ui/geometry/OffsetKt;->Offset(FF)J
-HSPLandroidx/compose/ui/geometry/Rect;-><clinit>()V
-HSPLandroidx/compose/ui/geometry/Rect;-><init>(FFFF)V
-HSPLandroidx/compose/ui/geometry/Rect;->translate(FF)Landroidx/compose/ui/geometry/Rect;
-HSPLandroidx/compose/ui/geometry/Rect;->translate-k-4lQ0M(J)Landroidx/compose/ui/geometry/Rect;
-HSPLandroidx/compose/ui/geometry/RoundRect;-><clinit>()V
-HSPLandroidx/compose/ui/geometry/RoundRect;-><init>(FFFFJJJJ)V
-HSPLandroidx/compose/ui/geometry/RoundRectKt;->RoundRect-gG7oq9Y(FFFFJ)Landroidx/compose/ui/geometry/RoundRect;
-HSPLandroidx/compose/ui/geometry/Size;-><clinit>()V
-HSPLandroidx/compose/ui/geometry/Size;-><init>(J)V
-HSPLandroidx/compose/ui/geometry/Size;->equals-impl0(JJ)Z
-HSPLandroidx/compose/ui/geometry/Size;->getHeight-impl(J)F
-HSPLandroidx/compose/ui/geometry/Size;->getMinDimension-impl(J)F
-HSPLandroidx/compose/ui/geometry/Size;->getWidth-impl(J)F
-HSPLandroidx/compose/ui/geometry/Size;->isEmpty-impl(J)Z
-HSPLandroidx/compose/ui/geometry/SizeKt;->Size(FF)J
-HSPLandroidx/compose/ui/graphics/AndroidCanvas;-><init>()V
-HSPLandroidx/compose/ui/graphics/AndroidCanvas;->drawImageRect-HPBpro0(Landroidx/compose/ui/graphics/ImageBitmap;JJJJLandroidx/compose/ui/graphics/AndroidPaint;)V
-HSPLandroidx/compose/ui/graphics/AndroidCanvas;->drawRect(FFFFLandroidx/compose/ui/graphics/AndroidPaint;)V
-HSPLandroidx/compose/ui/graphics/AndroidCanvas;->drawRoundRect(FFFFFFLandroidx/compose/ui/graphics/AndroidPaint;)V
-HSPLandroidx/compose/ui/graphics/AndroidCanvas;->restore()V
-HSPLandroidx/compose/ui/graphics/AndroidCanvas;->save()V
-HSPLandroidx/compose/ui/graphics/AndroidCanvas;->setInternalCanvas(Landroid/graphics/Canvas;)V
-HSPLandroidx/compose/ui/graphics/AndroidCanvas;->translate(FF)V
-HSPLandroidx/compose/ui/graphics/AndroidCanvas_androidKt;-><clinit>()V
-HSPLandroidx/compose/ui/graphics/AndroidImageBitmap;-><init>(Landroid/graphics/Bitmap;)V
-HSPLandroidx/compose/ui/graphics/AndroidImageBitmap;->getHeight()I
-HSPLandroidx/compose/ui/graphics/AndroidImageBitmap;->getWidth()I
-HSPLandroidx/compose/ui/graphics/AndroidMatrixConversions_androidKt;->setFrom-tU-YjHk(Landroid/graphics/Matrix;[F)V
-HSPLandroidx/compose/ui/graphics/AndroidPaint;-><init>(Landroid/graphics/Paint;)V
-HSPLandroidx/compose/ui/graphics/AndroidPaint;->getAlpha()F
-HSPLandroidx/compose/ui/graphics/AndroidPaint;->getColor-0d7_KjU()J
-HSPLandroidx/compose/ui/graphics/AndroidPaint;->setAlpha(F)V
-HSPLandroidx/compose/ui/graphics/AndroidPaint;->setColor-8_81llA(J)V
-HSPLandroidx/compose/ui/graphics/AndroidPaint;->setShader(Landroid/graphics/Shader;)V
-HSPLandroidx/compose/ui/graphics/AndroidPaint;->setStyle-k9PVt8s(I)V
-HSPLandroidx/compose/ui/graphics/AndroidPaint_androidKt$WhenMappings;-><clinit>()V
-HSPLandroidx/compose/ui/graphics/AndroidPaint_androidKt;->Paint()Landroidx/compose/ui/graphics/AndroidPaint;
-HSPLandroidx/compose/ui/graphics/Brush;-><init>()V
-HSPLandroidx/compose/ui/graphics/CanvasHolder;-><init>()V
-HSPLandroidx/compose/ui/graphics/Color;-><clinit>()V
-HSPLandroidx/compose/ui/graphics/Color;-><init>(J)V
-HSPLandroidx/compose/ui/graphics/Color;->convert-vNxB06k(JLandroidx/compose/ui/graphics/colorspace/ColorSpace;)J
-HSPLandroidx/compose/ui/graphics/Color;->copy-wmQWz5c$default(JF)J
-HSPLandroidx/compose/ui/graphics/Color;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/graphics/Color;->equals-impl0(JJ)Z
-HSPLandroidx/compose/ui/graphics/Color;->getAlpha-impl(J)F
-HSPLandroidx/compose/ui/graphics/Color;->getBlue-impl(J)F
-HSPLandroidx/compose/ui/graphics/Color;->getColorSpace-impl(J)Landroidx/compose/ui/graphics/colorspace/ColorSpace;
-HSPLandroidx/compose/ui/graphics/Color;->getGreen-impl(J)F
-HSPLandroidx/compose/ui/graphics/Color;->getRed-impl(J)F
-HSPLandroidx/compose/ui/graphics/ColorKt;->Color$default(III)J
-HSPLandroidx/compose/ui/graphics/ColorKt;->Color(FFFFLandroidx/compose/ui/graphics/colorspace/ColorSpace;)J
-HSPLandroidx/compose/ui/graphics/ColorKt;->Color(J)J
-HSPLandroidx/compose/ui/graphics/ColorKt;->toArgb-8_81llA(J)I
-HSPLandroidx/compose/ui/graphics/Float16$Companion;-><init>()V
-HSPLandroidx/compose/ui/graphics/Float16;-><clinit>()V
-HSPLandroidx/compose/ui/graphics/Float16;->constructor-impl(F)S
-HSPLandroidx/compose/ui/graphics/Float16;->toFloat-impl(S)F
-HSPLandroidx/compose/ui/graphics/GraphicsLayerModifierKt;->graphicsLayer-Ap8cVGQ$default(Landroidx/compose/ui/Modifier;FFLandroidx/compose/ui/graphics/Shape;ZI)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/ui/graphics/GraphicsLayerModifierNodeElement;-><init>(FFFFFFFFFFJLandroidx/compose/ui/graphics/Shape;ZJJI)V
-HSPLandroidx/compose/ui/graphics/GraphicsLayerModifierNodeElement;->create()Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/graphics/GraphicsLayerModifierNodeElement;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/graphics/GraphicsLayerModifierNodeElement;->update(Landroidx/compose/ui/Modifier$Node;)Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/graphics/GraphicsLayerScopeKt;-><clinit>()V
-HSPLandroidx/compose/ui/graphics/Matrix;->constructor-impl$default()[F
-HSPLandroidx/compose/ui/graphics/Matrix;->map-MK-Hz9U([FJ)J
-HSPLandroidx/compose/ui/graphics/Matrix;->map-impl([FLandroidx/compose/ui/geometry/MutableRect;)V
-HSPLandroidx/compose/ui/graphics/Outline$Rectangle;-><init>(Landroidx/compose/ui/geometry/Rect;)V
-HSPLandroidx/compose/ui/graphics/Outline$Rounded;-><init>(Landroidx/compose/ui/geometry/RoundRect;)V
-HSPLandroidx/compose/ui/graphics/Outline;-><init>()V
-HSPLandroidx/compose/ui/graphics/RectangleShapeKt$RectangleShape$1;-><init>()V
-HSPLandroidx/compose/ui/graphics/RectangleShapeKt$RectangleShape$1;->createOutline-Pq9zytI(JLandroidx/compose/ui/unit/LayoutDirection;Landroidx/compose/ui/unit/Density;)Landroidx/compose/ui/graphics/Outline;
-HSPLandroidx/compose/ui/graphics/RectangleShapeKt;-><clinit>()V
-HSPLandroidx/compose/ui/graphics/ReusableGraphicsLayerScope;-><init>()V
-HSPLandroidx/compose/ui/graphics/ReusableGraphicsLayerScope;->setAlpha(F)V
-HSPLandroidx/compose/ui/graphics/ReusableGraphicsLayerScope;->setAmbientShadowColor-8_81llA(J)V
-HSPLandroidx/compose/ui/graphics/ReusableGraphicsLayerScope;->setCameraDistance(F)V
-HSPLandroidx/compose/ui/graphics/ReusableGraphicsLayerScope;->setClip(Z)V
-HSPLandroidx/compose/ui/graphics/ReusableGraphicsLayerScope;->setCompositingStrategy-aDBOjCE(I)V
-HSPLandroidx/compose/ui/graphics/ReusableGraphicsLayerScope;->setRenderEffect()V
-HSPLandroidx/compose/ui/graphics/ReusableGraphicsLayerScope;->setRotationX(F)V
-HSPLandroidx/compose/ui/graphics/ReusableGraphicsLayerScope;->setRotationY(F)V
-HSPLandroidx/compose/ui/graphics/ReusableGraphicsLayerScope;->setRotationZ(F)V
-HSPLandroidx/compose/ui/graphics/ReusableGraphicsLayerScope;->setScaleX(F)V
-HSPLandroidx/compose/ui/graphics/ReusableGraphicsLayerScope;->setScaleY(F)V
-HSPLandroidx/compose/ui/graphics/ReusableGraphicsLayerScope;->setShadowElevation(F)V
-HSPLandroidx/compose/ui/graphics/ReusableGraphicsLayerScope;->setShape(Landroidx/compose/ui/graphics/Shape;)V
-HSPLandroidx/compose/ui/graphics/ReusableGraphicsLayerScope;->setSpotShadowColor-8_81llA(J)V
-HSPLandroidx/compose/ui/graphics/ReusableGraphicsLayerScope;->setTransformOrigin-__ExYCQ(J)V
-HSPLandroidx/compose/ui/graphics/ReusableGraphicsLayerScope;->setTranslationX(F)V
-HSPLandroidx/compose/ui/graphics/ReusableGraphicsLayerScope;->setTranslationY(F)V
-HSPLandroidx/compose/ui/graphics/Shadow;-><clinit>()V
-HSPLandroidx/compose/ui/graphics/Shadow;-><init>(JJF)V
-HSPLandroidx/compose/ui/graphics/Shadow;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/graphics/SimpleGraphicsLayerModifier$layerBlock$1;-><init>(Landroidx/compose/ui/graphics/SimpleGraphicsLayerModifier;)V
-HSPLandroidx/compose/ui/graphics/SimpleGraphicsLayerModifier$layerBlock$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/graphics/SimpleGraphicsLayerModifier$measure$1;-><init>(Landroidx/compose/ui/layout/Placeable;Landroidx/compose/ui/graphics/SimpleGraphicsLayerModifier;)V
-HSPLandroidx/compose/ui/graphics/SimpleGraphicsLayerModifier$measure$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/graphics/SimpleGraphicsLayerModifier;-><init>(FFFFFFFFFFJLandroidx/compose/ui/graphics/Shape;ZJJI)V
-HSPLandroidx/compose/ui/graphics/SimpleGraphicsLayerModifier;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Landroidx/compose/ui/layout/Measurable;J)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/ui/graphics/SolidColor;-><init>(J)V
-HSPLandroidx/compose/ui/graphics/SolidColor;->applyTo-Pq9zytI(FJLandroidx/compose/ui/graphics/AndroidPaint;)V
-HSPLandroidx/compose/ui/graphics/TransformOrigin;-><clinit>()V
-HSPLandroidx/compose/ui/graphics/TransformOrigin;->getPivotFractionY-impl(J)F
-HSPLandroidx/compose/ui/graphics/colorspace/Adaptation$Companion$Bradford$1;-><init>([F)V
-HSPLandroidx/compose/ui/graphics/colorspace/Adaptation;-><clinit>()V
-HSPLandroidx/compose/ui/graphics/colorspace/Adaptation;-><init>([F)V
-HSPLandroidx/compose/ui/graphics/colorspace/ColorModel;-><clinit>()V
-HSPLandroidx/compose/ui/graphics/colorspace/ColorModel;->equals-impl0(JJ)Z
-HSPLandroidx/compose/ui/graphics/colorspace/ColorSpace;-><init>(Ljava/lang/String;JI)V
-HSPLandroidx/compose/ui/graphics/colorspace/ColorSpace;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/graphics/colorspace/ColorSpace;->isSrgb()Z
-HSPLandroidx/compose/ui/graphics/colorspace/ColorSpaceKt;->adapt$default(Landroidx/compose/ui/graphics/colorspace/ColorSpace;)Landroidx/compose/ui/graphics/colorspace/ColorSpace;
-HSPLandroidx/compose/ui/graphics/colorspace/ColorSpaceKt;->chromaticAdaptation([F[F[F)[F
-HSPLandroidx/compose/ui/graphics/colorspace/ColorSpaceKt;->compare(Landroidx/compose/ui/graphics/colorspace/WhitePoint;Landroidx/compose/ui/graphics/colorspace/WhitePoint;)Z
-HSPLandroidx/compose/ui/graphics/colorspace/ColorSpaceKt;->connect-YBCOT_4$default(Landroidx/compose/ui/graphics/colorspace/ColorSpace;Landroidx/compose/ui/graphics/colorspace/ColorSpace;I)Landroidx/compose/ui/graphics/colorspace/Connector;
-HSPLandroidx/compose/ui/graphics/colorspace/ColorSpaceKt;->inverse3x3([F)[F
-HSPLandroidx/compose/ui/graphics/colorspace/ColorSpaceKt;->mul3x3([F[F)[F
-HSPLandroidx/compose/ui/graphics/colorspace/ColorSpaceKt;->mul3x3Diag([F[F)[F
-HSPLandroidx/compose/ui/graphics/colorspace/ColorSpaceKt;->mul3x3Float3([F[F)V
-HSPLandroidx/compose/ui/graphics/colorspace/ColorSpaceKt;->mul3x3Float3_0([FFFF)F
-HSPLandroidx/compose/ui/graphics/colorspace/ColorSpaceKt;->mul3x3Float3_1([FFFF)F
-HSPLandroidx/compose/ui/graphics/colorspace/ColorSpaceKt;->mul3x3Float3_2([FFFF)F
-HSPLandroidx/compose/ui/graphics/colorspace/ColorSpaces$$ExternalSyntheticLambda0;-><init>()V
-HSPLandroidx/compose/ui/graphics/colorspace/ColorSpaces;-><clinit>()V
-HSPLandroidx/compose/ui/graphics/colorspace/Connector$Companion$identity$1;-><init>(Landroidx/compose/ui/graphics/colorspace/ColorSpace;)V
-HSPLandroidx/compose/ui/graphics/colorspace/Connector$Companion;-><init>()V
-HSPLandroidx/compose/ui/graphics/colorspace/Connector;-><clinit>()V
-HSPLandroidx/compose/ui/graphics/colorspace/Connector;-><init>(Landroidx/compose/ui/graphics/colorspace/ColorSpace;Landroidx/compose/ui/graphics/colorspace/ColorSpace;I)V
-HSPLandroidx/compose/ui/graphics/colorspace/Connector;-><init>(Landroidx/compose/ui/graphics/colorspace/ColorSpace;Landroidx/compose/ui/graphics/colorspace/ColorSpace;Landroidx/compose/ui/graphics/colorspace/ColorSpace;[F)V
-HSPLandroidx/compose/ui/graphics/colorspace/Connector;->transformToColor-wmQWz5c$ui_graphics_release(FFFF)J
-HSPLandroidx/compose/ui/graphics/colorspace/Illuminant;-><clinit>()V
-HSPLandroidx/compose/ui/graphics/colorspace/Lab;-><init>()V
-HSPLandroidx/compose/ui/graphics/colorspace/Oklab;-><clinit>()V
-HSPLandroidx/compose/ui/graphics/colorspace/Oklab;-><init>()V
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb$$ExternalSyntheticLambda0;-><init>(Landroidx/compose/ui/graphics/colorspace/Rgb;)V
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb$$ExternalSyntheticLambda0;->invoke(D)D
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb$$ExternalSyntheticLambda1;-><init>(Landroidx/compose/ui/graphics/colorspace/Rgb;)V
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb$$ExternalSyntheticLambda1;->invoke(D)D
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb$$ExternalSyntheticLambda2;-><init>()V
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb$$ExternalSyntheticLambda2;->invoke(D)D
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb$$ExternalSyntheticLambda3;-><init>(Landroidx/compose/ui/graphics/colorspace/TransferParameters;)V
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb$$ExternalSyntheticLambda3;->invoke(D)D
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb$$ExternalSyntheticLambda5;-><init>(Landroidx/compose/ui/graphics/colorspace/TransferParameters;)V
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb$$ExternalSyntheticLambda5;->invoke(D)D
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb$$ExternalSyntheticLambda6;-><init>(D)V
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb$$ExternalSyntheticLambda7;-><init>(D)V
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb$Companion;->area([F)F
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb;-><clinit>()V
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb;-><init>(Ljava/lang/String;[FLandroidx/compose/ui/graphics/colorspace/WhitePoint;DFFI)V
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb;-><init>(Ljava/lang/String;[FLandroidx/compose/ui/graphics/colorspace/WhitePoint;Landroidx/compose/ui/graphics/colorspace/TransferParameters;I)V
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb;-><init>(Ljava/lang/String;[FLandroidx/compose/ui/graphics/colorspace/WhitePoint;[FLandroidx/compose/ui/graphics/colorspace/DoubleFunction;Landroidx/compose/ui/graphics/colorspace/DoubleFunction;FFLandroidx/compose/ui/graphics/colorspace/TransferParameters;I)V
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb;->getMaxValue(I)F
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb;->getMinValue(I)F
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb;->isSrgb()Z
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb;->toXy$ui_graphics_release(FFF)J
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb;->toZ$ui_graphics_release(FFF)F
-HSPLandroidx/compose/ui/graphics/colorspace/Rgb;->xyzaToColor-JlNiLsg$ui_graphics_release(FFFFLandroidx/compose/ui/graphics/colorspace/ColorSpace;)J
-HSPLandroidx/compose/ui/graphics/colorspace/TransferParameters;-><init>(DDDDD)V
-HSPLandroidx/compose/ui/graphics/colorspace/WhitePoint;-><init>(FF)V
-HSPLandroidx/compose/ui/graphics/colorspace/WhitePoint;->toXyz$ui_graphics_release()[F
-HSPLandroidx/compose/ui/graphics/colorspace/Xyz;-><init>()V
-HSPLandroidx/compose/ui/graphics/colorspace/Xyz;->clamp(F)F
-HSPLandroidx/compose/ui/graphics/colorspace/Xyz;->getMaxValue(I)F
-HSPLandroidx/compose/ui/graphics/colorspace/Xyz;->getMinValue(I)F
-HSPLandroidx/compose/ui/graphics/colorspace/Xyz;->toXy$ui_graphics_release(FFF)J
-HSPLandroidx/compose/ui/graphics/colorspace/Xyz;->toZ$ui_graphics_release(FFF)F
-HSPLandroidx/compose/ui/graphics/colorspace/Xyz;->xyzaToColor-JlNiLsg$ui_graphics_release(FFFFLandroidx/compose/ui/graphics/colorspace/ColorSpace;)J
-HSPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScope$DrawParams;-><init>()V
-HSPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScope$drawContext$1;-><init>(Landroidx/compose/ui/graphics/drawscope/CanvasDrawScope;)V
-HSPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScope$drawContext$1;->getCanvas()Landroidx/compose/ui/graphics/Canvas;
-HSPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScope$drawContext$1;->getSize-NH-jbRc()J
-HSPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScope$drawContext$1;->setSize-uvyYCjk(J)V
-HSPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScope;-><init>()V
-HSPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScope;->configurePaint-2qPWKa0$default(Landroidx/compose/ui/graphics/drawscope/CanvasDrawScope;JLandroidx/arch/core/executor/TaskExecutor;FLandroidx/compose/ui/graphics/ColorFilter;I)Landroidx/compose/ui/graphics/AndroidPaint;
-HSPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScope;->configurePaint-swdJneE$default(Landroidx/compose/ui/graphics/drawscope/CanvasDrawScope;Landroidx/compose/ui/graphics/Brush;Landroidx/arch/core/executor/TaskExecutor;FLandroidx/compose/ui/graphics/ColorFilter;I)Landroidx/compose/ui/graphics/AndroidPaint;
-HSPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScope;->configurePaint-swdJneE(Landroidx/compose/ui/graphics/Brush;Landroidx/arch/core/executor/TaskExecutor;FLandroidx/compose/ui/graphics/ColorFilter;II)Landroidx/compose/ui/graphics/AndroidPaint;
-HSPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScope;->drawImage-AZ2fEMs(Landroidx/compose/ui/graphics/ImageBitmap;JJJJFLandroidx/arch/core/executor/TaskExecutor;Landroidx/compose/ui/graphics/ColorFilter;II)V
-HSPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScope;->drawRect-AsUm42w(Landroidx/compose/ui/graphics/Brush;JJFLandroidx/arch/core/executor/TaskExecutor;Landroidx/compose/ui/graphics/ColorFilter;I)V
-HSPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScope;->drawRect-n-J9OG0(JJJFLandroidx/arch/core/executor/TaskExecutor;Landroidx/compose/ui/graphics/ColorFilter;I)V
-HSPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScope;->drawRoundRect-u-Aw5IA(JJJJLandroidx/arch/core/executor/TaskExecutor;FLandroidx/compose/ui/graphics/ColorFilter;I)V
-HSPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScope;->getDrawContext()Landroidx/compose/ui/graphics/drawscope/CanvasDrawScope$drawContext$1;
-HSPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScope;->selectPaint(Landroidx/arch/core/executor/TaskExecutor;)Landroidx/compose/ui/graphics/AndroidPaint;
-HSPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScopeKt$asDrawTransform$1;-><init>(Landroidx/compose/ui/graphics/drawscope/DrawContext;)V
-HSPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScopeKt$asDrawTransform$1;->inset(FFFF)V
-HSPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScopeKt$asDrawTransform$1;->translate(FF)V
-HSPLandroidx/compose/ui/graphics/drawscope/CanvasDrawScopeKt;-><clinit>()V
-HSPLandroidx/compose/ui/graphics/drawscope/DrawScope;->drawImage-AZ2fEMs$default(Landroidx/compose/ui/graphics/drawscope/DrawScope;Landroidx/compose/ui/graphics/ImageBitmap;JJJJFLandroidx/arch/core/executor/TaskExecutor;Landroidx/compose/ui/graphics/ColorFilter;III)V
-HSPLandroidx/compose/ui/graphics/drawscope/DrawScope;->drawRect-AsUm42w$default(Landroidx/compose/ui/graphics/drawscope/DrawScope;Landroidx/compose/ui/graphics/Brush;JJFLandroidx/arch/core/executor/TaskExecutor;I)V
-HSPLandroidx/compose/ui/graphics/drawscope/DrawScope;->drawRect-n-J9OG0$default(Landroidx/compose/ui/graphics/drawscope/DrawScope;JJI)V
-HSPLandroidx/compose/ui/graphics/drawscope/DrawScope;->getSize-NH-jbRc()J
-HSPLandroidx/compose/ui/graphics/drawscope/DrawScope;->offsetSize-PENXr5M(JJ)J
-HSPLandroidx/compose/ui/graphics/drawscope/EmptyCanvas;-><init>()V
-HSPLandroidx/compose/ui/graphics/drawscope/Fill;-><clinit>()V
-HSPLandroidx/compose/ui/graphics/drawscope/Fill;-><init>()V
-HSPLandroidx/compose/ui/graphics/drawscope/Stroke;-><init>(FFIII)V
-HSPLandroidx/compose/ui/graphics/drawscope/Stroke;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/graphics/painter/BitmapPainter;-><init>(Landroidx/compose/ui/graphics/ImageBitmap;)V
-HSPLandroidx/compose/ui/graphics/painter/BitmapPainter;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/graphics/painter/BitmapPainter;->getIntrinsicSize-NH-jbRc()J
-HSPLandroidx/compose/ui/graphics/painter/BitmapPainter;->onDraw(Landroidx/compose/ui/graphics/drawscope/DrawScope;)V
-HSPLandroidx/compose/ui/graphics/painter/Painter;-><init>()V
-HSPLandroidx/compose/ui/hapticfeedback/PlatformHapticFeedback;-><init>(Landroid/view/View;)V
-HSPLandroidx/compose/ui/input/InputMode;-><init>(I)V
-HSPLandroidx/compose/ui/input/InputMode;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/input/InputModeManagerImpl;-><init>(ILandroidx/compose/ui/platform/AndroidComposeView$_inputModeManager$1;)V
-HSPLandroidx/compose/ui/input/InputModeManagerImpl;->getInputMode-aOaMEAU()I
-HSPLandroidx/compose/ui/input/key/Key;-><clinit>()V
-HSPLandroidx/compose/ui/input/key/Key;->equals-impl0(JJ)Z
-HSPLandroidx/compose/ui/input/key/KeyEvent;-><init>(Landroid/view/KeyEvent;)V
-HSPLandroidx/compose/ui/input/key/KeyEvent_androidKt;->getKey-ZmokQxo(Landroid/view/KeyEvent;)J
-HSPLandroidx/compose/ui/input/key/KeyEvent_androidKt;->getType-ZmokQxo(Landroid/view/KeyEvent;)I
-HSPLandroidx/compose/ui/input/key/KeyInputInputModifierNodeImpl;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/input/key/KeyInputInputModifierNodeImpl;->onKeyEvent-ZmokQxo(Landroid/view/KeyEvent;)Z
-HSPLandroidx/compose/ui/input/key/KeyInputInputModifierNodeImpl;->onPreKeyEvent-ZmokQxo(Landroid/view/KeyEvent;)Z
-HSPLandroidx/compose/ui/input/key/KeyInputModifierKt$onKeyEvent$$inlined$modifierElementOf$2;-><init>(Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/input/key/KeyInputModifierKt$onKeyEvent$$inlined$modifierElementOf$2;->create()Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/input/key/KeyInputModifierKt$onKeyEvent$$inlined$modifierElementOf$2;->update(Landroidx/compose/ui/Modifier$Node;)Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/input/key/KeyInputModifierKt;->onKeyEvent(Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/ui/input/key/Key_androidKt;->Key(I)J
-HSPLandroidx/compose/ui/input/nestedscroll/NestedScrollDispatcher$calculateNestedScrollScope$1;-><init>(Landroidx/compose/ui/input/nestedscroll/NestedScrollDispatcher;)V
-HSPLandroidx/compose/ui/input/nestedscroll/NestedScrollDispatcher;-><init>()V
-HSPLandroidx/compose/ui/input/nestedscroll/NestedScrollModifierKt$nestedScroll$2;-><init>(Landroidx/compose/ui/input/nestedscroll/NestedScrollConnection;Landroidx/compose/ui/input/nestedscroll/NestedScrollDispatcher;)V
-HSPLandroidx/compose/ui/input/nestedscroll/NestedScrollModifierKt$nestedScroll$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/input/nestedscroll/NestedScrollModifierLocal$1;-><init>(Landroidx/compose/ui/input/nestedscroll/NestedScrollModifierLocal;)V
-HSPLandroidx/compose/ui/input/nestedscroll/NestedScrollModifierLocal;-><init>(Landroidx/compose/ui/input/nestedscroll/NestedScrollConnection;Landroidx/compose/ui/input/nestedscroll/NestedScrollDispatcher;)V
-HSPLandroidx/compose/ui/input/nestedscroll/NestedScrollModifierLocal;->getKey()Landroidx/compose/ui/modifier/ProvidableModifierLocal;
-HSPLandroidx/compose/ui/input/nestedscroll/NestedScrollModifierLocal;->getParent()Landroidx/compose/ui/input/nestedscroll/NestedScrollModifierLocal;
-HSPLandroidx/compose/ui/input/nestedscroll/NestedScrollModifierLocal;->onModifierLocalsUpdated(Landroidx/compose/ui/modifier/ModifierLocalReadScope;)V
-HSPLandroidx/compose/ui/input/nestedscroll/NestedScrollModifierLocalKt$ModifierLocalNestedScroll$1;-><clinit>()V
-HSPLandroidx/compose/ui/input/nestedscroll/NestedScrollModifierLocalKt$ModifierLocalNestedScroll$1;-><init>()V
-HSPLandroidx/compose/ui/input/nestedscroll/NestedScrollModifierLocalKt$ModifierLocalNestedScroll$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/input/nestedscroll/NestedScrollModifierLocalKt;-><clinit>()V
-HSPLandroidx/compose/ui/input/pointer/AwaitPointerEventScope;->awaitPointerEvent$default(Landroidx/compose/ui/input/pointer/AwaitPointerEventScope;Lkotlin/coroutines/jvm/internal/BaseContinuationImpl;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/input/pointer/HitPathTracker;-><init>(Landroidx/compose/ui/node/InnerNodeCoordinator;)V
-HSPLandroidx/compose/ui/input/pointer/MotionEventAdapter;-><init>()V
-HSPLandroidx/compose/ui/input/pointer/NodeParent;-><init>()V
-HSPLandroidx/compose/ui/input/pointer/PointerEvent;-><init>(Ljava/util/List;)V
-HSPLandroidx/compose/ui/input/pointer/PointerEvent;-><init>(Ljava/util/List;Landroidx/compose/ui/input/pointer/InternalPointerEvent;)V
-HSPLandroidx/compose/ui/input/pointer/PointerEventPass;-><clinit>()V
-HSPLandroidx/compose/ui/input/pointer/PointerEventPass;-><init>(ILjava/lang/String;)V
-HSPLandroidx/compose/ui/input/pointer/PointerInputChangeEventProducer;-><init>()V
-HSPLandroidx/compose/ui/input/pointer/PointerInputEventProcessor;-><init>(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/input/pointer/PointerInputFilter;-><init>()V
-HSPLandroidx/compose/ui/input/pointer/PointerInputFilter;->getSize-YbymL2g()J
-HSPLandroidx/compose/ui/input/pointer/PointerKeyboardModifiers;-><init>(I)V
-HSPLandroidx/compose/ui/input/pointer/PointerKeyboardModifiers;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilter$PointerEventHandlerCoroutine;-><init>(Landroidx/compose/ui/input/pointer/SuspendingPointerInputFilter;Lkotlinx/coroutines/CancellableContinuationImpl;)V
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilter$PointerEventHandlerCoroutine;->awaitPointerEvent(Landroidx/compose/ui/input/pointer/PointerEventPass;Lkotlin/coroutines/jvm/internal/BaseContinuationImpl;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilter$PointerEventHandlerCoroutine;->getContext()Lkotlin/coroutines/CoroutineContext;
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilter$awaitPointerEventScope$2$2;-><init>(Landroidx/compose/ui/input/pointer/SuspendingPointerInputFilter$PointerEventHandlerCoroutine;)V
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilter;-><init>(Landroidx/compose/ui/platform/ViewConfiguration;Landroidx/compose/ui/unit/Density;)V
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilter;->awaitPointerEventScope(Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilter;->getPointerInputFilter()Landroidx/compose/ui/input/pointer/SuspendingPointerInputFilter;
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$2$2$1;-><init>(Landroidx/compose/ui/input/pointer/SuspendingPointerInputFilter;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$2$2$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$2$2$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$2;-><init>(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$4$2$1;-><init>(Landroidx/compose/ui/input/pointer/SuspendingPointerInputFilter;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$4$2$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$4$2$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$4;-><init>(Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$4;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$6$2$1;-><init>(Landroidx/compose/ui/input/pointer/SuspendingPointerInputFilter;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$6$2$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$6$2$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$6;-><init>([Ljava/lang/Object;Landroidx/compose/foundation/gestures/DraggableKt$draggable$9$3;)V
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$6;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt;-><clinit>()V
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt;->pointerInput(Landroidx/compose/ui/Modifier;Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt;->pointerInput(Landroidx/compose/ui/Modifier;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/ui/input/pointer/util/VelocityTracker1D;-><init>()V
-HSPLandroidx/compose/ui/input/pointer/util/VelocityTracker;-><init>()V
-HSPLandroidx/compose/ui/input/rotary/RotaryInputModifierKt$onRotaryScrollEvent$$inlined$modifierElementOf$2;-><init>()V
-HSPLandroidx/compose/ui/input/rotary/RotaryInputModifierKt$onRotaryScrollEvent$$inlined$modifierElementOf$2;->create()Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/input/rotary/RotaryInputModifierNodeImpl;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/layout/AlignmentLine;-><init>(Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/ui/layout/AlignmentLineKt$FirstBaseline$1;-><clinit>()V
-HSPLandroidx/compose/ui/layout/AlignmentLineKt$FirstBaseline$1;-><init>()V
-HSPLandroidx/compose/ui/layout/AlignmentLineKt$LastBaseline$1;-><clinit>()V
-HSPLandroidx/compose/ui/layout/AlignmentLineKt$LastBaseline$1;-><init>()V
-HSPLandroidx/compose/ui/layout/AlignmentLineKt;-><clinit>()V
-HSPLandroidx/compose/ui/layout/BeyondBoundsLayoutKt$ModifierLocalBeyondBoundsLayout$1;-><clinit>()V
-HSPLandroidx/compose/ui/layout/BeyondBoundsLayoutKt$ModifierLocalBeyondBoundsLayout$1;-><init>()V
-HSPLandroidx/compose/ui/layout/BeyondBoundsLayoutKt$ModifierLocalBeyondBoundsLayout$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/layout/BeyondBoundsLayoutKt;-><clinit>()V
-HSPLandroidx/compose/ui/layout/ComposableSingletons$SubcomposeLayoutKt$lambda-1$1;-><clinit>()V
-HSPLandroidx/compose/ui/layout/ComposableSingletons$SubcomposeLayoutKt$lambda-1$1;-><init>()V
-HSPLandroidx/compose/ui/layout/ComposableSingletons$SubcomposeLayoutKt;-><clinit>()V
-HSPLandroidx/compose/ui/layout/ContentScale$Companion$FillBounds$1;-><init>()V
-HSPLandroidx/compose/ui/layout/ContentScale$Companion$Fit$1;-><init>()V
-HSPLandroidx/compose/ui/layout/ContentScale$Companion$Fit$1;->computeScaleFactor-H7hwNQA(JJ)J
-HSPLandroidx/compose/ui/layout/ContentScale$Companion$Inside$1;-><init>()V
-HSPLandroidx/compose/ui/layout/ContentScale$Companion;-><clinit>()V
-HSPLandroidx/compose/ui/layout/HorizontalAlignmentLine;-><init>(Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/ui/layout/IntrinsicsMeasureScope;-><init>(Landroidx/compose/ui/unit/Density;Landroidx/compose/ui/unit/LayoutDirection;)V
-HSPLandroidx/compose/ui/layout/IntrinsicsMeasureScope;->roundToPx-0680j_4(F)I
-HSPLandroidx/compose/ui/layout/LayoutKt$materializerOf$1;-><init>(Landroidx/compose/ui/Modifier;)V
-HSPLandroidx/compose/ui/layout/LayoutKt$materializerOf$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/layout/LayoutKt;->materializerOf(Landroidx/compose/ui/Modifier;)Landroidx/compose/runtime/internal/ComposableLambdaImpl;
-HSPLandroidx/compose/ui/layout/LayoutModifier;->maxIntrinsicHeight(Landroidx/compose/ui/layout/IntrinsicMeasureScope;Landroidx/compose/ui/layout/IntrinsicMeasurable;I)I
-HSPLandroidx/compose/ui/layout/LayoutModifier;->maxIntrinsicWidth(Landroidx/compose/ui/layout/IntrinsicMeasureScope;Landroidx/compose/ui/layout/IntrinsicMeasurable;I)I
-HSPLandroidx/compose/ui/layout/LayoutModifierImpl;-><init>(Lkotlin/jvm/functions/Function3;)V
-HSPLandroidx/compose/ui/layout/LayoutModifierImpl;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/layout/LayoutModifierImpl;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Landroidx/compose/ui/layout/Measurable;J)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/ui/layout/LayoutModifierKt;->layout(Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function3;)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState$NodeState;-><init>(Ljava/lang/Object;Landroidx/compose/runtime/internal/ComposableLambdaImpl;)V
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState$Scope;-><init>(Landroidx/compose/ui/layout/LayoutNodeSubcompositionsState;)V
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState$Scope;->getDensity()F
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState$Scope;->getLayoutDirection()Landroidx/compose/ui/unit/LayoutDirection;
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState$Scope;->subcompose(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/util/List;
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState$createMeasurePolicy$1$measure$1;-><init>(Landroidx/compose/ui/layout/MeasureResult;Landroidx/compose/ui/layout/LayoutNodeSubcompositionsState;I)V
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState$createMeasurePolicy$1$measure$1;->getAlignmentLines()Ljava/util/Map;
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState$createMeasurePolicy$1$measure$1;->getHeight()I
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState$createMeasurePolicy$1$measure$1;->getWidth()I
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState$createMeasurePolicy$1$measure$1;->placeChildren()V
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState$createMeasurePolicy$1;-><init>(Landroidx/compose/ui/layout/LayoutNodeSubcompositionsState;Lkotlin/jvm/functions/Function2;Ljava/lang/String;)V
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState$createMeasurePolicy$1;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Ljava/util/List;J)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState$precompose$1;-><init>(Landroidx/compose/ui/layout/LayoutNodeSubcompositionsState;Ljava/lang/Object;)V
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState$precompose$1;->dispose()V
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState$precompose$1;->getPlaceablesCount()I
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState$precompose$1;->premeasure-0kLqBqw(JI)V
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState$subcompose$2$1$1;-><init>(Landroidx/compose/ui/layout/LayoutNodeSubcompositionsState$NodeState;Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState$subcompose$2$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState;-><init>(Landroidx/compose/ui/node/LayoutNode;Landroidx/compose/ui/layout/SubcomposeSlotReusePolicy;)V
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState;->disposeOrReuseStartingFromIndex(I)V
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState;->makeSureStateIsConsistent()V
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState;->subcompose(Landroidx/compose/ui/node/LayoutNode;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/ui/layout/LayoutNodeSubcompositionsState;->takeNodeFromReusables(Ljava/lang/Object;)Landroidx/compose/ui/node/LayoutNode;
-HSPLandroidx/compose/ui/layout/MeasureScope$layout$1;-><init>(IILjava/util/Map;Landroidx/compose/ui/layout/MeasureScope;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/layout/MeasureScope$layout$1;->getAlignmentLines()Ljava/util/Map;
-HSPLandroidx/compose/ui/layout/MeasureScope$layout$1;->getHeight()I
-HSPLandroidx/compose/ui/layout/MeasureScope$layout$1;->getWidth()I
-HSPLandroidx/compose/ui/layout/MeasureScope$layout$1;->placeChildren()V
-HSPLandroidx/compose/ui/layout/MeasureScope;->layout(IILjava/util/Map;Lkotlin/jvm/functions/Function1;)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/ui/layout/MeasuringIntrinsics$DefaultIntrinsicMeasurable;-><init>(Landroidx/compose/ui/layout/IntrinsicMeasurable;II)V
-HSPLandroidx/compose/ui/layout/MeasuringIntrinsics$DefaultIntrinsicMeasurable;->measure-BRTryo0(J)Landroidx/compose/ui/layout/Placeable;
-HSPLandroidx/compose/ui/layout/MeasuringIntrinsics$EmptyPlaceable;-><init>(II)V
-HSPLandroidx/compose/ui/layout/NoOpSubcomposeSlotReusePolicy;-><clinit>()V
-HSPLandroidx/compose/ui/layout/NoOpSubcomposeSlotReusePolicy;-><init>()V
-HSPLandroidx/compose/ui/layout/OnGloballyPositionedModifierImpl;-><init>(Landroidx/compose/foundation/text/TextController$coreModifiers$1;)V
-HSPLandroidx/compose/ui/layout/OnGloballyPositionedModifierImpl;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/layout/OnGloballyPositionedModifierImpl;->onGloballyPositioned(Landroidx/compose/ui/node/NodeCoordinator;)V
-HSPLandroidx/compose/ui/layout/OnSizeChangedModifier;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/layout/OnSizeChangedModifier;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/layout/OnSizeChangedModifier;->onRemeasured-ozmzZPI(J)V
-HSPLandroidx/compose/ui/layout/PinnableContainerKt$LocalPinnableContainer$1;-><clinit>()V
-HSPLandroidx/compose/ui/layout/PinnableContainerKt$LocalPinnableContainer$1;-><init>()V
-HSPLandroidx/compose/ui/layout/PinnableContainerKt$LocalPinnableContainer$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/layout/PinnableContainerKt;-><clinit>()V
-HSPLandroidx/compose/ui/layout/Placeable$PlacementScope$Companion;-><init>(I)V
-HSPLandroidx/compose/ui/layout/Placeable$PlacementScope$Companion;->access$configureForPlacingForAlignment(Landroidx/compose/ui/layout/Placeable$PlacementScope$Companion;Landroidx/compose/ui/node/LookaheadCapablePlaceable;)Z
-HSPLandroidx/compose/ui/layout/Placeable$PlacementScope$Companion;->getParentLayoutDirection()Landroidx/compose/ui/unit/LayoutDirection;
-HSPLandroidx/compose/ui/layout/Placeable$PlacementScope;-><clinit>()V
-HSPLandroidx/compose/ui/layout/Placeable$PlacementScope;-><init>()V
-HSPLandroidx/compose/ui/layout/Placeable$PlacementScope;->place(Landroidx/compose/ui/layout/Placeable;IIF)V
-HSPLandroidx/compose/ui/layout/Placeable$PlacementScope;->place-70tqf50(Landroidx/compose/ui/layout/Placeable;JF)V
-HSPLandroidx/compose/ui/layout/Placeable$PlacementScope;->placeRelative$default(Landroidx/compose/ui/layout/Placeable$PlacementScope;Landroidx/compose/ui/layout/Placeable;II)V
-HSPLandroidx/compose/ui/layout/Placeable$PlacementScope;->placeRelativeWithLayer$default(Landroidx/compose/ui/layout/Placeable$PlacementScope;Landroidx/compose/ui/layout/Placeable;II)V
-HSPLandroidx/compose/ui/layout/Placeable$PlacementScope;->placeRelativeWithLayer-aW-9-wM$default(Landroidx/compose/ui/layout/Placeable$PlacementScope;Landroidx/compose/ui/layout/Placeable;J)V
-HSPLandroidx/compose/ui/layout/Placeable$PlacementScope;->placeWithLayer$default(Landroidx/compose/ui/layout/Placeable$PlacementScope;Landroidx/compose/ui/layout/Placeable;IILkotlin/jvm/functions/Function1;I)V
-HSPLandroidx/compose/ui/layout/Placeable$PlacementScope;->placeWithLayer-aW-9-wM(Landroidx/compose/ui/layout/Placeable;JFLkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/layout/Placeable;-><init>()V
-HSPLandroidx/compose/ui/layout/Placeable;->getApparentToRealOffset-nOcc-ac()J
-HSPLandroidx/compose/ui/layout/Placeable;->getMeasuredHeight()I
-HSPLandroidx/compose/ui/layout/Placeable;->getMeasuredWidth()I
-HSPLandroidx/compose/ui/layout/Placeable;->recalculateWidthAndHeight()V
-HSPLandroidx/compose/ui/layout/Placeable;->setMeasuredSize-ozmzZPI(J)V
-HSPLandroidx/compose/ui/layout/Placeable;->setMeasurementConstraints-BRTryo0(J)V
-HSPLandroidx/compose/ui/layout/PlaceableKt$DefaultLayerBlock$1;-><clinit>()V
-HSPLandroidx/compose/ui/layout/PlaceableKt$DefaultLayerBlock$1;-><init>()V
-HSPLandroidx/compose/ui/layout/PlaceableKt$DefaultLayerBlock$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/layout/PlaceableKt;-><clinit>()V
-HSPLandroidx/compose/ui/layout/RootMeasurePolicy$measure$2;-><init>(Landroidx/compose/ui/layout/Placeable;)V
-HSPLandroidx/compose/ui/layout/RootMeasurePolicy$measure$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/layout/RootMeasurePolicy;-><clinit>()V
-HSPLandroidx/compose/ui/layout/RootMeasurePolicy;-><init>()V
-HSPLandroidx/compose/ui/layout/RootMeasurePolicy;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Ljava/util/List;J)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/ui/layout/ScaleFactor;-><clinit>()V
-HSPLandroidx/compose/ui/layout/ScaleFactorKt;->ScaleFactor(FF)J
-HSPLandroidx/compose/ui/layout/ScaleFactorKt;->times-UQTWf7w(JJ)J
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutKt$SubcomposeLayout$$inlined$ComposeNode$1;-><init>(Landroidx/compose/ui/node/LayoutNode$Companion$Constructor$1;)V
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutKt$SubcomposeLayout$$inlined$ComposeNode$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutKt$SubcomposeLayout$4;-><init>(Landroidx/compose/ui/layout/SubcomposeLayoutState;)V
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutKt$SubcomposeLayout$4;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutKt$SubcomposeLayout$5$1$invoke$$inlined$onDispose$1;-><init>(Landroidx/compose/runtime/State;)V
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutKt$SubcomposeLayout$5$1;-><init>(Landroidx/compose/runtime/MutableState;)V
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutKt$SubcomposeLayout$5$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutKt$SubcomposeLayout$6;-><init>(Landroidx/compose/ui/layout/SubcomposeLayoutState;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function2;II)V
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutKt;->SubcomposeLayout(Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutKt;->SubcomposeLayout(Landroidx/compose/ui/layout/SubcomposeLayoutState;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutState$setCompositionContext$1;-><init>(Landroidx/compose/ui/layout/SubcomposeLayoutState;)V
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutState$setCompositionContext$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutState$setMeasurePolicy$1;-><init>(Landroidx/compose/ui/layout/SubcomposeLayoutState;)V
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutState$setMeasurePolicy$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutState$setRoot$1;-><init>(Landroidx/compose/ui/layout/SubcomposeLayoutState;)V
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutState$setRoot$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutState;-><init>()V
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutState;-><init>(Landroidx/compose/ui/layout/SubcomposeSlotReusePolicy;)V
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutState;->getState()Landroidx/compose/ui/layout/LayoutNodeSubcompositionsState;
-HSPLandroidx/compose/ui/layout/SubcomposeLayoutState;->precompose(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Landroidx/compose/ui/layout/LayoutNodeSubcompositionsState$precompose$1;
-HSPLandroidx/compose/ui/layout/SubcomposeSlotReusePolicy$SlotIdsSet;-><init>(I)V
-HSPLandroidx/compose/ui/layout/SubcomposeSlotReusePolicy$SlotIdsSet;->clear()V
-HSPLandroidx/compose/ui/layout/SubcomposeSlotReusePolicy$SlotIdsSet;->contains(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/layout/SubcomposeSlotReusePolicy$SlotIdsSet;->iterator()Ljava/util/Iterator;
-HSPLandroidx/compose/ui/modifier/BackwardsCompatLocalMap;-><init>(Landroidx/compose/ui/modifier/ModifierLocalProvider;)V
-HSPLandroidx/compose/ui/modifier/BackwardsCompatLocalMap;->contains$ui_release(Landroidx/compose/ui/modifier/ModifierLocal;)Z
-HSPLandroidx/compose/ui/modifier/BackwardsCompatLocalMap;->get$ui_release(Landroidx/compose/ui/modifier/ProvidableModifierLocal;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/modifier/EmptyMap;-><clinit>()V
-HSPLandroidx/compose/ui/modifier/EmptyMap;-><init>()V
-HSPLandroidx/compose/ui/modifier/EmptyMap;->contains$ui_release(Landroidx/compose/ui/modifier/ModifierLocal;)Z
-HSPLandroidx/compose/ui/modifier/ModifierLocal;-><init>(Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/ui/modifier/ModifierLocalManager;-><init>(Landroidx/compose/ui/node/Owner;)V
-HSPLandroidx/compose/ui/modifier/ModifierLocalMap;-><init>()V
-HSPLandroidx/compose/ui/modifier/ModifierLocalNode;->getCurrent(Landroidx/compose/ui/modifier/ProvidableModifierLocal;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/modifier/ModifierLocalNode;->getProvidedValues()Landroidx/compose/ui/modifier/ModifierLocalMap;
-HSPLandroidx/compose/ui/modifier/ProvidableModifierLocal;-><init>(Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/ui/node/AlignmentLines;-><init>(Landroidx/compose/ui/node/AlignmentLinesOwner;)V
-HSPLandroidx/compose/ui/node/AlignmentLines;->getQueried$ui_release()Z
-HSPLandroidx/compose/ui/node/AlignmentLines;->getRequired$ui_release()Z
-HSPLandroidx/compose/ui/node/AlignmentLines;->onAlignmentsChanged()V
-HSPLandroidx/compose/ui/node/AlignmentLines;->recalculateQueryOwner()V
-HSPLandroidx/compose/ui/node/BackwardsCompatNode$initializeModifier$1;-><init>(Landroidx/compose/ui/node/BackwardsCompatNode;)V
-HSPLandroidx/compose/ui/node/BackwardsCompatNode$initializeModifier$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/BackwardsCompatNode$initializeModifier$2;-><init>(Landroidx/compose/ui/node/BackwardsCompatNode;)V
-HSPLandroidx/compose/ui/node/BackwardsCompatNode$initializeModifier$2;->onLayoutComplete()V
-HSPLandroidx/compose/ui/node/BackwardsCompatNode$updateDrawCache$1;-><init>(Landroidx/compose/ui/Modifier$Element;Landroidx/compose/ui/node/BackwardsCompatNode;)V
-HSPLandroidx/compose/ui/node/BackwardsCompatNode$updateDrawCache$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/BackwardsCompatNode$updateModifierLocalConsumer$1;-><init>(Landroidx/compose/ui/node/BackwardsCompatNode;)V
-HSPLandroidx/compose/ui/node/BackwardsCompatNode$updateModifierLocalConsumer$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;-><init>(Landroidx/compose/ui/Modifier$Element;)V
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->draw(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)V
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->getCurrent(Landroidx/compose/ui/modifier/ProvidableModifierLocal;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->getDensity()Landroidx/compose/ui/unit/Density;
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->getLayoutDirection()Landroidx/compose/ui/unit/LayoutDirection;
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->getProvidedValues()Landroidx/compose/ui/modifier/ModifierLocalMap;
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->getSemanticsConfiguration()Landroidx/compose/ui/semantics/SemanticsConfiguration;
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->getSize-NH-jbRc()J
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->initializeModifier(Z)V
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->isValid()Z
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->maxIntrinsicHeight(Landroidx/compose/ui/layout/IntrinsicMeasureScope;Landroidx/compose/ui/layout/IntrinsicMeasurable;I)I
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->maxIntrinsicWidth(Landroidx/compose/ui/layout/IntrinsicMeasureScope;Landroidx/compose/ui/layout/IntrinsicMeasurable;I)I
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->measure-3p2s80s(Landroidx/compose/ui/layout/MeasureScope;Landroidx/compose/ui/layout/Measurable;J)Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->modifyParentData(Landroidx/compose/ui/unit/Density;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->onAttach()V
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->onDetach()V
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->onGloballyPositioned(Landroidx/compose/ui/node/NodeCoordinator;)V
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->onMeasureResultChanged()V
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->onPlaced(Landroidx/compose/ui/node/NodeCoordinator;)V
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->onRemeasured-ozmzZPI(J)V
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->unInitializeModifier()V
-HSPLandroidx/compose/ui/node/BackwardsCompatNode;->updateModifierLocalConsumer()V
-HSPLandroidx/compose/ui/node/BackwardsCompatNodeKt$DetachedModifierLocalReadScope$1;-><init>()V
-HSPLandroidx/compose/ui/node/BackwardsCompatNodeKt$DetachedModifierLocalReadScope$1;->getCurrent(Landroidx/compose/ui/modifier/ProvidableModifierLocal;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/BackwardsCompatNodeKt$onDrawCacheReadsChanged$1;-><clinit>()V
-HSPLandroidx/compose/ui/node/BackwardsCompatNodeKt$onDrawCacheReadsChanged$1;-><init>()V
-HSPLandroidx/compose/ui/node/BackwardsCompatNodeKt$updateModifierLocalConsumer$1;-><clinit>()V
-HSPLandroidx/compose/ui/node/BackwardsCompatNodeKt$updateModifierLocalConsumer$1;-><init>()V
-HSPLandroidx/compose/ui/node/BackwardsCompatNodeKt;-><clinit>()V
-HSPLandroidx/compose/ui/node/CanFocusChecker;-><clinit>()V
-HSPLandroidx/compose/ui/node/CanFocusChecker;-><init>()V
-HSPLandroidx/compose/ui/node/CanFocusChecker;->setCanFocus(Z)V
-HSPLandroidx/compose/ui/node/ComposeUiNode$Companion$SetDensity$1;-><clinit>()V
-HSPLandroidx/compose/ui/node/ComposeUiNode$Companion$SetDensity$1;-><init>()V
-HSPLandroidx/compose/ui/node/ComposeUiNode$Companion$SetDensity$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/ComposeUiNode$Companion$SetLayoutDirection$1;-><clinit>()V
-HSPLandroidx/compose/ui/node/ComposeUiNode$Companion$SetLayoutDirection$1;-><init>()V
-HSPLandroidx/compose/ui/node/ComposeUiNode$Companion$SetLayoutDirection$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/ComposeUiNode$Companion$SetMeasurePolicy$1;-><clinit>()V
-HSPLandroidx/compose/ui/node/ComposeUiNode$Companion$SetMeasurePolicy$1;-><init>()V
-HSPLandroidx/compose/ui/node/ComposeUiNode$Companion$SetMeasurePolicy$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/ComposeUiNode$Companion$SetModifier$1;-><clinit>()V
-HSPLandroidx/compose/ui/node/ComposeUiNode$Companion$SetModifier$1;-><init>()V
-HSPLandroidx/compose/ui/node/ComposeUiNode$Companion$SetModifier$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/ComposeUiNode$Companion$SetViewConfiguration$1;-><clinit>()V
-HSPLandroidx/compose/ui/node/ComposeUiNode$Companion$SetViewConfiguration$1;-><init>()V
-HSPLandroidx/compose/ui/node/ComposeUiNode$Companion$SetViewConfiguration$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/ComposeUiNode$Companion;-><clinit>()V
-HSPLandroidx/compose/ui/node/ComposeUiNode$Companion;-><init>()V
-HSPLandroidx/compose/ui/node/ComposeUiNode;-><clinit>()V
-HSPLandroidx/compose/ui/node/DelegatableNodeKt;->access$addLayoutNodeChildren(Landroidx/compose/runtime/collection/MutableVector;Landroidx/compose/ui/Modifier$Node;)V
-HSPLandroidx/compose/ui/node/DelegatableNodeKt;->ancestors(Landroidx/compose/ui/node/DelegatableNode;I)Ljava/util/ArrayList;
-HSPLandroidx/compose/ui/node/DelegatableNodeKt;->nearestAncestor(Landroidx/compose/ui/node/DelegatableNode;I)Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/node/DelegatableNodeKt;->requireCoordinator-64DMado(Landroidx/compose/ui/node/DelegatableNode;I)Landroidx/compose/ui/node/NodeCoordinator;
-HSPLandroidx/compose/ui/node/DelegatableNodeKt;->requireLayoutNode(Landroidx/compose/ui/node/DelegatableNode;)Landroidx/compose/ui/node/LayoutNode;
-HSPLandroidx/compose/ui/node/DelegatableNodeKt;->requireOwner(Landroidx/compose/ui/node/DelegatableNode;)Landroidx/compose/ui/node/Owner;
-HSPLandroidx/compose/ui/node/DepthSortedSet$DepthComparator$1;-><init>()V
-HSPLandroidx/compose/ui/node/DepthSortedSet$DepthComparator$1;->compare(Ljava/lang/Object;Ljava/lang/Object;)I
-HSPLandroidx/compose/ui/node/DepthSortedSet$mapOfOriginalDepth$2;-><clinit>()V
-HSPLandroidx/compose/ui/node/DepthSortedSet$mapOfOriginalDepth$2;-><init>()V
-HSPLandroidx/compose/ui/node/DepthSortedSet;-><init>()V
-HSPLandroidx/compose/ui/node/DepthSortedSet;->add(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/node/DepthSortedSet;->remove(Landroidx/compose/ui/node/LayoutNode;)Z
-HSPLandroidx/compose/ui/node/DrawModifierNode;->onMeasureResultChanged()V
-HSPLandroidx/compose/ui/node/DrawModifierNodeKt;->invalidateDraw(Landroidx/compose/ui/node/DrawModifierNode;)V
-HSPLandroidx/compose/ui/node/HitTestResult;-><init>()V
-HSPLandroidx/compose/ui/node/InnerNodeCoordinator$tail$1;-><init>()V
-HSPLandroidx/compose/ui/node/InnerNodeCoordinator;-><clinit>()V
-HSPLandroidx/compose/ui/node/InnerNodeCoordinator;-><init>(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/node/InnerNodeCoordinator;->getTail()Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/node/InnerNodeCoordinator;->maxIntrinsicHeight(I)I
-HSPLandroidx/compose/ui/node/InnerNodeCoordinator;->maxIntrinsicWidth(I)I
-HSPLandroidx/compose/ui/node/InnerNodeCoordinator;->measure-BRTryo0(J)Landroidx/compose/ui/layout/Placeable;
-HSPLandroidx/compose/ui/node/InnerNodeCoordinator;->performDraw(Landroidx/compose/ui/graphics/Canvas;)V
-HSPLandroidx/compose/ui/node/InnerNodeCoordinator;->placeAt-f8xVGno(JFLkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/node/IntStack;-><init>(I)V
-HSPLandroidx/compose/ui/node/IntStack;->pop()I
-HSPLandroidx/compose/ui/node/IntStack;->pushDiagonal(III)V
-HSPLandroidx/compose/ui/node/IntStack;->pushRange(IIII)V
-HSPLandroidx/compose/ui/node/IntStack;->quickSort(II)V
-HSPLandroidx/compose/ui/node/IntStack;->swapDiagonal(II)V
-HSPLandroidx/compose/ui/node/IntrinsicsPolicy;-><init>(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/node/IntrinsicsPolicy;->measurePolicyFromState()Landroidx/compose/ui/layout/MeasurePolicy;
-HSPLandroidx/compose/ui/node/LayerPositionalProperties;-><init>()V
-HSPLandroidx/compose/ui/node/LayoutModifierNode;->forceRemeasure()V
-HSPLandroidx/compose/ui/node/LayoutModifierNode;->maxIntrinsicHeight(Landroidx/compose/ui/layout/IntrinsicMeasureScope;Landroidx/compose/ui/layout/IntrinsicMeasurable;I)I
-HSPLandroidx/compose/ui/node/LayoutModifierNode;->maxIntrinsicWidth(Landroidx/compose/ui/layout/IntrinsicMeasureScope;Landroidx/compose/ui/layout/IntrinsicMeasurable;I)I
-HSPLandroidx/compose/ui/node/LayoutModifierNodeCoordinator;-><clinit>()V
-HSPLandroidx/compose/ui/node/LayoutModifierNodeCoordinator;-><init>(Landroidx/compose/ui/node/LayoutNode;Landroidx/compose/ui/node/LayoutModifierNode;)V
-HSPLandroidx/compose/ui/node/LayoutModifierNodeCoordinator;->getTail()Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/node/LayoutModifierNodeCoordinator;->maxIntrinsicHeight(I)I
-HSPLandroidx/compose/ui/node/LayoutModifierNodeCoordinator;->maxIntrinsicWidth(I)I
-HSPLandroidx/compose/ui/node/LayoutModifierNodeCoordinator;->measure-BRTryo0(J)Landroidx/compose/ui/layout/Placeable;
-HSPLandroidx/compose/ui/node/LayoutModifierNodeCoordinator;->onLayoutModifierNodeChanged()V
-HSPLandroidx/compose/ui/node/LayoutModifierNodeCoordinator;->performDraw(Landroidx/compose/ui/graphics/Canvas;)V
-HSPLandroidx/compose/ui/node/LayoutModifierNodeCoordinator;->placeAt-f8xVGno(JFLkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/node/LayoutNode$$ExternalSyntheticLambda0;-><init>()V
-HSPLandroidx/compose/ui/node/LayoutNode$$ExternalSyntheticLambda0;->compare(Ljava/lang/Object;Ljava/lang/Object;)I
-HSPLandroidx/compose/ui/node/LayoutNode$Companion$Constructor$1;-><clinit>()V
-HSPLandroidx/compose/ui/node/LayoutNode$Companion$Constructor$1;-><init>()V
-HSPLandroidx/compose/ui/node/LayoutNode$Companion$Constructor$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/LayoutNode$Companion$DummyViewConfiguration$1;-><init>()V
-HSPLandroidx/compose/ui/node/LayoutNode$Companion$ErrorMeasurePolicy$1;-><init>()V
-HSPLandroidx/compose/ui/node/LayoutNode$NoIntrinsicsMeasurePolicy;-><init>(Ljava/lang/String;)V
-HSPLandroidx/compose/ui/node/LayoutNode$WhenMappings;-><clinit>()V
-HSPLandroidx/compose/ui/node/LayoutNode$_foldedChildren$1;-><init>(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/node/LayoutNode$_foldedChildren$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/LayoutNode;-><clinit>()V
-HSPLandroidx/compose/ui/node/LayoutNode;-><init>(IZ)V
-HSPLandroidx/compose/ui/node/LayoutNode;-><init>(IZI)V
-HSPLandroidx/compose/ui/node/LayoutNode;->attach$ui_release(Landroidx/compose/ui/node/Owner;)V
-HSPLandroidx/compose/ui/node/LayoutNode;->clearSubtreeIntrinsicsUsage$ui_release()V
-HSPLandroidx/compose/ui/node/LayoutNode;->clearSubtreePlacementIntrinsicsUsage()V
-HSPLandroidx/compose/ui/node/LayoutNode;->draw$ui_release(Landroidx/compose/ui/graphics/Canvas;)V
-HSPLandroidx/compose/ui/node/LayoutNode;->forceRemeasure()V
-HSPLandroidx/compose/ui/node/LayoutNode;->getChildMeasurables$ui_release()Ljava/util/List;
-HSPLandroidx/compose/ui/node/LayoutNode;->getChildren$ui_release()Ljava/util/List;
-HSPLandroidx/compose/ui/node/LayoutNode;->getFoldedChildren$ui_release()Ljava/util/List;
-HSPLandroidx/compose/ui/node/LayoutNode;->getParent$ui_release()Landroidx/compose/ui/node/LayoutNode;
-HSPLandroidx/compose/ui/node/LayoutNode;->getZSortedChildren()Landroidx/compose/runtime/collection/MutableVector;
-HSPLandroidx/compose/ui/node/LayoutNode;->get_children$ui_release()Landroidx/compose/runtime/collection/MutableVector;
-HSPLandroidx/compose/ui/node/LayoutNode;->insertAt$ui_release(ILandroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/node/LayoutNode;->invalidateLayer$ui_release()V
-HSPLandroidx/compose/ui/node/LayoutNode;->invalidateLayers$ui_release()V
-HSPLandroidx/compose/ui/node/LayoutNode;->invalidateMeasurements$ui_release()V
-HSPLandroidx/compose/ui/node/LayoutNode;->invalidateUnfoldedVirtualChildren()V
-HSPLandroidx/compose/ui/node/LayoutNode;->isAttached()Z
-HSPLandroidx/compose/ui/node/LayoutNode;->isValid()Z
-HSPLandroidx/compose/ui/node/LayoutNode;->markNodeAndSubtreeAsPlaced()V
-HSPLandroidx/compose/ui/node/LayoutNode;->markSubtreeAsNotPlaced()V
-HSPLandroidx/compose/ui/node/LayoutNode;->move$ui_release(III)V
-HSPLandroidx/compose/ui/node/LayoutNode;->onZSortedChildrenInvalidated$ui_release()V
-HSPLandroidx/compose/ui/node/LayoutNode;->replace$ui_release()V
-HSPLandroidx/compose/ui/node/LayoutNode;->requestRelayout$ui_release(Z)V
-HSPLandroidx/compose/ui/node/LayoutNode;->requestRemeasure$ui_release(Z)V
-HSPLandroidx/compose/ui/node/LayoutNode;->rescheduleRemeasureOrRelayout$ui_release(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/node/LayoutNode;->resetSubtreeIntrinsicsUsage$ui_release()V
-HSPLandroidx/compose/ui/node/LayoutNode;->setDensity(Landroidx/compose/ui/unit/Density;)V
-HSPLandroidx/compose/ui/node/LayoutNode;->setLayoutDirection(Landroidx/compose/ui/unit/LayoutDirection;)V
-HSPLandroidx/compose/ui/node/LayoutNode;->setMeasurePolicy(Landroidx/compose/ui/layout/MeasurePolicy;)V
-HSPLandroidx/compose/ui/node/LayoutNode;->setViewConfiguration(Landroidx/compose/ui/platform/ViewConfiguration;)V
-HSPLandroidx/compose/ui/node/LayoutNode;->updateChildrenIfDirty$ui_release()V
-HSPLandroidx/compose/ui/node/LayoutNodeAlignmentLines;-><init>(Landroidx/compose/ui/node/AlignmentLinesOwner;)V
-HSPLandroidx/compose/ui/node/LayoutNodeDrawScope;-><init>()V
-HSPLandroidx/compose/ui/node/LayoutNodeDrawScope;->draw-x_KDEd0$ui_release(Landroidx/compose/ui/graphics/Canvas;JLandroidx/compose/ui/node/NodeCoordinator;Landroidx/compose/ui/node/DrawModifierNode;)V
-HSPLandroidx/compose/ui/node/LayoutNodeDrawScope;->drawContent()V
-HSPLandroidx/compose/ui/node/LayoutNodeDrawScope;->drawImage-AZ2fEMs(Landroidx/compose/ui/graphics/ImageBitmap;JJJJFLandroidx/arch/core/executor/TaskExecutor;Landroidx/compose/ui/graphics/ColorFilter;II)V
-HSPLandroidx/compose/ui/node/LayoutNodeDrawScope;->drawRect-AsUm42w(Landroidx/compose/ui/graphics/Brush;JJFLandroidx/arch/core/executor/TaskExecutor;Landroidx/compose/ui/graphics/ColorFilter;I)V
-HSPLandroidx/compose/ui/node/LayoutNodeDrawScope;->drawRect-n-J9OG0(JJJFLandroidx/arch/core/executor/TaskExecutor;Landroidx/compose/ui/graphics/ColorFilter;I)V
-HSPLandroidx/compose/ui/node/LayoutNodeDrawScope;->drawRoundRect-u-Aw5IA(JJJJLandroidx/arch/core/executor/TaskExecutor;FLandroidx/compose/ui/graphics/ColorFilter;I)V
-HSPLandroidx/compose/ui/node/LayoutNodeDrawScope;->getDrawContext()Landroidx/compose/ui/graphics/drawscope/CanvasDrawScope$drawContext$1;
-HSPLandroidx/compose/ui/node/LayoutNodeDrawScope;->getLayoutDirection()Landroidx/compose/ui/unit/LayoutDirection;
-HSPLandroidx/compose/ui/node/LayoutNodeDrawScope;->getSize-NH-jbRc()J
-HSPLandroidx/compose/ui/node/LayoutNodeKt;->requireOwner(Landroidx/compose/ui/node/LayoutNode;)Landroidx/compose/ui/node/Owner;
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate$childMeasurables$1;-><clinit>()V
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate$childMeasurables$1;-><init>()V
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate$childMeasurables$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate$layoutChildren$1$1;-><init>(Landroidx/compose/ui/node/LayoutNodeLayoutDelegate;Landroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate;Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate$layoutChildren$1$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate$placeOuterCoordinator$1;-><init>(Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/node/LayoutNodeLayoutDelegate;JF)V
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate$placeOuterCoordinator$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate;-><init>(Landroidx/compose/ui/node/LayoutNodeLayoutDelegate;)V
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate;->getAlignmentLines()Landroidx/compose/ui/node/AlignmentLines;
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate;->getInnerCoordinator()Landroidx/compose/ui/node/InnerNodeCoordinator;
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate;->getMeasuredWidth()I
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate;->getParentAlignmentLinesOwner()Landroidx/compose/ui/node/AlignmentLinesOwner;
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate;->getParentData()Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate;->layoutChildren()V
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate;->maxIntrinsicHeight(I)I
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate;->maxIntrinsicWidth(I)I
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate;->measure-BRTryo0(J)Landroidx/compose/ui/layout/Placeable;
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate;->notifyChildrenUsingCoordinatesWhilePlacing()V
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate;->onIntrinsicsQueried()V
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate;->placeAt-f8xVGno(JFLkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate;->placeOuterCoordinator-f8xVGno(JFLkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate;->remeasure-BRTryo0(J)Z
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$performMeasure$2;-><init>(Landroidx/compose/ui/node/LayoutNodeLayoutDelegate;J)V
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate$performMeasure$2;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate;-><init>(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate;->getOuterCoordinator()Landroidx/compose/ui/node/NodeCoordinator;
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegate;->isOutMostLookaheadRoot(Landroidx/compose/ui/node/LayoutNode;)Z
-HSPLandroidx/compose/ui/node/LayoutNodeLayoutDelegateKt;->access$updateChildMeasurables(Landroidx/compose/ui/node/LayoutNode;Landroidx/compose/runtime/collection/MutableVector;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/node/LookaheadCapablePlaceable;-><init>()V
-HSPLandroidx/compose/ui/node/LookaheadCapablePlaceable;->invalidateAlignmentLinesFromPositionChange(Landroidx/compose/ui/node/NodeCoordinator;)V
-HSPLandroidx/compose/ui/node/MeasureAndLayoutDelegate;-><init>(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/node/MeasureAndLayoutDelegate;->dispatchOnPositionedCallbacks(Z)V
-HSPLandroidx/compose/ui/node/MeasureAndLayoutDelegate;->doRemeasure-sdFAvZA(Landroidx/compose/ui/node/LayoutNode;Landroidx/compose/ui/unit/Constraints;)Z
-HSPLandroidx/compose/ui/node/MeasureAndLayoutDelegate;->forceMeasureTheSubtree(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/node/MeasureAndLayoutDelegate;->measureAndLayout(Landroidx/compose/ui/platform/AndroidComposeView$resendMotionEventOnLayout$1;)Z
-HSPLandroidx/compose/ui/node/MeasureAndLayoutDelegate;->measureAndLayout-0kLqBqw(Landroidx/compose/ui/node/LayoutNode;J)V
-HSPLandroidx/compose/ui/node/MeasureAndLayoutDelegate;->measureOnly()V
-HSPLandroidx/compose/ui/node/MeasureAndLayoutDelegate;->recurseRemeasure(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/node/MeasureAndLayoutDelegate;->remeasureAndRelayoutIfNeeded(Landroidx/compose/ui/node/LayoutNode;)Z
-HSPLandroidx/compose/ui/node/MeasureAndLayoutDelegate;->remeasureOnly(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/node/MeasureAndLayoutDelegate;->requestRelayout(Landroidx/compose/ui/node/LayoutNode;Z)Z
-HSPLandroidx/compose/ui/node/MeasureAndLayoutDelegate;->requestRemeasure(Landroidx/compose/ui/node/LayoutNode;Z)Z
-HSPLandroidx/compose/ui/node/MeasureAndLayoutDelegate;->updateRootConstraints-BRTryo0(J)V
-HSPLandroidx/compose/ui/node/ModifierNodeElement;-><init>(Ljava/lang/Object;Z)V
-HSPLandroidx/compose/ui/node/ModifierNodeElement;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/node/MutableVectorWithMutationTracking;-><init>(Landroidx/compose/runtime/collection/MutableVector;Landroidx/compose/ui/node/LayoutNode$_foldedChildren$1;)V
-HSPLandroidx/compose/ui/node/NodeChain$Differ;-><init>(Landroidx/compose/ui/node/NodeChain;Landroidx/compose/ui/Modifier$Node;ILandroidx/compose/runtime/collection/MutableVector;Landroidx/compose/runtime/collection/MutableVector;)V
-HSPLandroidx/compose/ui/node/NodeChain$Differ;->remove()V
-HSPLandroidx/compose/ui/node/NodeChain$Differ;->same(II)V
-HSPLandroidx/compose/ui/node/NodeChain;-><init>(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/node/NodeChain;->attach(Z)V
-HSPLandroidx/compose/ui/node/NodeChain;->createAndInsertNodeAsParent(Landroidx/compose/ui/Modifier$Element;Landroidx/compose/ui/Modifier$Node;)Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/node/NodeChain;->structuralUpdate(Landroidx/compose/runtime/collection/MutableVector;ILandroidx/compose/runtime/collection/MutableVector;ILandroidx/compose/ui/Modifier$Node;)V
-HSPLandroidx/compose/ui/node/NodeChain;->updateNodeAndReplaceIfNeeded(Landroidx/compose/ui/Modifier$Element;Landroidx/compose/ui/Modifier$Element;Landroidx/compose/ui/Modifier$Node;)Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/node/NodeChainKt$SentinelHead$1;-><init>()V
-HSPLandroidx/compose/ui/node/NodeChainKt;-><clinit>()V
-HSPLandroidx/compose/ui/node/NodeCoordinator$Companion$PointerInputSource$1;-><init>()V
-HSPLandroidx/compose/ui/node/NodeCoordinator$Companion$SemanticsSource$1;-><init>()V
-HSPLandroidx/compose/ui/node/NodeCoordinator$Companion$onCommitAffectingLayer$1;-><clinit>()V
-HSPLandroidx/compose/ui/node/NodeCoordinator$Companion$onCommitAffectingLayer$1;-><init>()V
-HSPLandroidx/compose/ui/node/NodeCoordinator$Companion$onCommitAffectingLayer$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/NodeCoordinator$Companion$onCommitAffectingLayerParams$1;-><clinit>()V
-HSPLandroidx/compose/ui/node/NodeCoordinator$Companion$onCommitAffectingLayerParams$1;-><init>()V
-HSPLandroidx/compose/ui/node/NodeCoordinator$invalidateParentLayer$1;-><init>(Landroidx/compose/ui/node/NodeCoordinator;)V
-HSPLandroidx/compose/ui/node/NodeCoordinator$invalidateParentLayer$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/NodeCoordinator$invoke$1;-><init>(Landroidx/compose/ui/node/NodeCoordinator;Landroidx/compose/ui/graphics/Canvas;)V
-HSPLandroidx/compose/ui/node/NodeCoordinator$invoke$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/NodeCoordinator$updateLayerParameters$1;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/node/NodeCoordinator$updateLayerParameters$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/NodeCoordinator;-><clinit>()V
-HSPLandroidx/compose/ui/node/NodeCoordinator;-><init>(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/node/NodeCoordinator;->ancestorToLocal(Landroidx/compose/ui/node/NodeCoordinator;Landroidx/compose/ui/geometry/MutableRect;Z)V
-HSPLandroidx/compose/ui/node/NodeCoordinator;->draw(Landroidx/compose/ui/graphics/Canvas;)V
-HSPLandroidx/compose/ui/node/NodeCoordinator;->drawContainedDrawModifiers(Landroidx/compose/ui/graphics/Canvas;)V
-HSPLandroidx/compose/ui/node/NodeCoordinator;->findCommonAncestor$ui_release(Landroidx/compose/ui/node/NodeCoordinator;)Landroidx/compose/ui/node/NodeCoordinator;
-HSPLandroidx/compose/ui/node/NodeCoordinator;->getCoordinates()Landroidx/compose/ui/layout/LayoutCoordinates;
-HSPLandroidx/compose/ui/node/NodeCoordinator;->getDensity()F
-HSPLandroidx/compose/ui/node/NodeCoordinator;->getFontScale()F
-HSPLandroidx/compose/ui/node/NodeCoordinator;->getLayoutDirection()Landroidx/compose/ui/unit/LayoutDirection;
-HSPLandroidx/compose/ui/node/NodeCoordinator;->getLayoutNode()Landroidx/compose/ui/node/LayoutNode;
-HSPLandroidx/compose/ui/node/NodeCoordinator;->getMeasureResult$ui_release()Landroidx/compose/ui/layout/MeasureResult;
-HSPLandroidx/compose/ui/node/NodeCoordinator;->getParent()Landroidx/compose/ui/node/LookaheadCapablePlaceable;
-HSPLandroidx/compose/ui/node/NodeCoordinator;->getParentData()Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/NodeCoordinator;->getParentLayoutCoordinates()Landroidx/compose/ui/node/NodeCoordinator;
-HSPLandroidx/compose/ui/node/NodeCoordinator;->getSize-YbymL2g()J
-HSPLandroidx/compose/ui/node/NodeCoordinator;->headNode(Z)Landroidx/compose/ui/Modifier$Node;
-HSPLandroidx/compose/ui/node/NodeCoordinator;->invalidateLayer()V
-HSPLandroidx/compose/ui/node/NodeCoordinator;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/NodeCoordinator;->isAttached()Z
-HSPLandroidx/compose/ui/node/NodeCoordinator;->isValid()Z
-HSPLandroidx/compose/ui/node/NodeCoordinator;->localBoundingBoxOf(Landroidx/compose/ui/layout/LayoutCoordinates;Z)Landroidx/compose/ui/geometry/Rect;
-HSPLandroidx/compose/ui/node/NodeCoordinator;->localToRoot-MK-Hz9U(J)J
-HSPLandroidx/compose/ui/node/NodeCoordinator;->onLayerBlockUpdated(Lkotlin/jvm/functions/Function1;Z)V
-HSPLandroidx/compose/ui/node/NodeCoordinator;->onLayoutModifierNodeChanged()V
-HSPLandroidx/compose/ui/node/NodeCoordinator;->onMeasured()V
-HSPLandroidx/compose/ui/node/NodeCoordinator;->onPlaced$1()V
-HSPLandroidx/compose/ui/node/NodeCoordinator;->placeAt-f8xVGno(JFLkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/node/NodeCoordinator;->rectInParent$ui_release(Landroidx/compose/ui/geometry/MutableRect;ZZ)V
-HSPLandroidx/compose/ui/node/NodeCoordinator;->setMeasureResult$ui_release(Landroidx/compose/ui/layout/MeasureResult;)V
-HSPLandroidx/compose/ui/node/NodeCoordinator;->toParentPosition-MK-Hz9U(J)J
-HSPLandroidx/compose/ui/node/NodeCoordinator;->updateLayerParameters()V
-HSPLandroidx/compose/ui/node/NodeKind;->spring$default(FLjava/lang/Object;I)Landroidx/compose/animation/core/SpringSpec;
-HSPLandroidx/compose/ui/node/NodeKindKt;->autoInvalidateNode(Landroidx/compose/ui/Modifier$Node;I)V
-HSPLandroidx/compose/ui/node/NodeKindKt;->calculateNodeKindSetFrom(Landroidx/compose/ui/Modifier$Element;)I
-HSPLandroidx/compose/ui/node/NodeKindKt;->getIncludeSelfInTraversal-H91voCI(I)Z
-HSPLandroidx/compose/ui/node/NodeMeasuringIntrinsics$DefaultIntrinsicMeasurable;-><init>(Landroidx/compose/ui/layout/IntrinsicMeasurable;II)V
-HSPLandroidx/compose/ui/node/NodeMeasuringIntrinsics$DefaultIntrinsicMeasurable;->measure-BRTryo0(J)Landroidx/compose/ui/layout/Placeable;
-HSPLandroidx/compose/ui/node/NodeMeasuringIntrinsics$EmptyPlaceable;-><init>(II)V
-HSPLandroidx/compose/ui/node/ObserverNode$Companion$OnObserveReadsChanged$1;-><clinit>()V
-HSPLandroidx/compose/ui/node/ObserverNode$Companion$OnObserveReadsChanged$1;-><init>()V
-HSPLandroidx/compose/ui/node/ObserverNode$Companion;-><clinit>()V
-HSPLandroidx/compose/ui/node/ObserverNode$Companion;-><init>()V
-HSPLandroidx/compose/ui/node/ObserverNode;-><clinit>()V
-HSPLandroidx/compose/ui/node/ObserverNodeKt;->observeReads(Landroidx/compose/ui/Modifier$Node;Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/ui/node/OnPositionedDispatcher$Companion$DepthComparator;-><clinit>()V
-HSPLandroidx/compose/ui/node/OnPositionedDispatcher$Companion$DepthComparator;-><init>()V
-HSPLandroidx/compose/ui/node/OnPositionedDispatcher$Companion$DepthComparator;->compare(Ljava/lang/Object;Ljava/lang/Object;)I
-HSPLandroidx/compose/ui/node/OnPositionedDispatcher;-><init>()V
-HSPLandroidx/compose/ui/node/OnPositionedDispatcher;->dispatchHierarchy(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/node/OwnerSnapshotObserver$onCommitAffectingLayout$1;-><clinit>()V
-HSPLandroidx/compose/ui/node/OwnerSnapshotObserver$onCommitAffectingLayout$1;-><init>()V
-HSPLandroidx/compose/ui/node/OwnerSnapshotObserver$onCommitAffectingLayoutModifier$1;-><clinit>()V
-HSPLandroidx/compose/ui/node/OwnerSnapshotObserver$onCommitAffectingLayoutModifier$1;-><init>()V
-HSPLandroidx/compose/ui/node/OwnerSnapshotObserver$onCommitAffectingMeasure$1;-><clinit>()V
-HSPLandroidx/compose/ui/node/OwnerSnapshotObserver$onCommitAffectingMeasure$1;-><init>()V
-HSPLandroidx/compose/ui/node/OwnerSnapshotObserver$onCommitAffectingMeasure$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/node/OwnerSnapshotObserver;-><init>(Landroidx/compose/ui/platform/AndroidComposeView$snapshotObserver$1;)V
-HSPLandroidx/compose/ui/node/OwnerSnapshotObserver;->observeReads$ui_release(Landroidx/compose/ui/node/OwnerScope;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/ui/node/Ref;-><init>()V
-HSPLandroidx/compose/ui/node/SemanticsModifierNodeKt;->collapsedSemanticsConfiguration(Landroidx/compose/ui/node/SemanticsModifierNode;)Landroidx/compose/ui/semantics/SemanticsConfiguration;
-HSPLandroidx/compose/ui/node/Snake;->copy(Landroidx/compose/animation/core/AnimationVector;)Landroidx/compose/animation/core/AnimationVector;
-HSPLandroidx/compose/ui/node/Snake;->getDiagonalSize-impl([I)I
-HSPLandroidx/compose/ui/node/Snake;->newInstance(Landroidx/compose/animation/core/AnimationVector;)Landroidx/compose/animation/core/AnimationVector;
-HSPLandroidx/compose/ui/node/TreeSet;-><init>(Landroidx/compose/ui/node/DepthSortedSet$DepthComparator$1;)V
-HSPLandroidx/compose/ui/node/UiApplier;-><init>(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/node/UiApplier;->insertBottomUp(ILjava/lang/Object;)V
-HSPLandroidx/compose/ui/node/UiApplier;->insertTopDown(ILjava/lang/Object;)V
-HSPLandroidx/compose/ui/node/UiApplier;->onEndChanges()V
-HSPLandroidx/compose/ui/platform/AbstractComposeView$ensureCompositionCreated$1;-><init>(Landroidx/compose/ui/platform/AbstractComposeView;)V
-HSPLandroidx/compose/ui/platform/AbstractComposeView$ensureCompositionCreated$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/AbstractComposeView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;I)V
-HSPLandroidx/compose/ui/platform/AbstractComposeView;->addView(Landroid/view/View;ILandroid/view/ViewGroup$LayoutParams;)V
-HSPLandroidx/compose/ui/platform/AbstractComposeView;->addView(Landroid/view/View;Landroid/view/ViewGroup$LayoutParams;)V
-HSPLandroidx/compose/ui/platform/AbstractComposeView;->checkAddView()V
-HSPLandroidx/compose/ui/platform/AbstractComposeView;->ensureCompositionCreated()V
-HSPLandroidx/compose/ui/platform/AbstractComposeView;->internalOnLayout$ui_release(ZIIII)V
-HSPLandroidx/compose/ui/platform/AbstractComposeView;->internalOnMeasure$ui_release(II)V
-HSPLandroidx/compose/ui/platform/AbstractComposeView;->isAlive(Landroidx/compose/runtime/CompositionContext;)Z
-HSPLandroidx/compose/ui/platform/AbstractComposeView;->onAttachedToWindow()V
-HSPLandroidx/compose/ui/platform/AbstractComposeView;->onLayout(ZIIII)V
-HSPLandroidx/compose/ui/platform/AbstractComposeView;->onMeasure(II)V
-HSPLandroidx/compose/ui/platform/AbstractComposeView;->onRtlPropertiesChanged(I)V
-HSPLandroidx/compose/ui/platform/AbstractComposeView;->resolveParentCompositionContext()Landroidx/compose/runtime/CompositionContext;
-HSPLandroidx/compose/ui/platform/AbstractComposeView;->setParentCompositionContext(Landroidx/compose/runtime/CompositionContext;)V
-HSPLandroidx/compose/ui/platform/AbstractComposeView;->setParentContext(Landroidx/compose/runtime/CompositionContext;)V
-HSPLandroidx/compose/ui/platform/AbstractComposeView;->setPreviousAttachedWindowToken(Landroid/os/IBinder;)V
-HSPLandroidx/compose/ui/platform/AndroidAccessibilityManager;-><init>(Landroid/content/Context;)V
-HSPLandroidx/compose/ui/platform/AndroidClipboardManager;-><init>(Landroid/content/Context;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$$ExternalSyntheticLambda0;-><init>(Landroidx/compose/ui/platform/AndroidComposeView;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$$ExternalSyntheticLambda0;->onGlobalLayout()V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$$ExternalSyntheticLambda1;-><init>(Landroidx/compose/ui/platform/AndroidComposeView;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$$ExternalSyntheticLambda2;-><init>(Landroidx/compose/ui/platform/AndroidComposeView;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$$ExternalSyntheticLambda2;->onTouchModeChanged(Z)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$$ExternalSyntheticLambda3;-><init>(Landroidx/compose/ui/platform/AndroidComposeView;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$Companion;-><init>()V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$Companion;->access$getIsShowingLayoutBounds()Z
-HSPLandroidx/compose/ui/platform/AndroidComposeView$ViewTreeOwners;-><init>(Landroidx/lifecycle/LifecycleOwner;Landroidx/savedstate/SavedStateRegistryOwner;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$_inputModeManager$1;-><init>(Landroidx/compose/ui/platform/AndroidComposeView;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$configurationChangeObserver$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$configurationChangeObserver$1;-><init>()V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$focusOwner$1;-><init>(Landroidx/compose/ui/platform/AndroidComposeView;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$focusOwner$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/AndroidComposeView$keyInputModifier$1;-><init>(Landroidx/compose/ui/platform/AndroidComposeView;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$keyInputModifier$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/AndroidComposeView$pointerIconService$1;-><init>(Landroidx/compose/ui/platform/AndroidComposeView;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$resendMotionEventOnLayout$1;-><init>(Landroidx/compose/ui/platform/AndroidComposeView;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$resendMotionEventOnLayout$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/AndroidComposeView$resendMotionEventRunnable$1;-><init>(Landroidx/compose/ui/platform/AndroidComposeView;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$rotaryInputModifier$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$rotaryInputModifier$1;-><init>()V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$semanticsModifier$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$semanticsModifier$1;-><init>()V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$semanticsModifier$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/AndroidComposeView$snapshotObserver$1;-><init>(Landroidx/compose/ui/platform/AndroidComposeView;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView$snapshotObserver$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;-><clinit>()V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;-><init>(Landroid/content/Context;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->convertMeasureSpec(I)Lkotlin/Pair;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->createLayer(Landroidx/compose/ui/node/NodeCoordinator$invalidateParentLayer$1;Lkotlin/jvm/functions/Function1;)Landroidx/compose/ui/node/OwnedLayer;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->dispatchDraw(Landroid/graphics/Canvas;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->dispatchKeyEvent(Landroid/view/KeyEvent;)Z
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->forceMeasureTheSubtree(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getAccessibilityManager()Landroidx/compose/ui/platform/AccessibilityManager;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getAccessibilityManager()Landroidx/compose/ui/platform/AndroidAccessibilityManager;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getAutofill()Landroidx/compose/ui/autofill/Autofill;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getAutofillTree()Landroidx/compose/ui/autofill/AutofillTree;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getClipboardManager()Landroidx/compose/ui/platform/AndroidClipboardManager;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getClipboardManager()Landroidx/compose/ui/platform/ClipboardManager;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getDensity()Landroidx/compose/ui/unit/Density;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getFocusOwner()Landroidx/compose/ui/focus/FocusOwner;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getFontFamilyResolver()Landroidx/compose/ui/text/font/FontFamily$Resolver;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getFontLoader()Landroidx/compose/ui/text/font/Font$ResourceLoader;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getHapticFeedBack()Landroidx/compose/ui/hapticfeedback/HapticFeedback;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getInputModeManager()Landroidx/compose/ui/input/InputModeManager;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getLayoutDirection()Landroidx/compose/ui/unit/LayoutDirection;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getPointerIconService()Landroidx/compose/ui/input/pointer/PointerIconService;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getRoot()Landroidx/compose/ui/node/LayoutNode;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getSemanticsOwner()Landroidx/compose/ui/semantics/SemanticsOwner;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getSharedDrawScope()Landroidx/compose/ui/node/LayoutNodeDrawScope;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getShowLayoutBounds()Z
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getSnapshotObserver()Landroidx/compose/ui/node/OwnerSnapshotObserver;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getTextInputService()Landroidx/compose/ui/text/input/TextInputService;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getTextToolbar()Landroidx/compose/ui/platform/TextToolbar;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getView()Landroid/view/View;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getViewConfiguration()Landroidx/compose/ui/platform/ViewConfiguration;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getViewTreeOwners()Landroidx/compose/ui/platform/AndroidComposeView$ViewTreeOwners;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->getWindowInfo()Landroidx/compose/ui/platform/WindowInfo;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->invalidateLayers(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->invalidateLayoutNodeMeasurement(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->measureAndLayout(Z)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->measureAndLayout-0kLqBqw(Landroidx/compose/ui/node/LayoutNode;J)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->notifyLayerIsDirty$ui_release(Landroidx/compose/ui/node/OwnedLayer;Z)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->onAttach(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->onAttachedToWindow()V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->onCheckIsTextEditor()Z
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->onCreateInputConnection(Landroid/view/inputmethod/EditorInfo;)Landroid/view/inputmethod/InputConnection;
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->onDraw(Landroid/graphics/Canvas;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->onEndApplyChanges()V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->onFocusChanged(ZILandroid/graphics/Rect;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->onLayout(ZIIII)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->onLayoutChange(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->onMeasure(II)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->onRequestMeasure(Landroidx/compose/ui/node/LayoutNode;ZZ)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->onRequestRelayout(Landroidx/compose/ui/node/LayoutNode;ZZ)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->onResume(Landroidx/lifecycle/LifecycleOwner;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->onRtlPropertiesChanged(I)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->onSemanticsChange()V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->onWindowFocusChanged(Z)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->recycle$ui_release(Landroidx/compose/ui/node/OwnedLayer;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->registerOnEndApplyChangesListener(Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->registerOnLayoutCompletedListener(Landroidx/compose/ui/node/BackwardsCompatNode$initializeModifier$2;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->scheduleMeasureAndLayout(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->setConfigurationChangeObserver(Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->setLayoutDirection(Landroidx/compose/ui/unit/LayoutDirection;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->setOnViewTreeOwnersAvailable(Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->setShowLayoutBounds(Z)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->setViewTreeOwners(Landroidx/compose/ui/platform/AndroidComposeView$ViewTreeOwners;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeView;->updatePositionCacheAndDispatch()V
-HSPLandroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat$$ExternalSyntheticLambda0;-><init>(Landroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat$$ExternalSyntheticLambda1;-><init>(Landroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat$1;-><init>(Landroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat$1;->onViewAttachedToWindow(Landroid/view/View;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat$MyNodeProvider;-><init>(Landroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat$SemanticsNodeCopy;-><init>(Landroidx/compose/ui/semantics/SemanticsNode;Ljava/util/Map;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat$boundsUpdatesEventLoop$1;-><init>(Landroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat$sendScrollEventIfNeededLambda$1;-><init>(Landroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat;-><clinit>()V
-HSPLandroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat;-><init>(Landroidx/compose/ui/platform/AndroidComposeView;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat;->boundsUpdatesEventLoop(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat;->getAccessibilityNodeProvider(Landroid/view/View;)Landroidx/core/view/accessibility/AccessibilityNodeProviderCompat;
-HSPLandroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat;->isEnabled$ui_release()Z
-HSPLandroidx/compose/ui/platform/AndroidComposeViewForceDarkModeQ$$ExternalSyntheticApiModelOutline0;->m(Landroid/view/View;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeViewForceDarkModeQ;-><clinit>()V
-HSPLandroidx/compose/ui/platform/AndroidComposeViewForceDarkModeQ;-><init>()V
-HSPLandroidx/compose/ui/platform/AndroidComposeViewForceDarkModeQ;->disallowForceDark(Landroid/view/View;)V
-HSPLandroidx/compose/ui/platform/AndroidComposeViewVerificationHelperMethodsO;-><clinit>()V
-HSPLandroidx/compose/ui/platform/AndroidComposeViewVerificationHelperMethodsO;-><init>()V
-HSPLandroidx/compose/ui/platform/AndroidComposeViewVerificationHelperMethodsO;->focusable(Landroid/view/View;IZ)V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$LocalConfiguration$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$LocalConfiguration$1;-><init>()V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$LocalContext$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$LocalContext$1;-><init>()V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$LocalImageVectorCache$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$LocalImageVectorCache$1;-><init>()V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$LocalLifecycleOwner$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$LocalLifecycleOwner$1;-><init>()V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$LocalSavedStateRegistryOwner$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$LocalSavedStateRegistryOwner$1;-><init>()V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$LocalView$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$LocalView$1;-><init>()V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$1$1;-><init>(Landroidx/compose/runtime/MutableState;)V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$2$invoke$$inlined$onDispose$1;-><init>(Landroidx/compose/ui/platform/DisposableSaveableStateRegistry;)V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$2;-><init>(Landroidx/compose/ui/platform/DisposableSaveableStateRegistry;)V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3;-><init>(Landroidx/compose/ui/platform/AndroidComposeView;Landroidx/compose/ui/platform/AndroidUriHandler;Lkotlin/jvm/functions/Function2;I)V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$4;-><init>(Landroidx/compose/ui/platform/AndroidComposeView;Lkotlin/jvm/functions/Function2;I)V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$4;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$obtainImageVectorCache$1$invoke$$inlined$onDispose$1;-><init>(Landroid/content/Context;Landroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$obtainImageVectorCache$callbacks$1$1;)V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$obtainImageVectorCache$1;-><init>(Landroid/content/Context;Landroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$obtainImageVectorCache$callbacks$1$1;)V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$obtainImageVectorCache$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$obtainImageVectorCache$callbacks$1$1;-><init>(Landroid/content/res/Configuration;Landroidx/compose/ui/res/ImageVectorCache;)V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt;-><clinit>()V
-HSPLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt;->ProvideAndroidCompositionLocals(Landroidx/compose/ui/platform/AndroidComposeView;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;I)V
-HSPLandroidx/compose/ui/platform/AndroidFontResourceLoader;-><init>(Landroid/content/Context;)V
-HSPLandroidx/compose/ui/platform/AndroidTextToolbar;-><init>(Landroid/view/View;)V
-HSPLandroidx/compose/ui/platform/AndroidUiDispatcher$Companion$Main$2;-><clinit>()V
-HSPLandroidx/compose/ui/platform/AndroidUiDispatcher$Companion$Main$2;-><init>()V
-HSPLandroidx/compose/ui/platform/AndroidUiDispatcher$Companion$Main$2;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/AndroidUiDispatcher$Companion$currentThread$1;-><init>()V
-HSPLandroidx/compose/ui/platform/AndroidUiDispatcher$dispatchCallback$1;-><init>(Landroidx/compose/ui/platform/AndroidUiDispatcher;)V
-HSPLandroidx/compose/ui/platform/AndroidUiDispatcher$dispatchCallback$1;->doFrame(J)V
-HSPLandroidx/compose/ui/platform/AndroidUiDispatcher$dispatchCallback$1;->run()V
-HSPLandroidx/compose/ui/platform/AndroidUiDispatcher;-><clinit>()V
-HSPLandroidx/compose/ui/platform/AndroidUiDispatcher;-><init>(Landroid/view/Choreographer;Landroid/os/Handler;)V
-HSPLandroidx/compose/ui/platform/AndroidUiDispatcher;->access$performTrampolineDispatch(Landroidx/compose/ui/platform/AndroidUiDispatcher;)V
-HSPLandroidx/compose/ui/platform/AndroidUiDispatcher;->dispatch(Lkotlin/coroutines/CoroutineContext;Ljava/lang/Runnable;)V
-HSPLandroidx/compose/ui/platform/AndroidUiDispatcher_androidKt;->checkScrollableContainerConstraints-K40F9xA(JLandroidx/compose/foundation/gestures/Orientation;)V
-HSPLandroidx/compose/ui/platform/AndroidUiFrameClock$withFrameNanos$2$1;-><init>(Landroidx/compose/ui/platform/AndroidUiDispatcher;Landroidx/compose/ui/platform/AndroidUiFrameClock$withFrameNanos$2$callback$1;)V
-HSPLandroidx/compose/ui/platform/AndroidUiFrameClock$withFrameNanos$2$callback$1;-><init>(Lkotlinx/coroutines/CancellableContinuationImpl;Landroidx/compose/ui/platform/AndroidUiFrameClock;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/platform/AndroidUiFrameClock$withFrameNanos$2$callback$1;->doFrame(J)V
-HSPLandroidx/compose/ui/platform/AndroidUiFrameClock;-><init>(Landroid/view/Choreographer;)V
-HSPLandroidx/compose/ui/platform/AndroidUiFrameClock;->fold(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/AndroidUiFrameClock;->get(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
-HSPLandroidx/compose/ui/platform/AndroidUiFrameClock;->minusKey(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
-HSPLandroidx/compose/ui/platform/AndroidUiFrameClock;->withFrameNanos(Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/AndroidUriHandler;-><init>(Landroid/content/Context;)V
-HSPLandroidx/compose/ui/platform/AndroidViewConfiguration;-><init>(Landroid/view/ViewConfiguration;)V
-HSPLandroidx/compose/ui/platform/CalculateMatrixToWindowApi29;-><init>()V
-HSPLandroidx/compose/ui/platform/ComposableSingletons$Wrapper_androidKt$lambda-1$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/ComposableSingletons$Wrapper_androidKt$lambda-1$1;-><init>()V
-HSPLandroidx/compose/ui/platform/ComposableSingletons$Wrapper_androidKt;-><clinit>()V
-HSPLandroidx/compose/ui/platform/ComposeView$Content$1;-><init>(Landroidx/compose/ui/platform/ComposeView;I)V
-HSPLandroidx/compose/ui/platform/ComposeView;-><init>(Landroid/content/Context;)V
-HSPLandroidx/compose/ui/platform/ComposeView;->Content(Landroidx/compose/runtime/Composer;I)V
-HSPLandroidx/compose/ui/platform/ComposeView;->getShouldCreateCompositionOnAttachedToWindow()Z
-HSPLandroidx/compose/ui/platform/ComposeView;->setContent(Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalAccessibilityManager$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalAccessibilityManager$1;-><init>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalAutofill$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalAutofill$1;-><init>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalAutofillTree$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalAutofillTree$1;-><init>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalClipboardManager$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalClipboardManager$1;-><init>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalDensity$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalDensity$1;-><init>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalFocusManager$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalFocusManager$1;-><init>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalFontFamilyResolver$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalFontFamilyResolver$1;-><init>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalFontLoader$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalFontLoader$1;-><init>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalHapticFeedback$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalHapticFeedback$1;-><init>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalInputModeManager$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalInputModeManager$1;-><init>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalLayoutDirection$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalLayoutDirection$1;-><init>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalPointerIconService$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalPointerIconService$1;-><init>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalTextInputService$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalTextInputService$1;-><init>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalTextToolbar$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalTextToolbar$1;-><init>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalUriHandler$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalUriHandler$1;-><init>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalViewConfiguration$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalViewConfiguration$1;-><init>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalWindowInfo$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$LocalWindowInfo$1;-><init>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt$ProvideCommonCompositionLocals$1;-><init>(Landroidx/compose/ui/node/Owner;Landroidx/compose/ui/platform/UriHandler;Lkotlin/jvm/functions/Function2;I)V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt;-><clinit>()V
-HSPLandroidx/compose/ui/platform/CompositionLocalsKt;->ProvideCommonCompositionLocals(Landroidx/compose/ui/node/Owner;Landroidx/compose/ui/platform/UriHandler;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;I)V
-HSPLandroidx/compose/ui/platform/DisposableSaveableStateRegistry;-><init>(Landroidx/compose/runtime/saveable/SaveableStateRegistryImpl;Landroidx/compose/ui/platform/DisposableSaveableStateRegistry_androidKt$DisposableSaveableStateRegistry$1;)V
-HSPLandroidx/compose/ui/platform/DisposableSaveableStateRegistry;->canBeSaved(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/platform/DisposableSaveableStateRegistry;->consumeRestored(Ljava/lang/String;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/DisposableSaveableStateRegistry;->registerProvider(Ljava/lang/String;Landroidx/compose/runtime/saveable/RememberSaveableKt$rememberSaveable$1$valueProvider$1;)Landroidx/compose/runtime/saveable/SaveableStateRegistry$Entry;
-HSPLandroidx/compose/ui/platform/DisposableSaveableStateRegistry_androidKt$DisposableSaveableStateRegistry$1;-><init>(ZLandroidx/savedstate/SavedStateRegistry;Ljava/lang/String;)V
-HSPLandroidx/compose/ui/platform/DisposableSaveableStateRegistry_androidKt$DisposableSaveableStateRegistry$registered$1;-><init>(Landroidx/compose/runtime/saveable/SaveableStateRegistryImpl;)V
-HSPLandroidx/compose/ui/platform/DisposableSaveableStateRegistry_androidKt$DisposableSaveableStateRegistry$saveableStateRegistry$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/DisposableSaveableStateRegistry_androidKt$DisposableSaveableStateRegistry$saveableStateRegistry$1;-><init>()V
-HSPLandroidx/compose/ui/platform/DisposableSaveableStateRegistry_androidKt$DisposableSaveableStateRegistry$saveableStateRegistry$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/DisposableSaveableStateRegistry_androidKt;-><clinit>()V
-HSPLandroidx/compose/ui/platform/DisposableSaveableStateRegistry_androidKt;->canBeSavedToBundle(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/platform/GlobalSnapshotManager$ensureStarted$1;-><init>(Lkotlinx/coroutines/channels/Channel;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/ui/platform/GlobalSnapshotManager$ensureStarted$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/ui/platform/GlobalSnapshotManager$ensureStarted$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/GlobalSnapshotManager$ensureStarted$2;-><init>(Lkotlinx/coroutines/channels/AbstractChannel;)V
-HSPLandroidx/compose/ui/platform/GlobalSnapshotManager$ensureStarted$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/GlobalSnapshotManager;-><clinit>()V
-HSPLandroidx/compose/ui/platform/InspectableModifier$End;-><init>()V
-HSPLandroidx/compose/ui/platform/InspectableModifier;-><init>()V
-HSPLandroidx/compose/ui/platform/InspectableValueKt$NoInspectorInfo$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/InspectableValueKt$NoInspectorInfo$1;-><init>()V
-HSPLandroidx/compose/ui/platform/InspectorValueInfo;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/platform/LayerMatrixCache;-><init>(Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/ui/platform/LayerMatrixCache;->calculateMatrix-GrdbGEg(Ljava/lang/Object;)[F
-HSPLandroidx/compose/ui/platform/LayerMatrixCache;->invalidate()V
-HSPLandroidx/compose/ui/platform/MotionDurationScaleImpl;-><init>()V
-HSPLandroidx/compose/ui/platform/MotionDurationScaleImpl;->fold(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/MotionDurationScaleImpl;->get(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
-HSPLandroidx/compose/ui/platform/MotionDurationScaleImpl;->getScaleFactor()F
-HSPLandroidx/compose/ui/platform/MotionDurationScaleImpl;->minusKey(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
-HSPLandroidx/compose/ui/platform/OutlineResolver;-><init>(Landroidx/compose/ui/unit/Density;)V
-HSPLandroidx/compose/ui/platform/OutlineResolver;->getOutline()Landroid/graphics/Outline;
-HSPLandroidx/compose/ui/platform/OutlineResolver;->update(Landroidx/compose/ui/graphics/Shape;FZFLandroidx/compose/ui/unit/LayoutDirection;Landroidx/compose/ui/unit/Density;)Z
-HSPLandroidx/compose/ui/platform/OutlineResolver;->updateCache()V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29$$ExternalSyntheticApiModelOutline0;->m(Landroid/graphics/Canvas;Landroid/graphics/RenderNode;)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;-><init>(Landroidx/compose/ui/platform/AndroidComposeView;)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->discardDisplayList()V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->drawInto(Landroid/graphics/Canvas;)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->getAlpha()F
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->getClipToOutline()Z
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->getElevation()F
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->getHasDisplayList()Z
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->getHeight()I
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->getLeft()I
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->getMatrix(Landroid/graphics/Matrix;)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->getTop()I
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->getWidth()I
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->offsetLeftAndRight(I)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->offsetTopAndBottom(I)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->record(Landroidx/compose/ui/graphics/CanvasHolder;Landroidx/compose/ui/graphics/Path;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setAlpha(F)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setAmbientShadowColor(I)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setCameraDistance(F)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setClipToBounds(Z)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setClipToOutline(Z)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setCompositingStrategy-aDBOjCE(I)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setElevation(F)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setHasOverlappingRendering()Z
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setOutline(Landroid/graphics/Outline;)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setPivotX(F)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setPivotY(F)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setPosition(IIII)Z
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setRenderEffect()V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setRotationX(F)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setRotationY(F)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setRotationZ(F)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setScaleX(F)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setScaleY(F)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setSpotShadowColor(I)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setTranslationX(F)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29;->setTranslationY(F)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29VerificationHelper$$ExternalSyntheticApiModelOutline0;->m(Landroid/graphics/RenderNode;)V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29VerificationHelper;-><clinit>()V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29VerificationHelper;-><init>()V
-HSPLandroidx/compose/ui/platform/RenderNodeApi29VerificationHelper;->setRenderEffect(Landroid/graphics/RenderNode;Landroidx/compose/ui/graphics/RenderEffect;)V
-HSPLandroidx/compose/ui/platform/RenderNodeLayer$Companion$getMatrix$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/RenderNodeLayer$Companion$getMatrix$1;-><init>()V
-HSPLandroidx/compose/ui/platform/RenderNodeLayer$Companion$getMatrix$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/RenderNodeLayer;-><init>(Landroidx/compose/ui/platform/AndroidComposeView;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/node/NodeCoordinator$invalidateParentLayer$1;)V
-HSPLandroidx/compose/ui/platform/RenderNodeLayer;->destroy()V
-HSPLandroidx/compose/ui/platform/RenderNodeLayer;->drawLayer(Landroidx/compose/ui/graphics/Canvas;)V
-HSPLandroidx/compose/ui/platform/RenderNodeLayer;->invalidate()V
-HSPLandroidx/compose/ui/platform/RenderNodeLayer;->mapBounds(Landroidx/compose/ui/geometry/MutableRect;Z)V
-HSPLandroidx/compose/ui/platform/RenderNodeLayer;->mapOffset-8S9VItk(JZ)J
-HSPLandroidx/compose/ui/platform/RenderNodeLayer;->move--gyyYBs(J)V
-HSPLandroidx/compose/ui/platform/RenderNodeLayer;->resize-ozmzZPI(J)V
-HSPLandroidx/compose/ui/platform/RenderNodeLayer;->reuseLayer(Landroidx/compose/ui/node/NodeCoordinator$invalidateParentLayer$1;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/platform/RenderNodeLayer;->setDirty(Z)V
-HSPLandroidx/compose/ui/platform/RenderNodeLayer;->updateDisplayList()V
-HSPLandroidx/compose/ui/platform/RenderNodeLayer;->updateLayerProperties-dDxr-wY(FFFFFFFFFFJLandroidx/compose/ui/graphics/Shape;ZJJILandroidx/compose/ui/unit/LayoutDirection;Landroidx/compose/ui/unit/Density;)V
-HSPLandroidx/compose/ui/platform/ViewCompositionStrategy$DisposeOnDetachedFromWindowOrReleasedFromPool$installFor$1;-><init>(Landroidx/compose/ui/platform/AbstractComposeView;Landroidx/compose/ui/platform/ViewCompositionStrategy$DisposeOnDetachedFromWindowOrReleasedFromPool$installFor$listener$1;Landroidx/compose/ui/platform/ViewCompositionStrategy$DisposeOnDetachedFromWindowOrReleasedFromPool$installFor$poolingContainerListener$1;)V
-HSPLandroidx/compose/ui/platform/ViewCompositionStrategy$DisposeOnDetachedFromWindowOrReleasedFromPool$installFor$listener$1;-><init>(Landroidx/compose/ui/platform/AbstractComposeView;)V
-HSPLandroidx/compose/ui/platform/ViewCompositionStrategy$DisposeOnDetachedFromWindowOrReleasedFromPool$installFor$listener$1;->onViewAttachedToWindow(Landroid/view/View;)V
-HSPLandroidx/compose/ui/platform/ViewCompositionStrategy$DisposeOnDetachedFromWindowOrReleasedFromPool$installFor$poolingContainerListener$1;-><init>()V
-HSPLandroidx/compose/ui/platform/ViewLayer$Companion$OutlineProvider$1;-><init>()V
-HSPLandroidx/compose/ui/platform/ViewLayer;-><clinit>()V
-HSPLandroidx/compose/ui/platform/WeakCache;-><init>()V
-HSPLandroidx/compose/ui/platform/WindowInfoImpl;-><clinit>()V
-HSPLandroidx/compose/ui/platform/WindowInfoImpl;-><init>()V
-HSPLandroidx/compose/ui/platform/WindowRecomposerFactory$Companion$LifecycleAware$1;-><clinit>()V
-HSPLandroidx/compose/ui/platform/WindowRecomposerFactory$Companion$LifecycleAware$1;-><init>()V
-HSPLandroidx/compose/ui/platform/WindowRecomposerFactory$Companion$LifecycleAware$1;->createRecomposer(Landroid/view/View;)Landroidx/compose/runtime/Recomposer;
-HSPLandroidx/compose/ui/platform/WindowRecomposerFactory$Companion;-><clinit>()V
-HSPLandroidx/compose/ui/platform/WindowRecomposerFactory$Companion;-><init>()V
-HSPLandroidx/compose/ui/platform/WindowRecomposerFactory;-><clinit>()V
-HSPLandroidx/compose/ui/platform/WindowRecomposerPolicy$createAndInstallWindowRecomposer$1;-><init>(Lkotlinx/coroutines/StandaloneCoroutine;)V
-HSPLandroidx/compose/ui/platform/WindowRecomposerPolicy$createAndInstallWindowRecomposer$1;->onViewAttachedToWindow(Landroid/view/View;)V
-HSPLandroidx/compose/ui/platform/WindowRecomposerPolicy$createAndInstallWindowRecomposer$unsetJob$1;-><init>(Landroidx/compose/runtime/Recomposer;Landroid/view/View;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/ui/platform/WindowRecomposerPolicy$createAndInstallWindowRecomposer$unsetJob$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/ui/platform/WindowRecomposerPolicy$createAndInstallWindowRecomposer$unsetJob$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/WindowRecomposerPolicy;-><clinit>()V
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$1;-><init>(Landroid/view/View;Landroidx/compose/runtime/Recomposer;)V
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$1;->onViewAttachedToWindow(Landroid/view/View;)V
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$2$WhenMappings;-><clinit>()V
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$2$onStateChanged$1$1$1$1;-><init>(Landroidx/compose/ui/platform/MotionDurationScaleImpl;)V
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$2$onStateChanged$1$1$1$1;->emit(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$2$onStateChanged$1$1$1;-><init>(Lkotlinx/coroutines/flow/StateFlow;Landroidx/compose/ui/platform/MotionDurationScaleImpl;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$2$onStateChanged$1$1$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$2$onStateChanged$1$1$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$2$onStateChanged$1;-><init>(Lkotlin/jvm/internal/Ref$ObjectRef;Landroidx/compose/runtime/Recomposer;Landroidx/lifecycle/LifecycleOwner;Landroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$2;Landroid/view/View;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$2$onStateChanged$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$2$onStateChanged$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$2$onStateChanged$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$2;-><init>(Lkotlinx/coroutines/internal/ContextScope;Landroidx/compose/runtime/PausableMonotonicFrameClock;Landroidx/compose/runtime/Recomposer;Lkotlin/jvm/internal/Ref$ObjectRef;Landroid/view/View;)V
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$2;->onStateChanged(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle$Event;)V
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt$getAnimationScaleFlowFor$1$1$1;-><init>(Landroid/content/ContentResolver;Landroid/net/Uri;Landroidx/compose/ui/platform/WindowRecomposer_androidKt$getAnimationScaleFlowFor$1$1$contentObserver$1;Lkotlinx/coroutines/channels/Channel;Landroid/content/Context;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt$getAnimationScaleFlowFor$1$1$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt$getAnimationScaleFlowFor$1$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt$getAnimationScaleFlowFor$1$1$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt$getAnimationScaleFlowFor$1$1$contentObserver$1;-><init>(Lkotlinx/coroutines/channels/AbstractChannel;Landroid/os/Handler;)V
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt;-><clinit>()V
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt;->access$getAnimationScaleFlowFor(Landroid/content/Context;)Lkotlinx/coroutines/flow/StateFlow;
-HSPLandroidx/compose/ui/platform/WindowRecomposer_androidKt;->getCompositionContext(Landroid/view/View;)Landroidx/compose/runtime/CompositionContext;
-HSPLandroidx/compose/ui/platform/WrappedComposition$setContent$1$1$1;-><init>(Landroidx/compose/ui/platform/WrappedComposition;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/ui/platform/WrappedComposition$setContent$1$1$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/ui/platform/WrappedComposition$setContent$1$1$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/WrappedComposition$setContent$1$1$2;-><init>(Landroidx/compose/ui/platform/WrappedComposition;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/ui/platform/WrappedComposition$setContent$1$1$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/compose/ui/platform/WrappedComposition$setContent$1$1$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/WrappedComposition$setContent$1$1$3;-><init>(Landroidx/compose/ui/platform/WrappedComposition;Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/ui/platform/WrappedComposition$setContent$1$1$3;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/WrappedComposition$setContent$1$1;-><init>(Landroidx/compose/ui/platform/WrappedComposition;Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/ui/platform/WrappedComposition$setContent$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/WrappedComposition$setContent$1;-><init>(Landroidx/compose/ui/platform/WrappedComposition;Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/ui/platform/WrappedComposition$setContent$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/platform/WrappedComposition;-><init>(Landroidx/compose/ui/platform/AndroidComposeView;Landroidx/compose/runtime/CompositionImpl;)V
-HSPLandroidx/compose/ui/platform/WrappedComposition;->onStateChanged(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle$Event;)V
-HSPLandroidx/compose/ui/platform/WrappedComposition;->setContent(Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/ui/platform/WrapperRenderNodeLayerHelperMethods;-><clinit>()V
-HSPLandroidx/compose/ui/platform/WrapperRenderNodeLayerHelperMethods;-><init>()V
-HSPLandroidx/compose/ui/platform/WrapperRenderNodeLayerHelperMethods;->onDescendantInvalidated(Landroidx/compose/ui/platform/AndroidComposeView;)V
-HSPLandroidx/compose/ui/platform/WrapperVerificationHelperMethods$$ExternalSyntheticApiModelOutline0;->m(Landroid/view/View;)Ljava/util/Map;
-HSPLandroidx/compose/ui/platform/WrapperVerificationHelperMethods;-><clinit>()V
-HSPLandroidx/compose/ui/platform/WrapperVerificationHelperMethods;-><init>()V
-HSPLandroidx/compose/ui/platform/WrapperVerificationHelperMethods;->attributeSourceResourceMap(Landroid/view/View;)Ljava/util/Map;
-HSPLandroidx/compose/ui/platform/Wrapper_androidKt;-><clinit>()V
-HSPLandroidx/compose/ui/platform/Wrapper_androidKt;->setContent(Landroidx/compose/ui/platform/AbstractComposeView;Landroidx/compose/runtime/CompositionContext;Landroidx/compose/runtime/internal/ComposableLambdaImpl;)Landroidx/compose/runtime/Composition;
-HSPLandroidx/compose/ui/res/ImageVectorCache;-><init>()V
-HSPLandroidx/compose/ui/res/PainterResources_androidKt;->painterResource(ILandroidx/compose/runtime/Composer;)Landroidx/compose/ui/graphics/painter/Painter;
-HSPLandroidx/compose/ui/semantics/AccessibilityAction;-><init>(Ljava/lang/String;Lkotlin/Function;)V
-HSPLandroidx/compose/ui/semantics/AccessibilityAction;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/semantics/CollectionInfo;-><init>(II)V
-HSPLandroidx/compose/ui/semantics/Role;-><init>(I)V
-HSPLandroidx/compose/ui/semantics/Role;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/semantics/ScrollAxisRange;-><init>(Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Z)V
-HSPLandroidx/compose/ui/semantics/SemanticsActions;-><clinit>()V
-HSPLandroidx/compose/ui/semantics/SemanticsConfiguration;-><init>()V
-HSPLandroidx/compose/ui/semantics/SemanticsConfiguration;->contains(Landroidx/compose/ui/semantics/SemanticsPropertyKey;)Z
-HSPLandroidx/compose/ui/semantics/SemanticsConfiguration;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/semantics/SemanticsConfiguration;->set(Landroidx/compose/ui/semantics/SemanticsPropertyKey;Ljava/lang/Object;)V
-HSPLandroidx/compose/ui/semantics/SemanticsConfigurationKt;->getOrNull(Landroidx/compose/ui/semantics/SemanticsConfiguration;Landroidx/compose/ui/semantics/SemanticsPropertyKey;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/semantics/SemanticsModifierCore;-><clinit>()V
-HSPLandroidx/compose/ui/semantics/SemanticsModifierCore;-><init>(ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
-HSPLandroidx/compose/ui/semantics/SemanticsModifierCore;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/semantics/SemanticsModifierCore;->getSemanticsConfiguration()Landroidx/compose/ui/semantics/SemanticsConfiguration;
-HSPLandroidx/compose/ui/semantics/SemanticsModifierKt;->semantics(Landroidx/compose/ui/Modifier;ZLkotlin/jvm/functions/Function1;)Landroidx/compose/ui/Modifier;
-HSPLandroidx/compose/ui/semantics/SemanticsNode;-><init>(Landroidx/compose/ui/node/SemanticsModifierNode;ZLandroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/semantics/SemanticsNode;->getChildren(Z)Ljava/util/List;
-HSPLandroidx/compose/ui/semantics/SemanticsNode;->isMergingSemanticsOfDescendants()Z
-HSPLandroidx/compose/ui/semantics/SemanticsNode;->unmergedChildren$ui_release(ZZ)Ljava/util/List;
-HSPLandroidx/compose/ui/semantics/SemanticsNodeKt;->findOneLayerOfSemanticsWrappers(Landroidx/compose/ui/node/LayoutNode;Ljava/util/List;)V
-HSPLandroidx/compose/ui/semantics/SemanticsNodeKt;->getOuterSemantics(Landroidx/compose/ui/node/LayoutNode;)Landroidx/compose/ui/node/SemanticsModifierNode;
-HSPLandroidx/compose/ui/semantics/SemanticsOwner;-><init>(Landroidx/compose/ui/node/LayoutNode;)V
-HSPLandroidx/compose/ui/semantics/SemanticsOwner;->getUnmergedRootSemanticsNode()Landroidx/compose/ui/semantics/SemanticsNode;
-HSPLandroidx/compose/ui/semantics/SemanticsProperties$ContentDescription$1;-><clinit>()V
-HSPLandroidx/compose/ui/semantics/SemanticsProperties$ContentDescription$1;-><init>()V
-HSPLandroidx/compose/ui/semantics/SemanticsProperties$InvisibleToUser$1;-><clinit>()V
-HSPLandroidx/compose/ui/semantics/SemanticsProperties$InvisibleToUser$1;-><init>()V
-HSPLandroidx/compose/ui/semantics/SemanticsProperties$PaneTitle$1;-><clinit>()V
-HSPLandroidx/compose/ui/semantics/SemanticsProperties$PaneTitle$1;-><init>()V
-HSPLandroidx/compose/ui/semantics/SemanticsProperties$Role$1;-><clinit>()V
-HSPLandroidx/compose/ui/semantics/SemanticsProperties$Role$1;-><init>()V
-HSPLandroidx/compose/ui/semantics/SemanticsProperties$TestTag$1;-><clinit>()V
-HSPLandroidx/compose/ui/semantics/SemanticsProperties$TestTag$1;-><init>()V
-HSPLandroidx/compose/ui/semantics/SemanticsProperties$Text$1;-><clinit>()V
-HSPLandroidx/compose/ui/semantics/SemanticsProperties$Text$1;-><init>()V
-HSPLandroidx/compose/ui/semantics/SemanticsProperties;-><clinit>()V
-HSPLandroidx/compose/ui/semantics/SemanticsPropertiesKt$ActionPropertyKey$1;-><clinit>()V
-HSPLandroidx/compose/ui/semantics/SemanticsPropertiesKt$ActionPropertyKey$1;-><init>()V
-HSPLandroidx/compose/ui/semantics/SemanticsPropertiesKt;-><clinit>()V
-HSPLandroidx/compose/ui/semantics/SemanticsPropertiesKt;->setRole-kuIjeqM(Landroidx/compose/ui/semantics/SemanticsPropertyReceiver;I)V
-HSPLandroidx/compose/ui/semantics/SemanticsPropertyKey$1;-><clinit>()V
-HSPLandroidx/compose/ui/semantics/SemanticsPropertyKey$1;-><init>()V
-HSPLandroidx/compose/ui/semantics/SemanticsPropertyKey;-><init>(Ljava/lang/String;Lkotlin/jvm/functions/Function2;)V
-HSPLandroidx/compose/ui/semantics/SemanticsPropertyKey;->setValue(Landroidx/compose/ui/semantics/SemanticsPropertyReceiver;Lkotlin/reflect/KProperty;Ljava/lang/Object;)V
-HSPLandroidx/compose/ui/text/AndroidParagraph$wordBoundary$2;-><init>(Landroidx/compose/ui/text/AndroidParagraph;)V
-HSPLandroidx/compose/ui/text/AndroidParagraph;-><init>(Landroidx/compose/ui/text/platform/AndroidParagraphIntrinsics;IZJ)V
-HSPLandroidx/compose/ui/text/AndroidParagraph;->constructTextLayout(IILandroid/text/TextUtils$TruncateAt;IIIII)Landroidx/compose/ui/text/android/TextLayout;
-HSPLandroidx/compose/ui/text/AndroidParagraph;->getFirstBaseline()F
-HSPLandroidx/compose/ui/text/AndroidParagraph;->getHeight()F
-HSPLandroidx/compose/ui/text/AndroidParagraph;->getLastBaseline()F
-HSPLandroidx/compose/ui/text/AndroidParagraph;->getPlaceholderRects()Ljava/util/List;
-HSPLandroidx/compose/ui/text/AndroidParagraph;->getWidth()F
-HSPLandroidx/compose/ui/text/AndroidParagraph;->paint(Landroidx/compose/ui/graphics/Canvas;)V
-HSPLandroidx/compose/ui/text/AndroidParagraph;->paint-iJQMabo(Landroidx/compose/ui/graphics/Canvas;JLandroidx/compose/ui/graphics/Shadow;Landroidx/compose/ui/text/style/TextDecoration;Landroidx/arch/core/executor/TaskExecutor;)V
-HSPLandroidx/compose/ui/text/AnnotatedString$Range;-><init>(IILjava/lang/Object;)V
-HSPLandroidx/compose/ui/text/AnnotatedString$Range;-><init>(Ljava/lang/Object;IILjava/lang/String;)V
-HSPLandroidx/compose/ui/text/AnnotatedString;-><init>(Ljava/lang/String;)V
-HSPLandroidx/compose/ui/text/AnnotatedString;-><init>(Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;)V
-HSPLandroidx/compose/ui/text/AnnotatedString;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/text/AnnotatedStringKt;-><clinit>()V
-HSPLandroidx/compose/ui/text/MultiParagraph;-><init>(Landroidx/compose/ui/text/MultiParagraphIntrinsics;JIZ)V
-HSPLandroidx/compose/ui/text/MultiParagraph;->paint-iJQMabo(Landroidx/compose/ui/graphics/Canvas;JLandroidx/compose/ui/graphics/Shadow;Landroidx/compose/ui/text/style/TextDecoration;Landroidx/arch/core/executor/TaskExecutor;)V
-HSPLandroidx/compose/ui/text/MultiParagraphIntrinsics$maxIntrinsicWidth$2;-><init>(Landroidx/compose/ui/text/MultiParagraphIntrinsics;)V
-HSPLandroidx/compose/ui/text/MultiParagraphIntrinsics$maxIntrinsicWidth$2;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/text/MultiParagraphIntrinsics$minIntrinsicWidth$2;-><init>(Landroidx/compose/ui/text/MultiParagraphIntrinsics;)V
-HSPLandroidx/compose/ui/text/MultiParagraphIntrinsics;-><init>(Landroidx/compose/ui/text/AnnotatedString;Landroidx/compose/ui/text/TextStyle;Ljava/util/List;Landroidx/compose/ui/unit/Density;Landroidx/compose/ui/text/font/FontFamily$Resolver;)V
-HSPLandroidx/compose/ui/text/MultiParagraphIntrinsics;->getHasStaleResolvedFonts()Z
-HSPLandroidx/compose/ui/text/MultiParagraphIntrinsics;->getMaxIntrinsicWidth()F
-HSPLandroidx/compose/ui/text/ParagraphInfo;-><init>(Landroidx/compose/ui/text/AndroidParagraph;IIIIFF)V
-HSPLandroidx/compose/ui/text/ParagraphIntrinsicInfo;-><init>(Landroidx/compose/ui/text/platform/AndroidParagraphIntrinsics;II)V
-HSPLandroidx/compose/ui/text/ParagraphStyle;-><init>(Landroidx/compose/ui/text/style/TextAlign;Landroidx/compose/ui/text/style/TextDirection;JLandroidx/compose/ui/text/style/TextIndent;Landroidx/compose/ui/text/style/LineHeightStyle;Landroidx/compose/ui/text/style/LineBreak;Landroidx/compose/ui/text/style/Hyphens;)V
-HSPLandroidx/compose/ui/text/ParagraphStyle;-><init>(Landroidx/compose/ui/text/style/TextAlign;Landroidx/compose/ui/text/style/TextDirection;JLandroidx/compose/ui/text/style/TextIndent;Landroidx/compose/ui/text/style/LineHeightStyle;Landroidx/compose/ui/text/style/LineBreak;Landroidx/compose/ui/text/style/Hyphens;Landroidx/compose/ui/text/style/TextMotion;)V
-HSPLandroidx/compose/ui/text/ParagraphStyle;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/text/ParagraphStyle;->merge(Landroidx/compose/ui/text/ParagraphStyle;)Landroidx/compose/ui/text/ParagraphStyle;
-HSPLandroidx/compose/ui/text/ParagraphStyleKt;-><clinit>()V
-HSPLandroidx/compose/ui/text/SpanStyle;-><init>(JJLandroidx/compose/ui/text/font/FontWeight;Landroidx/compose/ui/text/font/FontStyle;Landroidx/compose/ui/text/font/FontSynthesis;Landroidx/compose/ui/text/font/FontFamily;Ljava/lang/String;JLandroidx/compose/ui/text/style/BaselineShift;Landroidx/compose/ui/text/style/TextGeometricTransform;Landroidx/compose/ui/text/intl/LocaleList;JLandroidx/compose/ui/text/style/TextDecoration;Landroidx/compose/ui/graphics/Shadow;I)V
-HSPLandroidx/compose/ui/text/SpanStyle;-><init>(Landroidx/compose/ui/text/style/TextForegroundStyle;JLandroidx/compose/ui/text/font/FontWeight;Landroidx/compose/ui/text/font/FontStyle;Landroidx/compose/ui/text/font/FontSynthesis;Landroidx/compose/ui/text/font/FontFamily;Ljava/lang/String;JLandroidx/compose/ui/text/style/BaselineShift;Landroidx/compose/ui/text/style/TextGeometricTransform;Landroidx/compose/ui/text/intl/LocaleList;JLandroidx/compose/ui/text/style/TextDecoration;Landroidx/compose/ui/graphics/Shadow;)V
-HSPLandroidx/compose/ui/text/SpanStyle;-><init>(Landroidx/compose/ui/text/style/TextForegroundStyle;JLandroidx/compose/ui/text/font/FontWeight;Landroidx/compose/ui/text/font/FontStyle;Landroidx/compose/ui/text/font/FontSynthesis;Landroidx/compose/ui/text/font/FontFamily;Ljava/lang/String;JLandroidx/compose/ui/text/style/BaselineShift;Landroidx/compose/ui/text/style/TextGeometricTransform;Landroidx/compose/ui/text/intl/LocaleList;JLandroidx/compose/ui/text/style/TextDecoration;Landroidx/compose/ui/graphics/Shadow;Landroidx/arch/core/executor/TaskExecutor;)V
-HSPLandroidx/compose/ui/text/SpanStyle;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/text/SpanStyle;->getBrush()Landroidx/compose/ui/graphics/Brush;
-HSPLandroidx/compose/ui/text/SpanStyle;->getColor-0d7_KjU()J
-HSPLandroidx/compose/ui/text/SpanStyle;->hasSameLayoutAffectingAttributes$ui_text_release(Landroidx/compose/ui/text/SpanStyle;)Z
-HSPLandroidx/compose/ui/text/SpanStyle;->merge(Landroidx/compose/ui/text/SpanStyle;)Landroidx/compose/ui/text/SpanStyle;
-HSPLandroidx/compose/ui/text/SpanStyleKt$resolveSpanStyleDefaults$1;-><clinit>()V
-HSPLandroidx/compose/ui/text/SpanStyleKt$resolveSpanStyleDefaults$1;-><init>()V
-HSPLandroidx/compose/ui/text/SpanStyleKt;-><clinit>()V
-HSPLandroidx/compose/ui/text/TextLayoutInput;-><init>(Landroidx/compose/ui/text/AnnotatedString;Landroidx/compose/ui/text/TextStyle;Ljava/util/List;IZILandroidx/compose/ui/unit/Density;Landroidx/compose/ui/unit/LayoutDirection;Landroidx/compose/ui/text/font/FontFamily$Resolver;J)V
-HSPLandroidx/compose/ui/text/TextLayoutInput;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/text/TextLayoutResult;-><init>(Landroidx/compose/ui/text/TextLayoutInput;Landroidx/compose/ui/text/MultiParagraph;J)V
-HSPLandroidx/compose/ui/text/TextLayoutResult;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/text/TextRange;-><clinit>()V
-HSPLandroidx/compose/ui/text/TextRange;->getEnd-impl(J)I
-HSPLandroidx/compose/ui/text/TextRangeKt;->TextRange(II)J
-HSPLandroidx/compose/ui/text/TextRangeKt;->constrain-8ffj60Q(IJ)J
-HSPLandroidx/compose/ui/text/TextStyle;-><clinit>()V
-HSPLandroidx/compose/ui/text/TextStyle;-><init>(JJLandroidx/compose/ui/text/font/FontWeight;Landroidx/compose/ui/text/font/FontStyle;Landroidx/compose/ui/text/font/FontFamily;JLandroidx/compose/ui/text/style/TextDecoration;Landroidx/compose/ui/text/style/TextAlign;JI)V
-HSPLandroidx/compose/ui/text/TextStyle;-><init>(JJLandroidx/compose/ui/text/font/FontWeight;Landroidx/compose/ui/text/font/FontStyle;Landroidx/compose/ui/text/font/FontFamily;JLandroidx/compose/ui/text/style/TextDecoration;Landroidx/compose/ui/text/style/TextAlign;JII)V
-HSPLandroidx/compose/ui/text/TextStyle;-><init>(Landroidx/compose/ui/text/SpanStyle;Landroidx/compose/ui/text/ParagraphStyle;)V
-HSPLandroidx/compose/ui/text/TextStyle;-><init>(Landroidx/compose/ui/text/SpanStyle;Landroidx/compose/ui/text/ParagraphStyle;Lkotlin/internal/ProgressionUtilKt;)V
-HSPLandroidx/compose/ui/text/TextStyle;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/text/TextStyle;->merge(Landroidx/compose/ui/text/TextStyle;)Landroidx/compose/ui/text/TextStyle;
-HSPLandroidx/compose/ui/text/android/BoringLayoutFactory;->create(Ljava/lang/CharSequence;Landroidx/compose/ui/text/platform/AndroidTextPaint;ILandroid/text/BoringLayout$Metrics;Landroid/text/Layout$Alignment;ZZLandroid/text/TextUtils$TruncateAt;I)Landroid/text/BoringLayout;
-HSPLandroidx/compose/ui/text/android/BoringLayoutFactoryDefault;->create(Ljava/lang/CharSequence;Landroid/text/TextPaint;ILandroid/text/Layout$Alignment;FFLandroid/text/BoringLayout$Metrics;ZLandroid/text/TextUtils$TruncateAt;I)Landroid/text/BoringLayout;
-HSPLandroidx/compose/ui/text/android/BoringLayoutFactoryDefault;->isBoring(Ljava/lang/CharSequence;Landroid/text/TextPaint;Landroid/text/TextDirectionHeuristic;)Landroid/text/BoringLayout$Metrics;
-HSPLandroidx/compose/ui/text/android/LayoutIntrinsics$boringMetrics$2;-><init>(ILandroidx/compose/ui/text/platform/AndroidTextPaint;Ljava/lang/CharSequence;)V
-HSPLandroidx/compose/ui/text/android/LayoutIntrinsics$boringMetrics$2;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/text/android/LayoutIntrinsics$maxIntrinsicWidth$2;-><init>(Landroidx/compose/ui/text/android/LayoutIntrinsics;Ljava/lang/CharSequence;Landroidx/compose/ui/text/platform/AndroidTextPaint;)V
-HSPLandroidx/compose/ui/text/android/LayoutIntrinsics$maxIntrinsicWidth$2;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/text/android/LayoutIntrinsics$minIntrinsicWidth$2;-><init>(Ljava/lang/CharSequence;Landroidx/compose/ui/text/platform/AndroidTextPaint;)V
-HSPLandroidx/compose/ui/text/android/LayoutIntrinsics;-><init>(ILandroidx/compose/ui/text/platform/AndroidTextPaint;Ljava/lang/CharSequence;)V
-HSPLandroidx/compose/ui/text/android/StaticLayoutFactory23;-><init>()V
-HSPLandroidx/compose/ui/text/android/StaticLayoutFactory23;->create(Landroidx/compose/ui/text/android/StaticLayoutParams;)Landroid/text/StaticLayout;
-HSPLandroidx/compose/ui/text/android/StaticLayoutFactory26;->setJustificationMode(Landroid/text/StaticLayout$Builder;I)V
-HSPLandroidx/compose/ui/text/android/StaticLayoutFactory28;->setUseLineSpacingFromFallbacks(Landroid/text/StaticLayout$Builder;Z)V
-HSPLandroidx/compose/ui/text/android/StaticLayoutFactory;-><clinit>()V
-HSPLandroidx/compose/ui/text/android/StaticLayoutFactory;->create(Ljava/lang/CharSequence;IILandroidx/compose/ui/text/platform/AndroidTextPaint;ILandroid/text/TextDirectionHeuristic;Landroid/text/Layout$Alignment;ILandroid/text/TextUtils$TruncateAt;IFFIZZIIII[I[I)Landroid/text/StaticLayout;
-HSPLandroidx/compose/ui/text/android/StaticLayoutParams;-><init>(Ljava/lang/CharSequence;IILandroidx/compose/ui/text/platform/AndroidTextPaint;ILandroid/text/TextDirectionHeuristic;Landroid/text/Layout$Alignment;ILandroid/text/TextUtils$TruncateAt;IFFIZZIIII[I[I)V
-HSPLandroidx/compose/ui/text/android/TextAlignmentAdapter;-><clinit>()V
-HSPLandroidx/compose/ui/text/android/TextAndroidCanvas;-><init>()V
-HSPLandroidx/compose/ui/text/android/TextAndroidCanvas;->drawTextRun(Ljava/lang/CharSequence;IIIIFFZLandroid/graphics/Paint;)V
-HSPLandroidx/compose/ui/text/android/TextAndroidCanvas;->getClipBounds(Landroid/graphics/Rect;)Z
-HSPLandroidx/compose/ui/text/android/TextLayout$layoutHelper$2;-><init>(Landroidx/compose/ui/text/android/TextLayout;)V
-HSPLandroidx/compose/ui/text/android/TextLayout;-><init>(Ljava/lang/CharSequence;FLandroidx/compose/ui/text/platform/AndroidTextPaint;ILandroid/text/TextUtils$TruncateAt;IIIIIIILandroidx/compose/ui/text/android/LayoutIntrinsics;)V
-HSPLandroidx/compose/ui/text/android/TextLayout;->getHeight()I
-HSPLandroidx/compose/ui/text/android/TextLayout;->getLineBaseline(I)F
-HSPLandroidx/compose/ui/text/android/TextLayout;->getText()Ljava/lang/CharSequence;
-HSPLandroidx/compose/ui/text/android/TextLayoutKt;-><clinit>()V
-HSPLandroidx/compose/ui/text/android/TextLayoutKt;->getTextDirectionHeuristic(I)Landroid/text/TextDirectionHeuristic;
-HSPLandroidx/compose/ui/text/android/style/IndentationFixSpanKt;->getEllipsizedLeftPadding(Landroid/text/Layout;ILandroid/graphics/Paint;)F
-HSPLandroidx/compose/ui/text/android/style/IndentationFixSpanKt;->getEllipsizedRightPadding(Landroid/text/Layout;ILandroid/graphics/Paint;)F
-HSPLandroidx/compose/ui/text/android/style/LetterSpacingSpanPx;-><init>(F)V
-HSPLandroidx/compose/ui/text/android/style/LetterSpacingSpanPx;->updateDrawState(Landroid/text/TextPaint;)V
-HSPLandroidx/compose/ui/text/android/style/LetterSpacingSpanPx;->updateMeasureState(Landroid/text/TextPaint;)V
-HSPLandroidx/compose/ui/text/android/style/LineHeightSpan;-><init>(F)V
-HSPLandroidx/compose/ui/text/android/style/LineHeightSpan;->chooseHeight(Ljava/lang/CharSequence;IIIILandroid/graphics/Paint$FontMetricsInt;)V
-HSPLandroidx/compose/ui/text/caches/ContainerHelpersKt;-><clinit>()V
-HSPLandroidx/compose/ui/text/caches/LruCache;-><init>()V
-HSPLandroidx/compose/ui/text/caches/LruCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/text/caches/LruCache;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/text/caches/LruCache;->size()I
-HSPLandroidx/compose/ui/text/caches/SimpleArrayMap;-><init>(I)V
-HSPLandroidx/compose/ui/text/font/AndroidFontLoader;-><init>(Landroid/content/Context;)V
-HSPLandroidx/compose/ui/text/font/AndroidFontLoader;->getCacheKey()V
-HSPLandroidx/compose/ui/text/font/AndroidFontResolveInterceptor;-><init>(I)V
-HSPLandroidx/compose/ui/text/font/AndroidFontResolveInterceptor;->interceptFontWeight(Landroidx/compose/ui/text/font/FontWeight;)Landroidx/compose/ui/text/font/FontWeight;
-HSPLandroidx/compose/ui/text/font/AndroidFontResolveInterceptor_androidKt;->AndroidFontResolveInterceptor(Landroid/content/Context;)Landroidx/compose/ui/text/font/AndroidFontResolveInterceptor;
-HSPLandroidx/compose/ui/text/font/AsyncTypefaceCache;-><init>()V
-HSPLandroidx/compose/ui/text/font/DefaultFontFamily;-><init>()V
-HSPLandroidx/compose/ui/text/font/FontFamily;-><clinit>()V
-HSPLandroidx/compose/ui/text/font/FontFamily;-><init>()V
-HSPLandroidx/compose/ui/text/font/FontFamilyResolverImpl$createDefaultTypeface$1;-><init>(Landroidx/compose/ui/text/font/FontFamilyResolverImpl;)V
-HSPLandroidx/compose/ui/text/font/FontFamilyResolverImpl$resolve$result$1;-><init>(Landroidx/compose/ui/text/font/FontFamilyResolverImpl;Landroidx/compose/ui/text/font/TypefaceRequest;)V
-HSPLandroidx/compose/ui/text/font/FontFamilyResolverImpl$resolve$result$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/text/font/FontFamilyResolverImpl;-><init>(Landroidx/compose/ui/text/font/AndroidFontLoader;Landroidx/compose/ui/text/font/AndroidFontResolveInterceptor;)V
-HSPLandroidx/compose/ui/text/font/FontFamilyResolverImpl;->resolve(Landroidx/compose/ui/text/font/TypefaceRequest;)Landroidx/compose/ui/text/font/TypefaceResult;
-HSPLandroidx/compose/ui/text/font/FontFamilyResolverImpl;->resolve-DPcqOEQ(Landroidx/compose/ui/text/font/FontFamily;Landroidx/compose/ui/text/font/FontWeight;II)Landroidx/compose/ui/text/font/TypefaceResult;
-HSPLandroidx/compose/ui/text/font/FontFamilyResolverKt;-><clinit>()V
-HSPLandroidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapter$special$$inlined$CoroutineExceptionHandler$1;-><init>()V
-HSPLandroidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapter;-><clinit>()V
-HSPLandroidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapter;-><init>(Landroidx/compose/ui/text/font/AsyncTypefaceCache;)V
-HSPLandroidx/compose/ui/text/font/FontStyle;-><init>(I)V
-HSPLandroidx/compose/ui/text/font/FontSynthesis;-><init>(I)V
-HSPLandroidx/compose/ui/text/font/FontWeight;-><clinit>()V
-HSPLandroidx/compose/ui/text/font/FontWeight;-><init>(I)V
-HSPLandroidx/compose/ui/text/font/FontWeight;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/text/font/GenericFontFamily;-><init>(Ljava/lang/String;Ljava/lang/String;)V
-HSPLandroidx/compose/ui/text/font/PlatformFontFamilyTypefaceAdapter;-><init>()V
-HSPLandroidx/compose/ui/text/font/PlatformResolveInterceptor$Companion$Default$1;-><init>()V
-HSPLandroidx/compose/ui/text/font/PlatformResolveInterceptor$Companion;-><clinit>()V
-HSPLandroidx/compose/ui/text/font/PlatformResolveInterceptor$Companion;-><init>()V
-HSPLandroidx/compose/ui/text/font/PlatformResolveInterceptor;-><clinit>()V
-HSPLandroidx/compose/ui/text/font/PlatformTypefacesApi28;-><init>()V
-HSPLandroidx/compose/ui/text/font/PlatformTypefacesApi28;->createAndroidTypefaceApi28-RetOiIg(Ljava/lang/String;Landroidx/compose/ui/text/font/FontWeight;I)Landroid/graphics/Typeface;
-HSPLandroidx/compose/ui/text/font/SystemFontFamily;-><init>()V
-HSPLandroidx/compose/ui/text/font/TypefaceRequest;-><init>(Landroidx/compose/ui/text/font/FontFamily;Landroidx/compose/ui/text/font/FontWeight;IILjava/lang/Object;)V
-HSPLandroidx/compose/ui/text/font/TypefaceRequest;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/text/font/TypefaceRequest;->hashCode()I
-HSPLandroidx/compose/ui/text/font/TypefaceRequestCache$runCached$currentTypefaceResult$1;-><init>(Landroidx/compose/ui/text/font/TypefaceRequestCache;Landroidx/compose/ui/text/font/TypefaceRequest;)V
-HSPLandroidx/compose/ui/text/font/TypefaceRequestCache;-><init>()V
-HSPLandroidx/compose/ui/text/font/TypefaceResult$Immutable;-><init>(Ljava/lang/Object;Z)V
-HSPLandroidx/compose/ui/text/font/TypefaceResult$Immutable;->getCacheable()Z
-HSPLandroidx/compose/ui/text/font/TypefaceResult$Immutable;->getValue()Ljava/lang/Object;
-HSPLandroidx/compose/ui/text/input/ImmHelper30;-><init>(Landroid/view/View;)V
-HSPLandroidx/compose/ui/text/input/InputMethodManagerImpl$imm$2;-><init>(Landroidx/compose/ui/text/input/InputMethodManagerImpl;)V
-HSPLandroidx/compose/ui/text/input/InputMethodManagerImpl;-><init>(Landroid/view/View;)V
-HSPLandroidx/compose/ui/text/input/TextFieldValue$Companion$Saver$1;-><clinit>()V
-HSPLandroidx/compose/ui/text/input/TextFieldValue$Companion$Saver$1;-><init>()V
-HSPLandroidx/compose/ui/text/input/TextFieldValue$Companion$Saver$2;-><clinit>()V
-HSPLandroidx/compose/ui/text/input/TextFieldValue$Companion$Saver$2;-><init>()V
-HSPLandroidx/compose/ui/text/input/TextFieldValue;-><clinit>()V
-HSPLandroidx/compose/ui/text/input/TextFieldValue;-><init>(Landroidx/compose/ui/text/AnnotatedString;JLandroidx/compose/ui/text/TextRange;)V
-HSPLandroidx/compose/ui/text/input/TextInputService;-><init>(Landroidx/compose/ui/text/input/PlatformTextInputService;)V
-HSPLandroidx/compose/ui/text/input/TextInputServiceAndroid$baseInputConnection$2;-><init>(Landroidx/compose/ui/text/input/TextInputServiceAndroid;)V
-HSPLandroidx/compose/ui/text/input/TextInputServiceAndroid$textInputCommandEventLoop$1;-><init>(Landroidx/compose/ui/text/input/TextInputServiceAndroid;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/compose/ui/text/input/TextInputServiceAndroid;-><init>(Landroid/view/View;)V
-HSPLandroidx/compose/ui/text/input/TextInputServiceAndroid;->textInputCommandEventLoop(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/compose/ui/text/intl/AndroidLocale;-><init>(Ljava/util/Locale;)V
-HSPLandroidx/compose/ui/text/intl/AndroidLocale;->toLanguageTag()Ljava/lang/String;
-HSPLandroidx/compose/ui/text/intl/Locale;-><init>(Landroidx/compose/ui/text/intl/PlatformLocale;)V
-HSPLandroidx/compose/ui/text/intl/Locale;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/text/intl/LocaleList$Companion;->getCurrent()Landroidx/compose/ui/text/intl/LocaleList;
-HSPLandroidx/compose/ui/text/intl/LocaleList;-><init>(Ljava/util/ArrayList;)V
-HSPLandroidx/compose/ui/text/intl/LocaleList;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/text/platform/AndroidParagraphHelper_androidKt$NoopSpan$1;-><init>()V
-HSPLandroidx/compose/ui/text/platform/AndroidParagraphHelper_androidKt;-><clinit>()V
-HSPLandroidx/compose/ui/text/platform/AndroidParagraphIntrinsics$resolveTypeface$1;-><init>(Landroidx/compose/ui/text/platform/AndroidParagraphIntrinsics;)V
-HSPLandroidx/compose/ui/text/platform/AndroidParagraphIntrinsics;-><init>(Landroidx/compose/ui/text/TextStyle;Landroidx/compose/ui/text/font/FontFamily$Resolver;Landroidx/compose/ui/unit/Density;Ljava/lang/String;Ljava/util/List;Ljava/util/List;)V
-HSPLandroidx/compose/ui/text/platform/AndroidParagraphIntrinsics;->getHasStaleResolvedFonts()Z
-HSPLandroidx/compose/ui/text/platform/AndroidParagraphIntrinsics;->getMaxIntrinsicWidth()F
-HSPLandroidx/compose/ui/text/platform/AndroidTextPaint;-><init>(F)V
-HSPLandroidx/compose/ui/text/platform/AndroidTextPaint;->setBrush-12SF9DM(Landroidx/compose/ui/graphics/Brush;JF)V
-HSPLandroidx/compose/ui/text/platform/AndroidTextPaint;->setColor-8_81llA(J)V
-HSPLandroidx/compose/ui/text/platform/AndroidTextPaint;->setDrawStyle(Landroidx/arch/core/executor/TaskExecutor;)V
-HSPLandroidx/compose/ui/text/platform/AndroidTextPaint;->setShadow(Landroidx/compose/ui/graphics/Shadow;)V
-HSPLandroidx/compose/ui/text/platform/AndroidTextPaint;->setTextDecoration(Landroidx/compose/ui/text/style/TextDecoration;)V
-HSPLandroidx/compose/ui/text/platform/DefaultImpl$getFontLoadState$initCallback$1;-><init>(Landroidx/compose/runtime/ParcelableSnapshotMutableState;Landroidx/compose/ui/text/platform/DefaultImpl;)V
-HSPLandroidx/compose/ui/text/platform/DefaultImpl$getFontLoadState$initCallback$1;->onInitialized()V
-HSPLandroidx/compose/ui/text/platform/DefaultImpl;-><init>()V
-HSPLandroidx/compose/ui/text/platform/DefaultImpl;->getFontLoadState()Landroidx/compose/runtime/State;
-HSPLandroidx/compose/ui/text/platform/EmojiCompatStatus;-><clinit>()V
-HSPLandroidx/compose/ui/text/platform/ImmutableBool;-><init>(Z)V
-HSPLandroidx/compose/ui/text/platform/ImmutableBool;->getValue()Ljava/lang/Object;
-HSPLandroidx/compose/ui/text/platform/TypefaceDirtyTracker;-><init>(Landroidx/compose/runtime/State;)V
-HSPLandroidx/compose/ui/text/platform/extensions/SpanRange;-><init>(IILandroid/text/style/MetricAffectingSpan;)V
-HSPLandroidx/compose/ui/text/platform/extensions/SpannableExtensions_androidKt$setFontAttributes$1;-><init>(Landroid/text/Spannable;Landroidx/compose/ui/text/platform/AndroidParagraphIntrinsics$resolveTypeface$1;)V
-HSPLandroidx/compose/ui/text/platform/extensions/SpannableExtensions_androidKt;->resolveLineHeightInPx-o2QH7mI(JFLandroidx/compose/ui/unit/Density;)F
-HSPLandroidx/compose/ui/text/platform/extensions/SpannableExtensions_androidKt;->setBackground-RPmYEkk(Landroid/text/Spannable;JII)V
-HSPLandroidx/compose/ui/text/platform/extensions/SpannableExtensions_androidKt;->setColor-RPmYEkk(Landroid/text/Spannable;JII)V
-HSPLandroidx/compose/ui/text/platform/extensions/SpannableExtensions_androidKt;->setFontSize-KmRG4DE(Landroid/text/Spannable;JLandroidx/compose/ui/unit/Density;II)V
-HSPLandroidx/compose/ui/text/platform/extensions/SpannableExtensions_androidKt;->setSpan(Landroid/text/Spannable;Ljava/lang/Object;II)V
-HSPLandroidx/compose/ui/text/platform/extensions/TextPaintExtensions_androidKt;->hasFontAttributes(Landroidx/compose/ui/text/SpanStyle;)Z
-HSPLandroidx/compose/ui/text/style/BaselineShift;-><init>(F)V
-HSPLandroidx/compose/ui/text/style/ColorStyle;-><init>(J)V
-HSPLandroidx/compose/ui/text/style/ColorStyle;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/text/style/ColorStyle;->getAlpha()F
-HSPLandroidx/compose/ui/text/style/ColorStyle;->getBrush()Landroidx/compose/ui/graphics/Brush;
-HSPLandroidx/compose/ui/text/style/ColorStyle;->getColor-0d7_KjU()J
-HSPLandroidx/compose/ui/text/style/Hyphens;-><clinit>()V
-HSPLandroidx/compose/ui/text/style/Hyphens;-><init>()V
-HSPLandroidx/compose/ui/text/style/LineBreak$Strategy;-><init>(I)V
-HSPLandroidx/compose/ui/text/style/LineBreak$Strictness;-><init>(I)V
-HSPLandroidx/compose/ui/text/style/LineBreak$WordBreak;-><init>(I)V
-HSPLandroidx/compose/ui/text/style/LineBreak;-><clinit>()V
-HSPLandroidx/compose/ui/text/style/LineBreak;-><init>()V
-HSPLandroidx/compose/ui/text/style/TextAlign;-><init>(I)V
-HSPLandroidx/compose/ui/text/style/TextDecoration;-><clinit>()V
-HSPLandroidx/compose/ui/text/style/TextDecoration;-><init>(I)V
-HSPLandroidx/compose/ui/text/style/TextDecoration;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/text/style/TextDirection;-><init>(I)V
-HSPLandroidx/compose/ui/text/style/TextForegroundStyle$Unspecified;-><clinit>()V
-HSPLandroidx/compose/ui/text/style/TextForegroundStyle$Unspecified;-><init>()V
-HSPLandroidx/compose/ui/text/style/TextForegroundStyle$Unspecified;->getAlpha()F
-HSPLandroidx/compose/ui/text/style/TextForegroundStyle$Unspecified;->getBrush()Landroidx/compose/ui/graphics/Brush;
-HSPLandroidx/compose/ui/text/style/TextForegroundStyle$Unspecified;->getColor-0d7_KjU()J
-HSPLandroidx/compose/ui/text/style/TextForegroundStyle$merge$2;-><init>(Landroidx/compose/ui/text/style/TextForegroundStyle;)V
-HSPLandroidx/compose/ui/text/style/TextForegroundStyle$merge$2;->invoke()Ljava/lang/Object;
-HSPLandroidx/compose/ui/text/style/TextForegroundStyle;->merge(Landroidx/compose/ui/text/style/TextForegroundStyle;)Landroidx/compose/ui/text/style/TextForegroundStyle;
-HSPLandroidx/compose/ui/text/style/TextForegroundStyle;->takeOrElse(Lkotlin/jvm/functions/Function0;)Landroidx/compose/ui/text/style/TextForegroundStyle;
-HSPLandroidx/compose/ui/text/style/TextGeometricTransform;-><clinit>()V
-HSPLandroidx/compose/ui/text/style/TextGeometricTransform;-><init>(FF)V
-HSPLandroidx/compose/ui/text/style/TextGeometricTransform;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/text/style/TextIndent;-><clinit>()V
-HSPLandroidx/compose/ui/text/style/TextIndent;-><init>(JJ)V
-HSPLandroidx/compose/ui/text/style/TextMotion;-><clinit>()V
-HSPLandroidx/compose/ui/text/style/TextMotion;-><init>(IZ)V
-HSPLandroidx/compose/ui/unit/AndroidDensity_androidKt;->Density(Landroid/content/Context;)Landroidx/compose/ui/unit/DensityImpl;
-HSPLandroidx/compose/ui/unit/Constraints$Companion;->bitsNeedForSize(I)I
-HSPLandroidx/compose/ui/unit/Constraints$Companion;->createConstraints-Zbe2FdA$ui_unit_release(IIII)J
-HSPLandroidx/compose/ui/unit/Constraints$Companion;->fixed-JhjzzOo(II)J
-HSPLandroidx/compose/ui/unit/Constraints;-><clinit>()V
-HSPLandroidx/compose/ui/unit/Constraints;-><init>(J)V
-HSPLandroidx/compose/ui/unit/Constraints;->copy-Zbe2FdA$default(JIIIII)J
-HSPLandroidx/compose/ui/unit/Constraints;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/unit/Constraints;->equals-impl0(JJ)Z
-HSPLandroidx/compose/ui/unit/Constraints;->getHasBoundedHeight-impl(J)Z
-HSPLandroidx/compose/ui/unit/Constraints;->getHasBoundedWidth-impl(J)Z
-HSPLandroidx/compose/ui/unit/Constraints;->getHasFixedHeight-impl(J)Z
-HSPLandroidx/compose/ui/unit/Constraints;->getHasFixedWidth-impl(J)Z
-HSPLandroidx/compose/ui/unit/Constraints;->getMaxHeight-impl(J)I
-HSPLandroidx/compose/ui/unit/Constraints;->getMaxWidth-impl(J)I
-HSPLandroidx/compose/ui/unit/Constraints;->getMinHeight-impl(J)I
-HSPLandroidx/compose/ui/unit/Constraints;->getMinWidth-impl(J)I
-HSPLandroidx/compose/ui/unit/ConstraintsKt;->Constraints$default(III)J
-HSPLandroidx/compose/ui/unit/ConstraintsKt;->Constraints(IIII)J
-HSPLandroidx/compose/ui/unit/ConstraintsKt;->constrain-4WqzIAM(JJ)J
-HSPLandroidx/compose/ui/unit/ConstraintsKt;->constrainHeight-K40F9xA(JI)I
-HSPLandroidx/compose/ui/unit/ConstraintsKt;->constrainWidth-K40F9xA(JI)I
-HSPLandroidx/compose/ui/unit/ConstraintsKt;->offset-NN6Ew-U(IIJ)J
-HSPLandroidx/compose/ui/unit/Density;->roundToPx-0680j_4(F)I
-HSPLandroidx/compose/ui/unit/Density;->toDp-u2uoSUM(I)F
-HSPLandroidx/compose/ui/unit/Density;->toPx--R2X_6o(J)F
-HSPLandroidx/compose/ui/unit/Density;->toPx-0680j_4(F)F
-HSPLandroidx/compose/ui/unit/DensityImpl;-><init>(FF)V
-HSPLandroidx/compose/ui/unit/DensityImpl;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/unit/DensityImpl;->getDensity()F
-HSPLandroidx/compose/ui/unit/DensityImpl;->getFontScale()F
-HSPLandroidx/compose/ui/unit/Dp;-><init>(F)V
-HSPLandroidx/compose/ui/unit/Dp;->compareTo(Ljava/lang/Object;)I
-HSPLandroidx/compose/ui/unit/Dp;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/unit/Dp;->equals-impl0(FF)Z
-HSPLandroidx/compose/ui/unit/DpKt;->DpOffset-YgX7TsA(FF)J
-HSPLandroidx/compose/ui/unit/DpOffset;-><clinit>()V
-HSPLandroidx/compose/ui/unit/DpRect;-><init>(FFFF)V
-HSPLandroidx/compose/ui/unit/DpRect;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/compose/ui/unit/IntOffset$Companion;-><init>()V
-HSPLandroidx/compose/ui/unit/IntOffset;-><clinit>()V
-HSPLandroidx/compose/ui/unit/IntOffset;-><init>(J)V
-HSPLandroidx/compose/ui/unit/IntOffset;->equals-impl0(JJ)Z
-HSPLandroidx/compose/ui/unit/IntOffset;->getY-impl(J)I
-HSPLandroidx/compose/ui/unit/IntOffsetKt;->IntOffset(II)J
-HSPLandroidx/compose/ui/unit/IntSize$Companion;-><init>()V
-HSPLandroidx/compose/ui/unit/IntSize;-><clinit>()V
-HSPLandroidx/compose/ui/unit/IntSize;-><init>(J)V
-HSPLandroidx/compose/ui/unit/IntSize;->equals-impl0(JJ)Z
-HSPLandroidx/compose/ui/unit/IntSize;->getHeight-impl(J)I
-HSPLandroidx/compose/ui/unit/IntSizeKt;->IntSize(II)J
-HSPLandroidx/compose/ui/unit/IntSizeKt;->toSize-ozmzZPI(J)J
-HSPLandroidx/compose/ui/unit/LayoutDirection;-><clinit>()V
-HSPLandroidx/compose/ui/unit/LayoutDirection;-><init>(ILjava/lang/String;)V
-HSPLandroidx/compose/ui/unit/TextUnit;-><clinit>()V
-HSPLandroidx/compose/ui/unit/TextUnit;->equals-impl0(JJ)Z
-HSPLandroidx/compose/ui/unit/TextUnit;->getType-UIouoOA(J)J
-HSPLandroidx/compose/ui/unit/TextUnit;->getValue-impl(J)F
-HSPLandroidx/compose/ui/unit/TextUnitKt;->getSp(D)J
-HSPLandroidx/compose/ui/unit/TextUnitKt;->getSp(I)J
-HSPLandroidx/compose/ui/unit/TextUnitKt;->isUnspecified--R2X_6o(J)Z
-HSPLandroidx/compose/ui/unit/TextUnitKt;->pack(FJ)J
-HSPLandroidx/compose/ui/unit/TextUnitType;-><init>(J)V
-HSPLandroidx/compose/ui/unit/TextUnitType;->equals-impl0(JJ)Z
-HSPLandroidx/compose/ui/util/MathHelpersKt;->compareValues(Ljava/lang/Comparable;Ljava/lang/Comparable;)I
-HSPLandroidx/core/R$id;->createCoroutineUnintercepted(Ljava/lang/Object;Lkotlin/coroutines/Continuation;Lkotlin/jvm/functions/Function2;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/core/R$id;->intercepted(Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLandroidx/core/R$id;->systemProp$default(Ljava/lang/String;IIII)I
-HSPLandroidx/core/R$id;->systemProp(Ljava/lang/String;JJJ)J
-HSPLandroidx/core/app/ComponentActivity;-><init>()V
-HSPLandroidx/core/app/ComponentActivity;->dispatchKeyEvent(Landroid/view/KeyEvent;)Z
-HSPLandroidx/core/app/ComponentActivity;->onCreate(Landroid/os/Bundle;)V
-HSPLandroidx/core/app/ComponentActivity;->superDispatchKeyEvent(Landroid/view/KeyEvent;)Z
-HSPLandroidx/core/app/CoreComponentFactory;-><init>()V
-HSPLandroidx/core/app/CoreComponentFactory;->checkCompatWrapper(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/core/app/CoreComponentFactory;->instantiateActivity(Ljava/lang/ClassLoader;Ljava/lang/String;Landroid/content/Intent;)Landroid/app/Activity;
-HSPLandroidx/core/app/CoreComponentFactory;->instantiateApplication(Ljava/lang/ClassLoader;Ljava/lang/String;)Landroid/app/Application;
-HSPLandroidx/core/app/CoreComponentFactory;->instantiateProvider(Ljava/lang/ClassLoader;Ljava/lang/String;)Landroid/content/ContentProvider;
-HSPLandroidx/core/graphics/TypefaceCompat;-><clinit>()V
-HSPLandroidx/core/graphics/TypefaceCompatApi29Impl;-><init>()V
-HSPLandroidx/core/graphics/TypefaceCompatApi29Impl;->createFromFontInfo(Landroid/content/Context;[Landroidx/core/provider/FontsContractCompat$FontInfo;I)Landroid/graphics/Typeface;
-HSPLandroidx/core/graphics/TypefaceCompatApi29Impl;->findBaseFont(Landroid/graphics/fonts/FontFamily;I)Landroid/graphics/fonts/Font;
-HSPLandroidx/core/graphics/TypefaceCompatApi29Impl;->getMatchScore(Landroid/graphics/fonts/FontStyle;Landroid/graphics/fonts/FontStyle;)I
-HSPLandroidx/core/graphics/TypefaceCompatBaseImpl;-><init>()V
-HSPLandroidx/core/graphics/TypefaceCompatUtil$Api19Impl;->openFileDescriptor(Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Landroid/os/CancellationSignal;)Landroid/os/ParcelFileDescriptor;
-HSPLandroidx/core/graphics/TypefaceCompatUtil;->mmap(Landroid/content/Context;Landroid/net/Uri;)Ljava/nio/MappedByteBuffer;
-HSPLandroidx/core/os/BuildCompat;->isAtLeastT()Z
-HSPLandroidx/core/os/TraceCompat$Api18Impl;->beginSection(Ljava/lang/String;)V
-HSPLandroidx/core/os/TraceCompat$Api18Impl;->endSection()V
-HSPLandroidx/core/os/TraceCompat;-><clinit>()V
-HSPLandroidx/core/provider/FontProvider$$ExternalSyntheticLambda0;-><init>()V
-HSPLandroidx/core/provider/FontProvider$Api16Impl;->query(Landroid/content/ContentResolver;Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)Landroid/database/Cursor;
-HSPLandroidx/core/provider/FontProvider;-><clinit>()V
-HSPLandroidx/core/provider/FontProvider;->getFontFamilyResult(Landroid/content/Context;Landroidx/core/provider/FontRequest;)Landroidx/core/provider/FontsContractCompat$FontFamilyResult;
-HSPLandroidx/core/provider/FontRequest;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V
-HSPLandroidx/core/provider/FontsContractCompat$FontFamilyResult;-><init>(I[Landroidx/core/provider/FontsContractCompat$FontInfo;)V
-HSPLandroidx/core/provider/FontsContractCompat$FontInfo;-><init>(Landroid/net/Uri;IIZI)V
-HSPLandroidx/core/util/Preconditions;->checkArgument(Ljava/lang/String;Z)V
-HSPLandroidx/core/util/Preconditions;->checkNotNull(Ljava/lang/Object;Ljava/lang/String;)V
-HSPLandroidx/core/view/AccessibilityDelegateCompat$AccessibilityDelegateAdapter;-><init>(Landroidx/core/view/AccessibilityDelegateCompat;)V
-HSPLandroidx/core/view/AccessibilityDelegateCompat$AccessibilityDelegateAdapter;->dispatchPopulateAccessibilityEvent(Landroid/view/View;Landroid/view/accessibility/AccessibilityEvent;)Z
-HSPLandroidx/core/view/AccessibilityDelegateCompat$AccessibilityDelegateAdapter;->getAccessibilityNodeProvider(Landroid/view/View;)Landroid/view/accessibility/AccessibilityNodeProvider;
-HSPLandroidx/core/view/AccessibilityDelegateCompat$AccessibilityDelegateAdapter;->onInitializeAccessibilityEvent(Landroid/view/View;Landroid/view/accessibility/AccessibilityEvent;)V
-HSPLandroidx/core/view/AccessibilityDelegateCompat$AccessibilityDelegateAdapter;->onPopulateAccessibilityEvent(Landroid/view/View;Landroid/view/accessibility/AccessibilityEvent;)V
-HSPLandroidx/core/view/AccessibilityDelegateCompat$AccessibilityDelegateAdapter;->sendAccessibilityEvent(Landroid/view/View;I)V
-HSPLandroidx/core/view/AccessibilityDelegateCompat$AccessibilityDelegateAdapter;->sendAccessibilityEventUnchecked(Landroid/view/View;Landroid/view/accessibility/AccessibilityEvent;)V
-HSPLandroidx/core/view/AccessibilityDelegateCompat;-><clinit>()V
-HSPLandroidx/core/view/AccessibilityDelegateCompat;-><init>()V
-HSPLandroidx/core/view/MenuHostHelper;-><init>()V
-HSPLandroidx/core/view/ViewCompat$$ExternalSyntheticLambda0;-><init>()V
-HSPLandroidx/core/view/ViewCompat;-><clinit>()V
-HSPLandroidx/core/view/accessibility/AccessibilityNodeProviderCompat;-><init>(Landroid/view/accessibility/AccessibilityNodeProvider;)V
-HSPLandroidx/customview/poolingcontainer/PoolingContainer;->getPoolingContainerListenerHolder(Landroid/view/View;)Landroidx/customview/poolingcontainer/PoolingContainerListenerHolder;
-HSPLandroidx/customview/poolingcontainer/PoolingContainerListenerHolder;-><init>()V
-HSPLandroidx/emoji2/text/ConcurrencyHelpers$$ExternalSyntheticLambda0;-><init>(Ljava/lang/String;)V
-HSPLandroidx/emoji2/text/ConcurrencyHelpers$$ExternalSyntheticLambda0;->newThread(Ljava/lang/Runnable;)Ljava/lang/Thread;
-HSPLandroidx/emoji2/text/ConcurrencyHelpers$Handler28Impl;->createAsync(Landroid/os/Looper;)Landroid/os/Handler;
-HSPLandroidx/emoji2/text/DefaultEmojiCompatConfig;->create(Landroid/content/Context;)Landroidx/emoji2/text/FontRequestEmojiCompatConfig;
-HSPLandroidx/emoji2/text/DefaultGlyphChecker;-><clinit>()V
-HSPLandroidx/emoji2/text/DefaultGlyphChecker;-><init>()V
-HSPLandroidx/emoji2/text/EmojiCompat$CompatInternal19$1;-><init>(Landroidx/emoji2/text/EmojiCompat$CompatInternal19;)V
-HSPLandroidx/emoji2/text/EmojiCompat$CompatInternal19$1;->onLoaded(Landroidx/emoji2/text/MetadataRepo;)V
-HSPLandroidx/emoji2/text/EmojiCompat$CompatInternal19;-><init>(Landroidx/emoji2/text/EmojiCompat;)V
-HSPLandroidx/emoji2/text/EmojiCompat$CompatInternal;-><init>(Landroidx/emoji2/text/EmojiCompat;)V
-HSPLandroidx/emoji2/text/EmojiCompat$Config;-><init>(Landroidx/emoji2/text/EmojiCompat$MetadataRepoLoader;)V
-HSPLandroidx/emoji2/text/EmojiCompat$DefaultSpanFactory;-><init>()V
-HSPLandroidx/emoji2/text/EmojiCompat$InitCallback;-><init>()V
-HSPLandroidx/emoji2/text/EmojiCompat$ListenerDispatcher;-><init>(Ljava/util/List;ILjava/lang/Throwable;)V
-HSPLandroidx/emoji2/text/EmojiCompat$ListenerDispatcher;->run()V
-HSPLandroidx/emoji2/text/EmojiCompat$MetadataRepoLoaderCallback;-><init>()V
-HSPLandroidx/emoji2/text/EmojiCompat;-><clinit>()V
-HSPLandroidx/emoji2/text/EmojiCompat;-><init>(Landroidx/emoji2/text/EmojiCompatInitializer$BackgroundDefaultConfig;)V
-HSPLandroidx/emoji2/text/EmojiCompat;->get()Landroidx/emoji2/text/EmojiCompat;
-HSPLandroidx/emoji2/text/EmojiCompat;->getLoadState()I
-HSPLandroidx/emoji2/text/EmojiCompat;->load()V
-HSPLandroidx/emoji2/text/EmojiCompat;->onMetadataLoadSuccess()V
-HSPLandroidx/emoji2/text/EmojiCompatInitializer$1;-><init>(Landroidx/emoji2/text/EmojiCompatInitializer;Landroidx/lifecycle/Lifecycle;)V
-HSPLandroidx/emoji2/text/EmojiCompatInitializer$1;->onResume(Landroidx/lifecycle/LifecycleOwner;)V
-HSPLandroidx/emoji2/text/EmojiCompatInitializer$BackgroundDefaultConfig;-><init>(Landroid/content/Context;)V
-HSPLandroidx/emoji2/text/EmojiCompatInitializer$BackgroundDefaultLoader$$ExternalSyntheticLambda0;-><init>(Landroidx/emoji2/text/EmojiCompatInitializer$BackgroundDefaultLoader;Landroidx/emoji2/text/EmojiCompat$MetadataRepoLoaderCallback;Ljava/util/concurrent/ThreadPoolExecutor;)V
-HSPLandroidx/emoji2/text/EmojiCompatInitializer$BackgroundDefaultLoader$$ExternalSyntheticLambda0;->run()V
-HSPLandroidx/emoji2/text/EmojiCompatInitializer$BackgroundDefaultLoader$1;-><init>(Landroidx/emoji2/text/EmojiCompat$MetadataRepoLoaderCallback;Ljava/util/concurrent/ThreadPoolExecutor;)V
-HSPLandroidx/emoji2/text/EmojiCompatInitializer$BackgroundDefaultLoader$1;->onLoaded(Landroidx/emoji2/text/MetadataRepo;)V
-HSPLandroidx/emoji2/text/EmojiCompatInitializer$BackgroundDefaultLoader;-><init>(Landroid/content/Context;)V
-HSPLandroidx/emoji2/text/EmojiCompatInitializer$BackgroundDefaultLoader;->load(Landroidx/emoji2/text/EmojiCompat$MetadataRepoLoaderCallback;)V
-HSPLandroidx/emoji2/text/EmojiCompatInitializer$LoadEmojiCompatRunnable;-><init>()V
-HSPLandroidx/emoji2/text/EmojiCompatInitializer$LoadEmojiCompatRunnable;->run()V
-HSPLandroidx/emoji2/text/EmojiCompatInitializer;-><init>()V
-HSPLandroidx/emoji2/text/EmojiCompatInitializer;->create(Landroid/content/Context;)Ljava/lang/Boolean;
-HSPLandroidx/emoji2/text/EmojiCompatInitializer;->create(Landroid/content/Context;)Ljava/lang/Object;
-HSPLandroidx/emoji2/text/EmojiCompatInitializer;->dependencies()Ljava/util/List;
-HSPLandroidx/emoji2/text/EmojiExclusions$EmojiExclusions_Reflections;->getExclusions()Ljava/util/Set;
-HSPLandroidx/emoji2/text/EmojiProcessor$EmojiProcessAddSpanCallback;-><init>(Landroidx/emoji2/text/UnprecomputeTextOnModificationSpannable;Landroidx/emoji2/text/EmojiCompat$SpanFactory;)V
-HSPLandroidx/emoji2/text/EmojiProcessor$EmojiProcessAddSpanCallback;->getResult()Ljava/lang/Object;
-HSPLandroidx/emoji2/text/EmojiProcessor$ProcessorSm;-><init>(Landroidx/emoji2/text/MetadataRepo$Node;Z[I)V
-HSPLandroidx/emoji2/text/EmojiProcessor$ProcessorSm;->reset()V
-HSPLandroidx/emoji2/text/EmojiProcessor$ProcessorSm;->shouldUseEmojiPresentationStyleForSingleCodepoint()Z
-HSPLandroidx/emoji2/text/EmojiProcessor;-><init>(Landroidx/emoji2/text/MetadataRepo;Landroidx/emoji2/text/EmojiCompat$DefaultSpanFactory;Landroidx/emoji2/text/DefaultGlyphChecker;Ljava/util/Set;)V
-HSPLandroidx/emoji2/text/EmojiProcessor;->process(Ljava/lang/CharSequence;IIIZLandroidx/emoji2/text/EmojiProcessor$EmojiProcessCallback;)Ljava/lang/Object;
-HSPLandroidx/emoji2/text/FontRequestEmojiCompatConfig$FontProviderHelper;-><init>()V
-HSPLandroidx/emoji2/text/FontRequestEmojiCompatConfig$FontRequestMetadataLoader$$ExternalSyntheticLambda0;-><init>(Landroidx/emoji2/text/FontRequestEmojiCompatConfig$FontRequestMetadataLoader;)V
-HSPLandroidx/emoji2/text/FontRequestEmojiCompatConfig$FontRequestMetadataLoader$$ExternalSyntheticLambda0;->run()V
-HSPLandroidx/emoji2/text/FontRequestEmojiCompatConfig$FontRequestMetadataLoader;-><init>(Landroid/content/Context;Landroidx/core/provider/FontRequest;)V
-HSPLandroidx/emoji2/text/FontRequestEmojiCompatConfig$FontRequestMetadataLoader;->cleanUp()V
-HSPLandroidx/emoji2/text/FontRequestEmojiCompatConfig$FontRequestMetadataLoader;->load(Landroidx/emoji2/text/EmojiCompat$MetadataRepoLoaderCallback;)V
-HSPLandroidx/emoji2/text/FontRequestEmojiCompatConfig$FontRequestMetadataLoader;->loadInternal()V
-HSPLandroidx/emoji2/text/FontRequestEmojiCompatConfig$FontRequestMetadataLoader;->retrieveFontInfo()Landroidx/core/provider/FontsContractCompat$FontInfo;
-HSPLandroidx/emoji2/text/FontRequestEmojiCompatConfig;-><clinit>()V
-HSPLandroidx/emoji2/text/FontRequestEmojiCompatConfig;-><init>(Landroid/content/Context;Landroidx/core/provider/FontRequest;)V
-HSPLandroidx/emoji2/text/MetadataListReader$ByteBufferReader;-><init>(Ljava/nio/ByteBuffer;)V
-HSPLandroidx/emoji2/text/MetadataListReader$ByteBufferReader;->readUnsignedInt()J
-HSPLandroidx/emoji2/text/MetadataListReader$ByteBufferReader;->skip(I)V
-HSPLandroidx/emoji2/text/MetadataListReader;->read(Ljava/nio/MappedByteBuffer;)Landroidx/emoji2/text/flatbuffer/MetadataList;
-HSPLandroidx/emoji2/text/MetadataRepo$Node;-><init>()V
-HSPLandroidx/emoji2/text/MetadataRepo$Node;-><init>(I)V
-HSPLandroidx/emoji2/text/MetadataRepo$Node;->put(Landroidx/emoji2/text/TypefaceEmojiRasterizer;II)V
-HSPLandroidx/emoji2/text/MetadataRepo;-><init>(Landroid/graphics/Typeface;Landroidx/emoji2/text/flatbuffer/MetadataList;)V
-HSPLandroidx/emoji2/text/TypefaceEmojiRasterizer;-><clinit>()V
-HSPLandroidx/emoji2/text/TypefaceEmojiRasterizer;-><init>(Landroidx/emoji2/text/MetadataRepo;I)V
-HSPLandroidx/emoji2/text/TypefaceEmojiRasterizer;->getCodepointAt(I)I
-HSPLandroidx/emoji2/text/TypefaceEmojiRasterizer;->getCodepointsLength()I
-HSPLandroidx/emoji2/text/flatbuffer/MetadataItem;-><init>()V
-HSPLandroidx/emoji2/text/flatbuffer/MetadataList;-><init>()V
-HSPLandroidx/emoji2/text/flatbuffer/Table;-><init>()V
-HSPLandroidx/emoji2/text/flatbuffer/Table;->__offset(I)I
-HSPLandroidx/emoji2/text/flatbuffer/Table;->__reset(ILjava/nio/ByteBuffer;)V
-HSPLandroidx/emoji2/text/flatbuffer/Utf8Safe;-><init>()V
-HSPLandroidx/lifecycle/DefaultLifecycleObserver;->onStart(Landroidx/lifecycle/LifecycleOwner;)V
-HSPLandroidx/lifecycle/DefaultLifecycleObserverAdapter$WhenMappings;-><clinit>()V
-HSPLandroidx/lifecycle/DefaultLifecycleObserverAdapter;-><init>(Landroidx/lifecycle/DefaultLifecycleObserver;Landroidx/lifecycle/LifecycleEventObserver;)V
-HSPLandroidx/lifecycle/DefaultLifecycleObserverAdapter;->onStateChanged(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle$Event;)V
-HSPLandroidx/lifecycle/EmptyActivityLifecycleCallbacks;-><init>()V
-HSPLandroidx/lifecycle/EmptyActivityLifecycleCallbacks;->onActivityCreated(Landroid/app/Activity;Landroid/os/Bundle;)V
-HSPLandroidx/lifecycle/EmptyActivityLifecycleCallbacks;->onActivityResumed(Landroid/app/Activity;)V
-HSPLandroidx/lifecycle/EmptyActivityLifecycleCallbacks;->onActivityStarted(Landroid/app/Activity;)V
-HSPLandroidx/lifecycle/Lifecycle$Event$Companion;-><init>()V
-HSPLandroidx/lifecycle/Lifecycle$Event$Companion;->upFrom(Landroidx/lifecycle/Lifecycle$State;)Landroidx/lifecycle/Lifecycle$Event;
-HSPLandroidx/lifecycle/Lifecycle$Event$WhenMappings;-><clinit>()V
-HSPLandroidx/lifecycle/Lifecycle$Event;-><clinit>()V
-HSPLandroidx/lifecycle/Lifecycle$Event;-><init>(ILjava/lang/String;)V
-HSPLandroidx/lifecycle/Lifecycle$Event;->getTargetState()Landroidx/lifecycle/Lifecycle$State;
-HSPLandroidx/lifecycle/Lifecycle$Event;->values()[Landroidx/lifecycle/Lifecycle$Event;
-HSPLandroidx/lifecycle/Lifecycle$State;-><clinit>()V
-HSPLandroidx/lifecycle/Lifecycle$State;-><init>(ILjava/lang/String;)V
-HSPLandroidx/lifecycle/Lifecycle;-><init>()V
-HSPLandroidx/lifecycle/LifecycleDispatcher$DispatcherActivityCallback;-><init>()V
-HSPLandroidx/lifecycle/LifecycleDispatcher$DispatcherActivityCallback;->onActivityCreated(Landroid/app/Activity;Landroid/os/Bundle;)V
-HSPLandroidx/lifecycle/LifecycleDispatcher;-><clinit>()V
-HSPLandroidx/lifecycle/LifecycleRegistry$ObserverWithState;-><init>(Landroidx/lifecycle/LifecycleObserver;Landroidx/lifecycle/Lifecycle$State;)V
-HSPLandroidx/lifecycle/LifecycleRegistry$ObserverWithState;->dispatchEvent(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle$Event;)V
-HSPLandroidx/lifecycle/LifecycleRegistry;-><init>(Landroidx/lifecycle/LifecycleOwner;)V
-HSPLandroidx/lifecycle/LifecycleRegistry;->addObserver(Landroidx/lifecycle/LifecycleObserver;)V
-HSPLandroidx/lifecycle/LifecycleRegistry;->calculateTargetState(Landroidx/lifecycle/LifecycleObserver;)Landroidx/lifecycle/Lifecycle$State;
-HSPLandroidx/lifecycle/LifecycleRegistry;->enforceMainThreadIfNeeded(Ljava/lang/String;)V
-HSPLandroidx/lifecycle/LifecycleRegistry;->handleLifecycleEvent(Landroidx/lifecycle/Lifecycle$Event;)V
-HSPLandroidx/lifecycle/LifecycleRegistry;->moveToState(Landroidx/lifecycle/Lifecycle$State;)V
-HSPLandroidx/lifecycle/LifecycleRegistry;->removeObserver(Landroidx/lifecycle/LifecycleObserver;)V
-HSPLandroidx/lifecycle/LifecycleRegistry;->sync()V
-HSPLandroidx/lifecycle/Lifecycling;-><clinit>()V
-HSPLandroidx/lifecycle/ProcessLifecycleInitializer;-><init>()V
-HSPLandroidx/lifecycle/ProcessLifecycleInitializer;->create(Landroid/content/Context;)Ljava/lang/Object;
-HSPLandroidx/lifecycle/ProcessLifecycleInitializer;->dependencies()Ljava/util/List;
-HSPLandroidx/lifecycle/ProcessLifecycleOwner$1;-><init>(Landroidx/lifecycle/ProcessLifecycleOwner;)V
-HSPLandroidx/lifecycle/ProcessLifecycleOwner$2;-><init>(Landroidx/lifecycle/ProcessLifecycleOwner;)V
-HSPLandroidx/lifecycle/ProcessLifecycleOwner$3$$ExternalSyntheticApiModelOutline0;->m(Landroid/app/Activity;Landroidx/lifecycle/ProcessLifecycleOwner$3$1;)V
-HSPLandroidx/lifecycle/ProcessLifecycleOwner$3$1;-><init>(Landroidx/lifecycle/ProcessLifecycleOwner$3;)V
-HSPLandroidx/lifecycle/ProcessLifecycleOwner$3$1;->onActivityPostResumed(Landroid/app/Activity;)V
-HSPLandroidx/lifecycle/ProcessLifecycleOwner$3$1;->onActivityPostStarted(Landroid/app/Activity;)V
-HSPLandroidx/lifecycle/ProcessLifecycleOwner$3;-><init>(Landroidx/lifecycle/ProcessLifecycleOwner;)V
-HSPLandroidx/lifecycle/ProcessLifecycleOwner$3;->onActivityCreated(Landroid/app/Activity;Landroid/os/Bundle;)V
-HSPLandroidx/lifecycle/ProcessLifecycleOwner$3;->onActivityPreCreated(Landroid/app/Activity;Landroid/os/Bundle;)V
-HSPLandroidx/lifecycle/ProcessLifecycleOwner;-><clinit>()V
-HSPLandroidx/lifecycle/ProcessLifecycleOwner;-><init>()V
-HSPLandroidx/lifecycle/ProcessLifecycleOwner;->activityResumed()V
-HSPLandroidx/lifecycle/ProcessLifecycleOwner;->getLifecycle()Landroidx/lifecycle/LifecycleRegistry;
-HSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks$$ExternalSyntheticApiModelOutline0;->m(Landroid/app/Activity;Landroidx/lifecycle/ReportFragment$LifecycleCallbacks;)V
-HSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;-><init>()V
-HSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityCreated(Landroid/app/Activity;Landroid/os/Bundle;)V
-HSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityPostCreated(Landroid/app/Activity;Landroid/os/Bundle;)V
-HSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityPostResumed(Landroid/app/Activity;)V
-HSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityPostStarted(Landroid/app/Activity;)V
-HSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityResumed(Landroid/app/Activity;)V
-HSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityStarted(Landroid/app/Activity;)V
-HSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->registerIn(Landroid/app/Activity;)V
-HSPLandroidx/lifecycle/ReportFragment;-><init>()V
-HSPLandroidx/lifecycle/ReportFragment;->dispatch(Landroid/app/Activity;Landroidx/lifecycle/Lifecycle$Event;)V
-HSPLandroidx/lifecycle/ReportFragment;->dispatch(Landroidx/lifecycle/Lifecycle$Event;)V
-HSPLandroidx/lifecycle/ReportFragment;->injectIfNeededIn(Landroid/app/Activity;)V
-HSPLandroidx/lifecycle/ReportFragment;->onActivityCreated(Landroid/os/Bundle;)V
-HSPLandroidx/lifecycle/ReportFragment;->onResume()V
-HSPLandroidx/lifecycle/ReportFragment;->onStart()V
-HSPLandroidx/lifecycle/SavedStateHandleAttacher;-><init>(Landroidx/lifecycle/SavedStateHandlesProvider;)V
-HSPLandroidx/lifecycle/SavedStateHandleAttacher;->onStateChanged(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle$Event;)V
-HSPLandroidx/lifecycle/SavedStateHandleSupport$DEFAULT_ARGS_KEY$1;-><init>()V
-HSPLandroidx/lifecycle/SavedStateHandleSupport$SAVED_STATE_REGISTRY_OWNER_KEY$1;-><init>()V
-HSPLandroidx/lifecycle/SavedStateHandleSupport$VIEW_MODEL_STORE_OWNER_KEY$1;-><init>()V
-HSPLandroidx/lifecycle/SavedStateHandleSupport$savedStateHandlesVM$1$1;-><clinit>()V
-HSPLandroidx/lifecycle/SavedStateHandleSupport$savedStateHandlesVM$1$1;-><init>()V
-HSPLandroidx/lifecycle/SavedStateHandleSupport$savedStateHandlesVM$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/lifecycle/SavedStateHandleSupport;-><clinit>()V
-HSPLandroidx/lifecycle/SavedStateHandlesProvider$viewModel$2;-><init>(Landroidx/lifecycle/ViewModelStoreOwner;)V
-HSPLandroidx/lifecycle/SavedStateHandlesProvider$viewModel$2;->invoke()Ljava/lang/Object;
-HSPLandroidx/lifecycle/SavedStateHandlesProvider;-><init>(Landroidx/savedstate/SavedStateRegistry;Landroidx/lifecycle/ViewModelStoreOwner;)V
-HSPLandroidx/lifecycle/SavedStateHandlesVM;-><init>()V
-HSPLandroidx/lifecycle/ViewModel;-><init>()V
-HSPLandroidx/lifecycle/ViewModelProvider$AndroidViewModelFactory$Companion$ApplicationKeyImpl;-><clinit>()V
-HSPLandroidx/lifecycle/ViewModelProvider$AndroidViewModelFactory$Companion$ApplicationKeyImpl;-><init>()V
-HSPLandroidx/lifecycle/ViewModelProvider$NewInstanceFactory$Companion$ViewModelKeyImpl;-><clinit>()V
-HSPLandroidx/lifecycle/ViewModelProvider$NewInstanceFactory$Companion$ViewModelKeyImpl;-><init>()V
-HSPLandroidx/lifecycle/ViewModelStore;-><init>()V
-HSPLandroidx/lifecycle/ViewTreeLifecycleOwner$findViewTreeLifecycleOwner$1;-><clinit>()V
-HSPLandroidx/lifecycle/ViewTreeLifecycleOwner$findViewTreeLifecycleOwner$1;-><init>()V
-HSPLandroidx/lifecycle/ViewTreeLifecycleOwner$findViewTreeLifecycleOwner$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/lifecycle/ViewTreeLifecycleOwner$findViewTreeLifecycleOwner$2;-><clinit>()V
-HSPLandroidx/lifecycle/ViewTreeLifecycleOwner$findViewTreeLifecycleOwner$2;-><init>()V
-HSPLandroidx/lifecycle/ViewTreeLifecycleOwner$findViewTreeLifecycleOwner$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/lifecycle/ViewTreeLifecycleOwner;->get(Landroid/view/View;)Landroidx/lifecycle/LifecycleOwner;
-HSPLandroidx/lifecycle/runtime/R$id;-><init>()V
-HSPLandroidx/lifecycle/viewmodel/CreationExtras$Empty;-><clinit>()V
-HSPLandroidx/lifecycle/viewmodel/CreationExtras$Empty;-><init>()V
-HSPLandroidx/lifecycle/viewmodel/CreationExtras;-><init>()V
-HSPLandroidx/lifecycle/viewmodel/InitializerViewModelFactory;-><init>([Landroidx/lifecycle/viewmodel/ViewModelInitializer;)V
-HSPLandroidx/lifecycle/viewmodel/InitializerViewModelFactory;->create(Landroidx/lifecycle/viewmodel/MutableCreationExtras;)Landroidx/lifecycle/ViewModel;
-HSPLandroidx/lifecycle/viewmodel/MutableCreationExtras;-><init>()V
-HSPLandroidx/lifecycle/viewmodel/MutableCreationExtras;-><init>(Landroidx/lifecycle/viewmodel/CreationExtras;)V
-HSPLandroidx/lifecycle/viewmodel/ViewModelInitializer;-><init>(Ljava/lang/Class;)V
-HSPLandroidx/metrics/performance/DelegatingFrameMetricsListener;-><init>(Ljava/util/ArrayList;)V
-HSPLandroidx/metrics/performance/DelegatingFrameMetricsListener;->onFrameMetricsAvailable(Landroid/view/Window;Landroid/view/FrameMetrics;I)V
-HSPLandroidx/metrics/performance/FrameData;-><init>(JJZLjava/util/ArrayList;)V
-HSPLandroidx/metrics/performance/FrameDataApi24;-><init>(JJJZLjava/util/ArrayList;)V
-HSPLandroidx/metrics/performance/FrameDataApi31;-><init>(JJJJZLjava/util/ArrayList;)V
-HSPLandroidx/metrics/performance/FrameDataApi31;->copy()Landroidx/metrics/performance/FrameData;
-HSPLandroidx/metrics/performance/JankStats;-><init>(Landroid/view/Window;Lcom/example/tvcomposebasedtests/JankStatsAggregator$listener$1;)V
-HSPLandroidx/metrics/performance/JankStatsApi16Impl$onFrameListenerDelegate$1;-><init>(Landroidx/metrics/performance/JankStats;Landroidx/metrics/performance/JankStatsApi16Impl;)V
-HSPLandroidx/metrics/performance/JankStatsApi16Impl;-><init>(Landroidx/metrics/performance/JankStats;Landroid/view/View;)V
-HSPLandroidx/metrics/performance/JankStatsApi22Impl;-><init>(Landroidx/metrics/performance/JankStats;Landroid/view/View;)V
-HSPLandroidx/metrics/performance/JankStatsApi24Impl$$ExternalSyntheticLambda0;-><init>(Landroidx/metrics/performance/JankStatsApi24Impl;Landroidx/metrics/performance/JankStats;)V
-HSPLandroidx/metrics/performance/JankStatsApi24Impl$$ExternalSyntheticLambda0;->onFrameMetricsAvailable(Landroid/view/Window;Landroid/view/FrameMetrics;I)V
-HSPLandroidx/metrics/performance/JankStatsApi24Impl;-><init>(Landroidx/metrics/performance/JankStats;Landroid/view/View;Landroid/view/Window;)V
-HSPLandroidx/metrics/performance/JankStatsApi24Impl;->getOrCreateFrameMetricsListenerDelegator(Landroid/view/Window;)Landroidx/metrics/performance/DelegatingFrameMetricsListener;
-HSPLandroidx/metrics/performance/JankStatsApi24Impl;->setupFrameTimer(Z)V
-HSPLandroidx/metrics/performance/JankStatsApi26Impl;-><init>(Landroidx/metrics/performance/JankStats;Landroid/view/View;Landroid/view/Window;)V
-HSPLandroidx/metrics/performance/JankStatsApi26Impl;->getFrameStartTime$metrics_performance_release(Landroid/view/FrameMetrics;)J
-HSPLandroidx/metrics/performance/JankStatsApi31Impl;-><init>(Landroidx/metrics/performance/JankStats;Landroid/view/View;Landroid/view/Window;)V
-HSPLandroidx/metrics/performance/JankStatsApi31Impl;->getExpectedFrameDuration(Landroid/view/FrameMetrics;)J
-HSPLandroidx/metrics/performance/JankStatsApi31Impl;->getFrameData$metrics_performance_release(JJLandroid/view/FrameMetrics;)Landroidx/metrics/performance/FrameDataApi24;
-HSPLandroidx/metrics/performance/JankStatsBaseImpl;-><init>(Landroidx/metrics/performance/JankStats;)V
-HSPLandroidx/metrics/performance/PerformanceMetricsState$Holder;-><init>()V
-HSPLandroidx/metrics/performance/PerformanceMetricsState;-><init>()V
-HSPLandroidx/metrics/performance/PerformanceMetricsState;->addFrameState(JJLjava/util/ArrayList;Ljava/util/ArrayList;)V
-HSPLandroidx/metrics/performance/PerformanceMetricsState;->cleanupSingleFrameStates$metrics_performance_release()V
-HSPLandroidx/metrics/performance/PerformanceMetricsState;->getIntervalStates$metrics_performance_release(JJLjava/util/ArrayList;)V
-HSPLandroidx/metrics/performance/R$id;->createZeroVectorFrom(Landroidx/compose/animation/core/TwoWayConverter;Ljava/lang/Object;)Landroidx/compose/animation/core/AnimationVector;
-HSPLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda0;-><init>(Landroidx/profileinstaller/ProfileInstallerInitializer;Landroid/content/Context;)V
-HSPLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda0;->run()V
-HSPLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda1;-><init>(ILjava/lang/Object;)V
-HSPLandroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl$$ExternalSyntheticLambda0;-><init>(Ljava/lang/Runnable;)V
-HSPLandroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl$$ExternalSyntheticLambda0;->doFrame(J)V
-HSPLandroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl;->postFrameCallback(Ljava/lang/Runnable;)V
-HSPLandroidx/profileinstaller/ProfileInstallerInitializer$Handler28Impl;->createAsync(Landroid/os/Looper;)Landroid/os/Handler;
-HSPLandroidx/profileinstaller/ProfileInstallerInitializer$Result;-><init>()V
-HSPLandroidx/profileinstaller/ProfileInstallerInitializer;-><init>()V
-HSPLandroidx/profileinstaller/ProfileInstallerInitializer;->create(Landroid/content/Context;)Ljava/lang/Object;
-HSPLandroidx/profileinstaller/ProfileInstallerInitializer;->dependencies()Ljava/util/List;
-HSPLandroidx/savedstate/Recreator;-><init>(Landroidx/savedstate/SavedStateRegistryOwner;)V
-HSPLandroidx/savedstate/Recreator;->onStateChanged(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle$Event;)V
-HSPLandroidx/savedstate/SavedStateRegistry$$ExternalSyntheticLambda0;-><init>(Landroidx/savedstate/SavedStateRegistry;)V
-HSPLandroidx/savedstate/SavedStateRegistry$$ExternalSyntheticLambda0;->onStateChanged(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle$Event;)V
-HSPLandroidx/savedstate/SavedStateRegistry;-><init>()V
-HSPLandroidx/savedstate/SavedStateRegistry;->consumeRestoredStateForKey(Ljava/lang/String;)Landroid/os/Bundle;
-HSPLandroidx/savedstate/SavedStateRegistry;->registerSavedStateProvider(Ljava/lang/String;Landroidx/savedstate/SavedStateRegistry$SavedStateProvider;)V
-HSPLandroidx/savedstate/SavedStateRegistryController;-><init>(Landroidx/savedstate/SavedStateRegistryOwner;)V
-HSPLandroidx/savedstate/SavedStateRegistryController;->performAttach()V
-HSPLandroidx/savedstate/ViewTreeSavedStateRegistryOwner$findViewTreeSavedStateRegistryOwner$1;-><clinit>()V
-HSPLandroidx/savedstate/ViewTreeSavedStateRegistryOwner$findViewTreeSavedStateRegistryOwner$1;-><init>()V
-HSPLandroidx/savedstate/ViewTreeSavedStateRegistryOwner$findViewTreeSavedStateRegistryOwner$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/savedstate/ViewTreeSavedStateRegistryOwner$findViewTreeSavedStateRegistryOwner$2;-><clinit>()V
-HSPLandroidx/savedstate/ViewTreeSavedStateRegistryOwner$findViewTreeSavedStateRegistryOwner$2;-><init>()V
-HSPLandroidx/savedstate/ViewTreeSavedStateRegistryOwner$findViewTreeSavedStateRegistryOwner$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/startup/AppInitializer;-><clinit>()V
-HSPLandroidx/startup/AppInitializer;-><init>(Landroid/content/Context;)V
-HSPLandroidx/startup/AppInitializer;->discoverAndInitialize(Landroid/os/Bundle;)V
-HSPLandroidx/startup/AppInitializer;->doInitialize(Ljava/lang/Class;Ljava/util/HashSet;)Ljava/lang/Object;
-HSPLandroidx/startup/AppInitializer;->getInstance(Landroid/content/Context;)Landroidx/startup/AppInitializer;
-HSPLandroidx/startup/InitializationProvider;-><init>()V
-HSPLandroidx/startup/InitializationProvider;->onCreate()Z
-HSPLandroidx/tracing/Trace$$ExternalSyntheticApiModelOutline0;->m()Z
-HSPLandroidx/tracing/Trace;->isEnabled()Z
-HSPLandroidx/tv/foundation/ContentInViewModifier$modifier$1;-><init>(Landroidx/tv/foundation/ContentInViewModifier;)V
-HSPLandroidx/tv/foundation/ContentInViewModifier$modifier$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/ContentInViewModifier;-><init>(Lkotlinx/coroutines/CoroutineScope;Landroidx/compose/foundation/gestures/Orientation;Landroidx/compose/foundation/gestures/ScrollableState;ZLandroidx/tv/foundation/PivotOffsets;)V
-HSPLandroidx/tv/foundation/ContentInViewModifier;->bringChildIntoView(Landroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$2$1$1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/ContentInViewModifier;->calculateRectForParent(Landroidx/compose/ui/geometry/Rect;)Landroidx/compose/ui/geometry/Rect;
-HSPLandroidx/tv/foundation/ContentInViewModifier;->computeDestination-JVtK1S4(Landroidx/compose/ui/geometry/Rect;JLandroidx/tv/foundation/PivotOffsets;)Landroidx/compose/ui/geometry/Rect;
-HSPLandroidx/tv/foundation/ContentInViewModifier;->onPlaced(Landroidx/compose/ui/node/NodeCoordinator;)V
-HSPLandroidx/tv/foundation/ContentInViewModifier;->onRemeasured-ozmzZPI(J)V
-HSPLandroidx/tv/foundation/ContentInViewModifier;->performBringIntoView(Landroidx/compose/ui/geometry/Rect;Landroidx/compose/ui/geometry/Rect;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/ContentInViewModifier;->relocationDistance(FFFLandroidx/tv/foundation/PivotOffsets;)F
-HSPLandroidx/tv/foundation/PivotOffsets;-><init>(I)V
-HSPLandroidx/tv/foundation/PivotOffsets;->validateFraction(F)V
-HSPLandroidx/tv/foundation/ScrollableWithPivotKt$scrollableNestedScrollConnection$1;-><init>(Landroidx/compose/runtime/MutableState;Z)V
-HSPLandroidx/tv/foundation/ScrollableWithPivotKt$scrollableWithPivot$2;-><init>(Landroidx/compose/foundation/gestures/Orientation;Landroidx/compose/foundation/gestures/ScrollableState;ZZLandroidx/tv/foundation/PivotOffsets;)V
-HSPLandroidx/tv/foundation/ScrollableWithPivotKt$scrollableWithPivot$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/ScrollableWithPivotKt;->scrollableWithPivot(Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/gestures/ScrollableState;Landroidx/compose/foundation/gestures/Orientation;Landroidx/tv/foundation/PivotOffsets;ZZ)Landroidx/compose/ui/Modifier;
-HSPLandroidx/tv/foundation/ScrollingLogic;-><init>(Landroidx/compose/foundation/gestures/Orientation;ZLandroidx/compose/foundation/gestures/ScrollableState;)V
-HSPLandroidx/tv/foundation/lazy/layout/LazyLayoutSemanticsKt$lazyLayoutSemantics$1$1;-><init>(Landroidx/tv/foundation/lazy/layout/LazyLayoutSemanticsKt$lazyLayoutSemantics$1$indexForKeyMapping$1;ZLandroidx/compose/ui/semantics/ScrollAxisRange;Landroidx/tv/foundation/lazy/layout/LazyLayoutSemanticsKt$lazyLayoutSemantics$1$scrollByAction$1;Landroidx/tv/foundation/lazy/layout/LazyLayoutSemanticsKt$lazyLayoutSemantics$1$scrollToIndexAction$1;Landroidx/compose/ui/semantics/CollectionInfo;)V
-HSPLandroidx/tv/foundation/lazy/layout/LazyLayoutSemanticsKt$lazyLayoutSemantics$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/layout/LazyLayoutSemanticsKt$lazyLayoutSemantics$1$indexForKeyMapping$1;-><init>(Landroidx/compose/foundation/lazy/layout/LazyLayoutItemProvider;)V
-HSPLandroidx/tv/foundation/lazy/layout/LazyLayoutSemanticsKt$lazyLayoutSemantics$1$scrollByAction$1;-><init>(ZLkotlinx/coroutines/CoroutineScope;Landroidx/tv/foundation/lazy/layout/LazyLayoutSemanticState;)V
-HSPLandroidx/tv/foundation/lazy/layout/LazyLayoutSemanticsKt$lazyLayoutSemantics$1$scrollToIndexAction$1;-><init>(Landroidx/compose/foundation/lazy/layout/LazyLayoutItemProvider;Lkotlinx/coroutines/CoroutineScope;Landroidx/tv/foundation/lazy/layout/LazyLayoutSemanticState;)V
-HSPLandroidx/tv/foundation/lazy/layout/LazyLayoutSemanticsKt;->lazyLayoutSemantics(Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/lazy/layout/LazyLayoutItemProvider;Landroidx/tv/foundation/lazy/layout/LazyLayoutSemanticState;Landroidx/compose/foundation/gestures/Orientation;ZLandroidx/compose/runtime/Composer;)Landroidx/compose/ui/Modifier;
-HSPLandroidx/tv/foundation/lazy/list/AwaitFirstLayoutModifier$waitForFirstLayout$1;-><init>(Landroidx/tv/foundation/lazy/list/AwaitFirstLayoutModifier;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/tv/foundation/lazy/list/AwaitFirstLayoutModifier;-><init>()V
-HSPLandroidx/tv/foundation/lazy/list/AwaitFirstLayoutModifier;->onGloballyPositioned(Landroidx/compose/ui/node/NodeCoordinator;)V
-HSPLandroidx/tv/foundation/lazy/list/AwaitFirstLayoutModifier;->waitForFirstLayout(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/DataIndex;-><init>(I)V
-HSPLandroidx/tv/foundation/lazy/list/DataIndex;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/tv/foundation/lazy/list/EmptyLazyListLayoutInfo;-><clinit>()V
-HSPLandroidx/tv/foundation/lazy/list/EmptyLazyListLayoutInfo;-><init>()V
-HSPLandroidx/tv/foundation/lazy/list/LazyDslKt;->TvLazyRow(Landroidx/compose/ui/Modifier;Landroidx/tv/foundation/lazy/list/TvLazyListState;Landroidx/compose/foundation/layout/PaddingValues;ZLandroidx/compose/foundation/layout/Arrangement$Horizontal;Landroidx/compose/ui/Alignment$Vertical;ZLandroidx/tv/foundation/PivotOffsets;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListBeyondBoundsInfo$Interval;-><init>(II)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListBeyondBoundsInfo$Interval;->equals(Ljava/lang/Object;)Z
-HSPLandroidx/tv/foundation/lazy/list/LazyListBeyondBoundsInfo;-><init>()V
-HSPLandroidx/tv/foundation/lazy/list/LazyListBeyondBoundsModifierLocal$Companion$emptyBeyondBoundsScope$1;-><init>()V
-HSPLandroidx/tv/foundation/lazy/list/LazyListBeyondBoundsModifierLocal$layout$2;-><init>(Landroidx/tv/foundation/lazy/list/LazyListBeyondBoundsModifierLocal;Lkotlin/jvm/internal/Ref$ObjectRef;I)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListBeyondBoundsModifierLocal;-><clinit>()V
-HSPLandroidx/tv/foundation/lazy/list/LazyListBeyondBoundsModifierLocal;-><init>(Landroidx/tv/foundation/lazy/list/TvLazyListState;Landroidx/tv/foundation/lazy/list/LazyListBeyondBoundsInfo;ZLandroidx/compose/ui/unit/LayoutDirection;Landroidx/compose/foundation/gestures/Orientation;)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListBeyondBoundsModifierLocal;->getKey()Landroidx/compose/ui/modifier/ProvidableModifierLocal;
-HSPLandroidx/tv/foundation/lazy/list/LazyListBeyondBoundsModifierLocal;->getValue()Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/LazyListBeyondBoundsModifierLocal;->hasMoreContent-FR3nfPY(Landroidx/tv/foundation/lazy/list/LazyListBeyondBoundsInfo$Interval;I)Z
-HSPLandroidx/tv/foundation/lazy/list/LazyListBeyondBoundsModifierLocal;->hasMoreContent_FR3nfPY$hasMoreItemsAfter(Landroidx/tv/foundation/lazy/list/LazyListBeyondBoundsInfo$Interval;Landroidx/tv/foundation/lazy/list/LazyListBeyondBoundsModifierLocal;)Z
-HSPLandroidx/tv/foundation/lazy/list/LazyListBeyondBoundsModifierLocal;->layout-o7g1Pn8(ILkotlin/jvm/functions/Function1;)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/LazyListIntervalContent;-><init>(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/internal/ComposableLambdaImpl;)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListIntervalContent;->getKey()Lkotlin/jvm/functions/Function1;
-HSPLandroidx/tv/foundation/lazy/list/LazyListIntervalContent;->getType()Lkotlin/jvm/functions/Function1;
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemPlacementAnimator;-><init>(Lkotlinx/coroutines/CoroutineScope;Z)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderImpl$1$1;-><init>(Landroidx/compose/foundation/lazy/layout/IntervalList$Interval;Landroidx/tv/foundation/lazy/list/TvLazyListItemScopeImpl;I)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderImpl$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderImpl$1;-><init>(Landroidx/tv/foundation/lazy/list/TvLazyListState;Landroidx/tv/foundation/lazy/list/TvLazyListItemScopeImpl;)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderImpl$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderImpl;-><init>(Landroidx/compose/foundation/lazy/layout/MutableIntervalList;Lkotlin/ranges/IntRange;Landroidx/tv/foundation/lazy/list/TvLazyListItemScopeImpl;Landroidx/tv/foundation/lazy/list/TvLazyListState;)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderImpl;->Item(ILandroidx/compose/runtime/Composer;I)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderImpl;->getContentType(I)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderImpl;->getItemCount()I
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderImpl;->getKey(I)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderImpl;->getKeyToIndexMap()Ljava/util/Map;
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$1$1;-><init>(Landroidx/compose/runtime/DerivedSnapshotState;)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$1$1;->Item(ILandroidx/compose/runtime/Composer;I)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$1$1;->getContentType(I)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$1$1;->getHeaderIndexes()Ljava/util/List;
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$1$1;->getItemCount()I
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$1$1;->getItemScope()Landroidx/tv/foundation/lazy/list/TvLazyListItemScopeImpl;
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$1$1;->getKey(I)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$1$1;->getKeyToIndexMap()Ljava/util/Map;
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$1$itemProviderState$1;-><init>(Landroidx/compose/runtime/MutableState;Landroidx/compose/runtime/MutableState;Landroidx/tv/foundation/lazy/list/TvLazyListItemScopeImpl;Landroidx/tv/foundation/lazy/list/TvLazyListState;)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$1$itemProviderState$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$nearestItemsRangeState$1$1;-><init>(Landroidx/tv/foundation/lazy/list/TvLazyListState;)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$nearestItemsRangeState$1$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$nearestItemsRangeState$2;-><clinit>()V
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$nearestItemsRangeState$2;-><init>()V
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$nearestItemsRangeState$2;->invoke()Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$nearestItemsRangeState$3;-><clinit>()V
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$nearestItemsRangeState$3;-><init>()V
-HSPLandroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$nearestItemsRangeState$3;->invoke()Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/LazyListKt$ScrollPositionUpdater$1;-><init>(Landroidx/tv/foundation/lazy/list/LazyListItemProvider;Landroidx/tv/foundation/lazy/list/TvLazyListState;I)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListKt$ScrollPositionUpdater$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/LazyListKt$rememberLazyListMeasurePolicy$1$1$2;-><init>(Landroidx/compose/foundation/lazy/layout/LazyLayoutMeasureScope;JII)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListKt$rememberLazyListMeasurePolicy$1$1$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/LazyListKt$rememberLazyListMeasurePolicy$1$1$measuredItemProvider$1;-><init>(IILandroidx/compose/foundation/lazy/layout/LazyLayoutMeasureScope;ZLandroidx/compose/ui/Alignment$Horizontal;Landroidx/compose/ui/Alignment$Vertical;ZIILandroidx/tv/foundation/lazy/list/LazyListItemPlacementAnimator;J)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListKt$rememberLazyListMeasurePolicy$1$1$measuredItemProvider$1;->createItem-44a8dek(ILjava/lang/Object;Ljava/util/List;)Landroidx/tv/foundation/lazy/list/LazyMeasuredItem;
-HSPLandroidx/tv/foundation/lazy/list/LazyListKt$rememberLazyListMeasurePolicy$1$1;-><init>(ZLandroidx/compose/foundation/layout/PaddingValues;ZLandroidx/tv/foundation/lazy/list/TvLazyListState;Landroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$1$1;Landroidx/compose/foundation/layout/Arrangement$Vertical;Landroidx/compose/foundation/layout/Arrangement$Horizontal;Landroidx/tv/foundation/lazy/list/LazyListItemPlacementAnimator;Landroidx/tv/foundation/lazy/list/LazyListBeyondBoundsInfo;ILandroidx/compose/ui/Alignment$Horizontal;Landroidx/compose/ui/Alignment$Vertical;)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListKt$rememberLazyListMeasurePolicy$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/LazyListKt;->LazyList(Landroidx/compose/ui/Modifier;Landroidx/tv/foundation/lazy/list/TvLazyListState;Landroidx/compose/foundation/layout/PaddingValues;ZZZILandroidx/tv/foundation/PivotOffsets;Landroidx/compose/ui/Alignment$Horizontal;Landroidx/compose/foundation/layout/Arrangement$Vertical;Landroidx/compose/ui/Alignment$Vertical;Landroidx/compose/foundation/layout/Arrangement$Horizontal;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;III)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListKt;->ScrollPositionUpdater(Landroidx/tv/foundation/lazy/list/LazyListItemProvider;Landroidx/tv/foundation/lazy/list/TvLazyListState;Landroidx/compose/runtime/Composer;I)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListMeasureKt$measureLazyList$5;-><init>(Ljava/util/ArrayList;Landroidx/tv/foundation/lazy/list/LazyListPositionedItem;)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListMeasureKt$measureLazyList$5;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/LazyListMeasureKt;->createItemsAfterList$addItem(Lkotlin/jvm/internal/Ref$ObjectRef;Landroidx/tv/foundation/lazy/list/LazyMeasuredItemProvider;I)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListMeasureResult;-><init>(Landroidx/tv/foundation/lazy/list/LazyMeasuredItem;IZFLandroidx/compose/ui/layout/MeasureResult;Ljava/util/List;ILandroidx/compose/foundation/gestures/Orientation;)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListMeasureResult;->getAlignmentLines()Ljava/util/Map;
-HSPLandroidx/tv/foundation/lazy/list/LazyListMeasureResult;->getHeight()I
-HSPLandroidx/tv/foundation/lazy/list/LazyListMeasureResult;->getTotalItemsCount()I
-HSPLandroidx/tv/foundation/lazy/list/LazyListMeasureResult;->getVisibleItemsInfo()Ljava/util/List;
-HSPLandroidx/tv/foundation/lazy/list/LazyListMeasureResult;->getWidth()I
-HSPLandroidx/tv/foundation/lazy/list/LazyListMeasureResult;->placeChildren()V
-HSPLandroidx/tv/foundation/lazy/list/LazyListPinnableContainerProviderKt$LazyListPinnableContainerProvider$1$1$invoke$$inlined$onDispose$1;-><init>(Landroidx/tv/foundation/lazy/list/LazyListPinnableItem;)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListPinnableContainerProviderKt$LazyListPinnableContainerProvider$1$1$invoke$$inlined$onDispose$1;->dispose()V
-HSPLandroidx/tv/foundation/lazy/list/LazyListPinnableContainerProviderKt$LazyListPinnableContainerProvider$1$1;-><init>(Landroidx/tv/foundation/lazy/list/LazyListPinnableItem;)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListPinnableContainerProviderKt$LazyListPinnableContainerProvider$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/LazyListPinnableContainerProviderKt;->LazyListPinnableContainerProvider(Landroidx/tv/foundation/lazy/list/TvLazyListState;ILkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;I)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListPinnableItem;-><init>(Landroidx/tv/foundation/lazy/list/TvLazyListState;)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListPinnableItem;->getIndex()I
-HSPLandroidx/tv/foundation/lazy/list/LazyListPinnableItem;->getPinsCount()I
-HSPLandroidx/tv/foundation/lazy/list/LazyListPinnableItem;->pin()Landroidx/tv/foundation/lazy/list/LazyListPinnableItem;
-HSPLandroidx/tv/foundation/lazy/list/LazyListPinnableItem;->unpin()V
-HSPLandroidx/tv/foundation/lazy/list/LazyListPlaceableWrapper;-><init>(JLandroidx/compose/ui/layout/Placeable;)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListPositionedItem;-><init>(IILjava/lang/Object;IIIZLjava/util/ArrayList;Landroidx/tv/foundation/lazy/list/LazyListItemPlacementAnimator;JZI)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListPositionedItem;->getAnimationSpec(I)Landroidx/compose/animation/core/FiniteAnimationSpec;
-HSPLandroidx/tv/foundation/lazy/list/LazyListPositionedItem;->getIndex()I
-HSPLandroidx/tv/foundation/lazy/list/LazyListPositionedItem;->getOffset-Bjo55l4(I)J
-HSPLandroidx/tv/foundation/lazy/list/LazyListPositionedItem;->place(Landroidx/compose/ui/layout/Placeable$PlacementScope;)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListScrollPosition;-><init>(II)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListScrollPosition;->update-ELO9Zo8(II)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListStateKt$rememberTvLazyListState$1$1;-><init>(II)V
-HSPLandroidx/tv/foundation/lazy/list/LazyListStateKt$rememberTvLazyListState$1$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/LazyListStateKt;->rememberTvLazyListState(Landroidx/compose/runtime/Composer;)Landroidx/tv/foundation/lazy/list/TvLazyListState;
-HSPLandroidx/tv/foundation/lazy/list/LazyMeasuredItem;-><init>(ILjava/util/List;ZLandroidx/compose/ui/Alignment$Horizontal;Landroidx/compose/ui/Alignment$Vertical;Landroidx/compose/ui/unit/LayoutDirection;ZIILandroidx/tv/foundation/lazy/list/LazyListItemPlacementAnimator;IJLjava/lang/Object;)V
-HSPLandroidx/tv/foundation/lazy/list/LazyMeasuredItem;->position(III)Landroidx/tv/foundation/lazy/list/LazyListPositionedItem;
-HSPLandroidx/tv/foundation/lazy/list/LazyMeasuredItemProvider;-><init>(JZLandroidx/tv/foundation/lazy/list/LazyListItemProvider;Landroidx/compose/foundation/lazy/layout/LazyLayoutMeasureScope;Landroidx/tv/foundation/lazy/list/LazyListKt$rememberLazyListMeasurePolicy$1$1$measuredItemProvider$1;)V
-HSPLandroidx/tv/foundation/lazy/list/LazyMeasuredItemProvider;->getAndMeasure-KvsoDyw(I)Landroidx/tv/foundation/lazy/list/LazyMeasuredItem;
-HSPLandroidx/tv/foundation/lazy/list/LazySemanticsKt$rememberLazyListSemanticState$1$1$scrollAxisRange$1;-><init>(Landroidx/tv/foundation/lazy/list/TvLazyListState;)V
-HSPLandroidx/tv/foundation/lazy/list/LazySemanticsKt$rememberLazyListSemanticState$1$1$scrollAxisRange$2;-><init>(Landroidx/tv/foundation/lazy/list/TvLazyListState;Landroidx/compose/foundation/lazy/layout/LazyLayoutItemProvider;)V
-HSPLandroidx/tv/foundation/lazy/list/LazySemanticsKt$rememberLazyListSemanticState$1$1;-><init>(ZLandroidx/tv/foundation/lazy/list/TvLazyListState;Landroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$1$1;Z)V
-HSPLandroidx/tv/foundation/lazy/list/LazySemanticsKt$rememberLazyListSemanticState$1$1;->collectionInfo()Landroidx/compose/ui/semantics/CollectionInfo;
-HSPLandroidx/tv/foundation/lazy/list/LazySemanticsKt$rememberLazyListSemanticState$1$1;->scrollAxisRange()Landroidx/compose/ui/semantics/ScrollAxisRange;
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListItemScopeImpl;-><init>()V
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListScope$items$1;-><clinit>()V
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListScope$items$1;-><init>()V
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListScope$items$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListScopeImpl;-><init>()V
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListScopeImpl;->items(ILkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/internal/ComposableLambdaImpl;)V
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListState$Companion$Saver$1;-><clinit>()V
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListState$Companion$Saver$1;-><init>()V
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListState$Companion$Saver$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListState$Companion$Saver$2;-><clinit>()V
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListState$Companion$Saver$2;-><init>()V
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListState$remeasurementModifier$1;-><init>(Landroidx/tv/foundation/lazy/list/TvLazyListState;)V
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListState$remeasurementModifier$1;->onRemeasurementAvailable(Landroidx/compose/ui/layout/Remeasurement;)V
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListState$scroll$1;-><init>(Landroidx/tv/foundation/lazy/list/TvLazyListState;Lkotlin/coroutines/Continuation;)V
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListState$scroll$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListState$scrollableState$1;-><init>(Landroidx/tv/foundation/lazy/list/TvLazyListState;)V
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListState$scrollableState$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListState;-><clinit>()V
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListState;-><init>(II)V
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListState;->getCanScrollBackward()Z
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListState;->getCanScrollForward()Z
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListState;->getFirstVisibleItemIndex()I
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListState;->getLayoutInfo()Landroidx/tv/foundation/lazy/list/TvLazyListLayoutInfo;
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListState;->scroll(Landroidx/compose/foundation/MutatePriority;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLandroidx/tv/foundation/lazy/list/TvLazyListState;->updateScrollPositionIfTheFirstItemWasMoved$tv_foundation_release(Landroidx/tv/foundation/lazy/list/LazyListItemProvider;)V
-HSPLandroidx/tv/material3/ColorScheme;-><init>(JJJJJJJJJJJJJJJJJJJJJJJJJJJJJ)V
-HSPLandroidx/tv/material3/ColorSchemeKt$LocalColorScheme$1;-><clinit>()V
-HSPLandroidx/tv/material3/ColorSchemeKt$LocalColorScheme$1;-><init>()V
-HSPLandroidx/tv/material3/ColorSchemeKt;-><clinit>()V
-HSPLandroidx/tv/material3/ContentColorKt$LocalContentColor$1;-><clinit>()V
-HSPLandroidx/tv/material3/ContentColorKt$LocalContentColor$1;-><init>()V
-HSPLandroidx/tv/material3/ContentColorKt;-><clinit>()V
-HSPLandroidx/tv/material3/MaterialThemeKt$$ExternalSyntheticOutline0;->m(JLandroidx/compose/runtime/ParcelableSnapshotMutableState;)V
-HSPLandroidx/tv/material3/MaterialThemeKt$MaterialTheme$1;-><init>(Landroidx/tv/material3/Typography;Lkotlin/jvm/functions/Function2;I)V
-HSPLandroidx/tv/material3/MaterialThemeKt$MaterialTheme$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/material3/MaterialThemeKt$MaterialTheme$2;-><init>(Landroidx/tv/material3/ColorScheme;Landroidx/tv/material3/Shapes;Landroidx/tv/material3/Typography;Lkotlin/jvm/functions/Function2;II)V
-HSPLandroidx/tv/material3/MaterialThemeKt;->MaterialTheme(Landroidx/tv/material3/ColorScheme;Landroidx/tv/material3/Shapes;Landroidx/tv/material3/Typography;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V
-HSPLandroidx/tv/material3/ShapeDefaults;-><clinit>()V
-HSPLandroidx/tv/material3/Shapes;-><init>(I)V
-HSPLandroidx/tv/material3/ShapesKt$LocalShapes$1;-><clinit>()V
-HSPLandroidx/tv/material3/ShapesKt$LocalShapes$1;-><init>()V
-HSPLandroidx/tv/material3/ShapesKt$LocalShapes$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/tv/material3/ShapesKt;-><clinit>()V
-HSPLandroidx/tv/material3/TabColors;-><init>(JJJJJJJJ)V
-HSPLandroidx/tv/material3/TabKt$Tab$1;-><clinit>()V
-HSPLandroidx/tv/material3/TabKt$Tab$1;-><init>()V
-HSPLandroidx/tv/material3/TabKt$Tab$3$1$1;-><init>(Z)V
-HSPLandroidx/tv/material3/TabKt$Tab$3$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/material3/TabKt$Tab$3$2$1;-><init>(Landroidx/compose/runtime/MutableState;Lkotlin/jvm/functions/Function0;)V
-HSPLandroidx/tv/material3/TabKt$Tab$3$2$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/material3/TabKt$Tab$3;-><init>(Landroidx/compose/ui/Modifier;ZILandroidx/compose/runtime/MutableState;Lkotlin/jvm/functions/Function0;ZLandroidx/compose/foundation/interaction/MutableInteractionSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function3;)V
-HSPLandroidx/tv/material3/TabKt$Tab$4;-><init>(ZLkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function0;ZLandroidx/tv/material3/TabColors;Landroidx/compose/foundation/interaction/MutableInteractionSource;Lkotlin/jvm/functions/Function3;II)V
-HSPLandroidx/tv/material3/TabKt$Tab$4;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/material3/TabKt;->Tab(ZLkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function0;ZLandroidx/tv/material3/TabColors;Landroidx/compose/foundation/interaction/MutableInteractionSource;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V
-HSPLandroidx/tv/material3/TabRowDefaults$PillIndicator$1;-><init>(Landroidx/tv/material3/TabRowDefaults;Landroidx/compose/ui/unit/DpRect;Landroidx/compose/ui/Modifier;JJII)V
-HSPLandroidx/tv/material3/TabRowDefaults$PillIndicator$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/material3/TabRowDefaults;-><clinit>()V
-HSPLandroidx/tv/material3/TabRowDefaults;-><init>()V
-HSPLandroidx/tv/material3/TabRowDefaults;->PillIndicator-eaDK9VM(Landroidx/compose/ui/unit/DpRect;Landroidx/compose/ui/Modifier;JJLandroidx/compose/runtime/Composer;II)V
-HSPLandroidx/tv/material3/TabRowKt$LocalTabRowHasFocus$1;-><clinit>()V
-HSPLandroidx/tv/material3/TabRowKt$LocalTabRowHasFocus$1;-><init>()V
-HSPLandroidx/tv/material3/TabRowKt$TabRow$1;-><init>(I)V
-HSPLandroidx/tv/material3/TabRowKt$TabRow$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/material3/TabRowKt$TabRow$2$1$1;-><init>(Landroidx/compose/runtime/MutableState;)V
-HSPLandroidx/tv/material3/TabRowKt$TabRow$2$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/material3/TabRowKt$TabRow$2$2$1$1$2;-><init>(Lkotlin/jvm/functions/Function3;Ljava/util/ArrayList;I)V
-HSPLandroidx/tv/material3/TabRowKt$TabRow$2$2$1$1$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/material3/TabRowKt$TabRow$2$2$1$1;-><init>(Ljava/util/ArrayList;Landroidx/compose/ui/layout/SubcomposeMeasureScope;Ljava/util/ArrayList;ILkotlin/jvm/functions/Function3;III)V
-HSPLandroidx/tv/material3/TabRowKt$TabRow$2$2$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/material3/TabRowKt$TabRow$2$2$1$separators$1;-><init>(ILkotlin/jvm/functions/Function2;I)V
-HSPLandroidx/tv/material3/TabRowKt$TabRow$2$2$1$separators$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/material3/TabRowKt$TabRow$2$2$1;-><init>(Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/functions/Function3;)V
-HSPLandroidx/tv/material3/TabRowKt$TabRow$2$2$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/material3/TabRowKt$TabRow$2;-><init>(Landroidx/compose/ui/Modifier;JLandroidx/compose/foundation/ScrollState;Landroidx/compose/runtime/MutableState;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;I)V
-HSPLandroidx/tv/material3/TabRowKt$TabRow$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/material3/TabRowKt$TabRow$3;-><init>(ILandroidx/compose/ui/Modifier;JJLkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;II)V
-HSPLandroidx/tv/material3/TabRowKt$TabRow$3;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/material3/TabRowKt;-><clinit>()V
-HSPLandroidx/tv/material3/TabRowKt;->TabRow-pAZo6Ak(ILandroidx/compose/ui/Modifier;JJLkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V
-HSPLandroidx/tv/material3/TabRowSlots;-><clinit>()V
-HSPLandroidx/tv/material3/TabRowSlots;-><init>(ILjava/lang/String;)V
-HSPLandroidx/tv/material3/TextKt$LocalTextStyle$1;-><clinit>()V
-HSPLandroidx/tv/material3/TextKt$LocalTextStyle$1;-><init>()V
-HSPLandroidx/tv/material3/TextKt$LocalTextStyle$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/tv/material3/TextKt$Text$1;-><clinit>()V
-HSPLandroidx/tv/material3/TextKt$Text$1;-><init>()V
-HSPLandroidx/tv/material3/TextKt$Text$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLandroidx/tv/material3/TextKt$Text$2;-><init>(Ljava/lang/String;Landroidx/compose/ui/Modifier;JJLandroidx/compose/ui/text/font/FontStyle;Landroidx/compose/ui/text/font/FontWeight;Landroidx/compose/ui/text/font/FontFamily;JLandroidx/compose/ui/text/style/TextDecoration;Landroidx/compose/ui/text/style/TextAlign;JIZILkotlin/jvm/functions/Function1;Landroidx/compose/ui/text/TextStyle;III)V
-HSPLandroidx/tv/material3/TextKt;-><clinit>()V
-HSPLandroidx/tv/material3/TextKt;->ProvideTextStyle(Landroidx/compose/ui/text/TextStyle;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;I)V
-HSPLandroidx/tv/material3/TextKt;->Text-fLXpl1I(Ljava/lang/String;Landroidx/compose/ui/Modifier;JJLandroidx/compose/ui/text/font/FontStyle;Landroidx/compose/ui/text/font/FontWeight;Landroidx/compose/ui/text/font/FontFamily;JLandroidx/compose/ui/text/style/TextDecoration;Landroidx/compose/ui/text/style/TextAlign;JIZILkotlin/jvm/functions/Function1;Landroidx/compose/ui/text/TextStyle;Landroidx/compose/runtime/Composer;III)V
-HSPLandroidx/tv/material3/Typography;-><init>(I)V
-HSPLandroidx/tv/material3/TypographyKt$LocalTypography$1;-><clinit>()V
-HSPLandroidx/tv/material3/TypographyKt$LocalTypography$1;-><init>()V
-HSPLandroidx/tv/material3/TypographyKt$LocalTypography$1;->invoke()Ljava/lang/Object;
-HSPLandroidx/tv/material3/TypographyKt;-><clinit>()V
-HSPLandroidx/tv/material3/tokens/ColorDarkTokens;-><clinit>()V
-HSPLandroidx/tv/material3/tokens/PaletteTokens;-><clinit>()V
-HSPLandroidx/tv/material3/tokens/ShapeTokens;-><clinit>()V
-HSPLandroidx/tv/material3/tokens/TypeScaleTokens;-><clinit>()V
-HSPLandroidx/tv/material3/tokens/TypefaceTokens;-><clinit>()V
-HSPLandroidx/tv/material3/tokens/TypographyTokens;-><clinit>()V
-HSPLcoil/base/R$id;->hasSpan(Landroid/text/Spanned;Ljava/lang/Class;)Z
-HSPLcoil/request/ViewTargetDisposable;->findRootCoordinates(Landroidx/compose/ui/node/NodeCoordinator;)Landroidx/compose/ui/layout/LayoutCoordinates;
-HSPLcoil/request/ViewTargetDisposable;->updateChangedFlags(I)I
-HSPLcom/example/tvcomposebasedtests/ComposableSingletons$MainActivityKt$lambda-1$1;-><clinit>()V
-HSPLcom/example/tvcomposebasedtests/ComposableSingletons$MainActivityKt$lambda-1$1;-><init>()V
-HSPLcom/example/tvcomposebasedtests/ComposableSingletons$MainActivityKt$lambda-1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLcom/example/tvcomposebasedtests/ComposableSingletons$MainActivityKt;-><clinit>()V
-HSPLcom/example/tvcomposebasedtests/JankStatsAggregator$listener$1;-><init>(Lcom/example/tvcomposebasedtests/JankStatsAggregator;)V
-HSPLcom/example/tvcomposebasedtests/JankStatsAggregator$listener$1;->onFrame(Landroidx/metrics/performance/FrameData;)V
-HSPLcom/example/tvcomposebasedtests/JankStatsAggregator;-><init>(Landroid/view/Window;Lcom/example/tvcomposebasedtests/MainActivity$jankReportListener$1;)V
-HSPLcom/example/tvcomposebasedtests/MainActivity$jankReportListener$1;-><init>(Lcom/example/tvcomposebasedtests/MainActivity;)V
-HSPLcom/example/tvcomposebasedtests/MainActivity$startFrameMetrics$listener$1;-><init>(Lcom/example/tvcomposebasedtests/MainActivity;)V
-HSPLcom/example/tvcomposebasedtests/MainActivity$startFrameMetrics$listener$1;->onFrameMetricsAvailable(Landroid/view/Window;Landroid/view/FrameMetrics;I)V
-HSPLcom/example/tvcomposebasedtests/MainActivity;-><init>()V
-HSPLcom/example/tvcomposebasedtests/MainActivity;->onCreate(Landroid/os/Bundle;)V
-HSPLcom/example/tvcomposebasedtests/MainActivity;->onResume()V
-HSPLcom/example/tvcomposebasedtests/UtilsKt;-><clinit>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/AppKt$App$1$1$1$1;-><init>(Landroidx/compose/runtime/MutableState;)V
-HSPLcom/example/tvcomposebasedtests/tvComponents/AppKt$App$1$1$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLcom/example/tvcomposebasedtests/tvComponents/AppKt$App$1;-><init>(Landroidx/compose/runtime/MutableState;)V
-HSPLcom/example/tvcomposebasedtests/tvComponents/AppKt$App$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLcom/example/tvcomposebasedtests/tvComponents/AppKt;->App(Landroidx/compose/runtime/Composer;I)V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$LazyContainersKt$lambda-1$1;-><clinit>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$LazyContainersKt$lambda-1$1;-><init>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$LazyContainersKt$lambda-2$1;-><clinit>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$LazyContainersKt$lambda-2$1;-><init>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$LazyContainersKt$lambda-3$1;-><clinit>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$LazyContainersKt$lambda-3$1;-><init>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$LazyContainersKt$lambda-3$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$LazyContainersKt$lambda-4$1;-><clinit>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$LazyContainersKt$lambda-4$1;-><init>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$LazyContainersKt;-><clinit>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-1$1;-><clinit>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-1$1;-><init>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-2$1;-><clinit>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-2$1;-><init>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-3$1;-><clinit>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-3$1;-><init>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-4$1;-><clinit>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-4$1;-><init>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-5$1;-><clinit>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-5$1;-><init>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-6$1;-><clinit>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-6$1;-><init>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-7$1;-><clinit>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-7$1;-><init>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-7$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt;-><clinit>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$SampleCardItem$2;-><init>(II)V
-HSPLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$SampleCardItem$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$SampleTvLazyRow$1$1;-><init>(I)V
-HSPLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$SampleTvLazyRow$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$drawBorderAndScaleOnFocus$1$1;-><init>(Landroidx/compose/runtime/MutableState;)V
-HSPLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$drawBorderAndScaleOnFocus$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt;->SampleCardItem(ILandroidx/compose/runtime/Composer;I)V
-HSPLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt;->SampleTvLazyRow(ILandroidx/compose/runtime/Composer;I)V
-HSPLcom/example/tvcomposebasedtests/tvComponents/Navigation;-><clinit>()V
-HSPLcom/example/tvcomposebasedtests/tvComponents/Navigation;-><init>(Ljava/lang/String;ILjava/lang/String;Landroidx/compose/runtime/internal/ComposableLambdaImpl;)V
-HSPLcom/example/tvcomposebasedtests/tvComponents/Navigation;->values()[Lcom/example/tvcomposebasedtests/tvComponents/Navigation;
-HSPLcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt$PillIndicatorTabRow$1$1$1$1;-><init>(ILkotlin/jvm/functions/Function1;)V
-HSPLcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt$PillIndicatorTabRow$1$1$1$1;->invoke()Ljava/lang/Object;
-HSPLcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt$PillIndicatorTabRow$1$1$2;-><init>(Ljava/lang/String;)V
-HSPLcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt$PillIndicatorTabRow$1$1$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt$PillIndicatorTabRow$1;-><init>(IILjava/util/List;Lkotlin/jvm/functions/Function1;)V
-HSPLcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt$PillIndicatorTabRow$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt$TopNavigation$2$1;-><init>(Landroidx/compose/runtime/MutableState;)V
-HSPLcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt$TopNavigation$2$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt$TopNavigation$3$1;-><init>(Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/MutableState;Lkotlin/coroutines/Continuation;)V
-HSPLcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt$TopNavigation$3$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt$TopNavigation$3$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt$TopNavigation$4;-><init>(IILkotlin/jvm/functions/Function1;)V
-HSPLcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt;->PillIndicatorTabRow(Ljava/util/List;ILkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;I)V
-HSPLcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt;->TopNavigation(Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V
-HSPLkotlin/LazyKt__LazyJVMKt;->lazy(Lkotlin/jvm/functions/Function0;)Lkotlin/Lazy;
-HSPLkotlin/Pair;-><init>(Ljava/lang/Object;Ljava/lang/Object;)V
-HSPLkotlin/Result$Failure;-><init>(Ljava/lang/Throwable;)V
-HSPLkotlin/Result;->exceptionOrNull-impl(Ljava/lang/Object;)Ljava/lang/Throwable;
-HSPLkotlin/ResultKt;->createFailure(Ljava/lang/Throwable;)Lkotlin/Result$Failure;
-HSPLkotlin/ResultKt;->throwOnFailure(Ljava/lang/Object;)V
-HSPLkotlin/SynchronizedLazyImpl;-><init>(Lkotlin/jvm/functions/Function0;)V
-HSPLkotlin/SynchronizedLazyImpl;->getValue()Ljava/lang/Object;
-HSPLkotlin/UNINITIALIZED_VALUE;-><clinit>()V
-HSPLkotlin/UNINITIALIZED_VALUE;-><init>()V
-HSPLkotlin/Unit;-><clinit>()V
-HSPLkotlin/Unit;-><init>()V
-HSPLkotlin/UnsafeLazyImpl;-><init>(Lkotlin/jvm/functions/Function0;)V
-HSPLkotlin/UnsafeLazyImpl;->getValue()Ljava/lang/Object;
-HSPLkotlin/UnsignedKt;->ulongToDouble(J)D
-HSPLkotlin/collections/AbstractCollection;-><init>()V
-HSPLkotlin/collections/AbstractCollection;->isEmpty()Z
-HSPLkotlin/collections/AbstractCollection;->size()I
-HSPLkotlin/collections/AbstractList;-><init>()V
-HSPLkotlin/collections/AbstractList;->equals(Ljava/lang/Object;)Z
-HSPLkotlin/collections/AbstractMap;-><init>()V
-HSPLkotlin/collections/AbstractMap;->entrySet()Ljava/util/Set;
-HSPLkotlin/collections/AbstractMap;->equals(Ljava/lang/Object;)Z
-HSPLkotlin/collections/AbstractMap;->size()I
-HSPLkotlin/collections/AbstractMutableList;-><init>()V
-HSPLkotlin/collections/AbstractMutableMap;-><init>()V
-HSPLkotlin/collections/AbstractSet;-><init>()V
-HSPLkotlin/collections/AbstractSet;->equals(Ljava/lang/Object;)Z
-HSPLkotlin/collections/ArrayDeque;-><clinit>()V
-HSPLkotlin/collections/ArrayDeque;-><init>()V
-HSPLkotlin/collections/ArrayDeque;->addLast(Ljava/lang/Object;)V
-HSPLkotlin/collections/ArrayDeque;->ensureCapacity(I)V
-HSPLkotlin/collections/ArrayDeque;->getSize()I
-HSPLkotlin/collections/ArrayDeque;->incremented(I)I
-HSPLkotlin/collections/ArrayDeque;->isEmpty()Z
-HSPLkotlin/collections/ArrayDeque;->positiveMod(I)I
-HSPLkotlin/collections/ArrayDeque;->removeFirst()Ljava/lang/Object;
-HSPLkotlin/collections/ArraysKt___ArraysKt;->asList([Ljava/lang/Object;)Ljava/util/List;
-HSPLkotlin/collections/ArraysKt___ArraysKt;->copyInto$default([I[III)V
-HSPLkotlin/collections/ArraysKt___ArraysKt;->copyInto$default([Ljava/lang/Object;[Ljava/lang/Object;IIII)V
-HSPLkotlin/collections/ArraysKt___ArraysKt;->copyInto([I[IIII)V
-HSPLkotlin/collections/ArraysKt___ArraysKt;->copyInto([Ljava/lang/Object;[Ljava/lang/Object;III)V
-HSPLkotlin/collections/ArraysKt___ArraysKt;->fill$default([Ljava/lang/Object;Lkotlinx/coroutines/internal/Symbol;)V
-HSPLkotlin/collections/ArraysKt___ArraysKt;->fill(II[Ljava/lang/Object;)V
-HSPLkotlin/collections/ArraysKt___ArraysKt;->indexOf([Ljava/lang/Object;Ljava/lang/Object;)I
-HSPLkotlin/collections/CollectionsKt__CollectionsKt;->getLastIndex(Ljava/util/List;)I
-HSPLkotlin/collections/CollectionsKt__CollectionsKt;->listOf(Ljava/lang/Object;)Ljava/util/List;
-HSPLkotlin/collections/CollectionsKt__CollectionsKt;->listOf([Ljava/lang/Object;)Ljava/util/List;
-HSPLkotlin/collections/CollectionsKt__IteratorsJVMKt;->collectionSizeOrDefault(Ljava/lang/Iterable;)I
-HSPLkotlin/collections/CollectionsKt__MutableCollectionsJVMKt;->sortWith(Ljava/util/List;Ljava/util/Comparator;)V
-HSPLkotlin/collections/CollectionsKt__ReversedViewsKt;->addAll(Ljava/lang/Iterable;Ljava/util/Collection;)V
-HSPLkotlin/collections/CollectionsKt___CollectionsKt;->first(Ljava/util/List;)Ljava/lang/Object;
-HSPLkotlin/collections/CollectionsKt___CollectionsKt;->firstOrNull(Ljava/util/List;)Ljava/lang/Object;
-HSPLkotlin/collections/CollectionsKt___CollectionsKt;->last(Ljava/util/List;)Ljava/lang/Object;
-HSPLkotlin/collections/CollectionsKt___CollectionsKt;->lastOrNull(Ljava/util/List;)Ljava/lang/Object;
-HSPLkotlin/collections/CollectionsKt___CollectionsKt;->plus(Ljava/lang/Iterable;Ljava/util/Collection;)Ljava/util/ArrayList;
-HSPLkotlin/collections/CollectionsKt___CollectionsKt;->toMutableList(Ljava/util/Collection;)Ljava/util/ArrayList;
-HSPLkotlin/collections/EmptyList;-><clinit>()V
-HSPLkotlin/collections/EmptyList;-><init>()V
-HSPLkotlin/collections/EmptyList;->equals(Ljava/lang/Object;)Z
-HSPLkotlin/collections/EmptyList;->isEmpty()Z
-HSPLkotlin/collections/EmptyList;->size()I
-HSPLkotlin/collections/EmptyList;->toArray()[Ljava/lang/Object;
-HSPLkotlin/collections/EmptyMap;-><clinit>()V
-HSPLkotlin/collections/EmptyMap;-><init>()V
-HSPLkotlin/collections/EmptyMap;->isEmpty()Z
-HSPLkotlin/collections/EmptyMap;->size()I
-HSPLkotlin/collections/IntIterator;-><init>()V
-HSPLkotlin/collections/SetsKt__SetsKt;->mapCapacity(I)I
-HSPLkotlin/coroutines/AbstractCoroutineContextElement;-><init>(Lkotlin/coroutines/CoroutineContext$Key;)V
-HSPLkotlin/coroutines/AbstractCoroutineContextElement;->fold(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
-HSPLkotlin/coroutines/AbstractCoroutineContextElement;->get(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
-HSPLkotlin/coroutines/AbstractCoroutineContextElement;->getKey()Lkotlin/coroutines/CoroutineContext$Key;
-HSPLkotlin/coroutines/AbstractCoroutineContextElement;->minusKey(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
-HSPLkotlin/coroutines/AbstractCoroutineContextElement;->plus(Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
-HSPLkotlin/coroutines/AbstractCoroutineContextKey;-><init>(Lkotlin/coroutines/CoroutineContext$Key;Lkotlin/jvm/functions/Function1;)V
-HSPLkotlin/coroutines/CombinedContext;-><init>(Lkotlin/coroutines/CoroutineContext$Element;Lkotlin/coroutines/CoroutineContext;)V
-HSPLkotlin/coroutines/CombinedContext;->fold(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
-HSPLkotlin/coroutines/CombinedContext;->minusKey(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
-HSPLkotlin/coroutines/CombinedContext;->plus(Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
-HSPLkotlin/coroutines/ContinuationInterceptor$Key;-><clinit>()V
-HSPLkotlin/coroutines/ContinuationInterceptor$Key;-><init>()V
-HSPLkotlin/coroutines/ContinuationKt;->access$lowestBitOf(J)I
-HSPLkotlin/coroutines/ContinuationKt;->binarySearch([II)I
-HSPLkotlin/coroutines/CoroutineContext$DefaultImpls;->plus(Lkotlin/coroutines/CoroutineContext;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
-HSPLkotlin/coroutines/CoroutineContext$Element$DefaultImpls;->get(Lkotlin/coroutines/CoroutineContext$Element;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
-HSPLkotlin/coroutines/CoroutineContext$Element$DefaultImpls;->minusKey(Lkotlin/coroutines/CoroutineContext$Element;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
-HSPLkotlin/coroutines/CoroutineContext$plus$1;-><clinit>()V
-HSPLkotlin/coroutines/CoroutineContext$plus$1;-><init>()V
-HSPLkotlin/coroutines/CoroutineContext$plus$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlin/coroutines/EmptyCoroutineContext;-><clinit>()V
-HSPLkotlin/coroutines/EmptyCoroutineContext;-><init>()V
-HSPLkotlin/coroutines/EmptyCoroutineContext;->fold(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
-HSPLkotlin/coroutines/EmptyCoroutineContext;->get(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
-HSPLkotlin/coroutines/EmptyCoroutineContext;->plus(Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
-HSPLkotlin/coroutines/SafeContinuation;-><clinit>()V
-HSPLkotlin/coroutines/SafeContinuation;-><init>(Lkotlin/coroutines/intrinsics/CoroutineSingletons;Lkotlin/coroutines/Continuation;)V
-HSPLkotlin/coroutines/SafeContinuation;->resumeWith(Ljava/lang/Object;)V
-HSPLkotlin/coroutines/intrinsics/CoroutineSingletons;-><clinit>()V
-HSPLkotlin/coroutines/intrinsics/CoroutineSingletons;-><init>(ILjava/lang/String;)V
-HSPLkotlin/coroutines/jvm/internal/BaseContinuationImpl;-><init>(Lkotlin/coroutines/Continuation;)V
-HSPLkotlin/coroutines/jvm/internal/BaseContinuationImpl;->resumeWith(Ljava/lang/Object;)V
-HSPLkotlin/coroutines/jvm/internal/CompletedContinuation;-><clinit>()V
-HSPLkotlin/coroutines/jvm/internal/CompletedContinuation;-><init>()V
-HSPLkotlin/coroutines/jvm/internal/ContinuationImpl;-><init>(Lkotlin/coroutines/Continuation;)V
-HSPLkotlin/coroutines/jvm/internal/ContinuationImpl;-><init>(Lkotlin/coroutines/Continuation;Lkotlin/coroutines/CoroutineContext;)V
-HSPLkotlin/coroutines/jvm/internal/ContinuationImpl;->getContext()Lkotlin/coroutines/CoroutineContext;
-HSPLkotlin/coroutines/jvm/internal/ContinuationImpl;->releaseIntercepted()V
-HSPLkotlin/coroutines/jvm/internal/RestrictedContinuationImpl;-><init>(Lkotlin/coroutines/Continuation;)V
-HSPLkotlin/coroutines/jvm/internal/RestrictedContinuationImpl;->getContext()Lkotlin/coroutines/CoroutineContext;
-HSPLkotlin/coroutines/jvm/internal/RestrictedSuspendLambda;-><init>(Lkotlin/coroutines/Continuation;)V
-HSPLkotlin/coroutines/jvm/internal/SuspendLambda;-><init>(ILkotlin/coroutines/Continuation;)V
-HSPLkotlin/coroutines/jvm/internal/SuspendLambda;->getArity()I
-HSPLkotlin/internal/ProgressionUtilKt;->getProgressionLastElement(III)I
-HSPLkotlin/jvm/JvmClassMappingKt;->Rect-tz77jQw(JJ)Landroidx/compose/ui/geometry/Rect;
-HSPLkotlin/jvm/JvmClassMappingKt;->resolveDefaults(Landroidx/compose/ui/text/TextStyle;Landroidx/compose/ui/unit/LayoutDirection;)Landroidx/compose/ui/text/TextStyle;
-HSPLkotlin/jvm/internal/CallableReference$NoReceiver;-><clinit>()V
-HSPLkotlin/jvm/internal/CallableReference$NoReceiver;-><init>()V
-HSPLkotlin/jvm/internal/CallableReference;-><init>(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Z)V
-HSPLkotlin/jvm/internal/ClassReference;-><clinit>()V
-HSPLkotlin/jvm/internal/ClassReference;-><init>(Ljava/lang/Class;)V
-HSPLkotlin/jvm/internal/ClassReference;->getJClass()Ljava/lang/Class;
-HSPLkotlin/jvm/internal/CollectionToArray;-><clinit>()V
-HSPLkotlin/jvm/internal/CollectionToArray;->toArray(Ljava/util/Collection;)[Ljava/lang/Object;
-HSPLkotlin/jvm/internal/FunctionReference;-><init>(ILjava/lang/Class;Ljava/lang/String;Ljava/lang/String;I)V
-HSPLkotlin/jvm/internal/FunctionReference;->getArity()I
-HSPLkotlin/jvm/internal/FunctionReferenceImpl;-><init>(ILjava/lang/Class;Ljava/lang/String;Ljava/lang/String;I)V
-HSPLkotlin/jvm/internal/Intrinsics$$ExternalSyntheticCheckNotZero0;->m(ILjava/lang/String;)V
-HSPLkotlin/jvm/internal/Intrinsics;->areEqual(Ljava/lang/Object;Ljava/lang/Object;)Z
-HSPLkotlin/jvm/internal/Intrinsics;->checkNotNull(Ljava/lang/Object;)V
-HSPLkotlin/jvm/internal/Intrinsics;->checkNotNull(Ljava/lang/Object;Ljava/lang/String;)V
-HSPLkotlin/jvm/internal/Intrinsics;->checkNotNullExpressionValue(Ljava/lang/Object;Ljava/lang/String;)V
-HSPLkotlin/jvm/internal/Intrinsics;->checkNotNullParameter(Ljava/lang/Object;Ljava/lang/String;)V
-HSPLkotlin/jvm/internal/Intrinsics;->compare(II)I
-HSPLkotlin/jvm/internal/Lambda;-><init>(I)V
-HSPLkotlin/jvm/internal/Lambda;->getArity()I
-HSPLkotlin/jvm/internal/MutablePropertyReference1;-><init>(Ljava/lang/String;Ljava/lang/String;)V
-HSPLkotlin/jvm/internal/MutablePropertyReference1Impl;-><init>(Ljava/lang/String;Ljava/lang/String;)V
-HSPLkotlin/jvm/internal/MutablePropertyReference;-><init>(Ljava/lang/String;Ljava/lang/String;)V
-HSPLkotlin/jvm/internal/PropertyReference;-><init>(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)V
-HSPLkotlin/jvm/internal/Ref$BooleanRef;-><init>()V
-HSPLkotlin/jvm/internal/Ref$FloatRef;-><init>()V
-HSPLkotlin/jvm/internal/Ref$ObjectRef;-><init>()V
-HSPLkotlin/jvm/internal/Reflection;-><clinit>()V
-HSPLkotlin/jvm/internal/ReflectionFactory;-><init>()V
-HSPLkotlin/jvm/internal/TypeIntrinsics;->beforeCheckcastToFunctionOfArity(ILjava/lang/Object;)V
-HSPLkotlin/math/MathKt__MathJVMKt;->roundToInt(F)I
-HSPLkotlin/ranges/IntProgression;-><init>(III)V
-HSPLkotlin/ranges/IntProgression;->iterator()Ljava/util/Iterator;
-HSPLkotlin/ranges/IntProgression;->iterator()Lkotlin/ranges/IntProgressionIterator;
-HSPLkotlin/ranges/IntProgressionIterator;-><init>(III)V
-HSPLkotlin/ranges/IntProgressionIterator;->hasNext()Z
-HSPLkotlin/ranges/IntProgressionIterator;->nextInt()I
-HSPLkotlin/ranges/IntRange;-><clinit>()V
-HSPLkotlin/ranges/IntRange;-><init>(II)V
-HSPLkotlin/ranges/IntRange;->equals(Ljava/lang/Object;)Z
-HSPLkotlin/ranges/IntRange;->isEmpty()Z
-HSPLkotlin/ranges/RangesKt___RangesKt;->coerceIn(DDD)D
-HSPLkotlin/ranges/RangesKt___RangesKt;->coerceIn(FFF)F
-HSPLkotlin/ranges/RangesKt___RangesKt;->coerceIn(III)I
-HSPLkotlin/ranges/RangesKt___RangesKt;->until(II)Lkotlin/ranges/IntRange;
-HSPLkotlin/sequences/ConstrainedOnceSequence;-><init>(Lkotlin/sequences/SequencesKt__SequencesKt$asSequence$$inlined$Sequence$1;)V
-HSPLkotlin/sequences/ConstrainedOnceSequence;->iterator()Ljava/util/Iterator;
-HSPLkotlin/sequences/FilteringSequence$iterator$1;-><init>(Lkotlin/sequences/FilteringSequence;)V
-HSPLkotlin/sequences/FilteringSequence$iterator$1;->calcNext()V
-HSPLkotlin/sequences/FilteringSequence$iterator$1;->hasNext()Z
-HSPLkotlin/sequences/FilteringSequence$iterator$1;->next()Ljava/lang/Object;
-HSPLkotlin/sequences/FilteringSequence;-><init>(Lkotlin/sequences/TransformingSequence;)V
-HSPLkotlin/sequences/GeneratorSequence$iterator$1;-><init>(Lkotlin/sequences/GeneratorSequence;)V
-HSPLkotlin/sequences/GeneratorSequence$iterator$1;->calcNext()V
-HSPLkotlin/sequences/GeneratorSequence$iterator$1;->hasNext()Z
-HSPLkotlin/sequences/GeneratorSequence$iterator$1;->next()Ljava/lang/Object;
-HSPLkotlin/sequences/GeneratorSequence;-><init>(Lkotlin/sequences/SequencesKt__SequencesKt$generateSequence$2;Lkotlin/jvm/functions/Function1;)V
-HSPLkotlin/sequences/GeneratorSequence;->iterator()Ljava/util/Iterator;
-HSPLkotlin/sequences/SequencesKt__SequencesKt$asSequence$$inlined$Sequence$1;-><init>(Ljava/util/Iterator;)V
-HSPLkotlin/sequences/SequencesKt__SequencesKt$asSequence$$inlined$Sequence$1;->iterator()Ljava/util/Iterator;
-HSPLkotlin/sequences/SequencesKt__SequencesKt$generateSequence$2;-><init>(Ljava/lang/Object;)V
-HSPLkotlin/sequences/SequencesKt__SequencesKt$generateSequence$2;->invoke()Ljava/lang/Object;
-HSPLkotlin/sequences/SequencesKt__SequencesKt;->generateSequence(Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence;
-HSPLkotlin/sequences/SequencesKt___SequencesKt$filterNotNull$1;-><clinit>()V
-HSPLkotlin/sequences/SequencesKt___SequencesKt$filterNotNull$1;-><init>()V
-HSPLkotlin/sequences/SequencesKt___SequencesKt$filterNotNull$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlin/sequences/SequencesKt___SequencesKt;->toList(Lkotlin/sequences/Sequence;)Ljava/util/List;
-HSPLkotlin/sequences/TransformingSequence$iterator$1;-><init>(Lkotlin/sequences/TransformingSequence;)V
-HSPLkotlin/sequences/TransformingSequence$iterator$1;->hasNext()Z
-HSPLkotlin/sequences/TransformingSequence$iterator$1;->next()Ljava/lang/Object;
-HSPLkotlin/sequences/TransformingSequence;-><init>(Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)V
-HSPLkotlin/sequences/TransformingSequence;->iterator()Ljava/util/Iterator;
-HSPLkotlin/text/CharsKt__CharKt;->checkRadix(I)V
-HSPLkotlin/text/CharsKt__CharKt;->isWhitespace(C)Z
-HSPLkotlin/text/StringsKt__StringsJVMKt;->isBlank(Ljava/lang/CharSequence;)Z
-HSPLkotlin/text/StringsKt__StringsKt;->endsWith$default(Ljava/lang/CharSequence;Ljava/lang/String;)Z
-HSPLkotlin/text/StringsKt__StringsKt;->getLastIndex(Ljava/lang/CharSequence;)I
-HSPLkotlin/text/StringsKt__StringsKt;->lastIndexOf$default(Ljava/lang/CharSequence;II)I
-HSPLkotlin/text/StringsKt__StringsKt;->substringAfterLast$default(Ljava/lang/String;)Ljava/lang/String;
-HSPLkotlinx/coroutines/AbstractCoroutine;-><init>(Lkotlin/coroutines/CoroutineContext;Z)V
-HSPLkotlinx/coroutines/AbstractCoroutine;->afterResume(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/AbstractCoroutine;->cancellationExceptionMessage()Ljava/lang/String;
-HSPLkotlinx/coroutines/AbstractCoroutine;->getContext()Lkotlin/coroutines/CoroutineContext;
-HSPLkotlinx/coroutines/AbstractCoroutine;->getCoroutineContext()Lkotlin/coroutines/CoroutineContext;
-HSPLkotlinx/coroutines/AbstractCoroutine;->isActive()Z
-HSPLkotlinx/coroutines/AbstractCoroutine;->onCancelled(Ljava/lang/Throwable;Z)V
-HSPLkotlinx/coroutines/AbstractCoroutine;->onCompleted(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/AbstractCoroutine;->onCompletionInternal(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/AbstractCoroutine;->resumeWith(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/AbstractCoroutine;->start$enumunboxing$(ILkotlinx/coroutines/AbstractCoroutine;Lkotlin/jvm/functions/Function2;)V
-HSPLkotlinx/coroutines/Active;-><clinit>()V
-HSPLkotlinx/coroutines/Active;-><init>()V
-HSPLkotlinx/coroutines/BeforeResumeCancelHandler;-><init>()V
-HSPLkotlinx/coroutines/BlockingEventLoop;-><init>(Ljava/lang/Thread;)V
-HSPLkotlinx/coroutines/BuildersKt;->launch$default(Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/AbstractCoroutineContextElement;ILkotlin/jvm/functions/Function2;I)Lkotlinx/coroutines/StandaloneCoroutine;
-HSPLkotlinx/coroutines/BuildersKt;->launch(Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;ILkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/StandaloneCoroutine;
-HSPLkotlinx/coroutines/BuildersKt;->withContext(Lkotlin/coroutines/CoroutineContext$Element;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/CancelHandler;-><init>()V
-HSPLkotlinx/coroutines/CancelHandlerBase;-><init>()V
-HSPLkotlinx/coroutines/CancellableContinuationImpl;-><clinit>()V
-HSPLkotlinx/coroutines/CancellableContinuationImpl;-><init>(ILkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->callCancelHandler(Lkotlinx/coroutines/CancelHandler;Ljava/lang/Throwable;)V
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->cancel(Ljava/lang/Throwable;)Z
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->completeResume()V
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->detachChild$kotlinx_coroutines_core()V
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->dispatchResume(I)V
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->getContinuationCancellationCause(Lkotlinx/coroutines/JobSupport;)Ljava/lang/Throwable;
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->getDelegate$kotlinx_coroutines_core()Lkotlin/coroutines/Continuation;
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->getExceptionalResult$kotlinx_coroutines_core(Ljava/lang/Object;)Ljava/lang/Throwable;
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->getSuccessfulResult$kotlinx_coroutines_core(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->initCancellability()V
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->installParentHandle()Lkotlinx/coroutines/DisposableHandle;
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->invokeOnCancellation(Lkotlin/jvm/functions/Function1;)V
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->isReusable()Z
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->resetStateReusable()Z
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->resumeImpl(Ljava/lang/Object;ILkotlin/jvm/functions/Function1;)V
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->resumeWith(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->resumedState(Lkotlinx/coroutines/NotCompleted;Ljava/lang/Object;ILkotlin/jvm/functions/Function1;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->takeState$kotlinx_coroutines_core()Ljava/lang/Object;
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->tryResume(Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/internal/Symbol;
-HSPLkotlinx/coroutines/CancellableContinuationImpl;->tryResumeImpl(Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/internal/Symbol;
-HSPLkotlinx/coroutines/CancellableContinuationImplKt;-><clinit>()V
-HSPLkotlinx/coroutines/CancellableContinuationKt;->getOrCreateCancellableContinuation(Lkotlin/coroutines/Continuation;)Lkotlinx/coroutines/CancellableContinuationImpl;
-HSPLkotlinx/coroutines/CancelledContinuation;-><clinit>()V
-HSPLkotlinx/coroutines/CancelledContinuation;-><init>(Lkotlin/coroutines/Continuation;Ljava/lang/Throwable;Z)V
-HSPLkotlinx/coroutines/ChildContinuation;-><init>(Lkotlinx/coroutines/CancellableContinuationImpl;)V
-HSPLkotlinx/coroutines/ChildContinuation;->invoke(Ljava/lang/Throwable;)V
-HSPLkotlinx/coroutines/ChildHandleNode;-><init>(Lkotlinx/coroutines/JobSupport;)V
-HSPLkotlinx/coroutines/ChildHandleNode;->childCancelled(Ljava/lang/Throwable;)Z
-HSPLkotlinx/coroutines/CompletedContinuation;-><init>(Ljava/lang/Object;Lkotlinx/coroutines/CancelHandler;Lkotlin/jvm/functions/Function1;Ljava/lang/Object;Ljava/lang/Throwable;)V
-HSPLkotlinx/coroutines/CompletedContinuation;-><init>(Ljava/lang/Object;Lkotlinx/coroutines/CancelHandler;Lkotlin/jvm/functions/Function1;Ljava/lang/Object;Ljava/util/concurrent/CancellationException;I)V
-HSPLkotlinx/coroutines/CompletedExceptionally;-><clinit>()V
-HSPLkotlinx/coroutines/CompletedExceptionally;-><init>(Ljava/lang/Throwable;Z)V
-HSPLkotlinx/coroutines/CompletedExceptionally;->getHandled()Z
-HSPLkotlinx/coroutines/CompletionHandlerBase;-><init>()V
-HSPLkotlinx/coroutines/CompletionStateKt;->recoverResult(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/CoroutineContextKt$hasCopyableElements$1;-><clinit>()V
-HSPLkotlinx/coroutines/CoroutineContextKt$hasCopyableElements$1;-><init>()V
-HSPLkotlinx/coroutines/CoroutineContextKt$hasCopyableElements$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/CoroutineContextKt;->foldCopies(Lkotlin/coroutines/CoroutineContext;Lkotlin/coroutines/CoroutineContext;Z)Lkotlin/coroutines/CoroutineContext;
-HSPLkotlinx/coroutines/CoroutineDispatcher$Key$1;-><clinit>()V
-HSPLkotlinx/coroutines/CoroutineDispatcher$Key$1;-><init>()V
-HSPLkotlinx/coroutines/CoroutineDispatcher$Key;-><init>()V
-HSPLkotlinx/coroutines/CoroutineDispatcher;-><clinit>()V
-HSPLkotlinx/coroutines/CoroutineDispatcher;-><init>()V
-HSPLkotlinx/coroutines/CoroutineDispatcher;->get(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
-HSPLkotlinx/coroutines/CoroutineDispatcher;->interceptContinuation(Lkotlin/coroutines/jvm/internal/ContinuationImpl;)Lkotlinx/coroutines/internal/DispatchedContinuation;
-HSPLkotlinx/coroutines/CoroutineDispatcher;->isDispatchNeeded()Z
-HSPLkotlinx/coroutines/CoroutineDispatcher;->minusKey(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
-HSPLkotlinx/coroutines/CoroutineDispatcher;->releaseInterceptedContinuation(Lkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/CoroutineExceptionHandler$Key;-><clinit>()V
-HSPLkotlinx/coroutines/CoroutineExceptionHandler$Key;-><init>()V
-HSPLkotlinx/coroutines/CoroutineScopeKt;->CoroutineScope(Lkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/internal/ContextScope;
-HSPLkotlinx/coroutines/CoroutineScopeKt;->cancel$default(Lkotlinx/coroutines/CoroutineScope;)V
-HSPLkotlinx/coroutines/CoroutineScopeKt;->coroutineScope(Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/CoroutineScopeKt;->isActive(Lkotlinx/coroutines/CoroutineScope;)Z
-HSPLkotlinx/coroutines/CoroutineScopeKt;->modifierLocalOf(Lkotlin/jvm/functions/Function0;)Landroidx/compose/ui/modifier/ProvidableModifierLocal;
-HSPLkotlinx/coroutines/DefaultExecutor;-><clinit>()V
-HSPLkotlinx/coroutines/DefaultExecutor;-><init>()V
-HSPLkotlinx/coroutines/DefaultExecutorKt;-><clinit>()V
-HSPLkotlinx/coroutines/DispatchedTask;-><init>(I)V
-HSPLkotlinx/coroutines/DispatchedTask;->getExceptionalResult$kotlinx_coroutines_core(Ljava/lang/Object;)Ljava/lang/Throwable;
-HSPLkotlinx/coroutines/DispatchedTask;->getSuccessfulResult$kotlinx_coroutines_core(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/DispatchedTask;->handleFatalException(Ljava/lang/Throwable;Ljava/lang/Throwable;)V
-HSPLkotlinx/coroutines/DispatchedTask;->run()V
-HSPLkotlinx/coroutines/DispatchedTaskKt;->resume(Lkotlinx/coroutines/DispatchedTask;Lkotlin/coroutines/Continuation;Z)V
-HSPLkotlinx/coroutines/Dispatchers;-><clinit>()V
-HSPLkotlinx/coroutines/Empty;-><init>(Z)V
-HSPLkotlinx/coroutines/Empty;->getList()Lkotlinx/coroutines/NodeList;
-HSPLkotlinx/coroutines/Empty;->isActive()Z
-HSPLkotlinx/coroutines/EventLoop;-><init>()V
-HSPLkotlinx/coroutines/EventLoop;->decrementUseCount(Z)V
-HSPLkotlinx/coroutines/EventLoop;->incrementUseCount(Z)V
-HSPLkotlinx/coroutines/EventLoop;->isUnconfinedLoopActive()Z
-HSPLkotlinx/coroutines/EventLoop;->processUnconfinedEvent()Z
-HSPLkotlinx/coroutines/EventLoopImplBase;-><clinit>()V
-HSPLkotlinx/coroutines/EventLoopImplBase;-><init>()V
-HSPLkotlinx/coroutines/EventLoopImplPlatform;-><init>()V
-HSPLkotlinx/coroutines/ExecutorCoroutineDispatcher;-><clinit>()V
-HSPLkotlinx/coroutines/ExecutorCoroutineDispatcher;-><init>()V
-HSPLkotlinx/coroutines/GlobalScope;-><clinit>()V
-HSPLkotlinx/coroutines/GlobalScope;-><init>()V
-HSPLkotlinx/coroutines/GlobalScope;->getCoroutineContext()Lkotlin/coroutines/CoroutineContext;
-HSPLkotlinx/coroutines/InvokeOnCancel;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLkotlinx/coroutines/InvokeOnCancel;->invoke(Ljava/lang/Throwable;)V
-HSPLkotlinx/coroutines/InvokeOnCompletion;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLkotlinx/coroutines/Job$DefaultImpls;->invokeOnCompletion$default(Lkotlinx/coroutines/Job;ZLkotlinx/coroutines/JobNode;I)Lkotlinx/coroutines/DisposableHandle;
-HSPLkotlinx/coroutines/Job$Key;-><clinit>()V
-HSPLkotlinx/coroutines/Job$Key;-><init>()V
-HSPLkotlinx/coroutines/JobCancellationException;-><init>(Ljava/lang/String;Ljava/lang/Throwable;Lkotlinx/coroutines/Job;)V
-HSPLkotlinx/coroutines/JobCancellationException;->equals(Ljava/lang/Object;)Z
-HSPLkotlinx/coroutines/JobCancellationException;->fillInStackTrace()Ljava/lang/Throwable;
-HSPLkotlinx/coroutines/JobCancellingNode;-><init>()V
-HSPLkotlinx/coroutines/JobImpl;-><init>(Lkotlinx/coroutines/Job;)V
-HSPLkotlinx/coroutines/JobImpl;->getHandlesException$kotlinx_coroutines_core()Z
-HSPLkotlinx/coroutines/JobImpl;->getOnCancelComplete$kotlinx_coroutines_core()Z
-HSPLkotlinx/coroutines/JobKt;->access$insertEntryAtIndex([Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/Object;)[Ljava/lang/Object;
-HSPLkotlinx/coroutines/JobKt;->ensureActive(Lkotlin/coroutines/CoroutineContext;)V
-HSPLkotlinx/coroutines/JobKt;->getJob(Lkotlin/coroutines/CoroutineContext;)Lkotlinx/coroutines/Job;
-HSPLkotlinx/coroutines/JobKt;->isActive(Lkotlin/coroutines/CoroutineContext;)Z
-HSPLkotlinx/coroutines/JobNode;-><init>()V
-HSPLkotlinx/coroutines/JobNode;->dispose()V
-HSPLkotlinx/coroutines/JobNode;->getJob()Lkotlinx/coroutines/JobSupport;
-HSPLkotlinx/coroutines/JobNode;->getList()Lkotlinx/coroutines/NodeList;
-HSPLkotlinx/coroutines/JobNode;->isActive()Z
-HSPLkotlinx/coroutines/JobSupport$ChildCompletion;-><init>(Lkotlinx/coroutines/JobSupport;Lkotlinx/coroutines/JobSupport$Finishing;Lkotlinx/coroutines/ChildHandleNode;Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/JobSupport$ChildCompletion;->invoke(Ljava/lang/Throwable;)V
-HSPLkotlinx/coroutines/JobSupport$Finishing;-><init>(Lkotlinx/coroutines/NodeList;Ljava/lang/Throwable;)V
-HSPLkotlinx/coroutines/JobSupport$Finishing;->addExceptionLocked(Ljava/lang/Throwable;)V
-HSPLkotlinx/coroutines/JobSupport$Finishing;->getList()Lkotlinx/coroutines/NodeList;
-HSPLkotlinx/coroutines/JobSupport$Finishing;->getRootCause()Ljava/lang/Throwable;
-HSPLkotlinx/coroutines/JobSupport$Finishing;->isCancelling()Z
-HSPLkotlinx/coroutines/JobSupport$Finishing;->isCompleting()Z
-HSPLkotlinx/coroutines/JobSupport$Finishing;->sealLocked(Ljava/lang/Throwable;)Ljava/util/ArrayList;
-HSPLkotlinx/coroutines/JobSupport$Finishing;->setCompleting()V
-HSPLkotlinx/coroutines/JobSupport$addLastAtomic$$inlined$addLastIf$1;-><init>(Lkotlinx/coroutines/internal/LockFreeLinkedListNode;Lkotlinx/coroutines/JobSupport;Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/JobSupport$addLastAtomic$$inlined$addLastIf$1;->prepare(Ljava/lang/Object;)Lkotlinx/coroutines/internal/Symbol;
-HSPLkotlinx/coroutines/JobSupport;-><clinit>()V
-HSPLkotlinx/coroutines/JobSupport;-><init>(Z)V
-HSPLkotlinx/coroutines/JobSupport;->afterCompletion(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/JobSupport;->attachChild(Lkotlinx/coroutines/JobSupport;)Lkotlinx/coroutines/ChildHandle;
-HSPLkotlinx/coroutines/JobSupport;->cancel(Ljava/util/concurrent/CancellationException;)V
-HSPLkotlinx/coroutines/JobSupport;->cancelImpl$kotlinx_coroutines_core(Ljava/lang/Object;)Z
-HSPLkotlinx/coroutines/JobSupport;->cancelInternal(Ljava/util/concurrent/CancellationException;)V
-HSPLkotlinx/coroutines/JobSupport;->cancelParent(Ljava/lang/Throwable;)Z
-HSPLkotlinx/coroutines/JobSupport;->cancellationExceptionMessage()Ljava/lang/String;
-HSPLkotlinx/coroutines/JobSupport;->childCancelled(Ljava/lang/Throwable;)Z
-HSPLkotlinx/coroutines/JobSupport;->completeStateFinalization(Lkotlinx/coroutines/Incomplete;Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/JobSupport;->createCauseException(Ljava/lang/Object;)Ljava/lang/Throwable;
-HSPLkotlinx/coroutines/JobSupport;->finalizeFinishingState(Lkotlinx/coroutines/JobSupport$Finishing;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/JobSupport;->fold(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/JobSupport;->get(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
-HSPLkotlinx/coroutines/JobSupport;->getCancellationException()Ljava/util/concurrent/CancellationException;
-HSPLkotlinx/coroutines/JobSupport;->getFinalRootCause(Lkotlinx/coroutines/JobSupport$Finishing;Ljava/util/ArrayList;)Ljava/lang/Throwable;
-HSPLkotlinx/coroutines/JobSupport;->getKey()Lkotlin/coroutines/CoroutineContext$Key;
-HSPLkotlinx/coroutines/JobSupport;->getOnCancelComplete$kotlinx_coroutines_core()Z
-HSPLkotlinx/coroutines/JobSupport;->getOrPromoteCancellingList(Lkotlinx/coroutines/Incomplete;)Lkotlinx/coroutines/NodeList;
-HSPLkotlinx/coroutines/JobSupport;->getParentHandle$kotlinx_coroutines_core()Lkotlinx/coroutines/ChildHandle;
-HSPLkotlinx/coroutines/JobSupport;->getState$kotlinx_coroutines_core()Ljava/lang/Object;
-HSPLkotlinx/coroutines/JobSupport;->initParentJob(Lkotlinx/coroutines/Job;)V
-HSPLkotlinx/coroutines/JobSupport;->invokeOnCompletion(Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/DisposableHandle;
-HSPLkotlinx/coroutines/JobSupport;->invokeOnCompletion(ZZLkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/DisposableHandle;
-HSPLkotlinx/coroutines/JobSupport;->isActive()Z
-HSPLkotlinx/coroutines/JobSupport;->isScopedCoroutine()Z
-HSPLkotlinx/coroutines/JobSupport;->makeCompletingOnce$kotlinx_coroutines_core(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/JobSupport;->minusKey(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
-HSPLkotlinx/coroutines/JobSupport;->nextChild(Lkotlinx/coroutines/internal/LockFreeLinkedListNode;)Lkotlinx/coroutines/ChildHandleNode;
-HSPLkotlinx/coroutines/JobSupport;->notifyCancelling(Lkotlinx/coroutines/NodeList;Ljava/lang/Throwable;)V
-HSPLkotlinx/coroutines/JobSupport;->onCompletionInternal(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/JobSupport;->plus(Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
-HSPLkotlinx/coroutines/JobSupport;->promoteSingleToNodeList(Lkotlinx/coroutines/JobNode;)V
-HSPLkotlinx/coroutines/JobSupport;->start()Z
-HSPLkotlinx/coroutines/JobSupport;->startInternal(Ljava/lang/Object;)I
-HSPLkotlinx/coroutines/JobSupport;->tryMakeCompleting(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/JobSupport;->tryWaitForChild(Lkotlinx/coroutines/JobSupport$Finishing;Lkotlinx/coroutines/ChildHandleNode;Ljava/lang/Object;)Z
-HSPLkotlinx/coroutines/JobSupportKt;-><clinit>()V
-HSPLkotlinx/coroutines/JobSupportKt;-><init>()V
-HSPLkotlinx/coroutines/MainCoroutineDispatcher;-><init>()V
-HSPLkotlinx/coroutines/NodeList;-><init>()V
-HSPLkotlinx/coroutines/NodeList;->getList()Lkotlinx/coroutines/NodeList;
-HSPLkotlinx/coroutines/NodeList;->isActive()Z
-HSPLkotlinx/coroutines/NonDisposableHandle;-><clinit>()V
-HSPLkotlinx/coroutines/NonDisposableHandle;-><init>()V
-HSPLkotlinx/coroutines/NonDisposableHandle;->dispose()V
-HSPLkotlinx/coroutines/RemoveOnCancel;-><init>(Lkotlinx/coroutines/internal/LockFreeLinkedListNode;)V
-HSPLkotlinx/coroutines/StandaloneCoroutine;-><init>(Lkotlin/coroutines/CoroutineContext;Z)V
-HSPLkotlinx/coroutines/SupervisorJobImpl;-><init>(Lkotlinx/coroutines/Job;)V
-HSPLkotlinx/coroutines/ThreadLocalEventLoop;-><clinit>()V
-HSPLkotlinx/coroutines/ThreadLocalEventLoop;->getEventLoop$kotlinx_coroutines_core()Lkotlinx/coroutines/EventLoop;
-HSPLkotlinx/coroutines/Unconfined;-><clinit>()V
-HSPLkotlinx/coroutines/Unconfined;-><init>()V
-HSPLkotlinx/coroutines/UndispatchedCoroutine;-><init>(Lkotlin/coroutines/Continuation;Lkotlin/coroutines/CoroutineContext;)V
-HSPLkotlinx/coroutines/UndispatchedMarker;-><clinit>()V
-HSPLkotlinx/coroutines/UndispatchedMarker;-><init>()V
-HSPLkotlinx/coroutines/UndispatchedMarker;->fold(Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/UndispatchedMarker;->get(Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
-HSPLkotlinx/coroutines/UndispatchedMarker;->getKey()Lkotlin/coroutines/CoroutineContext$Key;
-HSPLkotlinx/coroutines/android/AndroidDispatcherFactory;-><init>()V
-HSPLkotlinx/coroutines/android/AndroidDispatcherFactory;->createDispatcher(Ljava/util/List;)Lkotlinx/coroutines/MainCoroutineDispatcher;
-HSPLkotlinx/coroutines/android/HandlerContext;-><init>(Landroid/os/Handler;)V
-HSPLkotlinx/coroutines/android/HandlerContext;-><init>(Landroid/os/Handler;Ljava/lang/String;Z)V
-HSPLkotlinx/coroutines/android/HandlerContext;->dispatch(Lkotlin/coroutines/CoroutineContext;Ljava/lang/Runnable;)V
-HSPLkotlinx/coroutines/android/HandlerContext;->isDispatchNeeded()Z
-HSPLkotlinx/coroutines/android/HandlerDispatcher;-><init>()V
-HSPLkotlinx/coroutines/android/HandlerDispatcherKt;-><clinit>()V
-HSPLkotlinx/coroutines/android/HandlerDispatcherKt;->asHandler(Landroid/os/Looper;)Landroid/os/Handler;
-HSPLkotlinx/coroutines/channels/AbstractChannel$Itr;-><init>(Lkotlinx/coroutines/channels/AbstractChannel;)V
-HSPLkotlinx/coroutines/channels/AbstractChannel$Itr;->hasNext(Lkotlin/coroutines/jvm/internal/ContinuationImpl;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/AbstractChannel$Itr;->next()Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/AbstractChannel$ReceiveElement;-><init>(Lkotlinx/coroutines/CancellableContinuationImpl;I)V
-HSPLkotlinx/coroutines/channels/AbstractChannel$ReceiveElement;->completeResumeReceive(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/channels/AbstractChannel$ReceiveElement;->tryResumeReceive(Ljava/lang/Object;)Lkotlinx/coroutines/internal/Symbol;
-HSPLkotlinx/coroutines/channels/AbstractChannel$ReceiveHasNext;-><init>(Lkotlinx/coroutines/channels/AbstractChannel$Itr;Lkotlinx/coroutines/CancellableContinuationImpl;)V
-HSPLkotlinx/coroutines/channels/AbstractChannel$ReceiveHasNext;->completeResumeReceive(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/channels/AbstractChannel$ReceiveHasNext;->resumeOnCancellationFun(Ljava/lang/Object;)Lkotlin/jvm/functions/Function1;
-HSPLkotlinx/coroutines/channels/AbstractChannel$ReceiveHasNext;->tryResumeReceive(Ljava/lang/Object;)Lkotlinx/coroutines/internal/Symbol;
-HSPLkotlinx/coroutines/channels/AbstractChannel$RemoveReceiveOnCancel;-><init>(Lkotlinx/coroutines/channels/AbstractChannel;Lkotlinx/coroutines/channels/Receive;)V
-HSPLkotlinx/coroutines/channels/AbstractChannel$RemoveReceiveOnCancel;->invoke(Ljava/lang/Throwable;)V
-HSPLkotlinx/coroutines/channels/AbstractChannel$enqueueReceiveInternal$$inlined$addLastIfPrevAndIf$1;-><init>(Lkotlinx/coroutines/internal/LockFreeLinkedListNode;Lkotlinx/coroutines/channels/AbstractChannel;)V
-HSPLkotlinx/coroutines/channels/AbstractChannel$enqueueReceiveInternal$$inlined$addLastIfPrevAndIf$1;->prepare(Ljava/lang/Object;)Lkotlinx/coroutines/internal/Symbol;
-HSPLkotlinx/coroutines/channels/AbstractChannel$receiveCatching$1;-><init>(Lkotlinx/coroutines/channels/AbstractChannel;Lkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/channels/AbstractChannel$receiveCatching$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/AbstractChannel;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLkotlinx/coroutines/channels/AbstractChannel;->enqueueReceiveInternal(Lkotlinx/coroutines/channels/Receive;)Z
-HSPLkotlinx/coroutines/channels/AbstractChannel;->iterator()Lkotlinx/coroutines/channels/ChannelIterator;
-HSPLkotlinx/coroutines/channels/AbstractChannel;->pollInternal()Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/AbstractChannel;->receive(Lkotlin/coroutines/jvm/internal/SuspendLambda;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/AbstractChannel;->receiveCatching-JP2dKIU(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/AbstractChannel;->receiveSuspend(ILkotlin/coroutines/jvm/internal/ContinuationImpl;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/AbstractChannel;->takeFirstReceiveOrPeekClosed()Lkotlinx/coroutines/channels/ReceiveOrClosed;
-HSPLkotlinx/coroutines/channels/AbstractChannel;->tryReceive-PtdJZtk()Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/AbstractChannelKt;-><clinit>()V
-HSPLkotlinx/coroutines/channels/AbstractSendChannel$SendBuffered;-><init>(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/channels/AbstractSendChannel$SendBuffered;->completeResumeSend()V
-HSPLkotlinx/coroutines/channels/AbstractSendChannel$SendBuffered;->getPollResult()Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/AbstractSendChannel$SendBuffered;->tryResumeSend()Lkotlinx/coroutines/internal/Symbol;
-HSPLkotlinx/coroutines/channels/AbstractSendChannel;-><clinit>()V
-HSPLkotlinx/coroutines/channels/AbstractSendChannel;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLkotlinx/coroutines/channels/AbstractSendChannel;->getClosedForSend()Lkotlinx/coroutines/channels/Closed;
-HSPLkotlinx/coroutines/channels/AbstractSendChannel;->offerInternal(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/AbstractSendChannel;->send(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/AbstractSendChannel;->takeFirstReceiveOrPeekClosed()Lkotlinx/coroutines/channels/ReceiveOrClosed;
-HSPLkotlinx/coroutines/channels/AbstractSendChannel;->takeFirstSendOrPeekClosed()Lkotlinx/coroutines/channels/Send;
-HSPLkotlinx/coroutines/channels/AbstractSendChannel;->trySend-JP2dKIU(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/ArrayChannel;-><init>(ILkotlinx/coroutines/channels/BufferOverflow;Lkotlin/jvm/functions/Function1;)V
-HSPLkotlinx/coroutines/channels/ArrayChannel;->enqueueReceiveInternal(Lkotlinx/coroutines/channels/Receive;)Z
-HSPLkotlinx/coroutines/channels/ArrayChannel;->isBufferAlwaysEmpty()Z
-HSPLkotlinx/coroutines/channels/ArrayChannel;->isBufferEmpty()Z
-HSPLkotlinx/coroutines/channels/ArrayChannel;->offerInternal(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/ArrayChannel;->pollInternal()Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/BufferOverflow;-><clinit>()V
-HSPLkotlinx/coroutines/channels/BufferOverflow;-><init>(ILjava/lang/String;)V
-HSPLkotlinx/coroutines/channels/Channel$Factory;-><clinit>()V
-HSPLkotlinx/coroutines/channels/Channel$Factory;-><init>()V
-HSPLkotlinx/coroutines/channels/Channel;-><clinit>()V
-HSPLkotlinx/coroutines/channels/ChannelCoroutine;-><init>(Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/channels/AbstractChannel;)V
-HSPLkotlinx/coroutines/channels/ChannelCoroutine;->receiveCatching-JP2dKIU(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/ChannelCoroutine;->send(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/ChannelKt;->Channel$default(ILkotlinx/coroutines/channels/BufferOverflow;I)Lkotlinx/coroutines/channels/AbstractChannel;
-HSPLkotlinx/coroutines/channels/ChannelKt;->invokeComposable(Landroidx/compose/runtime/Composer;Lkotlin/jvm/functions/Function2;)V
-HSPLkotlinx/coroutines/channels/ChannelResult$Failed;-><init>()V
-HSPLkotlinx/coroutines/channels/ChannelResult;-><clinit>()V
-HSPLkotlinx/coroutines/channels/ChannelResult;-><init>(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/channels/ChannelResult;->getOrThrow-impl(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/channels/ConflatedChannel;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLkotlinx/coroutines/channels/ConflatedChannel;->enqueueReceiveInternal(Lkotlinx/coroutines/channels/Receive;)Z
-HSPLkotlinx/coroutines/channels/ConflatedChannel;->isBufferAlwaysEmpty()Z
-HSPLkotlinx/coroutines/channels/ConflatedChannel;->isBufferEmpty()Z
-HSPLkotlinx/coroutines/channels/ConflatedChannel;->offerInternal(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/ConflatedChannel;->pollInternal()Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/LinkedListChannel;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLkotlinx/coroutines/channels/LinkedListChannel;->isBufferAlwaysEmpty()Z
-HSPLkotlinx/coroutines/channels/LinkedListChannel;->offerInternal(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/ProducerCoroutine;-><init>(Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/channels/AbstractChannel;)V
-HSPLkotlinx/coroutines/channels/Receive;-><init>()V
-HSPLkotlinx/coroutines/channels/Receive;->getOfferResult()Ljava/lang/Object;
-HSPLkotlinx/coroutines/channels/Receive;->resumeOnCancellationFun(Ljava/lang/Object;)Lkotlin/jvm/functions/Function1;
-HSPLkotlinx/coroutines/channels/RendezvousChannel;-><init>(Lkotlin/jvm/functions/Function1;)V
-HSPLkotlinx/coroutines/channels/RendezvousChannel;->isBufferAlwaysEmpty()Z
-HSPLkotlinx/coroutines/channels/Send;-><init>()V
-HSPLkotlinx/coroutines/flow/AbstractFlow$collect$1;-><init>(Lkotlinx/coroutines/flow/AbstractFlow;Lkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/flow/AbstractFlow;-><init>()V
-HSPLkotlinx/coroutines/flow/AbstractFlow;->collect(Lkotlinx/coroutines/flow/FlowCollector;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/DistinctFlowImpl$collect$2$emit$1;-><init>(Lkotlinx/coroutines/flow/DistinctFlowImpl$collect$2;Lkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/flow/DistinctFlowImpl$collect$2;-><init>(Lkotlinx/coroutines/flow/DistinctFlowImpl;Lkotlin/jvm/internal/Ref$ObjectRef;Lkotlinx/coroutines/flow/FlowCollector;)V
-HSPLkotlinx/coroutines/flow/DistinctFlowImpl$collect$2;->emit(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/DistinctFlowImpl;-><init>(Lkotlinx/coroutines/flow/Flow;)V
-HSPLkotlinx/coroutines/flow/DistinctFlowImpl;->collect(Lkotlinx/coroutines/flow/FlowCollector;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/FlowKt;->derivedStateOf(Lkotlin/jvm/functions/Function0;)Landroidx/compose/runtime/DerivedSnapshotState;
-HSPLkotlinx/coroutines/flow/FlowKt;->distinctUntilChanged(Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/flow/Flow;
-HSPLkotlinx/coroutines/flow/FlowKt;->first(Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/FlowKt;->mutableStateOf$default(Ljava/lang/Object;)Landroidx/compose/runtime/ParcelableSnapshotMutableState;
-HSPLkotlinx/coroutines/flow/FlowKt;->mutableStateOf(Ljava/lang/Object;Landroidx/compose/runtime/SnapshotMutationPolicy;)Landroidx/compose/runtime/ParcelableSnapshotMutableState;
-HSPLkotlinx/coroutines/flow/FlowKt;->observeDerivedStateRecalculations(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;)V
-HSPLkotlinx/coroutines/flow/FlowKt;->rememberUpdatedState(Ljava/lang/Object;Landroidx/compose/runtime/Composer;)Landroidx/compose/runtime/MutableState;
-HSPLkotlinx/coroutines/flow/FlowKt;->snapshotFlow(Lkotlin/jvm/functions/Function0;)Lkotlinx/coroutines/flow/SafeFlow;
-HSPLkotlinx/coroutines/flow/FlowKt;->stateIn(Lkotlinx/coroutines/flow/SafeFlow;Lkotlinx/coroutines/internal/ContextScope;Lkotlinx/coroutines/flow/StartedWhileSubscribed;Ljava/lang/Float;)Lkotlinx/coroutines/flow/ReadonlyStateFlow;
-HSPLkotlinx/coroutines/flow/FlowKt__ChannelsKt$emitAllImpl$1;-><init>(Lkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/flow/FlowKt__ChannelsKt$emitAllImpl$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/FlowKt__ChannelsKt;->emitAllImpl$FlowKt__ChannelsKt(Lkotlinx/coroutines/flow/FlowCollector;Lkotlinx/coroutines/channels/ProducerCoroutine;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/FlowKt__DistinctKt$defaultAreEquivalent$1;-><clinit>()V
-HSPLkotlinx/coroutines/flow/FlowKt__DistinctKt$defaultAreEquivalent$1;-><init>()V
-HSPLkotlinx/coroutines/flow/FlowKt__DistinctKt$defaultKeySelector$1;-><clinit>()V
-HSPLkotlinx/coroutines/flow/FlowKt__DistinctKt$defaultKeySelector$1;-><init>()V
-HSPLkotlinx/coroutines/flow/FlowKt__DistinctKt$defaultKeySelector$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/FlowKt__LimitKt$dropWhile$$inlined$unsafeFlow$1;-><init>(Lkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest;Lkotlinx/coroutines/flow/StartedWhileSubscribed$command$2;)V
-HSPLkotlinx/coroutines/flow/FlowKt__LimitKt$dropWhile$$inlined$unsafeFlow$1;->collect(Lkotlinx/coroutines/flow/FlowCollector;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/FlowKt__LimitKt$dropWhile$1$1$emit$1;-><init>(Lkotlinx/coroutines/flow/FlowKt__LimitKt$dropWhile$1$1;Lkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/flow/FlowKt__LimitKt$dropWhile$1$1;-><init>(Lkotlin/jvm/internal/Ref$BooleanRef;Lkotlinx/coroutines/flow/FlowCollector;Lkotlin/jvm/functions/Function2;)V
-HSPLkotlinx/coroutines/flow/FlowKt__LimitKt$dropWhile$1$1;->emit(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/FlowKt__MergeKt$mapLatest$1;-><init>(Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/flow/FlowKt__MergeKt$mapLatest$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/FlowKt__MergeKt$mapLatest$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/FlowKt__MergeKt;-><clinit>()V
-HSPLkotlinx/coroutines/flow/FlowKt__ReduceKt$first$$inlined$collectWhile$2$1;-><init>(Lkotlinx/coroutines/flow/FlowKt__ReduceKt$first$$inlined$collectWhile$2;Lkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/flow/FlowKt__ReduceKt$first$$inlined$collectWhile$2;-><init>(Lkotlin/jvm/functions/Function2;Lkotlin/jvm/internal/Ref$ObjectRef;)V
-HSPLkotlinx/coroutines/flow/FlowKt__ReduceKt$first$$inlined$collectWhile$2;->emit(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/FlowKt__ReduceKt$first$3;-><init>(Lkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/flow/FlowKt__ShareKt$launchSharing$1$2;-><init>(Lkotlinx/coroutines/flow/Flow;Lkotlinx/coroutines/flow/MutableSharedFlow;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/flow/FlowKt__ShareKt$launchSharing$1$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLkotlinx/coroutines/flow/FlowKt__ShareKt$launchSharing$1$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/FlowKt__ShareKt$launchSharing$1$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/FlowKt__ShareKt$launchSharing$1;-><init>(Lkotlinx/coroutines/flow/SharingStarted;Lkotlinx/coroutines/flow/Flow;Lkotlinx/coroutines/flow/MutableSharedFlow;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/flow/FlowKt__ShareKt$launchSharing$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLkotlinx/coroutines/flow/FlowKt__ShareKt$launchSharing$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/FlowKt__ShareKt$launchSharing$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/ReadonlyStateFlow;-><init>(Lkotlinx/coroutines/flow/StateFlowImpl;Lkotlinx/coroutines/StandaloneCoroutine;)V
-HSPLkotlinx/coroutines/flow/ReadonlyStateFlow;->collect(Lkotlinx/coroutines/flow/FlowCollector;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/ReadonlyStateFlow;->getValue()Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/SafeFlow;-><init>(Lkotlin/jvm/functions/Function2;)V
-HSPLkotlinx/coroutines/flow/SharedFlowImpl$collect$1;-><init>(Lkotlinx/coroutines/flow/SharedFlowImpl;Lkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/flow/SharedFlowImpl;-><init>(IILkotlinx/coroutines/channels/BufferOverflow;)V
-HSPLkotlinx/coroutines/flow/SharedFlowImpl;->awaitValue(Lkotlinx/coroutines/flow/SharedFlowSlot;Lkotlinx/coroutines/flow/SharedFlowImpl$collect$1;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/SharedFlowImpl;->cleanupTailLocked()V
-HSPLkotlinx/coroutines/flow/SharedFlowImpl;->collect$suspendImpl(Lkotlinx/coroutines/flow/SharedFlowImpl;Lkotlinx/coroutines/flow/FlowCollector;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/intrinsics/CoroutineSingletons;
-HSPLkotlinx/coroutines/flow/SharedFlowImpl;->collect(Lkotlinx/coroutines/flow/FlowCollector;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/SharedFlowImpl;->createSlot()Lkotlinx/coroutines/flow/internal/AbstractSharedFlowSlot;
-HSPLkotlinx/coroutines/flow/SharedFlowImpl;->createSlotArray()[Lkotlinx/coroutines/flow/internal/AbstractSharedFlowSlot;
-HSPLkotlinx/coroutines/flow/SharedFlowImpl;->dropOldestLocked()V
-HSPLkotlinx/coroutines/flow/SharedFlowImpl;->emit(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/SharedFlowImpl;->enqueueLocked(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/flow/SharedFlowImpl;->findSlotsToResumeLocked([Lkotlin/coroutines/Continuation;)[Lkotlin/coroutines/Continuation;
-HSPLkotlinx/coroutines/flow/SharedFlowImpl;->getHead()J
-HSPLkotlinx/coroutines/flow/SharedFlowImpl;->growBuffer(II[Ljava/lang/Object;)[Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/SharedFlowImpl;->tryEmit(Ljava/lang/Object;)Z
-HSPLkotlinx/coroutines/flow/SharedFlowImpl;->tryEmitLocked(Ljava/lang/Object;)Z
-HSPLkotlinx/coroutines/flow/SharedFlowImpl;->tryPeekLocked(Lkotlinx/coroutines/flow/SharedFlowSlot;)J
-HSPLkotlinx/coroutines/flow/SharedFlowImpl;->tryTakeValue(Lkotlinx/coroutines/flow/SharedFlowSlot;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/SharedFlowImpl;->updateBufferLocked(JJJJ)V
-HSPLkotlinx/coroutines/flow/SharedFlowImpl;->updateCollectorIndexLocked$kotlinx_coroutines_core(J)[Lkotlin/coroutines/Continuation;
-HSPLkotlinx/coroutines/flow/SharedFlowKt;-><clinit>()V
-HSPLkotlinx/coroutines/flow/SharedFlowSlot;-><init>()V
-HSPLkotlinx/coroutines/flow/SharedFlowSlot;->allocateLocked(Lkotlinx/coroutines/flow/internal/AbstractSharedFlow;)Z
-HSPLkotlinx/coroutines/flow/SharingCommand;-><clinit>()V
-HSPLkotlinx/coroutines/flow/SharingCommand;-><init>(ILjava/lang/String;)V
-HSPLkotlinx/coroutines/flow/SharingConfig;-><init>(Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/flow/Flow;)V
-HSPLkotlinx/coroutines/flow/SharingStarted$Companion;-><clinit>()V
-HSPLkotlinx/coroutines/flow/StartedEagerly;-><init>()V
-HSPLkotlinx/coroutines/flow/StartedLazily;-><init>()V
-HSPLkotlinx/coroutines/flow/StartedWhileSubscribed$command$1;-><init>(Lkotlinx/coroutines/flow/StartedWhileSubscribed;Lkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/flow/StartedWhileSubscribed$command$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/StartedWhileSubscribed$command$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/StartedWhileSubscribed$command$2;-><init>(Lkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/flow/StartedWhileSubscribed$command$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLkotlinx/coroutines/flow/StartedWhileSubscribed$command$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/StartedWhileSubscribed$command$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/StartedWhileSubscribed;-><init>(JJ)V
-HSPLkotlinx/coroutines/flow/StartedWhileSubscribed;->command(Lkotlinx/coroutines/flow/StateFlow;)Lkotlinx/coroutines/flow/Flow;
-HSPLkotlinx/coroutines/flow/StartedWhileSubscribed;->equals(Ljava/lang/Object;)Z
-HSPLkotlinx/coroutines/flow/StateFlowImpl$collect$1;-><init>(Lkotlinx/coroutines/flow/StateFlowImpl;Lkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/flow/StateFlowImpl$collect$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/StateFlowImpl;-><init>(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/flow/StateFlowImpl;->collect(Lkotlinx/coroutines/flow/FlowCollector;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/StateFlowImpl;->createSlot()Lkotlinx/coroutines/flow/internal/AbstractSharedFlowSlot;
-HSPLkotlinx/coroutines/flow/StateFlowImpl;->createSlotArray()[Lkotlinx/coroutines/flow/internal/AbstractSharedFlowSlot;
-HSPLkotlinx/coroutines/flow/StateFlowImpl;->getValue()Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/StateFlowImpl;->setValue(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/flow/StateFlowImpl;->updateState(Ljava/lang/Object;Ljava/lang/Object;)Z
-HSPLkotlinx/coroutines/flow/StateFlowKt;-><clinit>()V
-HSPLkotlinx/coroutines/flow/StateFlowSlot;-><clinit>()V
-HSPLkotlinx/coroutines/flow/StateFlowSlot;-><init>()V
-HSPLkotlinx/coroutines/flow/StateFlowSlot;->allocateLocked(Lkotlinx/coroutines/flow/internal/AbstractSharedFlow;)Z
-HSPLkotlinx/coroutines/flow/internal/AbstractSharedFlow;-><init>()V
-HSPLkotlinx/coroutines/flow/internal/AbstractSharedFlow;->allocateSlot()Lkotlinx/coroutines/flow/internal/AbstractSharedFlowSlot;
-HSPLkotlinx/coroutines/flow/internal/AbstractSharedFlow;->getSubscriptionCount()Lkotlinx/coroutines/flow/internal/SubscriptionCountStateFlow;
-HSPLkotlinx/coroutines/flow/internal/AbstractSharedFlowKt;-><clinit>()V
-HSPLkotlinx/coroutines/flow/internal/AbstractSharedFlowSlot;-><init>()V
-HSPLkotlinx/coroutines/flow/internal/ChannelFlow$collect$2;-><init>(Lkotlin/coroutines/Continuation;Lkotlinx/coroutines/flow/FlowCollector;Lkotlinx/coroutines/flow/internal/ChannelFlow;)V
-HSPLkotlinx/coroutines/flow/internal/ChannelFlow$collect$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLkotlinx/coroutines/flow/internal/ChannelFlow$collect$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/internal/ChannelFlow$collect$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/internal/ChannelFlow$collectToFun$1;-><init>(Lkotlinx/coroutines/flow/internal/ChannelFlow;Lkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/flow/internal/ChannelFlow$collectToFun$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLkotlinx/coroutines/flow/internal/ChannelFlow$collectToFun$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/internal/ChannelFlow;-><init>(Lkotlin/coroutines/CoroutineContext;ILkotlinx/coroutines/channels/BufferOverflow;)V
-HSPLkotlinx/coroutines/flow/internal/ChannelFlow;->collect(Lkotlinx/coroutines/flow/FlowCollector;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/internal/ChannelFlow;->fuse(Lkotlin/coroutines/CoroutineContext;ILkotlinx/coroutines/channels/BufferOverflow;)Lkotlinx/coroutines/flow/Flow;
-HSPLkotlinx/coroutines/flow/internal/ChannelFlowOperator;-><init>(ILkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/channels/BufferOverflow;Lkotlinx/coroutines/flow/Flow;)V
-HSPLkotlinx/coroutines/flow/internal/ChannelFlowOperator;->collect(Lkotlinx/coroutines/flow/FlowCollector;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/internal/ChannelFlowOperator;->collectTo(Lkotlinx/coroutines/channels/ProducerScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest$flowCollect$3$1$2;-><init>(Lkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest;Lkotlinx/coroutines/flow/FlowCollector;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest$flowCollect$3$1$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest$flowCollect$3$1$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest$flowCollect$3$1$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest$flowCollect$3$1$emit$1;-><init>(Lkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest$flowCollect$3$1;Lkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest$flowCollect$3$1;-><init>(Lkotlin/jvm/internal/Ref$ObjectRef;Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest;Lkotlinx/coroutines/flow/FlowCollector;)V
-HSPLkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest$flowCollect$3$1;->emit(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest$flowCollect$3;-><init>(Lkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest;Lkotlinx/coroutines/flow/FlowCollector;Lkotlin/coroutines/Continuation;)V
-HSPLkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest$flowCollect$3;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-HSPLkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest$flowCollect$3;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest$flowCollect$3;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest;-><init>(Lkotlin/jvm/functions/Function3;Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/CoroutineContext;ILkotlinx/coroutines/channels/BufferOverflow;)V
-HSPLkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest;->create(Lkotlin/coroutines/CoroutineContext;ILkotlinx/coroutines/channels/BufferOverflow;)Lkotlinx/coroutines/flow/internal/ChannelFlow;
-HSPLkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest;->flowCollect(Lkotlinx/coroutines/flow/FlowCollector;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/internal/NoOpContinuation;-><clinit>()V
-HSPLkotlinx/coroutines/flow/internal/NoOpContinuation;-><init>()V
-HSPLkotlinx/coroutines/flow/internal/NopCollector;-><clinit>()V
-HSPLkotlinx/coroutines/flow/internal/NopCollector;-><init>()V
-HSPLkotlinx/coroutines/flow/internal/NullSurrogateKt;-><clinit>()V
-HSPLkotlinx/coroutines/flow/internal/SafeCollector$collectContextSize$1;-><clinit>()V
-HSPLkotlinx/coroutines/flow/internal/SafeCollector$collectContextSize$1;-><init>()V
-HSPLkotlinx/coroutines/flow/internal/SafeCollector$collectContextSize$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/internal/SafeCollector;-><init>(Lkotlinx/coroutines/flow/FlowCollector;Lkotlin/coroutines/CoroutineContext;)V
-HSPLkotlinx/coroutines/flow/internal/SafeCollector;->emit(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/internal/SafeCollector;->emit(Lkotlin/coroutines/Continuation;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/internal/SafeCollectorKt$emitFun$1;-><clinit>()V
-HSPLkotlinx/coroutines/flow/internal/SafeCollectorKt$emitFun$1;-><init>()V
-HSPLkotlinx/coroutines/flow/internal/SafeCollectorKt$emitFun$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/internal/SafeCollectorKt;-><clinit>()V
-HSPLkotlinx/coroutines/flow/internal/SafeCollector_commonKt$checkContext$result$1;-><init>(Lkotlinx/coroutines/flow/internal/SafeCollector;)V
-HSPLkotlinx/coroutines/flow/internal/SafeCollector_commonKt$checkContext$result$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/internal/SendingCollector;-><init>(Lkotlinx/coroutines/channels/SendChannel;)V
-HSPLkotlinx/coroutines/flow/internal/SendingCollector;->emit(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/flow/internal/SubscriptionCountStateFlow;-><init>(I)V
-HSPLkotlinx/coroutines/internal/AtomicKt;-><clinit>()V
-HSPLkotlinx/coroutines/internal/AtomicOp;-><clinit>()V
-HSPLkotlinx/coroutines/internal/AtomicOp;-><init>()V
-HSPLkotlinx/coroutines/internal/AtomicOp;->perform(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/internal/ContextScope;-><init>(Lkotlin/coroutines/CoroutineContext;)V
-HSPLkotlinx/coroutines/internal/ContextScope;->getCoroutineContext()Lkotlin/coroutines/CoroutineContext;
-HSPLkotlinx/coroutines/internal/DispatchedContinuation;-><clinit>()V
-HSPLkotlinx/coroutines/internal/DispatchedContinuation;-><init>(Lkotlinx/coroutines/CoroutineDispatcher;Lkotlin/coroutines/jvm/internal/ContinuationImpl;)V
-HSPLkotlinx/coroutines/internal/DispatchedContinuation;->claimReusableCancellableContinuation()Lkotlinx/coroutines/CancellableContinuationImpl;
-HSPLkotlinx/coroutines/internal/DispatchedContinuation;->getDelegate$kotlinx_coroutines_core()Lkotlin/coroutines/Continuation;
-HSPLkotlinx/coroutines/internal/DispatchedContinuation;->isReusable()Z
-HSPLkotlinx/coroutines/internal/DispatchedContinuation;->postponeCancellation(Ljava/lang/Throwable;)Z
-HSPLkotlinx/coroutines/internal/DispatchedContinuation;->release()V
-HSPLkotlinx/coroutines/internal/DispatchedContinuation;->resumeWith(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/internal/DispatchedContinuation;->takeState$kotlinx_coroutines_core()Ljava/lang/Object;
-HSPLkotlinx/coroutines/internal/DispatchedContinuation;->tryReleaseClaimedContinuation(Lkotlinx/coroutines/CancellableContinuation;)Ljava/lang/Throwable;
-HSPLkotlinx/coroutines/internal/DispatchedContinuationKt;-><clinit>()V
-HSPLkotlinx/coroutines/internal/DispatchedContinuationKt;->resumeCancellableWith(Lkotlin/coroutines/Continuation;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V
-HSPLkotlinx/coroutines/internal/LimitedDispatcher;-><init>(Lkotlinx/coroutines/scheduling/UnlimitedIoScheduler;I)V
-HSPLkotlinx/coroutines/internal/LockFreeLinkedListHead;-><init>()V
-HSPLkotlinx/coroutines/internal/LockFreeLinkedListHead;->isRemoved()Z
-HSPLkotlinx/coroutines/internal/LockFreeLinkedListNode$CondAddOp;-><init>(Lkotlinx/coroutines/internal/LockFreeLinkedListNode;)V
-HSPLkotlinx/coroutines/internal/LockFreeLinkedListNode$CondAddOp;->complete(Ljava/lang/Object;Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;-><clinit>()V
-HSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;-><init>()V
-HSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;->addNext(Lkotlinx/coroutines/internal/LockFreeLinkedListNode;Lkotlinx/coroutines/internal/LockFreeLinkedListHead;)Z
-HSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;->correctPrev()Lkotlinx/coroutines/internal/LockFreeLinkedListNode;
-HSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;->finishAdd(Lkotlinx/coroutines/internal/LockFreeLinkedListNode;)V
-HSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;->getNext()Ljava/lang/Object;
-HSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;->getNextNode()Lkotlinx/coroutines/internal/LockFreeLinkedListNode;
-HSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;->getPrevNode()Lkotlinx/coroutines/internal/LockFreeLinkedListNode;
-HSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;->isRemoved()Z
-HSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;->remove$1()Z
-HSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;->removeOrNext()Lkotlinx/coroutines/internal/LockFreeLinkedListNode;
-HSPLkotlinx/coroutines/internal/LockFreeLinkedListNode;->tryCondAddNext(Lkotlinx/coroutines/internal/LockFreeLinkedListNode;Lkotlinx/coroutines/internal/LockFreeLinkedListNode;Lkotlinx/coroutines/internal/LockFreeLinkedListNode$CondAddOp;)I
-HSPLkotlinx/coroutines/internal/LockFreeTaskQueue;-><clinit>()V
-HSPLkotlinx/coroutines/internal/LockFreeTaskQueue;-><init>()V
-HSPLkotlinx/coroutines/internal/LockFreeTaskQueueCore;-><clinit>()V
-HSPLkotlinx/coroutines/internal/LockFreeTaskQueueCore;-><init>(IZ)V
-HSPLkotlinx/coroutines/internal/MainDispatcherLoader$$ExternalSyntheticServiceLoad0;->m()Ljava/util/Iterator;
-HSPLkotlinx/coroutines/internal/MainDispatcherLoader;-><clinit>()V
-HSPLkotlinx/coroutines/internal/OpDescriptor;-><init>()V
-HSPLkotlinx/coroutines/internal/Removed;-><init>(Lkotlinx/coroutines/internal/LockFreeLinkedListNode;)V
-HSPLkotlinx/coroutines/internal/ResizableAtomicArray;-><init>(I)V
-HSPLkotlinx/coroutines/internal/ScopeCoroutine;-><init>(Lkotlin/coroutines/Continuation;Lkotlin/coroutines/CoroutineContext;)V
-HSPLkotlinx/coroutines/internal/ScopeCoroutine;->afterCompletion(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/internal/ScopeCoroutine;->afterResume(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/internal/ScopeCoroutine;->isScopedCoroutine()Z
-HSPLkotlinx/coroutines/internal/Symbol;-><init>(Ljava/lang/String;)V
-HSPLkotlinx/coroutines/internal/SystemPropsKt__SystemPropsKt;-><clinit>()V
-HSPLkotlinx/coroutines/internal/ThreadContextKt$countAll$1;-><clinit>()V
-HSPLkotlinx/coroutines/internal/ThreadContextKt$countAll$1;-><init>()V
-HSPLkotlinx/coroutines/internal/ThreadContextKt$countAll$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/internal/ThreadContextKt;-><clinit>()V
-HSPLkotlinx/coroutines/internal/ThreadContextKt;->restoreThreadContext(Lkotlin/coroutines/CoroutineContext;Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/internal/ThreadContextKt;->threadContextElements(Lkotlin/coroutines/CoroutineContext;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/internal/ThreadContextKt;->updateThreadContext(Lkotlin/coroutines/CoroutineContext;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/intrinsics/UndispatchedKt;->startUndispatchedOrReturn(Lkotlinx/coroutines/internal/ScopeCoroutine;Lkotlinx/coroutines/internal/ScopeCoroutine;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/scheduling/CoroutineScheduler;-><clinit>()V
-HSPLkotlinx/coroutines/scheduling/CoroutineScheduler;-><init>(IIJLjava/lang/String;)V
-HSPLkotlinx/coroutines/scheduling/DefaultIoScheduler;-><clinit>()V
-HSPLkotlinx/coroutines/scheduling/DefaultIoScheduler;-><init>()V
-HSPLkotlinx/coroutines/scheduling/DefaultScheduler;-><clinit>()V
-HSPLkotlinx/coroutines/scheduling/DefaultScheduler;-><init>()V
-HSPLkotlinx/coroutines/scheduling/GlobalQueue;-><init>()V
-HSPLkotlinx/coroutines/scheduling/NanoTimeSource;-><clinit>()V
-HSPLkotlinx/coroutines/scheduling/NanoTimeSource;-><init>()V
-HSPLkotlinx/coroutines/scheduling/SchedulerCoroutineDispatcher;-><init>(IIJ)V
-HSPLkotlinx/coroutines/scheduling/SchedulerTimeSource;-><init>()V
-HSPLkotlinx/coroutines/scheduling/Task;-><init>()V
-HSPLkotlinx/coroutines/scheduling/TaskContextImpl;-><init>(I)V
-HSPLkotlinx/coroutines/scheduling/TaskContextImpl;->afterTask()V
-HSPLkotlinx/coroutines/scheduling/TasksKt;-><clinit>()V
-HSPLkotlinx/coroutines/scheduling/UnlimitedIoScheduler;-><clinit>()V
-HSPLkotlinx/coroutines/scheduling/UnlimitedIoScheduler;-><init>()V
-HSPLkotlinx/coroutines/sync/Empty;-><init>(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/sync/MutexImpl$LockCont$tryResumeLockWaiter$1;-><init>(Lkotlinx/coroutines/sync/MutexImpl;Lkotlinx/coroutines/sync/MutexImpl$LockCont;)V
-HSPLkotlinx/coroutines/sync/MutexImpl$LockCont;-><init>(Lkotlinx/coroutines/sync/MutexImpl;Ljava/lang/Object;Lkotlinx/coroutines/CancellableContinuationImpl;)V
-HSPLkotlinx/coroutines/sync/MutexImpl$LockCont;->completeResumeLockWaiter()V
-HSPLkotlinx/coroutines/sync/MutexImpl$LockCont;->tryResumeLockWaiter()Z
-HSPLkotlinx/coroutines/sync/MutexImpl$LockWaiter;-><clinit>()V
-HSPLkotlinx/coroutines/sync/MutexImpl$LockWaiter;-><init>(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/sync/MutexImpl$LockedQueue;-><init>(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/sync/MutexImpl$UnlockOp;-><init>(Lkotlinx/coroutines/sync/MutexImpl$LockedQueue;)V
-HSPLkotlinx/coroutines/sync/MutexImpl$UnlockOp;->complete(Ljava/lang/Object;Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/sync/MutexImpl$UnlockOp;->prepare(Ljava/lang/Object;)Lkotlinx/coroutines/internal/Symbol;
-HSPLkotlinx/coroutines/sync/MutexImpl;-><clinit>()V
-HSPLkotlinx/coroutines/sync/MutexImpl;-><init>(Z)V
-HSPLkotlinx/coroutines/sync/MutexImpl;->lock(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-HSPLkotlinx/coroutines/sync/MutexImpl;->unlock(Ljava/lang/Object;)V
-HSPLkotlinx/coroutines/sync/MutexKt;-><clinit>()V
-Landroidx/activity/ComponentActivity$$ExternalSyntheticLambda1;
-Landroidx/activity/ComponentActivity$$ExternalSyntheticLambda2;
-Landroidx/activity/ComponentActivity$$ExternalSyntheticOutline0;
-Landroidx/activity/ComponentActivity$1;
-Landroidx/activity/ComponentActivity$2;
-Landroidx/activity/ComponentActivity$3;
-Landroidx/activity/ComponentActivity$4;
-Landroidx/activity/ComponentActivity$5;
-Landroidx/activity/ComponentActivity$NonConfigurationInstances;
-Landroidx/activity/ComponentActivity;
-Landroidx/activity/OnBackPressedDispatcher;
-Landroidx/activity/compose/ComponentActivityKt;
-Landroidx/activity/contextaware/ContextAwareHelper;
-Landroidx/activity/contextaware/OnContextAvailableListener;
-Landroidx/activity/result/ActivityResultRegistry;
-Landroidx/arch/core/executor/ArchTaskExecutor;
-Landroidx/arch/core/executor/DefaultTaskExecutor$1;
-Landroidx/arch/core/executor/DefaultTaskExecutor;
-Landroidx/arch/core/executor/TaskExecutor;
-Landroidx/arch/core/internal/FastSafeIterableMap;
-Landroidx/arch/core/internal/SafeIterableMap$AscendingIterator;
-Landroidx/arch/core/internal/SafeIterableMap$Entry;
-Landroidx/arch/core/internal/SafeIterableMap$IteratorWithAdditions;
-Landroidx/arch/core/internal/SafeIterableMap$ListIterator;
-Landroidx/arch/core/internal/SafeIterableMap$SupportRemove;
-Landroidx/arch/core/internal/SafeIterableMap;
-Landroidx/collection/ArraySet;
-Landroidx/collection/ContainerHelpers;
-Landroidx/collection/SimpleArrayMap;
-Landroidx/collection/SparseArrayCompat;
-Landroidx/compose/animation/ColorVectorConverterKt$ColorToVector$1$1;
-Landroidx/compose/animation/ColorVectorConverterKt$ColorToVector$1$2;
-Landroidx/compose/animation/ColorVectorConverterKt;
-Landroidx/compose/animation/FlingCalculator;
-Landroidx/compose/animation/FlingCalculatorKt;
-Landroidx/compose/animation/SingleValueAnimationKt;
-Landroidx/compose/animation/SplineBasedFloatDecayAnimationSpec;
-Landroidx/compose/animation/SplineBasedFloatDecayAnimationSpec_androidKt;
-Landroidx/compose/animation/core/Animatable$runAnimation$2$1;
-Landroidx/compose/animation/core/Animatable$runAnimation$2;
-Landroidx/compose/animation/core/Animatable;
-Landroidx/compose/animation/core/AnimateAsStateKt$animateValueAsState$2;
-Landroidx/compose/animation/core/AnimateAsStateKt$animateValueAsState$3$1;
-Landroidx/compose/animation/core/AnimateAsStateKt$animateValueAsState$3;
-Landroidx/compose/animation/core/AnimateAsStateKt;
-Landroidx/compose/animation/core/Animation;
-Landroidx/compose/animation/core/AnimationEndReason$EnumUnboxingSharedUtility;
-Landroidx/compose/animation/core/AnimationResult;
-Landroidx/compose/animation/core/AnimationScope;
-Landroidx/compose/animation/core/AnimationSpec;
-Landroidx/compose/animation/core/AnimationState;
-Landroidx/compose/animation/core/AnimationVector1D;
-Landroidx/compose/animation/core/AnimationVector4D;
-Landroidx/compose/animation/core/AnimationVector;
-Landroidx/compose/animation/core/Animations;
-Landroidx/compose/animation/core/ComplexDouble;
-Landroidx/compose/animation/core/ComplexDoubleKt;
-Landroidx/compose/animation/core/DecayAnimationSpec;
-Landroidx/compose/animation/core/DecayAnimationSpecImpl;
-Landroidx/compose/animation/core/FiniteAnimationSpec;
-Landroidx/compose/animation/core/FloatAnimationSpec;
-Landroidx/compose/animation/core/FloatDecayAnimationSpec;
-Landroidx/compose/animation/core/FloatSpringSpec;
-Landroidx/compose/animation/core/MutatorMutex$Mutator;
-Landroidx/compose/animation/core/MutatorMutex$mutate$2;
-Landroidx/compose/animation/core/MutatorMutex;
-Landroidx/compose/animation/core/SpringSimulation;
-Landroidx/compose/animation/core/SpringSpec;
-Landroidx/compose/animation/core/SuspendAnimationKt$animate$3;
-Landroidx/compose/animation/core/SuspendAnimationKt$animate$4;
-Landroidx/compose/animation/core/SuspendAnimationKt$animate$6$1;
-Landroidx/compose/animation/core/SuspendAnimationKt$animate$6;
-Landroidx/compose/animation/core/SuspendAnimationKt$animate$7;
-Landroidx/compose/animation/core/SuspendAnimationKt$animate$9;
-Landroidx/compose/animation/core/SuspendAnimationKt$callWithFrameNanos$2;
-Landroidx/compose/animation/core/SuspendAnimationKt;
-Landroidx/compose/animation/core/TargetBasedAnimation;
-Landroidx/compose/animation/core/TwoWayConverter;
-Landroidx/compose/animation/core/TwoWayConverterImpl;
-Landroidx/compose/animation/core/VectorConvertersKt$DpOffsetToVector$1;
-Landroidx/compose/animation/core/VectorConvertersKt$DpOffsetToVector$2;
-Landroidx/compose/animation/core/VectorConvertersKt$DpToVector$1;
-Landroidx/compose/animation/core/VectorConvertersKt$DpToVector$2;
-Landroidx/compose/animation/core/VectorConvertersKt$FloatToVector$1;
-Landroidx/compose/animation/core/VectorConvertersKt$FloatToVector$2;
-Landroidx/compose/animation/core/VectorConvertersKt$IntOffsetToVector$1;
-Landroidx/compose/animation/core/VectorConvertersKt$IntOffsetToVector$2;
-Landroidx/compose/animation/core/VectorConvertersKt$IntSizeToVector$1;
-Landroidx/compose/animation/core/VectorConvertersKt$IntSizeToVector$2;
-Landroidx/compose/animation/core/VectorConvertersKt$IntToVector$1;
-Landroidx/compose/animation/core/VectorConvertersKt$IntToVector$2;
-Landroidx/compose/animation/core/VectorConvertersKt$OffsetToVector$1;
-Landroidx/compose/animation/core/VectorConvertersKt$OffsetToVector$2;
-Landroidx/compose/animation/core/VectorConvertersKt$RectToVector$1;
-Landroidx/compose/animation/core/VectorConvertersKt$RectToVector$2;
-Landroidx/compose/animation/core/VectorConvertersKt$SizeToVector$1;
-Landroidx/compose/animation/core/VectorConvertersKt$SizeToVector$2;
-Landroidx/compose/animation/core/VectorConvertersKt;
-Landroidx/compose/animation/core/VectorizedAnimationSpec;
-Landroidx/compose/animation/core/VectorizedAnimationSpecKt$createSpringAnimations$1;
-Landroidx/compose/animation/core/VectorizedAnimationSpecKt$createSpringAnimations$2;
-Landroidx/compose/animation/core/VectorizedFiniteAnimationSpec;
-Landroidx/compose/animation/core/VectorizedFloatAnimationSpec;
-Landroidx/compose/animation/core/VectorizedSpringSpec;
-Landroidx/compose/animation/core/VisibilityThresholdsKt;
-Landroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect$effectModifier$1$1;
-Landroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect$effectModifier$1;
-Landroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect$onNewSize$1;
-Landroidx/compose/foundation/AndroidEdgeEffectOverscrollEffect;
-Landroidx/compose/foundation/AndroidOverscrollKt$StretchOverscrollNonClippingLayer$1$1;
-Landroidx/compose/foundation/AndroidOverscrollKt$StretchOverscrollNonClippingLayer$1;
-Landroidx/compose/foundation/AndroidOverscrollKt$StretchOverscrollNonClippingLayer$2$1;
-Landroidx/compose/foundation/AndroidOverscrollKt$StretchOverscrollNonClippingLayer$2;
-Landroidx/compose/foundation/AndroidOverscrollKt;
-Landroidx/compose/foundation/Api31Impl$$ExternalSyntheticApiModelOutline1;
-Landroidx/compose/foundation/Api31Impl;
-Landroidx/compose/foundation/Background;
-Landroidx/compose/foundation/BackgroundKt;
-Landroidx/compose/foundation/BorderCache;
-Landroidx/compose/foundation/BorderKt$border$2$1;
-Landroidx/compose/foundation/BorderKt$border$2;
-Landroidx/compose/foundation/BorderKt$drawContentWithoutBorder$1;
-Landroidx/compose/foundation/BorderKt$drawGenericBorder$1;
-Landroidx/compose/foundation/BorderKt$drawRectBorder$1;
-Landroidx/compose/foundation/BorderKt$drawRoundRectBorder$1;
-Landroidx/compose/foundation/BorderKt$drawRoundRectBorder$2;
-Landroidx/compose/foundation/BorderKt;
-Landroidx/compose/foundation/ClickableKt$PressedInteractionSourceDisposableEffect$1$invoke$$inlined$onDispose$1;
-Landroidx/compose/foundation/ClickableKt$PressedInteractionSourceDisposableEffect$1;
-Landroidx/compose/foundation/ClickableKt$clickable$2;
-Landroidx/compose/foundation/ClickableKt$clickable$4$1$1;
-Landroidx/compose/foundation/ClickableKt$clickable$4$delayPressInteraction$1$1;
-Landroidx/compose/foundation/ClickableKt$clickable$4$gesture$1$1$1;
-Landroidx/compose/foundation/ClickableKt$clickable$4$gesture$1$1$2;
-Landroidx/compose/foundation/ClickableKt$clickable$4$gesture$1$1;
-Landroidx/compose/foundation/ClickableKt$clickable$4;
-Landroidx/compose/foundation/ClickableKt$genericClickableWithoutGesture$clickSemantics$1$1;
-Landroidx/compose/foundation/ClickableKt$genericClickableWithoutGesture$clickSemantics$1;
-Landroidx/compose/foundation/ClickableKt$genericClickableWithoutGesture$detectPressAndClickFromKey$1;
-Landroidx/compose/foundation/ClickableKt;
-Landroidx/compose/foundation/Clickable_androidKt$isComposeRootInScrollableContainer$1;
-Landroidx/compose/foundation/Clickable_androidKt;
-Landroidx/compose/foundation/ClipScrollableContainerKt$HorizontalScrollableClipModifier$1;
-Landroidx/compose/foundation/ClipScrollableContainerKt$VerticalScrollableClipModifier$1;
-Landroidx/compose/foundation/ClipScrollableContainerKt;
-Landroidx/compose/foundation/DefaultDebugIndication$DefaultDebugIndicationInstance;
-Landroidx/compose/foundation/DefaultDebugIndication;
-Landroidx/compose/foundation/DrawOverscrollModifier;
-Landroidx/compose/foundation/EdgeEffectCompat;
-Landroidx/compose/foundation/FocusableKt$focusGroup$1;
-Landroidx/compose/foundation/FocusableKt$focusable$2$1$1$invoke$$inlined$onDispose$1;
-Landroidx/compose/foundation/FocusableKt$focusable$2$1$1;
-Landroidx/compose/foundation/FocusableKt$focusable$2$2$invoke$$inlined$onDispose$1;
-Landroidx/compose/foundation/FocusableKt$focusable$2$2;
-Landroidx/compose/foundation/FocusableKt$focusable$2$3$1$invoke$$inlined$onDispose$1;
-Landroidx/compose/foundation/FocusableKt$focusable$2$3$1;
-Landroidx/compose/foundation/FocusableKt$focusable$2$4$1$1;
-Landroidx/compose/foundation/FocusableKt$focusable$2$4$1;
-Landroidx/compose/foundation/FocusableKt$focusable$2$5$1;
-Landroidx/compose/foundation/FocusableKt$focusable$2$5$2;
-Landroidx/compose/foundation/FocusableKt$focusable$2$5;
-Landroidx/compose/foundation/FocusableKt$focusable$2;
-Landroidx/compose/foundation/FocusableKt$focusableInNonTouchMode$2$1;
-Landroidx/compose/foundation/FocusableKt$focusableInNonTouchMode$2;
-Landroidx/compose/foundation/FocusableKt;
-Landroidx/compose/foundation/FocusedBoundsKt$ModifierLocalFocusedBoundsObserver$1;
-Landroidx/compose/foundation/FocusedBoundsKt$onFocusedBoundsChanged$2;
-Landroidx/compose/foundation/FocusedBoundsKt;
-Landroidx/compose/foundation/FocusedBoundsModifier;
-Landroidx/compose/foundation/FocusedBoundsObserverModifier;
-Landroidx/compose/foundation/HoverableKt$hoverable$2$1$1$invoke$$inlined$onDispose$1;
-Landroidx/compose/foundation/HoverableKt$hoverable$2$1$1;
-Landroidx/compose/foundation/HoverableKt$hoverable$2$2$1;
-Landroidx/compose/foundation/HoverableKt$hoverable$2$3$1;
-Landroidx/compose/foundation/HoverableKt$hoverable$2$3;
-Landroidx/compose/foundation/HoverableKt$hoverable$2;
-Landroidx/compose/foundation/ImageKt$Image$2$measure$1;
-Landroidx/compose/foundation/ImageKt$Image$2;
-Landroidx/compose/foundation/ImageKt$Image$3;
-Landroidx/compose/foundation/ImageKt$Image$semantics$1$1;
-Landroidx/compose/foundation/ImageKt;
-Landroidx/compose/foundation/Indication;
-Landroidx/compose/foundation/IndicationInstance;
-Landroidx/compose/foundation/IndicationKt$LocalIndication$1;
-Landroidx/compose/foundation/IndicationKt$indication$2;
-Landroidx/compose/foundation/IndicationKt;
-Landroidx/compose/foundation/IndicationModifier;
-Landroidx/compose/foundation/MutatePriority;
-Landroidx/compose/foundation/MutatorMutex$Mutator;
-Landroidx/compose/foundation/MutatorMutex$mutateWith$2;
-Landroidx/compose/foundation/MutatorMutex;
-Landroidx/compose/foundation/OverscrollConfiguration;
-Landroidx/compose/foundation/OverscrollConfigurationKt$LocalOverscrollConfiguration$1;
-Landroidx/compose/foundation/OverscrollConfigurationKt;
-Landroidx/compose/foundation/OverscrollEffect;
-Landroidx/compose/foundation/ScrollKt$rememberScrollState$1$1;
-Landroidx/compose/foundation/ScrollKt$scroll$2$semantics$1$1;
-Landroidx/compose/foundation/ScrollKt$scroll$2$semantics$1$accessibilityScrollState$1;
-Landroidx/compose/foundation/ScrollKt$scroll$2$semantics$1$accessibilityScrollState$2;
-Landroidx/compose/foundation/ScrollKt$scroll$2$semantics$1;
-Landroidx/compose/foundation/ScrollKt$scroll$2;
-Landroidx/compose/foundation/ScrollState$Companion$Saver$1;
-Landroidx/compose/foundation/ScrollState$Companion$Saver$2;
-Landroidx/compose/foundation/ScrollState$canScrollBackward$2;
-Landroidx/compose/foundation/ScrollState$canScrollForward$2;
-Landroidx/compose/foundation/ScrollState$scrollableState$1;
-Landroidx/compose/foundation/ScrollState;
-Landroidx/compose/foundation/ScrollingLayoutModifier$measure$1;
-Landroidx/compose/foundation/ScrollingLayoutModifier;
-Landroidx/compose/foundation/gestures/AndroidConfig;
-Landroidx/compose/foundation/gestures/BringIntoViewRequestPriorityQueue;
-Landroidx/compose/foundation/gestures/ContentInViewModifier$Request;
-Landroidx/compose/foundation/gestures/ContentInViewModifier$modifier$1;
-Landroidx/compose/foundation/gestures/ContentInViewModifier;
-Landroidx/compose/foundation/gestures/DefaultFlingBehavior;
-Landroidx/compose/foundation/gestures/DefaultScrollableState$scroll$2$1;
-Landroidx/compose/foundation/gestures/DefaultScrollableState$scroll$2;
-Landroidx/compose/foundation/gestures/DefaultScrollableState$scrollScope$1;
-Landroidx/compose/foundation/gestures/DefaultScrollableState;
-Landroidx/compose/foundation/gestures/DragLogic;
-Landroidx/compose/foundation/gestures/DragScope;
-Landroidx/compose/foundation/gestures/DraggableKt$awaitDownAndSlop$1;
-Landroidx/compose/foundation/gestures/DraggableKt$draggable$6;
-Landroidx/compose/foundation/gestures/DraggableKt$draggable$9$1$1$invoke$$inlined$onDispose$1;
-Landroidx/compose/foundation/gestures/DraggableKt$draggable$9$1$1;
-Landroidx/compose/foundation/gestures/DraggableKt$draggable$9$2;
-Landroidx/compose/foundation/gestures/DraggableKt$draggable$9$3$1$1;
-Landroidx/compose/foundation/gestures/DraggableKt$draggable$9$3$1;
-Landroidx/compose/foundation/gestures/DraggableKt$draggable$9$3;
-Landroidx/compose/foundation/gestures/DraggableKt$draggable$9;
-Landroidx/compose/foundation/gestures/DraggableKt;
-Landroidx/compose/foundation/gestures/DraggableState;
-Landroidx/compose/foundation/gestures/FlingBehavior;
-Landroidx/compose/foundation/gestures/ForEachGestureKt$awaitEachGesture$2;
-Landroidx/compose/foundation/gestures/ForEachGestureKt;
-Landroidx/compose/foundation/gestures/ModifierLocalScrollableContainerProvider;
-Landroidx/compose/foundation/gestures/Orientation;
-Landroidx/compose/foundation/gestures/PressGestureScope;
-Landroidx/compose/foundation/gestures/PressGestureScopeImpl$reset$1;
-Landroidx/compose/foundation/gestures/PressGestureScopeImpl;
-Landroidx/compose/foundation/gestures/ScrollConfig;
-Landroidx/compose/foundation/gestures/ScrollDraggableState;
-Landroidx/compose/foundation/gestures/ScrollExtensionsKt$animateScrollBy$1;
-Landroidx/compose/foundation/gestures/ScrollExtensionsKt$animateScrollBy$2$1;
-Landroidx/compose/foundation/gestures/ScrollExtensionsKt$animateScrollBy$2;
-Landroidx/compose/foundation/gestures/ScrollExtensionsKt;
-Landroidx/compose/foundation/gestures/ScrollScope;
-Landroidx/compose/foundation/gestures/ScrollableKt$DefaultScrollMotionDurationScale$1;
-Landroidx/compose/foundation/gestures/ScrollableKt$ModifierLocalScrollableContainer$1;
-Landroidx/compose/foundation/gestures/ScrollableKt$NoOpScrollScope$1;
-Landroidx/compose/foundation/gestures/ScrollableKt$awaitScrollEvent$1;
-Landroidx/compose/foundation/gestures/ScrollableKt$mouseWheelScroll$1$1;
-Landroidx/compose/foundation/gestures/ScrollableKt$mouseWheelScroll$1;
-Landroidx/compose/foundation/gestures/ScrollableKt$pointerScrollable$1;
-Landroidx/compose/foundation/gestures/ScrollableKt$pointerScrollable$2$1;
-Landroidx/compose/foundation/gestures/ScrollableKt$pointerScrollable$3$1;
-Landroidx/compose/foundation/gestures/ScrollableKt$scrollable$2;
-Landroidx/compose/foundation/gestures/ScrollableKt$scrollableNestedScrollConnection$1;
-Landroidx/compose/foundation/gestures/ScrollableKt;
-Landroidx/compose/foundation/gestures/ScrollableState;
-Landroidx/compose/foundation/gestures/ScrollingLogic;
-Landroidx/compose/foundation/gestures/TapGestureDetectorKt$NoPressGesture$1;
-Landroidx/compose/foundation/gestures/TapGestureDetectorKt$awaitFirstDown$2;
-Landroidx/compose/foundation/gestures/TapGestureDetectorKt$detectTapAndPress$2$1$1;
-Landroidx/compose/foundation/gestures/TapGestureDetectorKt$detectTapAndPress$2$1;
-Landroidx/compose/foundation/gestures/TapGestureDetectorKt$detectTapAndPress$2;
-Landroidx/compose/foundation/gestures/TapGestureDetectorKt;
-Landroidx/compose/foundation/gestures/UpdatableAnimationState;
-Landroidx/compose/foundation/interaction/FocusInteraction$Focus;
-Landroidx/compose/foundation/interaction/FocusInteraction$Unfocus;
-Landroidx/compose/foundation/interaction/FocusInteractionKt$collectIsFocusedAsState$1$1$1;
-Landroidx/compose/foundation/interaction/FocusInteractionKt$collectIsFocusedAsState$1$1;
-Landroidx/compose/foundation/interaction/HoverInteractionKt$collectIsHoveredAsState$1$1$1;
-Landroidx/compose/foundation/interaction/HoverInteractionKt$collectIsHoveredAsState$1$1;
-Landroidx/compose/foundation/interaction/Interaction;
-Landroidx/compose/foundation/interaction/InteractionSource;
-Landroidx/compose/foundation/interaction/MutableInteractionSource;
-Landroidx/compose/foundation/interaction/MutableInteractionSourceImpl;
-Landroidx/compose/foundation/interaction/PressInteractionKt$collectIsPressedAsState$1$1$1;
-Landroidx/compose/foundation/interaction/PressInteractionKt$collectIsPressedAsState$1$1;
-Landroidx/compose/foundation/layout/Arrangement$Bottom$1;
-Landroidx/compose/foundation/layout/Arrangement$Center$1;
-Landroidx/compose/foundation/layout/Arrangement$End$1;
-Landroidx/compose/foundation/layout/Arrangement$Horizontal;
-Landroidx/compose/foundation/layout/Arrangement$HorizontalOrVertical;
-Landroidx/compose/foundation/layout/Arrangement$SpaceAround$1;
-Landroidx/compose/foundation/layout/Arrangement$SpaceBetween$1;
-Landroidx/compose/foundation/layout/Arrangement$SpaceEvenly$1;
-Landroidx/compose/foundation/layout/Arrangement$SpacedAligned;
-Landroidx/compose/foundation/layout/Arrangement$Start$1;
-Landroidx/compose/foundation/layout/Arrangement$Top$1;
-Landroidx/compose/foundation/layout/Arrangement$Vertical;
-Landroidx/compose/foundation/layout/Arrangement$spacedBy$1;
-Landroidx/compose/foundation/layout/Arrangement;
-Landroidx/compose/foundation/layout/BoxChildData;
-Landroidx/compose/foundation/layout/BoxKt$EmptyBoxMeasurePolicy$1$measure$1;
-Landroidx/compose/foundation/layout/BoxKt$EmptyBoxMeasurePolicy$1;
-Landroidx/compose/foundation/layout/BoxKt$boxMeasurePolicy$1$measure$2;
-Landroidx/compose/foundation/layout/BoxKt$boxMeasurePolicy$1;
-Landroidx/compose/foundation/layout/BoxKt;
-Landroidx/compose/foundation/layout/ColumnKt$DefaultColumnMeasurePolicy$1;
-Landroidx/compose/foundation/layout/ColumnKt$columnMeasurePolicy$1$1;
-Landroidx/compose/foundation/layout/ColumnKt;
-Landroidx/compose/foundation/layout/CrossAxisAlignment$CenterCrossAxisAlignment;
-Landroidx/compose/foundation/layout/CrossAxisAlignment$EndCrossAxisAlignment;
-Landroidx/compose/foundation/layout/CrossAxisAlignment$HorizontalCrossAxisAlignment;
-Landroidx/compose/foundation/layout/CrossAxisAlignment$StartCrossAxisAlignment;
-Landroidx/compose/foundation/layout/CrossAxisAlignment$VerticalCrossAxisAlignment;
-Landroidx/compose/foundation/layout/CrossAxisAlignment;
-Landroidx/compose/foundation/layout/FillModifier$measure$1;
-Landroidx/compose/foundation/layout/FillModifier;
-Landroidx/compose/foundation/layout/HorizontalAlignModifier;
-Landroidx/compose/foundation/layout/IntrinsicMeasureBlocks$HorizontalMaxHeight$1$1;
-Landroidx/compose/foundation/layout/IntrinsicMeasureBlocks$HorizontalMaxHeight$1$2;
-Landroidx/compose/foundation/layout/IntrinsicMeasureBlocks$HorizontalMaxHeight$1;
-Landroidx/compose/foundation/layout/OffsetModifier$measure$1;
-Landroidx/compose/foundation/layout/OffsetModifier;
-Landroidx/compose/foundation/layout/OrientationIndependentConstraints$$ExternalSyntheticOutline0;
-Landroidx/compose/foundation/layout/PaddingKt;
-Landroidx/compose/foundation/layout/PaddingModifier$measure$1;
-Landroidx/compose/foundation/layout/PaddingModifier;
-Landroidx/compose/foundation/layout/PaddingValues;
-Landroidx/compose/foundation/layout/PaddingValuesImpl;
-Landroidx/compose/foundation/layout/RowColumnImplKt$rowColumnMeasurePolicy$1$measure$1;
-Landroidx/compose/foundation/layout/RowColumnImplKt$rowColumnMeasurePolicy$1;
-Landroidx/compose/foundation/layout/RowColumnImplKt;
-Landroidx/compose/foundation/layout/RowColumnMeasureHelperResult;
-Landroidx/compose/foundation/layout/RowColumnMeasurementHelper;
-Landroidx/compose/foundation/layout/RowColumnParentData;
-Landroidx/compose/foundation/layout/RowKt$DefaultRowMeasurePolicy$1;
-Landroidx/compose/foundation/layout/RowKt$rowMeasurePolicy$1$1;
-Landroidx/compose/foundation/layout/RowKt;
-Landroidx/compose/foundation/layout/RowScope;
-Landroidx/compose/foundation/layout/RowScopeInstance;
-Landroidx/compose/foundation/layout/SizeKt$createFillSizeModifier$1;
-Landroidx/compose/foundation/layout/SizeKt$createFillWidthModifier$1;
-Landroidx/compose/foundation/layout/SizeKt$createWrapContentSizeModifier$1;
-Landroidx/compose/foundation/layout/SizeKt$createWrapContentSizeModifier$2;
-Landroidx/compose/foundation/layout/SizeKt;
-Landroidx/compose/foundation/layout/SizeModifier$measure$1;
-Landroidx/compose/foundation/layout/SizeModifier;
-Landroidx/compose/foundation/layout/SpacerKt;
-Landroidx/compose/foundation/layout/SpacerMeasurePolicy$measure$1$1;
-Landroidx/compose/foundation/layout/SpacerMeasurePolicy;
-Landroidx/compose/foundation/layout/WrapContentModifier$measure$1;
-Landroidx/compose/foundation/layout/WrapContentModifier;
-Landroidx/compose/foundation/lazy/layout/DefaultDelegatingLazyLayoutItemProvider$Item$1;
-Landroidx/compose/foundation/lazy/layout/DefaultDelegatingLazyLayoutItemProvider;
-Landroidx/compose/foundation/lazy/layout/DefaultLazyKey$Companion$CREATOR$1;
-Landroidx/compose/foundation/lazy/layout/DefaultLazyKey;
-Landroidx/compose/foundation/lazy/layout/DefaultLazyLayoutItemsProvider$generateKeyToIndexMap$1$1;
-Landroidx/compose/foundation/lazy/layout/DefaultLazyLayoutItemsProvider;
-Landroidx/compose/foundation/lazy/layout/IntervalList$Interval;
-Landroidx/compose/foundation/lazy/layout/IntervalList;
-Landroidx/compose/foundation/lazy/layout/IntervalListKt;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutIntervalContent;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory$CachedItemContent$createContentLambda$1$1;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory$CachedItemContent$createContentLambda$1$2$invoke$$inlined$onDispose$1;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory$CachedItemContent$createContentLambda$1$2;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory$CachedItemContent$createContentLambda$1;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory$CachedItemContent;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutItemProvider;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutItemReusePolicy;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutKt$LazyLayout$1$2$1;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutKt$LazyLayout$1$itemContentFactory$1$1;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutKt$LazyLayout$1;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutKt;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutMeasureScope;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutMeasureScopeImpl;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState$PrefetchHandle;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState$Prefetcher;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutPrefetcher$PrefetchRequest;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutPrefetcher;
-Landroidx/compose/foundation/lazy/layout/LazyLayoutPrefetcher_androidKt;
-Landroidx/compose/foundation/lazy/layout/LazyNearestItemsRangeKt$rememberLazyNearestItemsRangeState$1$1$1;
-Landroidx/compose/foundation/lazy/layout/LazyNearestItemsRangeKt$rememberLazyNearestItemsRangeState$1$1$2;
-Landroidx/compose/foundation/lazy/layout/LazyNearestItemsRangeKt$rememberLazyNearestItemsRangeState$1$1;
-Landroidx/compose/foundation/lazy/layout/LazyNearestItemsRangeKt;
-Landroidx/compose/foundation/lazy/layout/LazySaveableStateHolder$1;
-Landroidx/compose/foundation/lazy/layout/LazySaveableStateHolder$Companion$saver$1;
-Landroidx/compose/foundation/lazy/layout/LazySaveableStateHolder$Companion$saver$2;
-Landroidx/compose/foundation/lazy/layout/LazySaveableStateHolder$SaveableStateProvider$1$invoke$$inlined$onDispose$1;
-Landroidx/compose/foundation/lazy/layout/LazySaveableStateHolder$SaveableStateProvider$1;
-Landroidx/compose/foundation/lazy/layout/LazySaveableStateHolder$SaveableStateProvider$2;
-Landroidx/compose/foundation/lazy/layout/LazySaveableStateHolder;
-Landroidx/compose/foundation/lazy/layout/LazySaveableStateHolderKt$LazySaveableStateHolderProvider$1;
-Landroidx/compose/foundation/lazy/layout/LazySaveableStateHolderKt$LazySaveableStateHolderProvider$holder$1;
-Landroidx/compose/foundation/lazy/layout/LazySaveableStateHolderKt;
-Landroidx/compose/foundation/lazy/layout/MutableIntervalList;
-Landroidx/compose/foundation/relocation/AndroidBringIntoViewParent;
-Landroidx/compose/foundation/relocation/BringIntoViewChildModifier;
-Landroidx/compose/foundation/relocation/BringIntoViewKt$ModifierLocalBringIntoViewParent$1;
-Landroidx/compose/foundation/relocation/BringIntoViewKt;
-Landroidx/compose/foundation/relocation/BringIntoViewParent;
-Landroidx/compose/foundation/relocation/BringIntoViewRequester;
-Landroidx/compose/foundation/relocation/BringIntoViewRequesterImpl$bringIntoView$1;
-Landroidx/compose/foundation/relocation/BringIntoViewRequesterImpl;
-Landroidx/compose/foundation/relocation/BringIntoViewRequesterKt$bringIntoViewRequester$2$1$invoke$$inlined$onDispose$1;
-Landroidx/compose/foundation/relocation/BringIntoViewRequesterKt$bringIntoViewRequester$2$1;
-Landroidx/compose/foundation/relocation/BringIntoViewRequesterKt$bringIntoViewRequester$2;
-Landroidx/compose/foundation/relocation/BringIntoViewRequesterModifier$bringIntoView$2;
-Landroidx/compose/foundation/relocation/BringIntoViewRequesterModifier;
-Landroidx/compose/foundation/relocation/BringIntoViewResponder;
-Landroidx/compose/foundation/relocation/BringIntoViewResponderKt$bringIntoViewResponder$2;
-Landroidx/compose/foundation/relocation/BringIntoViewResponderKt;
-Landroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$2$1$1;
-Landroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$2$1;
-Landroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$2$2;
-Landroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$2;
-Landroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$parentRect$1;
-Landroidx/compose/foundation/relocation/BringIntoViewResponderModifier;
-Landroidx/compose/foundation/relocation/BringIntoViewResponder_androidKt;
-Landroidx/compose/foundation/selection/SelectableGroupKt$selectableGroup$1;
-Landroidx/compose/foundation/shape/CornerBasedShape;
-Landroidx/compose/foundation/shape/CornerSize;
-Landroidx/compose/foundation/shape/DpCornerSize;
-Landroidx/compose/foundation/shape/PercentCornerSize;
-Landroidx/compose/foundation/shape/RoundedCornerShape;
-Landroidx/compose/foundation/shape/RoundedCornerShapeKt;
-Landroidx/compose/foundation/text/BasicTextKt$BasicText$1;
-Landroidx/compose/foundation/text/BasicTextKt$BasicText$2;
-Landroidx/compose/foundation/text/BasicTextKt$BasicText$selectableId$1;
-Landroidx/compose/foundation/text/BasicTextKt$BasicText-4YKlhWE$$inlined$Layout$1;
-Landroidx/compose/foundation/text/BasicTextKt$selectionIdSaver$1;
-Landroidx/compose/foundation/text/BasicTextKt$selectionIdSaver$2;
-Landroidx/compose/foundation/text/BasicTextKt;
-Landroidx/compose/foundation/text/HeightInLinesModifierKt$heightInLines$2;
-Landroidx/compose/foundation/text/HeightInLinesModifierKt;
-Landroidx/compose/foundation/text/TextController$coreModifiers$1;
-Landroidx/compose/foundation/text/TextController$createSemanticsModifierFor$1$1;
-Landroidx/compose/foundation/text/TextController$createSemanticsModifierFor$1;
-Landroidx/compose/foundation/text/TextController$drawTextAndSelectionBehind$1;
-Landroidx/compose/foundation/text/TextController$measurePolicy$1$measure$2;
-Landroidx/compose/foundation/text/TextController$measurePolicy$1;
-Landroidx/compose/foundation/text/TextController$update$1;
-Landroidx/compose/foundation/text/TextController$update$2;
-Landroidx/compose/foundation/text/TextController;
-Landroidx/compose/foundation/text/TextDelegate;
-Landroidx/compose/foundation/text/TextDelegateKt;
-Landroidx/compose/foundation/text/TextDragObserver;
-Landroidx/compose/foundation/text/TextState$onTextLayout$1;
-Landroidx/compose/foundation/text/TextState;
-Landroidx/compose/foundation/text/selection/SelectionRegistrar;
-Landroidx/compose/foundation/text/selection/SelectionRegistrarKt$LocalSelectionRegistrar$1;
-Landroidx/compose/foundation/text/selection/SelectionRegistrarKt;
-Landroidx/compose/foundation/text/selection/TextSelectionColors;
-Landroidx/compose/foundation/text/selection/TextSelectionColorsKt$LocalTextSelectionColors$1;
-Landroidx/compose/foundation/text/selection/TextSelectionColorsKt;
-Landroidx/compose/material3/ColorScheme$$ExternalSyntheticOutline0;
-Landroidx/compose/material3/TextKt$LocalTextStyle$1;
-Landroidx/compose/material3/TextKt$Text$1;
-Landroidx/compose/material3/TextKt;
-Landroidx/compose/runtime/AbstractApplier;
-Landroidx/compose/runtime/ActualAndroid_androidKt$DefaultMonotonicFrameClock$2;
-Landroidx/compose/runtime/ActualAndroid_androidKt;
-Landroidx/compose/runtime/Anchor;
-Landroidx/compose/runtime/Applier;
-Landroidx/compose/runtime/BroadcastFrameClock$FrameAwaiter;
-Landroidx/compose/runtime/BroadcastFrameClock$withFrameNanos$2$1;
-Landroidx/compose/runtime/BroadcastFrameClock;
-Landroidx/compose/runtime/ComposableSingletons$CompositionKt$lambda-1$1;
-Landroidx/compose/runtime/ComposableSingletons$CompositionKt$lambda-2$1;
-Landroidx/compose/runtime/ComposableSingletons$CompositionKt;
-Landroidx/compose/runtime/ComposablesKt;
-Landroidx/compose/runtime/Composer$Companion$Empty$1;
-Landroidx/compose/runtime/Composer$Companion;
-Landroidx/compose/runtime/Composer;
-Landroidx/compose/runtime/ComposerImpl$CompositionContextHolder;
-Landroidx/compose/runtime/ComposerImpl$CompositionContextImpl;
-Landroidx/compose/runtime/ComposerImpl$apply$operation$1$$ExternalSyntheticOutline0;
-Landroidx/compose/runtime/ComposerImpl$apply$operation$1;
-Landroidx/compose/runtime/ComposerImpl$createNode$2;
-Landroidx/compose/runtime/ComposerImpl$createNode$3;
-Landroidx/compose/runtime/ComposerImpl$deactivateToEndGroup$2$1;
-Landroidx/compose/runtime/ComposerImpl$deactivateToEndGroup$2$2;
-Landroidx/compose/runtime/ComposerImpl$doCompose$2$3;
-Landroidx/compose/runtime/ComposerImpl$doCompose$2$4;
-Landroidx/compose/runtime/ComposerImpl$doCompose$2$5;
-Landroidx/compose/runtime/ComposerImpl$doCompose$lambda$37$$inlined$sortBy$1;
-Landroidx/compose/runtime/ComposerImpl$endRestartGroup$1$1;
-Landroidx/compose/runtime/ComposerImpl$realizeDowns$1;
-Landroidx/compose/runtime/ComposerImpl$realizeMovement$1;
-Landroidx/compose/runtime/ComposerImpl$realizeMovement$2;
-Landroidx/compose/runtime/ComposerImpl$realizeOperationLocation$2;
-Landroidx/compose/runtime/ComposerImpl$realizeUps$1;
-Landroidx/compose/runtime/ComposerImpl$recordInsert$1;
-Landroidx/compose/runtime/ComposerImpl$recordInsert$2;
-Landroidx/compose/runtime/ComposerImpl$recordSideEffect$1;
-Landroidx/compose/runtime/ComposerImpl$recordSlotEditing$1;
-Landroidx/compose/runtime/ComposerImpl$start$2;
-Landroidx/compose/runtime/ComposerImpl$startProviders$currentProviders$1;
-Landroidx/compose/runtime/ComposerImpl$startReaderGroup$1;
-Landroidx/compose/runtime/ComposerImpl$updateValue$1;
-Landroidx/compose/runtime/ComposerImpl$updateValue$2;
-Landroidx/compose/runtime/ComposerImpl;
-Landroidx/compose/runtime/ComposerKt$endGroupInstance$1;
-Landroidx/compose/runtime/ComposerKt$removeCurrentGroupInstance$1;
-Landroidx/compose/runtime/ComposerKt$startRootGroup$1;
-Landroidx/compose/runtime/ComposerKt;
-Landroidx/compose/runtime/Composition;
-Landroidx/compose/runtime/CompositionContext;
-Landroidx/compose/runtime/CompositionContextKt;
-Landroidx/compose/runtime/CompositionImpl$RememberEventDispatcher;
-Landroidx/compose/runtime/CompositionImpl;
-Landroidx/compose/runtime/CompositionKt;
-Landroidx/compose/runtime/CompositionLocal;
-Landroidx/compose/runtime/CompositionLocalKt;
-Landroidx/compose/runtime/CompositionScopedCoroutineScopeCanceller;
-Landroidx/compose/runtime/ControlledComposition;
-Landroidx/compose/runtime/DerivedSnapshotState$ResultRecord;
-Landroidx/compose/runtime/DerivedSnapshotState$currentRecord$result$1$result$1;
-Landroidx/compose/runtime/DerivedSnapshotState;
-Landroidx/compose/runtime/DerivedState;
-Landroidx/compose/runtime/DisposableEffectImpl;
-Landroidx/compose/runtime/DisposableEffectResult;
-Landroidx/compose/runtime/DisposableEffectScope;
-Landroidx/compose/runtime/DynamicProvidableCompositionLocal;
-Landroidx/compose/runtime/EffectsKt;
-Landroidx/compose/runtime/GroupInfo;
-Landroidx/compose/runtime/IntStack;
-Landroidx/compose/runtime/Invalidation;
-Landroidx/compose/runtime/JoinedKey;
-Landroidx/compose/runtime/KeyInfo;
-Landroidx/compose/runtime/Latch;
-Landroidx/compose/runtime/LaunchedEffectImpl;
-Landroidx/compose/runtime/LazyValueHolder;
-Landroidx/compose/runtime/MonotonicFrameClock$Key;
-Landroidx/compose/runtime/MonotonicFrameClock;
-Landroidx/compose/runtime/MonotonicFrameClockKt;
-Landroidx/compose/runtime/MovableContent;
-Landroidx/compose/runtime/MovableContentState;
-Landroidx/compose/runtime/MovableContentStateReference;
-Landroidx/compose/runtime/MutableState;
-Landroidx/compose/runtime/NeverEqualPolicy;
-Landroidx/compose/runtime/OpaqueKey;
-Landroidx/compose/runtime/ParcelableSnapshotMutableState$Companion$CREATOR$1;
-Landroidx/compose/runtime/ParcelableSnapshotMutableState;
-Landroidx/compose/runtime/PausableMonotonicFrameClock$withFrameNanos$1;
-Landroidx/compose/runtime/PausableMonotonicFrameClock;
-Landroidx/compose/runtime/Pending$keyMap$2;
-Landroidx/compose/runtime/Pending;
-Landroidx/compose/runtime/PrioritySet;
-Landroidx/compose/runtime/ProvidableCompositionLocal;
-Landroidx/compose/runtime/ProvidedValue;
-Landroidx/compose/runtime/RecomposeScope;
-Landroidx/compose/runtime/RecomposeScopeImpl$end$1$2;
-Landroidx/compose/runtime/RecomposeScopeImpl;
-Landroidx/compose/runtime/Recomposer$Companion;
-Landroidx/compose/runtime/Recomposer$RecomposerErrorState;
-Landroidx/compose/runtime/Recomposer$RecomposerInfoImpl;
-Landroidx/compose/runtime/Recomposer$State;
-Landroidx/compose/runtime/Recomposer$broadcastFrameClock$1;
-Landroidx/compose/runtime/Recomposer$effectJob$1$1;
-Landroidx/compose/runtime/Recomposer$join$2;
-Landroidx/compose/runtime/Recomposer$performRecompose$1$1;
-Landroidx/compose/runtime/Recomposer$readObserverOf$1;
-Landroidx/compose/runtime/Recomposer$recompositionRunner$2$2;
-Landroidx/compose/runtime/Recomposer$recompositionRunner$2$unregisterApplyObserver$1;
-Landroidx/compose/runtime/Recomposer$recompositionRunner$2;
-Landroidx/compose/runtime/Recomposer$runRecomposeAndApplyChanges$2$2;
-Landroidx/compose/runtime/Recomposer$runRecomposeAndApplyChanges$2;
-Landroidx/compose/runtime/Recomposer$writeObserverOf$1;
-Landroidx/compose/runtime/Recomposer;
-Landroidx/compose/runtime/ReferentialEqualityPolicy;
-Landroidx/compose/runtime/RememberManager;
-Landroidx/compose/runtime/RememberObserver;
-Landroidx/compose/runtime/SkippableUpdater;
-Landroidx/compose/runtime/SlotReader;
-Landroidx/compose/runtime/SlotTable;
-Landroidx/compose/runtime/SlotTableKt;
-Landroidx/compose/runtime/SlotWriter$Companion;
-Landroidx/compose/runtime/SlotWriter$groupSlots$1;
-Landroidx/compose/runtime/SlotWriter;
-Landroidx/compose/runtime/SnapshotMutableStateImpl$StateStateRecord;
-Landroidx/compose/runtime/SnapshotMutableStateImpl;
-Landroidx/compose/runtime/SnapshotMutationPolicy;
-Landroidx/compose/runtime/SnapshotStateKt__DerivedStateKt;
-Landroidx/compose/runtime/SnapshotStateKt__SnapshotFlowKt$snapshotFlow$1$readObserver$1;
-Landroidx/compose/runtime/SnapshotStateKt__SnapshotFlowKt$snapshotFlow$1$unregisterApplyObserver$1;
-Landroidx/compose/runtime/SnapshotStateKt__SnapshotFlowKt$snapshotFlow$1;
-Landroidx/compose/runtime/SnapshotThreadLocal;
-Landroidx/compose/runtime/Stack;
-Landroidx/compose/runtime/State;
-Landroidx/compose/runtime/StaticProvidableCompositionLocal;
-Landroidx/compose/runtime/StaticValueHolder;
-Landroidx/compose/runtime/StructuralEqualityPolicy;
-Landroidx/compose/runtime/Updater;
-Landroidx/compose/runtime/collection/IdentityArrayIntMap;
-Landroidx/compose/runtime/collection/IdentityArrayMap;
-Landroidx/compose/runtime/collection/IdentityArraySet;
-Landroidx/compose/runtime/collection/IdentityScopeMap;
-Landroidx/compose/runtime/collection/MutableVector$MutableVectorList;
-Landroidx/compose/runtime/collection/MutableVector$VectorListIterator;
-Landroidx/compose/runtime/collection/MutableVector;
-Landroidx/compose/runtime/collection/MutableVectorKt;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/ExtensionsKt;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/ImmutableList;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/ImmutableSet;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/PersistentList;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/PersistentMap;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/PersistentSet;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableList/AbstractPersistentList;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableList/SmallPersistentVector;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/MapEntry;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMap;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapBaseIterator;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapBuilder;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapEntries;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapEntriesIterator;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode$ModificationResult;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNodeBaseIterator;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNodeEntriesIterator;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/persistentOrderedSet/Links;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/persistentOrderedSet/PersistentOrderedSet;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/internal/DeltaCounter;
-Landroidx/compose/runtime/external/kotlinx/collections/immutable/internal/ListImplementation;
-Landroidx/compose/runtime/internal/ComposableLambdaImpl$invoke$1;
-Landroidx/compose/runtime/internal/ComposableLambdaImpl$invoke$2;
-Landroidx/compose/runtime/internal/ComposableLambdaImpl;
-Landroidx/compose/runtime/internal/ComposableLambdaKt;
-Landroidx/compose/runtime/internal/ThreadMap;
-Landroidx/compose/runtime/internal/ThreadMapKt;
-Landroidx/compose/runtime/saveable/ListSaverKt$listSaver$1;
-Landroidx/compose/runtime/saveable/RememberSaveableKt$rememberSaveable$1$invoke$$inlined$onDispose$1;
-Landroidx/compose/runtime/saveable/RememberSaveableKt$rememberSaveable$1$valueProvider$1$1$1;
-Landroidx/compose/runtime/saveable/RememberSaveableKt$rememberSaveable$1$valueProvider$1;
-Landroidx/compose/runtime/saveable/RememberSaveableKt$rememberSaveable$1;
-Landroidx/compose/runtime/saveable/RememberSaveableKt;
-Landroidx/compose/runtime/saveable/SaveableStateHolder;
-Landroidx/compose/runtime/saveable/SaveableStateHolderImpl$Companion$Saver$1;
-Landroidx/compose/runtime/saveable/SaveableStateHolderImpl$Companion$Saver$2;
-Landroidx/compose/runtime/saveable/SaveableStateHolderImpl$RegistryHolder$registry$1;
-Landroidx/compose/runtime/saveable/SaveableStateHolderImpl$RegistryHolder;
-Landroidx/compose/runtime/saveable/SaveableStateHolderImpl$SaveableStateProvider$1$1$invoke$$inlined$onDispose$1;
-Landroidx/compose/runtime/saveable/SaveableStateHolderImpl$SaveableStateProvider$1$1;
-Landroidx/compose/runtime/saveable/SaveableStateHolderImpl;
-Landroidx/compose/runtime/saveable/SaveableStateHolderKt$rememberSaveableStateHolder$1;
-Landroidx/compose/runtime/saveable/SaveableStateRegistry$Entry;
-Landroidx/compose/runtime/saveable/SaveableStateRegistry;
-Landroidx/compose/runtime/saveable/SaveableStateRegistryImpl$registerProvider$3;
-Landroidx/compose/runtime/saveable/SaveableStateRegistryImpl;
-Landroidx/compose/runtime/saveable/SaveableStateRegistryKt$LocalSaveableStateRegistry$1;
-Landroidx/compose/runtime/saveable/SaveableStateRegistryKt;
-Landroidx/compose/runtime/saveable/Saver;
-Landroidx/compose/runtime/saveable/SaverKt$AutoSaver$1;
-Landroidx/compose/runtime/saveable/SaverKt$AutoSaver$2;
-Landroidx/compose/runtime/saveable/SaverKt$Saver$1;
-Landroidx/compose/runtime/saveable/SaverKt;
-Landroidx/compose/runtime/saveable/SaverScope;
-Landroidx/compose/runtime/snapshots/GlobalSnapshot$1$1$1;
-Landroidx/compose/runtime/snapshots/GlobalSnapshot$takeNestedMutableSnapshot$1;
-Landroidx/compose/runtime/snapshots/GlobalSnapshot$takeNestedSnapshot$1;
-Landroidx/compose/runtime/snapshots/GlobalSnapshot;
-Landroidx/compose/runtime/snapshots/MutableSnapshot;
-Landroidx/compose/runtime/snapshots/ObserverHandle;
-Landroidx/compose/runtime/snapshots/ReadonlySnapshot;
-Landroidx/compose/runtime/snapshots/Snapshot$Companion$registerApplyObserver$2;
-Landroidx/compose/runtime/snapshots/Snapshot$Companion;
-Landroidx/compose/runtime/snapshots/Snapshot;
-Landroidx/compose/runtime/snapshots/SnapshotApplyResult$Failure;
-Landroidx/compose/runtime/snapshots/SnapshotApplyResult$Success;
-Landroidx/compose/runtime/snapshots/SnapshotApplyResult;
-Landroidx/compose/runtime/snapshots/SnapshotDoubleIndexHeap;
-Landroidx/compose/runtime/snapshots/SnapshotIdSet;
-Landroidx/compose/runtime/snapshots/SnapshotKt$advanceGlobalSnapshot$2;
-Landroidx/compose/runtime/snapshots/SnapshotKt$emptyLambda$1;
-Landroidx/compose/runtime/snapshots/SnapshotKt$mergedReadObserver$1;
-Landroidx/compose/runtime/snapshots/SnapshotKt$mergedWriteObserver$1;
-Landroidx/compose/runtime/snapshots/SnapshotKt$takeNewSnapshot$1;
-Landroidx/compose/runtime/snapshots/SnapshotKt;
-Landroidx/compose/runtime/snapshots/SnapshotMutableState;
-Landroidx/compose/runtime/snapshots/SnapshotStateList$StateListStateRecord;
-Landroidx/compose/runtime/snapshots/SnapshotStateList;
-Landroidx/compose/runtime/snapshots/SnapshotStateListKt;
-Landroidx/compose/runtime/snapshots/SnapshotStateObserver$ObservedScopeMap$derivedStateEnterObserver$1;
-Landroidx/compose/runtime/snapshots/SnapshotStateObserver$ObservedScopeMap$derivedStateExitObserver$1;
-Landroidx/compose/runtime/snapshots/SnapshotStateObserver$ObservedScopeMap;
-Landroidx/compose/runtime/snapshots/SnapshotStateObserver$applyObserver$1;
-Landroidx/compose/runtime/snapshots/SnapshotStateObserver$observeReads$1$1;
-Landroidx/compose/runtime/snapshots/SnapshotStateObserver$readObserver$1;
-Landroidx/compose/runtime/snapshots/SnapshotStateObserver$sendNotifications$1;
-Landroidx/compose/runtime/snapshots/SnapshotStateObserver;
-Landroidx/compose/runtime/snapshots/StateObject;
-Landroidx/compose/runtime/snapshots/StateRecord;
-Landroidx/compose/runtime/snapshots/TransparentObserverMutableSnapshot;
-Landroidx/compose/runtime/tooling/InspectionTablesKt$LocalInspectionTables$1;
-Landroidx/compose/runtime/tooling/InspectionTablesKt;
-Landroidx/compose/ui/Alignment$Companion;
-Landroidx/compose/ui/Alignment$Horizontal;
-Landroidx/compose/ui/Alignment$Vertical;
-Landroidx/compose/ui/Alignment;
-Landroidx/compose/ui/BiasAlignment$Horizontal;
-Landroidx/compose/ui/BiasAlignment$Vertical;
-Landroidx/compose/ui/BiasAlignment;
-Landroidx/compose/ui/CombinedModifier;
-Landroidx/compose/ui/ComposedModifier;
-Landroidx/compose/ui/ComposedModifierKt$materialize$1;
-Landroidx/compose/ui/ComposedModifierKt$materialize$result$1;
-Landroidx/compose/ui/ComposedModifierKt;
-Landroidx/compose/ui/Modifier$Companion;
-Landroidx/compose/ui/Modifier$Element;
-Landroidx/compose/ui/Modifier$Node;
-Landroidx/compose/ui/Modifier;
-Landroidx/compose/ui/MotionDurationScale$Key;
-Landroidx/compose/ui/MotionDurationScale;
-Landroidx/compose/ui/ZIndexModifier$measure$1;
-Landroidx/compose/ui/ZIndexModifier;
-Landroidx/compose/ui/autofill/AndroidAutofill;
-Landroidx/compose/ui/autofill/Autofill;
-Landroidx/compose/ui/autofill/AutofillCallback;
-Landroidx/compose/ui/autofill/AutofillTree;
-Landroidx/compose/ui/draw/BuildDrawCacheParams;
-Landroidx/compose/ui/draw/CacheDrawScope;
-Landroidx/compose/ui/draw/ClipKt;
-Landroidx/compose/ui/draw/DrawBackgroundModifier;
-Landroidx/compose/ui/draw/DrawCacheModifier;
-Landroidx/compose/ui/draw/DrawContentCacheModifier;
-Landroidx/compose/ui/draw/DrawModifier;
-Landroidx/compose/ui/draw/DrawModifierKt$drawBehind$$inlined$modifierElementOf$1;
-Landroidx/compose/ui/draw/DrawModifierKt$drawWithCache$2;
-Landroidx/compose/ui/draw/DrawResult;
-Landroidx/compose/ui/draw/EmptyBuildDrawCacheParams;
-Landroidx/compose/ui/draw/PainterModifier$measure$1;
-Landroidx/compose/ui/draw/PainterModifier;
-Landroidx/compose/ui/focus/BeyondBoundsLayoutKt;
-Landroidx/compose/ui/focus/FocusChangedModifierKt$onFocusChanged$$inlined$modifierElementOf$2;
-Landroidx/compose/ui/focus/FocusChangedModifierKt;
-Landroidx/compose/ui/focus/FocusChangedModifierNode;
-Landroidx/compose/ui/focus/FocusDirection;
-Landroidx/compose/ui/focus/FocusEventModifier;
-Landroidx/compose/ui/focus/FocusEventModifierNode;
-Landroidx/compose/ui/focus/FocusEventModifierNodeKt;
-Landroidx/compose/ui/focus/FocusInvalidationManager$invalidateNodes$1;
-Landroidx/compose/ui/focus/FocusInvalidationManager;
-Landroidx/compose/ui/focus/FocusManager;
-Landroidx/compose/ui/focus/FocusOrderModifier;
-Landroidx/compose/ui/focus/FocusOwner;
-Landroidx/compose/ui/focus/FocusOwnerImpl$moveFocus$foundNextItem$1;
-Landroidx/compose/ui/focus/FocusOwnerImpl$special$$inlined$modifierElementOf$2;
-Landroidx/compose/ui/focus/FocusOwnerImpl;
-Landroidx/compose/ui/focus/FocusProperties;
-Landroidx/compose/ui/focus/FocusPropertiesImpl$enter$1;
-Landroidx/compose/ui/focus/FocusPropertiesImpl$exit$1;
-Landroidx/compose/ui/focus/FocusPropertiesImpl;
-Landroidx/compose/ui/focus/FocusPropertiesKt$focusProperties$$inlined$modifierElementOf$2;
-Landroidx/compose/ui/focus/FocusPropertiesModifierNode;
-Landroidx/compose/ui/focus/FocusPropertiesModifierNodeImpl;
-Landroidx/compose/ui/focus/FocusRequester;
-Landroidx/compose/ui/focus/FocusRequesterModifier;
-Landroidx/compose/ui/focus/FocusRequesterModifierKt$focusRequester$$inlined$modifierElementOf$2;
-Landroidx/compose/ui/focus/FocusRequesterModifierNode;
-Landroidx/compose/ui/focus/FocusRequesterModifierNodeImpl;
-Landroidx/compose/ui/focus/FocusState;
-Landroidx/compose/ui/focus/FocusStateImpl;
-Landroidx/compose/ui/focus/FocusTargetModifierNode$special$$inlined$modifierElementOf$2;
-Landroidx/compose/ui/focus/FocusTargetModifierNode;
-Landroidx/compose/ui/focus/FocusTransactionsKt$grantFocus$1;
-Landroidx/compose/ui/focus/FocusTransactionsKt;
-Landroidx/compose/ui/focus/FocusTraversalKt;
-Landroidx/compose/ui/focus/TwoDimensionalFocusSearchKt$generateAndSearchChildren$1;
-Landroidx/compose/ui/focus/TwoDimensionalFocusSearchKt;
-Landroidx/compose/ui/geometry/CornerRadius;
-Landroidx/compose/ui/geometry/CornerRadiusKt;
-Landroidx/compose/ui/geometry/MutableRect;
-Landroidx/compose/ui/geometry/Offset;
-Landroidx/compose/ui/geometry/OffsetKt;
-Landroidx/compose/ui/geometry/Rect;
-Landroidx/compose/ui/geometry/RoundRect;
-Landroidx/compose/ui/geometry/RoundRectKt;
-Landroidx/compose/ui/geometry/Size;
-Landroidx/compose/ui/geometry/SizeKt;
-Landroidx/compose/ui/graphics/AndroidBlendMode_androidKt;
-Landroidx/compose/ui/graphics/AndroidCanvas;
-Landroidx/compose/ui/graphics/AndroidCanvas_androidKt;
-Landroidx/compose/ui/graphics/AndroidImageBitmap;
-Landroidx/compose/ui/graphics/AndroidMatrixConversions_androidKt;
-Landroidx/compose/ui/graphics/AndroidPaint;
-Landroidx/compose/ui/graphics/AndroidPaint_androidKt$WhenMappings;
-Landroidx/compose/ui/graphics/AndroidPaint_androidKt;
-Landroidx/compose/ui/graphics/AndroidPath;
-Landroidx/compose/ui/graphics/BlendMode;
-Landroidx/compose/ui/graphics/BlendModeColorFilterHelper;
-Landroidx/compose/ui/graphics/Brush;
-Landroidx/compose/ui/graphics/Canvas;
-Landroidx/compose/ui/graphics/CanvasHolder;
-Landroidx/compose/ui/graphics/Color;
-Landroidx/compose/ui/graphics/ColorFilter;
-Landroidx/compose/ui/graphics/ColorKt;
-Landroidx/compose/ui/graphics/Float16$Companion;
-Landroidx/compose/ui/graphics/Float16;
-Landroidx/compose/ui/graphics/GraphicsLayerModifierKt;
-Landroidx/compose/ui/graphics/GraphicsLayerModifierNodeElement;
-Landroidx/compose/ui/graphics/GraphicsLayerScope;
-Landroidx/compose/ui/graphics/GraphicsLayerScopeKt;
-Landroidx/compose/ui/graphics/ImageBitmap;
-Landroidx/compose/ui/graphics/Matrix;
-Landroidx/compose/ui/graphics/Outline$Generic;
-Landroidx/compose/ui/graphics/Outline$Rectangle;
-Landroidx/compose/ui/graphics/Outline$Rounded;
-Landroidx/compose/ui/graphics/Outline;
-Landroidx/compose/ui/graphics/Paint;
-Landroidx/compose/ui/graphics/Path;
-Landroidx/compose/ui/graphics/RectangleShapeKt$RectangleShape$1;
-Landroidx/compose/ui/graphics/RectangleShapeKt;
-Landroidx/compose/ui/graphics/ReusableGraphicsLayerScope;
-Landroidx/compose/ui/graphics/ShaderBrush;
-Landroidx/compose/ui/graphics/Shadow;
-Landroidx/compose/ui/graphics/Shape;
-Landroidx/compose/ui/graphics/SimpleGraphicsLayerModifier$layerBlock$1;
-Landroidx/compose/ui/graphics/SimpleGraphicsLayerModifier$measure$1;
-Landroidx/compose/ui/graphics/SimpleGraphicsLayerModifier;
-Landroidx/compose/ui/graphics/SolidColor;
-Landroidx/compose/ui/graphics/TransformOrigin;
-Landroidx/compose/ui/graphics/colorspace/Adaptation$Companion$Bradford$1;
-Landroidx/compose/ui/graphics/colorspace/Adaptation;
-Landroidx/compose/ui/graphics/colorspace/ColorModel;
-Landroidx/compose/ui/graphics/colorspace/ColorSpace;
-Landroidx/compose/ui/graphics/colorspace/ColorSpaceKt;
-Landroidx/compose/ui/graphics/colorspace/ColorSpaces$$ExternalSyntheticLambda0;
-Landroidx/compose/ui/graphics/colorspace/ColorSpaces;
-Landroidx/compose/ui/graphics/colorspace/Connector$Companion$identity$1;
-Landroidx/compose/ui/graphics/colorspace/Connector$Companion;
-Landroidx/compose/ui/graphics/colorspace/Connector;
-Landroidx/compose/ui/graphics/colorspace/DoubleFunction;
-Landroidx/compose/ui/graphics/colorspace/Illuminant;
-Landroidx/compose/ui/graphics/colorspace/Lab;
-Landroidx/compose/ui/graphics/colorspace/Oklab;
-Landroidx/compose/ui/graphics/colorspace/Rgb$$ExternalSyntheticLambda0;
-Landroidx/compose/ui/graphics/colorspace/Rgb$$ExternalSyntheticLambda1;
-Landroidx/compose/ui/graphics/colorspace/Rgb$$ExternalSyntheticLambda2;
-Landroidx/compose/ui/graphics/colorspace/Rgb$$ExternalSyntheticLambda3;
-Landroidx/compose/ui/graphics/colorspace/Rgb$$ExternalSyntheticLambda5;
-Landroidx/compose/ui/graphics/colorspace/Rgb$$ExternalSyntheticLambda6;
-Landroidx/compose/ui/graphics/colorspace/Rgb$$ExternalSyntheticLambda7;
-Landroidx/compose/ui/graphics/colorspace/Rgb$Companion;
-Landroidx/compose/ui/graphics/colorspace/Rgb;
-Landroidx/compose/ui/graphics/colorspace/TransferParameters;
-Landroidx/compose/ui/graphics/colorspace/WhitePoint;
-Landroidx/compose/ui/graphics/colorspace/Xyz;
-Landroidx/compose/ui/graphics/drawscope/CanvasDrawScope$DrawParams;
-Landroidx/compose/ui/graphics/drawscope/CanvasDrawScope$drawContext$1;
-Landroidx/compose/ui/graphics/drawscope/CanvasDrawScope;
-Landroidx/compose/ui/graphics/drawscope/CanvasDrawScopeKt$asDrawTransform$1;
-Landroidx/compose/ui/graphics/drawscope/CanvasDrawScopeKt;
-Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;
-Landroidx/compose/ui/graphics/drawscope/DrawContext;
-Landroidx/compose/ui/graphics/drawscope/DrawScope;
-Landroidx/compose/ui/graphics/drawscope/EmptyCanvas;
-Landroidx/compose/ui/graphics/drawscope/Fill;
-Landroidx/compose/ui/graphics/drawscope/Stroke;
-Landroidx/compose/ui/graphics/painter/BitmapPainter;
-Landroidx/compose/ui/graphics/painter/Painter;
-Landroidx/compose/ui/graphics/vector/ImageVector$Builder$GroupParams;
-Landroidx/compose/ui/graphics/vector/ImageVector$Builder;
-Landroidx/compose/ui/graphics/vector/ImageVector;
-Landroidx/compose/ui/graphics/vector/VNode;
-Landroidx/compose/ui/graphics/vector/VectorComponent;
-Landroidx/compose/ui/graphics/vector/VectorGroup;
-Landroidx/compose/ui/graphics/vector/VectorKt;
-Landroidx/compose/ui/graphics/vector/VectorNode;
-Landroidx/compose/ui/graphics/vector/VectorPainter;
-Landroidx/compose/ui/graphics/vector/VectorPainterKt$rememberVectorPainter$3;
-Landroidx/compose/ui/graphics/vector/VectorPath;
-Landroidx/compose/ui/graphics/vector/compat/AndroidVectorParser;
-Landroidx/compose/ui/graphics/vector/compat/AndroidVectorResources;
-Landroidx/compose/ui/graphics/vector/compat/XmlVectorParser_androidKt;
-Landroidx/compose/ui/hapticfeedback/HapticFeedback;
-Landroidx/compose/ui/hapticfeedback/PlatformHapticFeedback;
-Landroidx/compose/ui/input/InputMode;
-Landroidx/compose/ui/input/InputModeManager;
-Landroidx/compose/ui/input/InputModeManagerImpl;
-Landroidx/compose/ui/input/key/Key;
-Landroidx/compose/ui/input/key/KeyEvent;
-Landroidx/compose/ui/input/key/KeyEvent_androidKt;
-Landroidx/compose/ui/input/key/KeyInputInputModifierNodeImpl;
-Landroidx/compose/ui/input/key/KeyInputModifierKt$onKeyEvent$$inlined$modifierElementOf$2;
-Landroidx/compose/ui/input/key/KeyInputModifierKt;
-Landroidx/compose/ui/input/key/KeyInputModifierNode;
-Landroidx/compose/ui/input/key/Key_androidKt;
-Landroidx/compose/ui/input/nestedscroll/NestedScrollConnection;
-Landroidx/compose/ui/input/nestedscroll/NestedScrollDispatcher$calculateNestedScrollScope$1;
-Landroidx/compose/ui/input/nestedscroll/NestedScrollDispatcher;
-Landroidx/compose/ui/input/nestedscroll/NestedScrollModifierKt$nestedScroll$2;
-Landroidx/compose/ui/input/nestedscroll/NestedScrollModifierLocal$1;
-Landroidx/compose/ui/input/nestedscroll/NestedScrollModifierLocal;
-Landroidx/compose/ui/input/nestedscroll/NestedScrollModifierLocalKt$ModifierLocalNestedScroll$1;
-Landroidx/compose/ui/input/nestedscroll/NestedScrollModifierLocalKt;
-Landroidx/compose/ui/input/pointer/AwaitPointerEventScope;
-Landroidx/compose/ui/input/pointer/HitPathTracker;
-Landroidx/compose/ui/input/pointer/MotionEventAdapter;
-Landroidx/compose/ui/input/pointer/Node;
-Landroidx/compose/ui/input/pointer/NodeParent;
-Landroidx/compose/ui/input/pointer/PointerEvent;
-Landroidx/compose/ui/input/pointer/PointerEventPass;
-Landroidx/compose/ui/input/pointer/PointerIconService;
-Landroidx/compose/ui/input/pointer/PointerInputChangeEventProducer;
-Landroidx/compose/ui/input/pointer/PointerInputEventProcessor;
-Landroidx/compose/ui/input/pointer/PointerInputFilter;
-Landroidx/compose/ui/input/pointer/PointerInputModifier;
-Landroidx/compose/ui/input/pointer/PointerInputScope;
-Landroidx/compose/ui/input/pointer/PointerKeyboardModifiers;
-Landroidx/compose/ui/input/pointer/PositionCalculator;
-Landroidx/compose/ui/input/pointer/SuspendingPointerInputFilter$PointerEventHandlerCoroutine;
-Landroidx/compose/ui/input/pointer/SuspendingPointerInputFilter$awaitPointerEventScope$2$2;
-Landroidx/compose/ui/input/pointer/SuspendingPointerInputFilter;
-Landroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$2$2$1;
-Landroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$2;
-Landroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$4$2$1;
-Landroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$4;
-Landroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$6$2$1;
-Landroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt$pointerInput$6;
-Landroidx/compose/ui/input/pointer/SuspendingPointerInputFilterKt;
-Landroidx/compose/ui/input/pointer/util/DataPointAtTime;
-Landroidx/compose/ui/input/pointer/util/VelocityTracker1D;
-Landroidx/compose/ui/input/pointer/util/VelocityTracker;
-Landroidx/compose/ui/input/rotary/RotaryInputModifierKt$onRotaryScrollEvent$$inlined$modifierElementOf$2;
-Landroidx/compose/ui/input/rotary/RotaryInputModifierNode;
-Landroidx/compose/ui/input/rotary/RotaryInputModifierNodeImpl;
-Landroidx/compose/ui/layout/AlignmentLine;
-Landroidx/compose/ui/layout/AlignmentLineKt$FirstBaseline$1;
-Landroidx/compose/ui/layout/AlignmentLineKt$LastBaseline$1;
-Landroidx/compose/ui/layout/AlignmentLineKt;
-Landroidx/compose/ui/layout/BeyondBoundsLayout$BeyondBoundsScope;
-Landroidx/compose/ui/layout/BeyondBoundsLayout;
-Landroidx/compose/ui/layout/BeyondBoundsLayoutKt$ModifierLocalBeyondBoundsLayout$1;
-Landroidx/compose/ui/layout/BeyondBoundsLayoutKt;
-Landroidx/compose/ui/layout/ComposableSingletons$SubcomposeLayoutKt$lambda-1$1;
-Landroidx/compose/ui/layout/ComposableSingletons$SubcomposeLayoutKt;
-Landroidx/compose/ui/layout/ContentScale$Companion$FillBounds$1;
-Landroidx/compose/ui/layout/ContentScale$Companion$Fit$1;
-Landroidx/compose/ui/layout/ContentScale$Companion$Inside$1;
-Landroidx/compose/ui/layout/ContentScale$Companion;
-Landroidx/compose/ui/layout/ContentScale;
-Landroidx/compose/ui/layout/HorizontalAlignmentLine;
-Landroidx/compose/ui/layout/IntermediateLayoutModifier;
-Landroidx/compose/ui/layout/IntrinsicMeasurable;
-Landroidx/compose/ui/layout/IntrinsicMeasureScope;
-Landroidx/compose/ui/layout/IntrinsicsMeasureScope;
-Landroidx/compose/ui/layout/LayoutCoordinates;
-Landroidx/compose/ui/layout/LayoutKt$materializerOf$1;
-Landroidx/compose/ui/layout/LayoutKt;
-Landroidx/compose/ui/layout/LayoutModifier;
-Landroidx/compose/ui/layout/LayoutModifierImpl;
-Landroidx/compose/ui/layout/LayoutModifierKt;
-Landroidx/compose/ui/layout/LayoutNodeSubcompositionsState$NodeState;
-Landroidx/compose/ui/layout/LayoutNodeSubcompositionsState$Scope;
-Landroidx/compose/ui/layout/LayoutNodeSubcompositionsState$createMeasurePolicy$1$measure$1;
-Landroidx/compose/ui/layout/LayoutNodeSubcompositionsState$createMeasurePolicy$1;
-Landroidx/compose/ui/layout/LayoutNodeSubcompositionsState$precompose$1;
-Landroidx/compose/ui/layout/LayoutNodeSubcompositionsState$subcompose$2$1$1;
-Landroidx/compose/ui/layout/LayoutNodeSubcompositionsState;
-Landroidx/compose/ui/layout/LookaheadLayoutCoordinatesImpl;
-Landroidx/compose/ui/layout/LookaheadOnPlacedModifier;
-Landroidx/compose/ui/layout/Measurable;
-Landroidx/compose/ui/layout/MeasurePolicy;
-Landroidx/compose/ui/layout/MeasureResult;
-Landroidx/compose/ui/layout/MeasureScope$layout$1;
-Landroidx/compose/ui/layout/MeasureScope;
-Landroidx/compose/ui/layout/Measured;
-Landroidx/compose/ui/layout/MeasuringIntrinsics$DefaultIntrinsicMeasurable;
-Landroidx/compose/ui/layout/MeasuringIntrinsics$EmptyPlaceable;
-Landroidx/compose/ui/layout/NoOpSubcomposeSlotReusePolicy;
-Landroidx/compose/ui/layout/OnGloballyPositionedModifier;
-Landroidx/compose/ui/layout/OnGloballyPositionedModifierImpl;
-Landroidx/compose/ui/layout/OnPlacedModifier;
-Landroidx/compose/ui/layout/OnRemeasuredModifier;
-Landroidx/compose/ui/layout/OnSizeChangedModifier;
-Landroidx/compose/ui/layout/ParentDataModifier;
-Landroidx/compose/ui/layout/PinnableContainer$PinnedHandle;
-Landroidx/compose/ui/layout/PinnableContainer;
-Landroidx/compose/ui/layout/PinnableContainerKt$LocalPinnableContainer$1;
-Landroidx/compose/ui/layout/PinnableContainerKt;
-Landroidx/compose/ui/layout/Placeable$PlacementScope$Companion;
-Landroidx/compose/ui/layout/Placeable$PlacementScope;
-Landroidx/compose/ui/layout/Placeable;
-Landroidx/compose/ui/layout/PlaceableKt$DefaultLayerBlock$1;
-Landroidx/compose/ui/layout/PlaceableKt;
-Landroidx/compose/ui/layout/Remeasurement;
-Landroidx/compose/ui/layout/RemeasurementModifier;
-Landroidx/compose/ui/layout/RootMeasurePolicy$measure$2;
-Landroidx/compose/ui/layout/RootMeasurePolicy;
-Landroidx/compose/ui/layout/ScaleFactor;
-Landroidx/compose/ui/layout/ScaleFactorKt;
-Landroidx/compose/ui/layout/SubcomposeLayoutKt$SubcomposeLayout$$inlined$ComposeNode$1;
-Landroidx/compose/ui/layout/SubcomposeLayoutKt$SubcomposeLayout$4;
-Landroidx/compose/ui/layout/SubcomposeLayoutKt$SubcomposeLayout$5$1$invoke$$inlined$onDispose$1;
-Landroidx/compose/ui/layout/SubcomposeLayoutKt$SubcomposeLayout$5$1;
-Landroidx/compose/ui/layout/SubcomposeLayoutKt$SubcomposeLayout$6;
-Landroidx/compose/ui/layout/SubcomposeLayoutKt;
-Landroidx/compose/ui/layout/SubcomposeLayoutState$PrecomposedSlotHandle;
-Landroidx/compose/ui/layout/SubcomposeLayoutState$setCompositionContext$1;
-Landroidx/compose/ui/layout/SubcomposeLayoutState$setMeasurePolicy$1;
-Landroidx/compose/ui/layout/SubcomposeLayoutState$setRoot$1;
-Landroidx/compose/ui/layout/SubcomposeLayoutState;
-Landroidx/compose/ui/layout/SubcomposeMeasureScope;
-Landroidx/compose/ui/layout/SubcomposeSlotReusePolicy$SlotIdsSet;
-Landroidx/compose/ui/layout/SubcomposeSlotReusePolicy;
-Landroidx/compose/ui/modifier/BackwardsCompatLocalMap;
-Landroidx/compose/ui/modifier/EmptyMap;
-Landroidx/compose/ui/modifier/ModifierLocal;
-Landroidx/compose/ui/modifier/ModifierLocalConsumer;
-Landroidx/compose/ui/modifier/ModifierLocalManager;
-Landroidx/compose/ui/modifier/ModifierLocalMap;
-Landroidx/compose/ui/modifier/ModifierLocalNode;
-Landroidx/compose/ui/modifier/ModifierLocalProvider;
-Landroidx/compose/ui/modifier/ModifierLocalReadScope;
-Landroidx/compose/ui/modifier/ProvidableModifierLocal;
-Landroidx/compose/ui/node/AlignmentLines;
-Landroidx/compose/ui/node/AlignmentLinesOwner;
-Landroidx/compose/ui/node/BackwardsCompatNode$initializeModifier$1;
-Landroidx/compose/ui/node/BackwardsCompatNode$initializeModifier$2;
-Landroidx/compose/ui/node/BackwardsCompatNode$updateDrawCache$1;
-Landroidx/compose/ui/node/BackwardsCompatNode$updateModifierLocalConsumer$1;
-Landroidx/compose/ui/node/BackwardsCompatNode;
-Landroidx/compose/ui/node/BackwardsCompatNodeKt$DetachedModifierLocalReadScope$1;
-Landroidx/compose/ui/node/BackwardsCompatNodeKt$onDrawCacheReadsChanged$1;
-Landroidx/compose/ui/node/BackwardsCompatNodeKt$updateModifierLocalConsumer$1;
-Landroidx/compose/ui/node/BackwardsCompatNodeKt;
-Landroidx/compose/ui/node/CanFocusChecker;
-Landroidx/compose/ui/node/ComposeUiNode$Companion$SetDensity$1;
-Landroidx/compose/ui/node/ComposeUiNode$Companion$SetLayoutDirection$1;
-Landroidx/compose/ui/node/ComposeUiNode$Companion$SetMeasurePolicy$1;
-Landroidx/compose/ui/node/ComposeUiNode$Companion$SetModifier$1;
-Landroidx/compose/ui/node/ComposeUiNode$Companion$SetViewConfiguration$1;
-Landroidx/compose/ui/node/ComposeUiNode$Companion;
-Landroidx/compose/ui/node/ComposeUiNode;
-Landroidx/compose/ui/node/DelegatableNode;
-Landroidx/compose/ui/node/DelegatableNodeKt;
-Landroidx/compose/ui/node/DepthSortedSet$DepthComparator$1;
-Landroidx/compose/ui/node/DepthSortedSet$mapOfOriginalDepth$2;
-Landroidx/compose/ui/node/DepthSortedSet;
-Landroidx/compose/ui/node/DrawModifierNode;
-Landroidx/compose/ui/node/DrawModifierNodeKt;
-Landroidx/compose/ui/node/GlobalPositionAwareModifierNode;
-Landroidx/compose/ui/node/HitTestResult;
-Landroidx/compose/ui/node/InnerNodeCoordinator$tail$1;
-Landroidx/compose/ui/node/InnerNodeCoordinator;
-Landroidx/compose/ui/node/IntStack;
-Landroidx/compose/ui/node/IntermediateLayoutModifierNode;
-Landroidx/compose/ui/node/IntrinsicsPolicy;
-Landroidx/compose/ui/node/LayerPositionalProperties;
-Landroidx/compose/ui/node/LayoutAwareModifierNode;
-Landroidx/compose/ui/node/LayoutModifierNode;
-Landroidx/compose/ui/node/LayoutModifierNodeCoordinator;
-Landroidx/compose/ui/node/LayoutNode$$ExternalSyntheticLambda0;
-Landroidx/compose/ui/node/LayoutNode$Companion$Constructor$1;
-Landroidx/compose/ui/node/LayoutNode$Companion$DummyViewConfiguration$1;
-Landroidx/compose/ui/node/LayoutNode$Companion$ErrorMeasurePolicy$1;
-Landroidx/compose/ui/node/LayoutNode$LayoutState$EnumUnboxingLocalUtility;
-Landroidx/compose/ui/node/LayoutNode$NoIntrinsicsMeasurePolicy;
-Landroidx/compose/ui/node/LayoutNode$UsageByParent$EnumUnboxingLocalUtility;
-Landroidx/compose/ui/node/LayoutNode$WhenMappings;
-Landroidx/compose/ui/node/LayoutNode$_foldedChildren$1;
-Landroidx/compose/ui/node/LayoutNode;
-Landroidx/compose/ui/node/LayoutNodeAlignmentLines;
-Landroidx/compose/ui/node/LayoutNodeDrawScope;
-Landroidx/compose/ui/node/LayoutNodeKt;
-Landroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate$childMeasurables$1;
-Landroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate$layoutChildren$1$1;
-Landroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate$placeOuterCoordinator$1;
-Landroidx/compose/ui/node/LayoutNodeLayoutDelegate$MeasurePassDelegate;
-Landroidx/compose/ui/node/LayoutNodeLayoutDelegate$performMeasure$2;
-Landroidx/compose/ui/node/LayoutNodeLayoutDelegate;
-Landroidx/compose/ui/node/LayoutNodeLayoutDelegateKt;
-Landroidx/compose/ui/node/LookaheadCapablePlaceable;
-Landroidx/compose/ui/node/LookaheadDelegate;
-Landroidx/compose/ui/node/MeasureAndLayoutDelegate$PostponedRequest;
-Landroidx/compose/ui/node/MeasureAndLayoutDelegate;
-Landroidx/compose/ui/node/ModifierNodeElement;
-Landroidx/compose/ui/node/MutableVectorWithMutationTracking;
-Landroidx/compose/ui/node/NodeChain$Differ;
-Landroidx/compose/ui/node/NodeChain;
-Landroidx/compose/ui/node/NodeChainKt$SentinelHead$1;
-Landroidx/compose/ui/node/NodeChainKt$fillVector$1;
-Landroidx/compose/ui/node/NodeChainKt;
-Landroidx/compose/ui/node/NodeCoordinator$Companion$PointerInputSource$1;
-Landroidx/compose/ui/node/NodeCoordinator$Companion$SemanticsSource$1;
-Landroidx/compose/ui/node/NodeCoordinator$Companion$onCommitAffectingLayer$1;
-Landroidx/compose/ui/node/NodeCoordinator$Companion$onCommitAffectingLayerParams$1;
-Landroidx/compose/ui/node/NodeCoordinator$HitTestSource;
-Landroidx/compose/ui/node/NodeCoordinator$invalidateParentLayer$1;
-Landroidx/compose/ui/node/NodeCoordinator$invoke$1;
-Landroidx/compose/ui/node/NodeCoordinator$updateLayerParameters$1;
-Landroidx/compose/ui/node/NodeCoordinator;
-Landroidx/compose/ui/node/NodeKind;
-Landroidx/compose/ui/node/NodeKindKt;
-Landroidx/compose/ui/node/NodeMeasuringIntrinsics$DefaultIntrinsicMeasurable;
-Landroidx/compose/ui/node/NodeMeasuringIntrinsics$EmptyPlaceable;
-Landroidx/compose/ui/node/ObserverNode$Companion$OnObserveReadsChanged$1;
-Landroidx/compose/ui/node/ObserverNode$Companion;
-Landroidx/compose/ui/node/ObserverNode;
-Landroidx/compose/ui/node/ObserverNodeKt;
-Landroidx/compose/ui/node/OnPositionedDispatcher$Companion$DepthComparator;
-Landroidx/compose/ui/node/OnPositionedDispatcher;
-Landroidx/compose/ui/node/OwnedLayer;
-Landroidx/compose/ui/node/Owner$OnLayoutCompletedListener;
-Landroidx/compose/ui/node/Owner;
-Landroidx/compose/ui/node/OwnerScope;
-Landroidx/compose/ui/node/OwnerSnapshotObserver$onCommitAffectingLayout$1;
-Landroidx/compose/ui/node/OwnerSnapshotObserver$onCommitAffectingLayoutModifier$1;
-Landroidx/compose/ui/node/OwnerSnapshotObserver$onCommitAffectingMeasure$1;
-Landroidx/compose/ui/node/OwnerSnapshotObserver;
-Landroidx/compose/ui/node/ParentDataModifierNode;
-Landroidx/compose/ui/node/PointerInputModifierNode;
-Landroidx/compose/ui/node/Ref;
-Landroidx/compose/ui/node/RootForTest;
-Landroidx/compose/ui/node/SemanticsModifierNode;
-Landroidx/compose/ui/node/SemanticsModifierNodeKt;
-Landroidx/compose/ui/node/Snake;
-Landroidx/compose/ui/node/TreeSet;
-Landroidx/compose/ui/node/UiApplier;
-Landroidx/compose/ui/platform/AbstractComposeView$ensureCompositionCreated$1;
-Landroidx/compose/ui/platform/AbstractComposeView;
-Landroidx/compose/ui/platform/AccessibilityManager;
-Landroidx/compose/ui/platform/AndroidAccessibilityManager;
-Landroidx/compose/ui/platform/AndroidClipboardManager;
-Landroidx/compose/ui/platform/AndroidComposeView$$ExternalSyntheticLambda0;
-Landroidx/compose/ui/platform/AndroidComposeView$$ExternalSyntheticLambda1;
-Landroidx/compose/ui/platform/AndroidComposeView$$ExternalSyntheticLambda2;
-Landroidx/compose/ui/platform/AndroidComposeView$$ExternalSyntheticLambda3;
-Landroidx/compose/ui/platform/AndroidComposeView$Companion;
-Landroidx/compose/ui/platform/AndroidComposeView$ViewTreeOwners;
-Landroidx/compose/ui/platform/AndroidComposeView$_inputModeManager$1;
-Landroidx/compose/ui/platform/AndroidComposeView$configurationChangeObserver$1;
-Landroidx/compose/ui/platform/AndroidComposeView$focusOwner$1;
-Landroidx/compose/ui/platform/AndroidComposeView$keyInputModifier$1;
-Landroidx/compose/ui/platform/AndroidComposeView$pointerIconService$1;
-Landroidx/compose/ui/platform/AndroidComposeView$resendMotionEventOnLayout$1;
-Landroidx/compose/ui/platform/AndroidComposeView$resendMotionEventRunnable$1;
-Landroidx/compose/ui/platform/AndroidComposeView$rotaryInputModifier$1;
-Landroidx/compose/ui/platform/AndroidComposeView$semanticsModifier$1;
-Landroidx/compose/ui/platform/AndroidComposeView$snapshotObserver$1;
-Landroidx/compose/ui/platform/AndroidComposeView;
-Landroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat$$ExternalSyntheticLambda0;
-Landroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat$$ExternalSyntheticLambda1;
-Landroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat$1;
-Landroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat$MyNodeProvider;
-Landroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat$SemanticsNodeCopy;
-Landroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat$boundsUpdatesEventLoop$1;
-Landroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat$sendScrollEventIfNeededLambda$1;
-Landroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat;
-Landroidx/compose/ui/platform/AndroidComposeViewForceDarkModeQ$$ExternalSyntheticApiModelOutline0;
-Landroidx/compose/ui/platform/AndroidComposeViewForceDarkModeQ;
-Landroidx/compose/ui/platform/AndroidComposeViewVerificationHelperMethodsO;
-Landroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$LocalConfiguration$1;
-Landroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$LocalContext$1;
-Landroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$LocalImageVectorCache$1;
-Landroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$LocalLifecycleOwner$1;
-Landroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$LocalSavedStateRegistryOwner$1;
-Landroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$LocalView$1;
-Landroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$1$1;
-Landroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$2$invoke$$inlined$onDispose$1;
-Landroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$2;
-Landroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3;
-Landroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$4;
-Landroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$obtainImageVectorCache$1$invoke$$inlined$onDispose$1;
-Landroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$obtainImageVectorCache$1;
-Landroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$obtainImageVectorCache$callbacks$1$1;
-Landroidx/compose/ui/platform/AndroidCompositionLocals_androidKt;
-Landroidx/compose/ui/platform/AndroidFontResourceLoader;
-Landroidx/compose/ui/platform/AndroidTextToolbar;
-Landroidx/compose/ui/platform/AndroidUiDispatcher$Companion$Main$2;
-Landroidx/compose/ui/platform/AndroidUiDispatcher$Companion$currentThread$1;
-Landroidx/compose/ui/platform/AndroidUiDispatcher$dispatchCallback$1;
-Landroidx/compose/ui/platform/AndroidUiDispatcher;
-Landroidx/compose/ui/platform/AndroidUiDispatcher_androidKt;
-Landroidx/compose/ui/platform/AndroidUiFrameClock$withFrameNanos$2$1;
-Landroidx/compose/ui/platform/AndroidUiFrameClock$withFrameNanos$2$callback$1;
-Landroidx/compose/ui/platform/AndroidUiFrameClock;
-Landroidx/compose/ui/platform/AndroidUriHandler;
-Landroidx/compose/ui/platform/AndroidViewConfiguration;
-Landroidx/compose/ui/platform/CalculateMatrixToWindow;
-Landroidx/compose/ui/platform/CalculateMatrixToWindowApi29;
-Landroidx/compose/ui/platform/ClipboardManager;
-Landroidx/compose/ui/platform/ComposableSingletons$Wrapper_androidKt$lambda-1$1;
-Landroidx/compose/ui/platform/ComposableSingletons$Wrapper_androidKt;
-Landroidx/compose/ui/platform/ComposeView$Content$1;
-Landroidx/compose/ui/platform/ComposeView;
-Landroidx/compose/ui/platform/CompositionLocalsKt$LocalAccessibilityManager$1;
-Landroidx/compose/ui/platform/CompositionLocalsKt$LocalAutofill$1;
-Landroidx/compose/ui/platform/CompositionLocalsKt$LocalAutofillTree$1;
-Landroidx/compose/ui/platform/CompositionLocalsKt$LocalClipboardManager$1;
-Landroidx/compose/ui/platform/CompositionLocalsKt$LocalDensity$1;
-Landroidx/compose/ui/platform/CompositionLocalsKt$LocalFocusManager$1;
-Landroidx/compose/ui/platform/CompositionLocalsKt$LocalFontFamilyResolver$1;
-Landroidx/compose/ui/platform/CompositionLocalsKt$LocalFontLoader$1;
-Landroidx/compose/ui/platform/CompositionLocalsKt$LocalHapticFeedback$1;
-Landroidx/compose/ui/platform/CompositionLocalsKt$LocalInputModeManager$1;
-Landroidx/compose/ui/platform/CompositionLocalsKt$LocalLayoutDirection$1;
-Landroidx/compose/ui/platform/CompositionLocalsKt$LocalPointerIconService$1;
-Landroidx/compose/ui/platform/CompositionLocalsKt$LocalTextInputService$1;
-Landroidx/compose/ui/platform/CompositionLocalsKt$LocalTextToolbar$1;
-Landroidx/compose/ui/platform/CompositionLocalsKt$LocalUriHandler$1;
-Landroidx/compose/ui/platform/CompositionLocalsKt$LocalViewConfiguration$1;
-Landroidx/compose/ui/platform/CompositionLocalsKt$LocalWindowInfo$1;
-Landroidx/compose/ui/platform/CompositionLocalsKt$ProvideCommonCompositionLocals$1;
-Landroidx/compose/ui/platform/CompositionLocalsKt;
-Landroidx/compose/ui/platform/DeviceRenderNode;
-Landroidx/compose/ui/platform/DisposableSaveableStateRegistry;
-Landroidx/compose/ui/platform/DisposableSaveableStateRegistry_androidKt$DisposableSaveableStateRegistry$1;
-Landroidx/compose/ui/platform/DisposableSaveableStateRegistry_androidKt$DisposableSaveableStateRegistry$registered$1;
-Landroidx/compose/ui/platform/DisposableSaveableStateRegistry_androidKt$DisposableSaveableStateRegistry$saveableStateRegistry$1;
-Landroidx/compose/ui/platform/DisposableSaveableStateRegistry_androidKt;
-Landroidx/compose/ui/platform/GlobalSnapshotManager$ensureStarted$1;
-Landroidx/compose/ui/platform/GlobalSnapshotManager$ensureStarted$2;
-Landroidx/compose/ui/platform/GlobalSnapshotManager;
-Landroidx/compose/ui/platform/InspectableModifier$End;
-Landroidx/compose/ui/platform/InspectableModifier;
-Landroidx/compose/ui/platform/InspectableValueKt$NoInspectorInfo$1;
-Landroidx/compose/ui/platform/InspectableValueKt;
-Landroidx/compose/ui/platform/InspectorValueInfo;
-Landroidx/compose/ui/platform/LayerMatrixCache;
-Landroidx/compose/ui/platform/MotionDurationScaleImpl;
-Landroidx/compose/ui/platform/OutlineResolver;
-Landroidx/compose/ui/platform/RenderNodeApi29$$ExternalSyntheticApiModelOutline0;
-Landroidx/compose/ui/platform/RenderNodeApi29;
-Landroidx/compose/ui/platform/RenderNodeApi29VerificationHelper$$ExternalSyntheticApiModelOutline0;
-Landroidx/compose/ui/platform/RenderNodeApi29VerificationHelper;
-Landroidx/compose/ui/platform/RenderNodeLayer$Companion$getMatrix$1;
-Landroidx/compose/ui/platform/RenderNodeLayer;
-Landroidx/compose/ui/platform/TextToolbar;
-Landroidx/compose/ui/platform/UriHandler;
-Landroidx/compose/ui/platform/ViewCompositionStrategy$DisposeOnDetachedFromWindowOrReleasedFromPool$installFor$1;
-Landroidx/compose/ui/platform/ViewCompositionStrategy$DisposeOnDetachedFromWindowOrReleasedFromPool$installFor$listener$1;
-Landroidx/compose/ui/platform/ViewCompositionStrategy$DisposeOnDetachedFromWindowOrReleasedFromPool$installFor$poolingContainerListener$1;
-Landroidx/compose/ui/platform/ViewConfiguration;
-Landroidx/compose/ui/platform/ViewLayer$Companion$OutlineProvider$1;
-Landroidx/compose/ui/platform/ViewLayer;
-Landroidx/compose/ui/platform/WeakCache;
-Landroidx/compose/ui/platform/WindowInfo;
-Landroidx/compose/ui/platform/WindowInfoImpl;
-Landroidx/compose/ui/platform/WindowRecomposerFactory$Companion$LifecycleAware$1;
-Landroidx/compose/ui/platform/WindowRecomposerFactory$Companion;
-Landroidx/compose/ui/platform/WindowRecomposerFactory;
-Landroidx/compose/ui/platform/WindowRecomposerPolicy$createAndInstallWindowRecomposer$1;
-Landroidx/compose/ui/platform/WindowRecomposerPolicy$createAndInstallWindowRecomposer$unsetJob$1;
-Landroidx/compose/ui/platform/WindowRecomposerPolicy;
-Landroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$1;
-Landroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$2$WhenMappings;
-Landroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$2$onStateChanged$1$1$1$1;
-Landroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$2$onStateChanged$1$1$1;
-Landroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$2$onStateChanged$1;
-Landroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$2;
-Landroidx/compose/ui/platform/WindowRecomposer_androidKt$getAnimationScaleFlowFor$1$1$1;
-Landroidx/compose/ui/platform/WindowRecomposer_androidKt$getAnimationScaleFlowFor$1$1$contentObserver$1;
-Landroidx/compose/ui/platform/WindowRecomposer_androidKt;
-Landroidx/compose/ui/platform/WrappedComposition$setContent$1$1$1;
-Landroidx/compose/ui/platform/WrappedComposition$setContent$1$1$2;
-Landroidx/compose/ui/platform/WrappedComposition$setContent$1$1$3;
-Landroidx/compose/ui/platform/WrappedComposition$setContent$1$1;
-Landroidx/compose/ui/platform/WrappedComposition$setContent$1;
-Landroidx/compose/ui/platform/WrappedComposition;
-Landroidx/compose/ui/platform/WrapperRenderNodeLayerHelperMethods;
-Landroidx/compose/ui/platform/WrapperVerificationHelperMethods$$ExternalSyntheticApiModelOutline0;
-Landroidx/compose/ui/platform/WrapperVerificationHelperMethods;
-Landroidx/compose/ui/platform/Wrapper_androidKt;
-Landroidx/compose/ui/res/ImageVectorCache$ImageVectorEntry;
-Landroidx/compose/ui/res/ImageVectorCache$Key;
-Landroidx/compose/ui/res/ImageVectorCache;
-Landroidx/compose/ui/res/PainterResources_androidKt;
-Landroidx/compose/ui/semantics/AccessibilityAction;
-Landroidx/compose/ui/semantics/CollectionInfo;
-Landroidx/compose/ui/semantics/Role;
-Landroidx/compose/ui/semantics/ScrollAxisRange;
-Landroidx/compose/ui/semantics/SemanticsActions;
-Landroidx/compose/ui/semantics/SemanticsConfiguration;
-Landroidx/compose/ui/semantics/SemanticsConfigurationKt;
-Landroidx/compose/ui/semantics/SemanticsModifier;
-Landroidx/compose/ui/semantics/SemanticsModifierCore;
-Landroidx/compose/ui/semantics/SemanticsModifierKt;
-Landroidx/compose/ui/semantics/SemanticsNode;
-Landroidx/compose/ui/semantics/SemanticsNodeKt;
-Landroidx/compose/ui/semantics/SemanticsOwner;
-Landroidx/compose/ui/semantics/SemanticsProperties$ContentDescription$1;
-Landroidx/compose/ui/semantics/SemanticsProperties$InvisibleToUser$1;
-Landroidx/compose/ui/semantics/SemanticsProperties$PaneTitle$1;
-Landroidx/compose/ui/semantics/SemanticsProperties$Role$1;
-Landroidx/compose/ui/semantics/SemanticsProperties$TestTag$1;
-Landroidx/compose/ui/semantics/SemanticsProperties$Text$1;
-Landroidx/compose/ui/semantics/SemanticsProperties;
-Landroidx/compose/ui/semantics/SemanticsPropertiesKt$ActionPropertyKey$1;
-Landroidx/compose/ui/semantics/SemanticsPropertiesKt;
-Landroidx/compose/ui/semantics/SemanticsPropertyKey$1;
-Landroidx/compose/ui/semantics/SemanticsPropertyKey;
-Landroidx/compose/ui/semantics/SemanticsPropertyReceiver;
-Landroidx/compose/ui/text/AndroidParagraph$wordBoundary$2;
-Landroidx/compose/ui/text/AndroidParagraph;
-Landroidx/compose/ui/text/AnnotatedString$Range;
-Landroidx/compose/ui/text/AnnotatedString;
-Landroidx/compose/ui/text/AnnotatedStringKt;
-Landroidx/compose/ui/text/MultiParagraph;
-Landroidx/compose/ui/text/MultiParagraphIntrinsics$maxIntrinsicWidth$2;
-Landroidx/compose/ui/text/MultiParagraphIntrinsics$minIntrinsicWidth$2;
-Landroidx/compose/ui/text/MultiParagraphIntrinsics;
-Landroidx/compose/ui/text/Paragraph;
-Landroidx/compose/ui/text/ParagraphInfo;
-Landroidx/compose/ui/text/ParagraphIntrinsicInfo;
-Landroidx/compose/ui/text/ParagraphIntrinsics;
-Landroidx/compose/ui/text/ParagraphStyle;
-Landroidx/compose/ui/text/ParagraphStyleKt;
-Landroidx/compose/ui/text/Placeholder;
-Landroidx/compose/ui/text/SpanStyle;
-Landroidx/compose/ui/text/SpanStyleKt$resolveSpanStyleDefaults$1;
-Landroidx/compose/ui/text/SpanStyleKt;
-Landroidx/compose/ui/text/TextLayoutInput;
-Landroidx/compose/ui/text/TextLayoutResult;
-Landroidx/compose/ui/text/TextRange;
-Landroidx/compose/ui/text/TextRangeKt;
-Landroidx/compose/ui/text/TextStyle;
-Landroidx/compose/ui/text/android/BoringLayoutFactory;
-Landroidx/compose/ui/text/android/BoringLayoutFactoryDefault;
-Landroidx/compose/ui/text/android/LayoutCompat;
-Landroidx/compose/ui/text/android/LayoutIntrinsics$boringMetrics$2;
-Landroidx/compose/ui/text/android/LayoutIntrinsics$maxIntrinsicWidth$2;
-Landroidx/compose/ui/text/android/LayoutIntrinsics$minIntrinsicWidth$2;
-Landroidx/compose/ui/text/android/LayoutIntrinsics;
-Landroidx/compose/ui/text/android/StaticLayoutFactory23;
-Landroidx/compose/ui/text/android/StaticLayoutFactory26;
-Landroidx/compose/ui/text/android/StaticLayoutFactory28;
-Landroidx/compose/ui/text/android/StaticLayoutFactory;
-Landroidx/compose/ui/text/android/StaticLayoutFactoryImpl;
-Landroidx/compose/ui/text/android/StaticLayoutParams;
-Landroidx/compose/ui/text/android/TextAlignmentAdapter;
-Landroidx/compose/ui/text/android/TextAndroidCanvas;
-Landroidx/compose/ui/text/android/TextLayout$layoutHelper$2;
-Landroidx/compose/ui/text/android/TextLayout;
-Landroidx/compose/ui/text/android/TextLayoutKt;
-Landroidx/compose/ui/text/android/style/BaselineShiftSpan;
-Landroidx/compose/ui/text/android/style/FontFeatureSpan;
-Landroidx/compose/ui/text/android/style/IndentationFixSpan;
-Landroidx/compose/ui/text/android/style/IndentationFixSpanKt;
-Landroidx/compose/ui/text/android/style/LetterSpacingSpanEm;
-Landroidx/compose/ui/text/android/style/LetterSpacingSpanPx;
-Landroidx/compose/ui/text/android/style/LineHeightSpan;
-Landroidx/compose/ui/text/android/style/LineHeightStyleSpan;
-Landroidx/compose/ui/text/android/style/PlaceholderSpan;
-Landroidx/compose/ui/text/android/style/ShadowSpan;
-Landroidx/compose/ui/text/android/style/SkewXSpan;
-Landroidx/compose/ui/text/android/style/TextDecorationSpan;
-Landroidx/compose/ui/text/caches/ContainerHelpersKt;
-Landroidx/compose/ui/text/caches/LruCache;
-Landroidx/compose/ui/text/caches/SimpleArrayMap;
-Landroidx/compose/ui/text/font/AndroidFontLoader;
-Landroidx/compose/ui/text/font/AndroidFontResolveInterceptor;
-Landroidx/compose/ui/text/font/AndroidFontResolveInterceptor_androidKt;
-Landroidx/compose/ui/text/font/AsyncTypefaceCache;
-Landroidx/compose/ui/text/font/DefaultFontFamily;
-Landroidx/compose/ui/text/font/FileBasedFontFamily;
-Landroidx/compose/ui/text/font/Font$ResourceLoader;
-Landroidx/compose/ui/text/font/FontFamily$Resolver;
-Landroidx/compose/ui/text/font/FontFamily;
-Landroidx/compose/ui/text/font/FontFamilyResolverImpl$createDefaultTypeface$1;
-Landroidx/compose/ui/text/font/FontFamilyResolverImpl$resolve$result$1;
-Landroidx/compose/ui/text/font/FontFamilyResolverImpl;
-Landroidx/compose/ui/text/font/FontFamilyResolverKt;
-Landroidx/compose/ui/text/font/FontListFontFamily;
-Landroidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapter$special$$inlined$CoroutineExceptionHandler$1;
-Landroidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapter;
-Landroidx/compose/ui/text/font/FontStyle;
-Landroidx/compose/ui/text/font/FontSynthesis;
-Landroidx/compose/ui/text/font/FontWeight;
-Landroidx/compose/ui/text/font/GenericFontFamily;
-Landroidx/compose/ui/text/font/PlatformFontFamilyTypefaceAdapter;
-Landroidx/compose/ui/text/font/PlatformFontLoader;
-Landroidx/compose/ui/text/font/PlatformResolveInterceptor$Companion$Default$1;
-Landroidx/compose/ui/text/font/PlatformResolveInterceptor$Companion;
-Landroidx/compose/ui/text/font/PlatformResolveInterceptor;
-Landroidx/compose/ui/text/font/PlatformTypefacesApi28;
-Landroidx/compose/ui/text/font/SystemFontFamily;
-Landroidx/compose/ui/text/font/TypefaceRequest;
-Landroidx/compose/ui/text/font/TypefaceRequestCache$runCached$currentTypefaceResult$1;
-Landroidx/compose/ui/text/font/TypefaceRequestCache;
-Landroidx/compose/ui/text/font/TypefaceResult$Immutable;
-Landroidx/compose/ui/text/font/TypefaceResult;
-Landroidx/compose/ui/text/input/ImmHelper30;
-Landroidx/compose/ui/text/input/ImmHelper;
-Landroidx/compose/ui/text/input/InputMethodManager;
-Landroidx/compose/ui/text/input/InputMethodManagerImpl$imm$2;
-Landroidx/compose/ui/text/input/InputMethodManagerImpl;
-Landroidx/compose/ui/text/input/PlatformTextInputService;
-Landroidx/compose/ui/text/input/TextFieldValue$Companion$Saver$1;
-Landroidx/compose/ui/text/input/TextFieldValue$Companion$Saver$2;
-Landroidx/compose/ui/text/input/TextFieldValue;
-Landroidx/compose/ui/text/input/TextInputService;
-Landroidx/compose/ui/text/input/TextInputServiceAndroid$baseInputConnection$2;
-Landroidx/compose/ui/text/input/TextInputServiceAndroid$textInputCommandEventLoop$1;
-Landroidx/compose/ui/text/input/TextInputServiceAndroid;
-Landroidx/compose/ui/text/intl/AndroidLocale;
-Landroidx/compose/ui/text/intl/Locale;
-Landroidx/compose/ui/text/intl/LocaleList$Companion;
-Landroidx/compose/ui/text/intl/LocaleList;
-Landroidx/compose/ui/text/intl/PlatformLocale;
-Landroidx/compose/ui/text/platform/AndroidParagraphHelper_androidKt$NoopSpan$1;
-Landroidx/compose/ui/text/platform/AndroidParagraphHelper_androidKt;
-Landroidx/compose/ui/text/platform/AndroidParagraphIntrinsics$resolveTypeface$1;
-Landroidx/compose/ui/text/platform/AndroidParagraphIntrinsics;
-Landroidx/compose/ui/text/platform/AndroidTextPaint;
-Landroidx/compose/ui/text/platform/DefaultImpl$getFontLoadState$initCallback$1;
-Landroidx/compose/ui/text/platform/DefaultImpl;
-Landroidx/compose/ui/text/platform/EmojiCompatStatus;
-Landroidx/compose/ui/text/platform/EmojiCompatStatusKt;
-Landroidx/compose/ui/text/platform/ImmutableBool;
-Landroidx/compose/ui/text/platform/TypefaceDirtyTracker;
-Landroidx/compose/ui/text/platform/extensions/LocaleListHelperMethods;
-Landroidx/compose/ui/text/platform/extensions/PlaceholderExtensions_androidKt;
-Landroidx/compose/ui/text/platform/extensions/SpanRange;
-Landroidx/compose/ui/text/platform/extensions/SpannableExtensions_androidKt$setFontAttributes$1;
-Landroidx/compose/ui/text/platform/extensions/SpannableExtensions_androidKt;
-Landroidx/compose/ui/text/platform/extensions/TextPaintExtensions_androidKt;
-Landroidx/compose/ui/text/platform/style/DrawStyleSpan;
-Landroidx/compose/ui/text/platform/style/ShaderBrushSpan;
-Landroidx/compose/ui/text/style/BaselineShift;
-Landroidx/compose/ui/text/style/BrushStyle;
-Landroidx/compose/ui/text/style/ColorStyle;
-Landroidx/compose/ui/text/style/Hyphens;
-Landroidx/compose/ui/text/style/LineBreak$Strategy;
-Landroidx/compose/ui/text/style/LineBreak$Strictness;
-Landroidx/compose/ui/text/style/LineBreak$WordBreak;
-Landroidx/compose/ui/text/style/LineBreak;
-Landroidx/compose/ui/text/style/TextAlign;
-Landroidx/compose/ui/text/style/TextDecoration;
-Landroidx/compose/ui/text/style/TextDirection;
-Landroidx/compose/ui/text/style/TextForegroundStyle$Unspecified;
-Landroidx/compose/ui/text/style/TextForegroundStyle$merge$2;
-Landroidx/compose/ui/text/style/TextForegroundStyle;
-Landroidx/compose/ui/text/style/TextGeometricTransform;
-Landroidx/compose/ui/text/style/TextIndent;
-Landroidx/compose/ui/text/style/TextMotion;
-Landroidx/compose/ui/unit/AndroidDensity_androidKt;
-Landroidx/compose/ui/unit/Constraints$Companion;
-Landroidx/compose/ui/unit/Constraints;
-Landroidx/compose/ui/unit/ConstraintsKt;
-Landroidx/compose/ui/unit/Density;
-Landroidx/compose/ui/unit/DensityImpl;
-Landroidx/compose/ui/unit/Dp;
-Landroidx/compose/ui/unit/DpKt;
-Landroidx/compose/ui/unit/DpOffset;
-Landroidx/compose/ui/unit/DpRect;
-Landroidx/compose/ui/unit/IntOffset$Companion;
-Landroidx/compose/ui/unit/IntOffset;
-Landroidx/compose/ui/unit/IntOffsetKt;
-Landroidx/compose/ui/unit/IntSize$Companion;
-Landroidx/compose/ui/unit/IntSize;
-Landroidx/compose/ui/unit/IntSizeKt;
-Landroidx/compose/ui/unit/LayoutDirection;
-Landroidx/compose/ui/unit/TextUnit;
-Landroidx/compose/ui/unit/TextUnitKt;
-Landroidx/compose/ui/unit/TextUnitType;
-Landroidx/compose/ui/util/MathHelpersKt;
-Landroidx/core/R$id;
-Landroidx/core/app/ComponentActivity;
-Landroidx/core/app/CoreComponentFactory$CompatWrapped;
-Landroidx/core/app/CoreComponentFactory;
-Landroidx/core/content/res/CamUtils;
-Landroidx/core/content/res/ColorStateListInflaterCompat;
-Landroidx/core/content/res/TypedArrayUtils;
-Landroidx/core/graphics/TypefaceCompat;
-Landroidx/core/graphics/TypefaceCompatApi29Impl;
-Landroidx/core/graphics/TypefaceCompatBaseImpl;
-Landroidx/core/graphics/TypefaceCompatUtil$Api19Impl;
-Landroidx/core/graphics/TypefaceCompatUtil;
-Landroidx/core/math/MathUtils;
-Landroidx/core/os/BuildCompat;
-Landroidx/core/os/TraceCompat$Api18Impl;
-Landroidx/core/os/TraceCompat;
-Landroidx/core/provider/FontProvider$$ExternalSyntheticLambda0;
-Landroidx/core/provider/FontProvider$Api16Impl;
-Landroidx/core/provider/FontProvider;
-Landroidx/core/provider/FontRequest;
-Landroidx/core/provider/FontsContractCompat$FontFamilyResult;
-Landroidx/core/provider/FontsContractCompat$FontInfo;
-Landroidx/core/text/TextUtilsCompat$Api17Impl;
-Landroidx/core/text/TextUtilsCompat;
-Landroidx/core/util/Preconditions;
-Landroidx/core/view/AccessibilityDelegateCompat$AccessibilityDelegateAdapter;
-Landroidx/core/view/AccessibilityDelegateCompat;
-Landroidx/core/view/MenuHostHelper;
-Landroidx/core/view/ViewCompat$$ExternalSyntheticLambda0;
-Landroidx/core/view/ViewCompat;
-Landroidx/core/view/accessibility/AccessibilityNodeProviderCompat;
-Landroidx/customview/poolingcontainer/PoolingContainer;
-Landroidx/customview/poolingcontainer/PoolingContainerListener;
-Landroidx/customview/poolingcontainer/PoolingContainerListenerHolder;
-Landroidx/emoji2/text/ConcurrencyHelpers$$ExternalSyntheticLambda0;
-Landroidx/emoji2/text/ConcurrencyHelpers$Handler28Impl;
-Landroidx/emoji2/text/DefaultEmojiCompatConfig;
-Landroidx/emoji2/text/DefaultGlyphChecker;
-Landroidx/emoji2/text/EmojiCompat$CompatInternal19$1;
-Landroidx/emoji2/text/EmojiCompat$CompatInternal19;
-Landroidx/emoji2/text/EmojiCompat$CompatInternal;
-Landroidx/emoji2/text/EmojiCompat$Config;
-Landroidx/emoji2/text/EmojiCompat$DefaultSpanFactory;
-Landroidx/emoji2/text/EmojiCompat$GlyphChecker;
-Landroidx/emoji2/text/EmojiCompat$InitCallback;
-Landroidx/emoji2/text/EmojiCompat$ListenerDispatcher;
-Landroidx/emoji2/text/EmojiCompat$MetadataRepoLoader;
-Landroidx/emoji2/text/EmojiCompat$MetadataRepoLoaderCallback;
-Landroidx/emoji2/text/EmojiCompat$SpanFactory;
-Landroidx/emoji2/text/EmojiCompat;
-Landroidx/emoji2/text/EmojiCompatInitializer$1;
-Landroidx/emoji2/text/EmojiCompatInitializer$BackgroundDefaultConfig;
-Landroidx/emoji2/text/EmojiCompatInitializer$BackgroundDefaultLoader$$ExternalSyntheticLambda0;
-Landroidx/emoji2/text/EmojiCompatInitializer$BackgroundDefaultLoader$1;
-Landroidx/emoji2/text/EmojiCompatInitializer$BackgroundDefaultLoader;
-Landroidx/emoji2/text/EmojiCompatInitializer$LoadEmojiCompatRunnable;
-Landroidx/emoji2/text/EmojiCompatInitializer;
-Landroidx/emoji2/text/EmojiExclusions$EmojiExclusions_Reflections;
-Landroidx/emoji2/text/EmojiProcessor$EmojiProcessAddSpanCallback;
-Landroidx/emoji2/text/EmojiProcessor$EmojiProcessCallback;
-Landroidx/emoji2/text/EmojiProcessor$ProcessorSm;
-Landroidx/emoji2/text/EmojiProcessor;
-Landroidx/emoji2/text/EmojiSpan;
-Landroidx/emoji2/text/FontRequestEmojiCompatConfig$FontProviderHelper;
-Landroidx/emoji2/text/FontRequestEmojiCompatConfig$FontRequestMetadataLoader$$ExternalSyntheticLambda0;
-Landroidx/emoji2/text/FontRequestEmojiCompatConfig$FontRequestMetadataLoader;
-Landroidx/emoji2/text/FontRequestEmojiCompatConfig;
-Landroidx/emoji2/text/MetadataListReader$ByteBufferReader;
-Landroidx/emoji2/text/MetadataListReader;
-Landroidx/emoji2/text/MetadataRepo$Node;
-Landroidx/emoji2/text/MetadataRepo;
-Landroidx/emoji2/text/SpannableBuilder;
-Landroidx/emoji2/text/TypefaceEmojiRasterizer;
-Landroidx/emoji2/text/UnprecomputeTextOnModificationSpannable;
-Landroidx/emoji2/text/flatbuffer/MetadataItem;
-Landroidx/emoji2/text/flatbuffer/MetadataList;
-Landroidx/emoji2/text/flatbuffer/Table;
-Landroidx/emoji2/text/flatbuffer/Utf8Safe;
-Landroidx/lifecycle/DefaultLifecycleObserver;
-Landroidx/lifecycle/DefaultLifecycleObserverAdapter$WhenMappings;
-Landroidx/lifecycle/DefaultLifecycleObserverAdapter;
-Landroidx/lifecycle/EmptyActivityLifecycleCallbacks;
-Landroidx/lifecycle/HasDefaultViewModelProviderFactory;
-Landroidx/lifecycle/Lifecycle$Event$Companion;
-Landroidx/lifecycle/Lifecycle$Event$WhenMappings;
-Landroidx/lifecycle/Lifecycle$Event;
-Landroidx/lifecycle/Lifecycle$State;
-Landroidx/lifecycle/Lifecycle;
-Landroidx/lifecycle/LifecycleDispatcher$DispatcherActivityCallback;
-Landroidx/lifecycle/LifecycleDispatcher;
-Landroidx/lifecycle/LifecycleEventObserver;
-Landroidx/lifecycle/LifecycleObserver;
-Landroidx/lifecycle/LifecycleOwner;
-Landroidx/lifecycle/LifecycleRegistry$ObserverWithState;
-Landroidx/lifecycle/LifecycleRegistry;
-Landroidx/lifecycle/LifecycleRegistryOwner;
-Landroidx/lifecycle/Lifecycling;
-Landroidx/lifecycle/ProcessLifecycleInitializer;
-Landroidx/lifecycle/ProcessLifecycleOwner$1;
-Landroidx/lifecycle/ProcessLifecycleOwner$2;
-Landroidx/lifecycle/ProcessLifecycleOwner$3$$ExternalSyntheticApiModelOutline0;
-Landroidx/lifecycle/ProcessLifecycleOwner$3$1;
-Landroidx/lifecycle/ProcessLifecycleOwner$3;
-Landroidx/lifecycle/ProcessLifecycleOwner;
-Landroidx/lifecycle/ReportFragment$ActivityInitializationListener;
-Landroidx/lifecycle/ReportFragment$LifecycleCallbacks$$ExternalSyntheticApiModelOutline0;
-Landroidx/lifecycle/ReportFragment$LifecycleCallbacks;
-Landroidx/lifecycle/ReportFragment;
-Landroidx/lifecycle/SavedStateHandleAttacher;
-Landroidx/lifecycle/SavedStateHandleSupport$DEFAULT_ARGS_KEY$1;
-Landroidx/lifecycle/SavedStateHandleSupport$SAVED_STATE_REGISTRY_OWNER_KEY$1;
-Landroidx/lifecycle/SavedStateHandleSupport$VIEW_MODEL_STORE_OWNER_KEY$1;
-Landroidx/lifecycle/SavedStateHandleSupport$savedStateHandlesVM$1$1;
-Landroidx/lifecycle/SavedStateHandleSupport;
-Landroidx/lifecycle/SavedStateHandlesProvider$viewModel$2;
-Landroidx/lifecycle/SavedStateHandlesProvider;
-Landroidx/lifecycle/SavedStateHandlesVM;
-Landroidx/lifecycle/ViewModel;
-Landroidx/lifecycle/ViewModelProvider$AndroidViewModelFactory$Companion$ApplicationKeyImpl;
-Landroidx/lifecycle/ViewModelProvider$Factory;
-Landroidx/lifecycle/ViewModelProvider$NewInstanceFactory$Companion$ViewModelKeyImpl;
-Landroidx/lifecycle/ViewModelStore;
-Landroidx/lifecycle/ViewModelStoreOwner;
-Landroidx/lifecycle/ViewTreeLifecycleOwner$findViewTreeLifecycleOwner$1;
-Landroidx/lifecycle/ViewTreeLifecycleOwner$findViewTreeLifecycleOwner$2;
-Landroidx/lifecycle/ViewTreeLifecycleOwner;
-Landroidx/lifecycle/runtime/R$id;
-Landroidx/lifecycle/viewmodel/CreationExtras$Empty;
-Landroidx/lifecycle/viewmodel/CreationExtras;
-Landroidx/lifecycle/viewmodel/InitializerViewModelFactory;
-Landroidx/lifecycle/viewmodel/MutableCreationExtras;
-Landroidx/lifecycle/viewmodel/ViewModelInitializer;
-Landroidx/metrics/performance/DelegatingFrameMetricsListener;
-Landroidx/metrics/performance/FrameData;
-Landroidx/metrics/performance/FrameDataApi24;
-Landroidx/metrics/performance/FrameDataApi31;
-Landroidx/metrics/performance/JankStats$OnFrameListener;
-Landroidx/metrics/performance/JankStats;
-Landroidx/metrics/performance/JankStatsApi16Impl$onFrameListenerDelegate$1;
-Landroidx/metrics/performance/JankStatsApi16Impl;
-Landroidx/metrics/performance/JankStatsApi22Impl;
-Landroidx/metrics/performance/JankStatsApi24Impl$$ExternalSyntheticLambda0;
-Landroidx/metrics/performance/JankStatsApi24Impl;
-Landroidx/metrics/performance/JankStatsApi26Impl;
-Landroidx/metrics/performance/JankStatsApi31Impl;
-Landroidx/metrics/performance/JankStatsBaseImpl;
-Landroidx/metrics/performance/PerformanceMetricsState$Holder;
-Landroidx/metrics/performance/PerformanceMetricsState;
-Landroidx/metrics/performance/R$id;
-Landroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda0;
-Landroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda1;
-Landroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl$$ExternalSyntheticLambda0;
-Landroidx/profileinstaller/ProfileInstallerInitializer$Choreographer16Impl;
-Landroidx/profileinstaller/ProfileInstallerInitializer$Handler28Impl;
-Landroidx/profileinstaller/ProfileInstallerInitializer$Result;
-Landroidx/profileinstaller/ProfileInstallerInitializer;
-Landroidx/savedstate/Recreator;
-Landroidx/savedstate/SavedStateRegistry$$ExternalSyntheticLambda0;
-Landroidx/savedstate/SavedStateRegistry$SavedStateProvider;
-Landroidx/savedstate/SavedStateRegistry;
-Landroidx/savedstate/SavedStateRegistryController;
-Landroidx/savedstate/SavedStateRegistryOwner;
-Landroidx/savedstate/ViewTreeSavedStateRegistryOwner$findViewTreeSavedStateRegistryOwner$1;
-Landroidx/savedstate/ViewTreeSavedStateRegistryOwner$findViewTreeSavedStateRegistryOwner$2;
-Landroidx/startup/AppInitializer;
-Landroidx/startup/InitializationProvider;
-Landroidx/startup/Initializer;
-Landroidx/tracing/Trace$$ExternalSyntheticApiModelOutline0;
-Landroidx/tracing/Trace;
-Landroidx/tv/foundation/ContentInViewModifier$modifier$1;
-Landroidx/tv/foundation/ContentInViewModifier;
-Landroidx/tv/foundation/PivotOffsets;
-Landroidx/tv/foundation/ScrollableWithPivotKt$scrollableNestedScrollConnection$1;
-Landroidx/tv/foundation/ScrollableWithPivotKt$scrollableWithPivot$2;
-Landroidx/tv/foundation/ScrollableWithPivotKt;
-Landroidx/tv/foundation/ScrollingLogic;
-Landroidx/tv/foundation/lazy/layout/LazyLayoutSemanticState;
-Landroidx/tv/foundation/lazy/layout/LazyLayoutSemanticsKt$lazyLayoutSemantics$1$1;
-Landroidx/tv/foundation/lazy/layout/LazyLayoutSemanticsKt$lazyLayoutSemantics$1$indexForKeyMapping$1;
-Landroidx/tv/foundation/lazy/layout/LazyLayoutSemanticsKt$lazyLayoutSemantics$1$scrollByAction$1;
-Landroidx/tv/foundation/lazy/layout/LazyLayoutSemanticsKt$lazyLayoutSemantics$1$scrollToIndexAction$1;
-Landroidx/tv/foundation/lazy/layout/LazyLayoutSemanticsKt;
-Landroidx/tv/foundation/lazy/list/AwaitFirstLayoutModifier$waitForFirstLayout$1;
-Landroidx/tv/foundation/lazy/list/AwaitFirstLayoutModifier;
-Landroidx/tv/foundation/lazy/list/DataIndex;
-Landroidx/tv/foundation/lazy/list/EmptyLazyListLayoutInfo;
-Landroidx/tv/foundation/lazy/list/ItemInfo;
-Landroidx/tv/foundation/lazy/list/LazyDslKt;
-Landroidx/tv/foundation/lazy/list/LazyListBeyondBoundsInfo$Interval;
-Landroidx/tv/foundation/lazy/list/LazyListBeyondBoundsInfo;
-Landroidx/tv/foundation/lazy/list/LazyListBeyondBoundsModifierLocal$Companion$emptyBeyondBoundsScope$1;
-Landroidx/tv/foundation/lazy/list/LazyListBeyondBoundsModifierLocal$layout$2;
-Landroidx/tv/foundation/lazy/list/LazyListBeyondBoundsModifierLocal;
-Landroidx/tv/foundation/lazy/list/LazyListIntervalContent;
-Landroidx/tv/foundation/lazy/list/LazyListItemPlacementAnimator$getAnimatedOffset$1;
-Landroidx/tv/foundation/lazy/list/LazyListItemPlacementAnimator$onMeasured$$inlined$sortBy$1;
-Landroidx/tv/foundation/lazy/list/LazyListItemPlacementAnimator$onMeasured$$inlined$sortBy$2;
-Landroidx/tv/foundation/lazy/list/LazyListItemPlacementAnimator$onMeasured$$inlined$sortByDescending$1;
-Landroidx/tv/foundation/lazy/list/LazyListItemPlacementAnimator$onMeasured$$inlined$sortByDescending$2;
-Landroidx/tv/foundation/lazy/list/LazyListItemPlacementAnimator;
-Landroidx/tv/foundation/lazy/list/LazyListItemProvider;
-Landroidx/tv/foundation/lazy/list/LazyListItemProviderImpl$1$1;
-Landroidx/tv/foundation/lazy/list/LazyListItemProviderImpl$1;
-Landroidx/tv/foundation/lazy/list/LazyListItemProviderImpl;
-Landroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$1$1;
-Landroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$1$itemProviderState$1;
-Landroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$nearestItemsRangeState$1$1;
-Landroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$nearestItemsRangeState$2;
-Landroidx/tv/foundation/lazy/list/LazyListItemProviderKt$rememberLazyListItemProvider$nearestItemsRangeState$3;
-Landroidx/tv/foundation/lazy/list/LazyListKt$ScrollPositionUpdater$1;
-Landroidx/tv/foundation/lazy/list/LazyListKt$rememberLazyListMeasurePolicy$1$1$2;
-Landroidx/tv/foundation/lazy/list/LazyListKt$rememberLazyListMeasurePolicy$1$1$measuredItemProvider$1;
-Landroidx/tv/foundation/lazy/list/LazyListKt$rememberLazyListMeasurePolicy$1$1;
-Landroidx/tv/foundation/lazy/list/LazyListKt;
-Landroidx/tv/foundation/lazy/list/LazyListMeasureKt$measureLazyList$1;
-Landroidx/tv/foundation/lazy/list/LazyListMeasureKt$measureLazyList$5;
-Landroidx/tv/foundation/lazy/list/LazyListMeasureKt;
-Landroidx/tv/foundation/lazy/list/LazyListMeasureResult;
-Landroidx/tv/foundation/lazy/list/LazyListPinnableContainerProviderKt$LazyListPinnableContainerProvider$1$1$invoke$$inlined$onDispose$1;
-Landroidx/tv/foundation/lazy/list/LazyListPinnableContainerProviderKt$LazyListPinnableContainerProvider$1$1;
-Landroidx/tv/foundation/lazy/list/LazyListPinnableContainerProviderKt;
-Landroidx/tv/foundation/lazy/list/LazyListPinnableItem;
-Landroidx/tv/foundation/lazy/list/LazyListPinnedItem;
-Landroidx/tv/foundation/lazy/list/LazyListPlaceableWrapper;
-Landroidx/tv/foundation/lazy/list/LazyListPositionedItem;
-Landroidx/tv/foundation/lazy/list/LazyListScrollPosition;
-Landroidx/tv/foundation/lazy/list/LazyListStateKt$rememberTvLazyListState$1$1;
-Landroidx/tv/foundation/lazy/list/LazyListStateKt;
-Landroidx/tv/foundation/lazy/list/LazyMeasuredItem;
-Landroidx/tv/foundation/lazy/list/LazyMeasuredItemProvider;
-Landroidx/tv/foundation/lazy/list/LazySemanticsKt$rememberLazyListSemanticState$1$1$scrollAxisRange$1;
-Landroidx/tv/foundation/lazy/list/LazySemanticsKt$rememberLazyListSemanticState$1$1$scrollAxisRange$2;
-Landroidx/tv/foundation/lazy/list/LazySemanticsKt$rememberLazyListSemanticState$1$1;
-Landroidx/tv/foundation/lazy/list/MeasuredItemFactory;
-Landroidx/tv/foundation/lazy/list/PlaceableInfo;
-Landroidx/tv/foundation/lazy/list/TvLazyListItemInfo;
-Landroidx/tv/foundation/lazy/list/TvLazyListItemScope;
-Landroidx/tv/foundation/lazy/list/TvLazyListItemScopeImpl;
-Landroidx/tv/foundation/lazy/list/TvLazyListLayoutInfo;
-Landroidx/tv/foundation/lazy/list/TvLazyListScope$items$1;
-Landroidx/tv/foundation/lazy/list/TvLazyListScope;
-Landroidx/tv/foundation/lazy/list/TvLazyListScopeImpl;
-Landroidx/tv/foundation/lazy/list/TvLazyListState$Companion$Saver$1;
-Landroidx/tv/foundation/lazy/list/TvLazyListState$Companion$Saver$2;
-Landroidx/tv/foundation/lazy/list/TvLazyListState$remeasurementModifier$1;
-Landroidx/tv/foundation/lazy/list/TvLazyListState$scroll$1;
-Landroidx/tv/foundation/lazy/list/TvLazyListState$scrollableState$1;
-Landroidx/tv/foundation/lazy/list/TvLazyListState;
-Landroidx/tv/material3/ColorScheme;
-Landroidx/tv/material3/ColorSchemeKt$LocalColorScheme$1;
-Landroidx/tv/material3/ColorSchemeKt;
-Landroidx/tv/material3/ContentColorKt$LocalContentColor$1;
-Landroidx/tv/material3/ContentColorKt;
-Landroidx/tv/material3/MaterialThemeKt$$ExternalSyntheticOutline0;
-Landroidx/tv/material3/MaterialThemeKt$MaterialTheme$1;
-Landroidx/tv/material3/MaterialThemeKt$MaterialTheme$2;
-Landroidx/tv/material3/MaterialThemeKt;
-Landroidx/tv/material3/ShapeDefaults;
-Landroidx/tv/material3/Shapes;
-Landroidx/tv/material3/ShapesKt$LocalShapes$1;
-Landroidx/tv/material3/ShapesKt;
-Landroidx/tv/material3/TabColors;
-Landroidx/tv/material3/TabKt$Tab$1;
-Landroidx/tv/material3/TabKt$Tab$3$1$1;
-Landroidx/tv/material3/TabKt$Tab$3$2$1;
-Landroidx/tv/material3/TabKt$Tab$3;
-Landroidx/tv/material3/TabKt$Tab$4;
-Landroidx/tv/material3/TabKt;
-Landroidx/tv/material3/TabRowDefaults$PillIndicator$1;
-Landroidx/tv/material3/TabRowDefaults;
-Landroidx/tv/material3/TabRowKt$LocalTabRowHasFocus$1;
-Landroidx/tv/material3/TabRowKt$TabRow$1;
-Landroidx/tv/material3/TabRowKt$TabRow$2$1$1;
-Landroidx/tv/material3/TabRowKt$TabRow$2$2$1$1$2;
-Landroidx/tv/material3/TabRowKt$TabRow$2$2$1$1;
-Landroidx/tv/material3/TabRowKt$TabRow$2$2$1$separators$1;
-Landroidx/tv/material3/TabRowKt$TabRow$2$2$1;
-Landroidx/tv/material3/TabRowKt$TabRow$2;
-Landroidx/tv/material3/TabRowKt$TabRow$3;
-Landroidx/tv/material3/TabRowKt;
-Landroidx/tv/material3/TabRowSlots;
-Landroidx/tv/material3/TextKt$LocalTextStyle$1;
-Landroidx/tv/material3/TextKt$Text$1;
-Landroidx/tv/material3/TextKt$Text$2;
-Landroidx/tv/material3/TextKt;
-Landroidx/tv/material3/Typography;
-Landroidx/tv/material3/TypographyKt$LocalTypography$1;
-Landroidx/tv/material3/TypographyKt;
-Landroidx/tv/material3/tokens/ColorDarkTokens;
-Landroidx/tv/material3/tokens/PaletteTokens;
-Landroidx/tv/material3/tokens/ShapeTokens;
-Landroidx/tv/material3/tokens/TypeScaleTokens;
-Landroidx/tv/material3/tokens/TypefaceTokens;
-Landroidx/tv/material3/tokens/TypographyTokens;
-Lcoil/base/R$id;
-Lcoil/request/ViewTargetDisposable;
-Lcom/example/tvcomposebasedtests/ComposableSingletons$MainActivityKt$lambda-1$1;
-Lcom/example/tvcomposebasedtests/ComposableSingletons$MainActivityKt;
-Lcom/example/tvcomposebasedtests/JankStatsAggregator$OnJankReportListener;
-Lcom/example/tvcomposebasedtests/JankStatsAggregator$listener$1;
-Lcom/example/tvcomposebasedtests/JankStatsAggregator;
-Lcom/example/tvcomposebasedtests/MainActivity$jankReportListener$1;
-Lcom/example/tvcomposebasedtests/MainActivity$startFrameMetrics$listener$1;
-Lcom/example/tvcomposebasedtests/MainActivity;
-Lcom/example/tvcomposebasedtests/UtilsKt;
-Lcom/example/tvcomposebasedtests/tvComponents/AppKt$App$1$1$1$1;
-Lcom/example/tvcomposebasedtests/tvComponents/AppKt$App$1;
-Lcom/example/tvcomposebasedtests/tvComponents/AppKt;
-Lcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$LazyContainersKt$lambda-1$1;
-Lcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$LazyContainersKt$lambda-2$1;
-Lcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$LazyContainersKt$lambda-3$1;
-Lcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$LazyContainersKt$lambda-4$1;
-Lcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$LazyContainersKt;
-Lcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-1$1;
-Lcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-2$1;
-Lcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-3$1;
-Lcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-4$1;
-Lcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-5$1;
-Lcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-6$1;
-Lcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-7$1;
-Lcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt;
-Lcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$SampleCardItem$2;
-Lcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$SampleTvLazyRow$1$1;
-Lcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$drawBorderAndScaleOnFocus$1$1;
-Lcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt;
-Lcom/example/tvcomposebasedtests/tvComponents/Navigation;
-Lcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt$PillIndicatorTabRow$1$1$1$1;
-Lcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt$PillIndicatorTabRow$1$1$2;
-Lcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt$PillIndicatorTabRow$1;
-Lcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt$TopNavigation$2$1;
-Lcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt$TopNavigation$3$1;
-Lcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt$TopNavigation$4;
-Lcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt;
-Lcom/google/gson/internal/ObjectConstructor;
-Lkotlin/ExceptionsKt;
-Lkotlin/Function;
-Lkotlin/Lazy;
-Lkotlin/LazyKt__LazyJVMKt;
-Lkotlin/NoWhenBranchMatchedException;
-Lkotlin/Pair;
-Lkotlin/Result$Failure;
-Lkotlin/Result;
-Lkotlin/ResultKt;
-Lkotlin/SynchronizedLazyImpl;
-Lkotlin/UNINITIALIZED_VALUE;
-Lkotlin/Unit;
-Lkotlin/UnsafeLazyImpl;
-Lkotlin/UnsignedKt;
-Lkotlin/collections/AbstractCollection;
-Lkotlin/collections/AbstractList;
-Lkotlin/collections/AbstractMap;
-Lkotlin/collections/AbstractMutableList;
-Lkotlin/collections/AbstractMutableMap;
-Lkotlin/collections/AbstractSet;
-Lkotlin/collections/ArrayDeque;
-Lkotlin/collections/ArraysKt__ArraysKt;
-Lkotlin/collections/ArraysKt___ArraysKt;
-Lkotlin/collections/CollectionsKt__CollectionsKt;
-Lkotlin/collections/CollectionsKt__IteratorsJVMKt;
-Lkotlin/collections/CollectionsKt__MutableCollectionsJVMKt;
-Lkotlin/collections/CollectionsKt__ReversedViewsKt;
-Lkotlin/collections/CollectionsKt___CollectionsKt;
-Lkotlin/collections/EmptyList;
-Lkotlin/collections/EmptyMap;
-Lkotlin/collections/IntIterator;
-Lkotlin/collections/MapsKt___MapsJvmKt;
-Lkotlin/collections/SetsKt__SetsKt;
-Lkotlin/coroutines/AbstractCoroutineContextElement;
-Lkotlin/coroutines/AbstractCoroutineContextKey;
-Lkotlin/coroutines/CombinedContext;
-Lkotlin/coroutines/Continuation;
-Lkotlin/coroutines/ContinuationInterceptor$Key;
-Lkotlin/coroutines/ContinuationInterceptor;
-Lkotlin/coroutines/ContinuationKt;
-Lkotlin/coroutines/CoroutineContext$DefaultImpls;
-Lkotlin/coroutines/CoroutineContext$Element$DefaultImpls;
-Lkotlin/coroutines/CoroutineContext$Element;
-Lkotlin/coroutines/CoroutineContext$Key;
-Lkotlin/coroutines/CoroutineContext$plus$1;
-Lkotlin/coroutines/CoroutineContext;
-Lkotlin/coroutines/EmptyCoroutineContext;
-Lkotlin/coroutines/SafeContinuation;
-Lkotlin/coroutines/intrinsics/CoroutineSingletons;
-Lkotlin/coroutines/jvm/internal/BaseContinuationImpl;
-Lkotlin/coroutines/jvm/internal/CompletedContinuation;
-Lkotlin/coroutines/jvm/internal/ContinuationImpl;
-Lkotlin/coroutines/jvm/internal/CoroutineStackFrame;
-Lkotlin/coroutines/jvm/internal/RestrictedContinuationImpl;
-Lkotlin/coroutines/jvm/internal/RestrictedSuspendLambda;
-Lkotlin/coroutines/jvm/internal/SuspendLambda;
-Lkotlin/internal/ProgressionUtilKt;
-Lkotlin/jvm/JvmClassMappingKt;
-Lkotlin/jvm/functions/Function0;
-Lkotlin/jvm/functions/Function10;
-Lkotlin/jvm/functions/Function11;
-Lkotlin/jvm/functions/Function12;
-Lkotlin/jvm/functions/Function13;
-Lkotlin/jvm/functions/Function14;
-Lkotlin/jvm/functions/Function15;
-Lkotlin/jvm/functions/Function16;
-Lkotlin/jvm/functions/Function17;
-Lkotlin/jvm/functions/Function18;
-Lkotlin/jvm/functions/Function19;
-Lkotlin/jvm/functions/Function1;
-Lkotlin/jvm/functions/Function20;
-Lkotlin/jvm/functions/Function21;
-Lkotlin/jvm/functions/Function22;
-Lkotlin/jvm/functions/Function2;
-Lkotlin/jvm/functions/Function3;
-Lkotlin/jvm/functions/Function4;
-Lkotlin/jvm/functions/Function5;
-Lkotlin/jvm/functions/Function6;
-Lkotlin/jvm/functions/Function7;
-Lkotlin/jvm/functions/Function8;
-Lkotlin/jvm/functions/Function9;
-Lkotlin/jvm/internal/CallableReference$NoReceiver;
-Lkotlin/jvm/internal/CallableReference;
-Lkotlin/jvm/internal/ClassBasedDeclarationContainer;
-Lkotlin/jvm/internal/ClassReference;
-Lkotlin/jvm/internal/CollectionToArray;
-Lkotlin/jvm/internal/FunctionBase;
-Lkotlin/jvm/internal/FunctionReference;
-Lkotlin/jvm/internal/FunctionReferenceImpl;
-Lkotlin/jvm/internal/Intrinsics$$ExternalSyntheticCheckNotZero0;
-Lkotlin/jvm/internal/Intrinsics$Kotlin;
-Lkotlin/jvm/internal/Intrinsics;
-Lkotlin/jvm/internal/Lambda;
-Lkotlin/jvm/internal/MutablePropertyReference1;
-Lkotlin/jvm/internal/MutablePropertyReference1Impl;
-Lkotlin/jvm/internal/MutablePropertyReference;
-Lkotlin/jvm/internal/PropertyReference;
-Lkotlin/jvm/internal/Ref$BooleanRef;
-Lkotlin/jvm/internal/Ref$FloatRef;
-Lkotlin/jvm/internal/Ref$ObjectRef;
-Lkotlin/jvm/internal/Reflection;
-Lkotlin/jvm/internal/ReflectionFactory;
-Lkotlin/jvm/internal/TypeIntrinsics;
-Lkotlin/jvm/internal/markers/KMappedMarker;
-Lkotlin/jvm/internal/markers/KMutableCollection;
-Lkotlin/jvm/internal/markers/KMutableMap;
-Lkotlin/math/MathKt;
-Lkotlin/math/MathKt__MathJVMKt;
-Lkotlin/ranges/ClosedRange;
-Lkotlin/ranges/IntProgression;
-Lkotlin/ranges/IntProgressionIterator;
-Lkotlin/ranges/IntRange;
-Lkotlin/ranges/RangesKt___RangesKt;
-Lkotlin/reflect/KCallable;
-Lkotlin/reflect/KClass;
-Lkotlin/reflect/KFunction;
-Lkotlin/reflect/KMutableProperty1;
-Lkotlin/reflect/KProperty1;
-Lkotlin/reflect/KProperty;
-Lkotlin/sequences/ConstrainedOnceSequence;
-Lkotlin/sequences/FilteringSequence$iterator$1;
-Lkotlin/sequences/FilteringSequence;
-Lkotlin/sequences/GeneratorSequence$iterator$1;
-Lkotlin/sequences/GeneratorSequence;
-Lkotlin/sequences/Sequence;
-Lkotlin/sequences/SequencesKt__SequencesJVMKt;
-Lkotlin/sequences/SequencesKt__SequencesKt$asSequence$$inlined$Sequence$1;
-Lkotlin/sequences/SequencesKt__SequencesKt$generateSequence$2;
-Lkotlin/sequences/SequencesKt__SequencesKt;
-Lkotlin/sequences/SequencesKt___SequencesJvmKt;
-Lkotlin/sequences/SequencesKt___SequencesKt$filterNotNull$1;
-Lkotlin/sequences/SequencesKt___SequencesKt;
-Lkotlin/sequences/TransformingSequence$iterator$1;
-Lkotlin/sequences/TransformingSequence;
-Lkotlin/text/CharsKt__CharKt;
-Lkotlin/text/StringsKt__AppendableKt;
-Lkotlin/text/StringsKt__IndentKt;
-Lkotlin/text/StringsKt__RegexExtensionsKt;
-Lkotlin/text/StringsKt__StringBuilderKt;
-Lkotlin/text/StringsKt__StringNumberConversionsKt;
-Lkotlin/text/StringsKt__StringsJVMKt;
-Lkotlin/text/StringsKt__StringsKt;
-Lkotlinx/coroutines/AbstractCoroutine;
-Lkotlinx/coroutines/Active;
-Lkotlinx/coroutines/BeforeResumeCancelHandler;
-Lkotlinx/coroutines/BlockingCoroutine;
-Lkotlinx/coroutines/BlockingEventLoop;
-Lkotlinx/coroutines/BuildersKt;
-Lkotlinx/coroutines/CancelHandler;
-Lkotlinx/coroutines/CancelHandlerBase;
-Lkotlinx/coroutines/CancellableContinuation;
-Lkotlinx/coroutines/CancellableContinuationImpl;
-Lkotlinx/coroutines/CancellableContinuationImplKt;
-Lkotlinx/coroutines/CancellableContinuationKt;
-Lkotlinx/coroutines/CancelledContinuation;
-Lkotlinx/coroutines/ChildContinuation;
-Lkotlinx/coroutines/ChildHandle;
-Lkotlinx/coroutines/ChildHandleNode;
-Lkotlinx/coroutines/ChildJob;
-Lkotlinx/coroutines/CompletableDeferredImpl;
-Lkotlinx/coroutines/CompletedContinuation;
-Lkotlinx/coroutines/CompletedExceptionally;
-Lkotlinx/coroutines/CompletedWithCancellation;
-Lkotlinx/coroutines/CompletionHandlerBase;
-Lkotlinx/coroutines/CompletionHandlerException;
-Lkotlinx/coroutines/CompletionStateKt;
-Lkotlinx/coroutines/CopyableThreadContextElement;
-Lkotlinx/coroutines/CoroutineContextKt$foldCopies$1;
-Lkotlinx/coroutines/CoroutineContextKt$foldCopies$folded$1;
-Lkotlinx/coroutines/CoroutineContextKt$hasCopyableElements$1;
-Lkotlinx/coroutines/CoroutineContextKt;
-Lkotlinx/coroutines/CoroutineDispatcher$Key$1;
-Lkotlinx/coroutines/CoroutineDispatcher$Key;
-Lkotlinx/coroutines/CoroutineDispatcher;
-Lkotlinx/coroutines/CoroutineExceptionHandler$Key;
-Lkotlinx/coroutines/CoroutineExceptionHandler;
-Lkotlinx/coroutines/CoroutineScope;
-Lkotlinx/coroutines/CoroutineScopeKt;
-Lkotlinx/coroutines/DefaultExecutor;
-Lkotlinx/coroutines/DefaultExecutorKt;
-Lkotlinx/coroutines/Delay;
-Lkotlinx/coroutines/DispatchedTask;
-Lkotlinx/coroutines/DispatchedTaskKt;
-Lkotlinx/coroutines/Dispatchers;
-Lkotlinx/coroutines/DisposableHandle;
-Lkotlinx/coroutines/Empty;
-Lkotlinx/coroutines/EventLoop;
-Lkotlinx/coroutines/EventLoopImplBase;
-Lkotlinx/coroutines/EventLoopImplPlatform;
-Lkotlinx/coroutines/ExecutorCoroutineDispatcher;
-Lkotlinx/coroutines/GlobalScope;
-Lkotlinx/coroutines/InactiveNodeList;
-Lkotlinx/coroutines/Incomplete;
-Lkotlinx/coroutines/IncompleteStateBox;
-Lkotlinx/coroutines/InvokeOnCancel;
-Lkotlinx/coroutines/InvokeOnCancelling;
-Lkotlinx/coroutines/InvokeOnCompletion;
-Lkotlinx/coroutines/Job$DefaultImpls;
-Lkotlinx/coroutines/Job$Key;
-Lkotlinx/coroutines/Job;
-Lkotlinx/coroutines/JobCancellationException;
-Lkotlinx/coroutines/JobCancellingNode;
-Lkotlinx/coroutines/JobImpl;
-Lkotlinx/coroutines/JobKt;
-Lkotlinx/coroutines/JobNode;
-Lkotlinx/coroutines/JobSupport$ChildCompletion;
-Lkotlinx/coroutines/JobSupport$Finishing;
-Lkotlinx/coroutines/JobSupport$addLastAtomic$$inlined$addLastIf$1;
-Lkotlinx/coroutines/JobSupport;
-Lkotlinx/coroutines/JobSupportKt;
-Lkotlinx/coroutines/MainCoroutineDispatcher;
-Lkotlinx/coroutines/NodeList;
-Lkotlinx/coroutines/NonDisposableHandle;
-Lkotlinx/coroutines/NotCompleted;
-Lkotlinx/coroutines/ParentJob;
-Lkotlinx/coroutines/RemoveOnCancel;
-Lkotlinx/coroutines/StandaloneCoroutine;
-Lkotlinx/coroutines/SupervisorJobImpl;
-Lkotlinx/coroutines/ThreadContextElement;
-Lkotlinx/coroutines/ThreadLocalEventLoop;
-Lkotlinx/coroutines/TimeoutCancellationException;
-Lkotlinx/coroutines/Unconfined;
-Lkotlinx/coroutines/UndispatchedCoroutine;
-Lkotlinx/coroutines/UndispatchedMarker;
-Lkotlinx/coroutines/android/AndroidDispatcherFactory;
-Lkotlinx/coroutines/android/HandlerContext;
-Lkotlinx/coroutines/android/HandlerDispatcher;
-Lkotlinx/coroutines/android/HandlerDispatcherKt;
-Lkotlinx/coroutines/channels/AbstractChannel$Itr;
-Lkotlinx/coroutines/channels/AbstractChannel$ReceiveElement;
-Lkotlinx/coroutines/channels/AbstractChannel$ReceiveHasNext;
-Lkotlinx/coroutines/channels/AbstractChannel$RemoveReceiveOnCancel;
-Lkotlinx/coroutines/channels/AbstractChannel$enqueueReceiveInternal$$inlined$addLastIfPrevAndIf$1;
-Lkotlinx/coroutines/channels/AbstractChannel$receiveCatching$1;
-Lkotlinx/coroutines/channels/AbstractChannel;
-Lkotlinx/coroutines/channels/AbstractChannelKt;
-Lkotlinx/coroutines/channels/AbstractSendChannel$SendBuffered;
-Lkotlinx/coroutines/channels/AbstractSendChannel;
-Lkotlinx/coroutines/channels/ArrayChannel;
-Lkotlinx/coroutines/channels/BufferOverflow;
-Lkotlinx/coroutines/channels/Channel$Factory;
-Lkotlinx/coroutines/channels/Channel;
-Lkotlinx/coroutines/channels/ChannelCoroutine;
-Lkotlinx/coroutines/channels/ChannelIterator;
-Lkotlinx/coroutines/channels/ChannelKt;
-Lkotlinx/coroutines/channels/ChannelResult$Closed;
-Lkotlinx/coroutines/channels/ChannelResult$Failed;
-Lkotlinx/coroutines/channels/ChannelResult;
-Lkotlinx/coroutines/channels/Closed;
-Lkotlinx/coroutines/channels/ClosedSendChannelException;
-Lkotlinx/coroutines/channels/ConflatedChannel;
-Lkotlinx/coroutines/channels/LinkedListChannel;
-Lkotlinx/coroutines/channels/ProducerCoroutine;
-Lkotlinx/coroutines/channels/ProducerScope;
-Lkotlinx/coroutines/channels/Receive;
-Lkotlinx/coroutines/channels/ReceiveChannel;
-Lkotlinx/coroutines/channels/ReceiveOrClosed;
-Lkotlinx/coroutines/channels/RendezvousChannel;
-Lkotlinx/coroutines/channels/Send;
-Lkotlinx/coroutines/channels/SendChannel;
-Lkotlinx/coroutines/flow/AbstractFlow$collect$1;
-Lkotlinx/coroutines/flow/AbstractFlow;
-Lkotlinx/coroutines/flow/DistinctFlowImpl$collect$2$emit$1;
-Lkotlinx/coroutines/flow/DistinctFlowImpl$collect$2;
-Lkotlinx/coroutines/flow/DistinctFlowImpl;
-Lkotlinx/coroutines/flow/Flow;
-Lkotlinx/coroutines/flow/FlowCollector;
-Lkotlinx/coroutines/flow/FlowKt;
-Lkotlinx/coroutines/flow/FlowKt__ChannelsKt$emitAllImpl$1;
-Lkotlinx/coroutines/flow/FlowKt__ChannelsKt;
-Lkotlinx/coroutines/flow/FlowKt__DistinctKt$defaultAreEquivalent$1;
-Lkotlinx/coroutines/flow/FlowKt__DistinctKt$defaultKeySelector$1;
-Lkotlinx/coroutines/flow/FlowKt__LimitKt$dropWhile$$inlined$unsafeFlow$1;
-Lkotlinx/coroutines/flow/FlowKt__LimitKt$dropWhile$1$1$emit$1;
-Lkotlinx/coroutines/flow/FlowKt__LimitKt$dropWhile$1$1;
-Lkotlinx/coroutines/flow/FlowKt__MergeKt$mapLatest$1;
-Lkotlinx/coroutines/flow/FlowKt__MergeKt;
-Lkotlinx/coroutines/flow/FlowKt__ReduceKt$first$$inlined$collectWhile$2$1;
-Lkotlinx/coroutines/flow/FlowKt__ReduceKt$first$$inlined$collectWhile$2;
-Lkotlinx/coroutines/flow/FlowKt__ReduceKt$first$3;
-Lkotlinx/coroutines/flow/FlowKt__ShareKt$launchSharing$1$2;
-Lkotlinx/coroutines/flow/FlowKt__ShareKt$launchSharing$1;
-Lkotlinx/coroutines/flow/MutableSharedFlow;
-Lkotlinx/coroutines/flow/MutableStateFlow;
-Lkotlinx/coroutines/flow/ReadonlyStateFlow;
-Lkotlinx/coroutines/flow/SafeFlow;
-Lkotlinx/coroutines/flow/SharedFlow;
-Lkotlinx/coroutines/flow/SharedFlowImpl$Emitter;
-Lkotlinx/coroutines/flow/SharedFlowImpl$collect$1;
-Lkotlinx/coroutines/flow/SharedFlowImpl;
-Lkotlinx/coroutines/flow/SharedFlowKt;
-Lkotlinx/coroutines/flow/SharedFlowSlot;
-Lkotlinx/coroutines/flow/SharingCommand;
-Lkotlinx/coroutines/flow/SharingConfig;
-Lkotlinx/coroutines/flow/SharingStarted$Companion;
-Lkotlinx/coroutines/flow/SharingStarted;
-Lkotlinx/coroutines/flow/StartedEagerly;
-Lkotlinx/coroutines/flow/StartedLazily;
-Lkotlinx/coroutines/flow/StartedWhileSubscribed$command$1;
-Lkotlinx/coroutines/flow/StartedWhileSubscribed$command$2;
-Lkotlinx/coroutines/flow/StartedWhileSubscribed;
-Lkotlinx/coroutines/flow/StateFlow;
-Lkotlinx/coroutines/flow/StateFlowImpl$collect$1;
-Lkotlinx/coroutines/flow/StateFlowImpl;
-Lkotlinx/coroutines/flow/StateFlowKt;
-Lkotlinx/coroutines/flow/StateFlowSlot;
-Lkotlinx/coroutines/flow/SubscribedFlowCollector;
-Lkotlinx/coroutines/flow/ThrowingCollector;
-Lkotlinx/coroutines/flow/internal/AbortFlowException;
-Lkotlinx/coroutines/flow/internal/AbstractSharedFlow;
-Lkotlinx/coroutines/flow/internal/AbstractSharedFlowKt;
-Lkotlinx/coroutines/flow/internal/AbstractSharedFlowSlot;
-Lkotlinx/coroutines/flow/internal/ChannelFlow$collect$2;
-Lkotlinx/coroutines/flow/internal/ChannelFlow$collectToFun$1;
-Lkotlinx/coroutines/flow/internal/ChannelFlow;
-Lkotlinx/coroutines/flow/internal/ChannelFlowOperator;
-Lkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest$flowCollect$3$1$2;
-Lkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest$flowCollect$3$1$emit$1;
-Lkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest$flowCollect$3$1;
-Lkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest$flowCollect$3;
-Lkotlinx/coroutines/flow/internal/ChannelFlowTransformLatest;
-Lkotlinx/coroutines/flow/internal/DownstreamExceptionContext;
-Lkotlinx/coroutines/flow/internal/FusibleFlow;
-Lkotlinx/coroutines/flow/internal/NoOpContinuation;
-Lkotlinx/coroutines/flow/internal/NopCollector;
-Lkotlinx/coroutines/flow/internal/NullSurrogateKt;
-Lkotlinx/coroutines/flow/internal/SafeCollector$collectContextSize$1;
-Lkotlinx/coroutines/flow/internal/SafeCollector;
-Lkotlinx/coroutines/flow/internal/SafeCollectorKt$emitFun$1;
-Lkotlinx/coroutines/flow/internal/SafeCollectorKt;
-Lkotlinx/coroutines/flow/internal/SafeCollector_commonKt$checkContext$result$1;
-Lkotlinx/coroutines/flow/internal/SendingCollector;
-Lkotlinx/coroutines/flow/internal/SubscriptionCountStateFlow;
-Lkotlinx/coroutines/internal/AtomicKt;
-Lkotlinx/coroutines/internal/AtomicOp;
-Lkotlinx/coroutines/internal/ContextScope;
-Lkotlinx/coroutines/internal/DispatchedContinuation;
-Lkotlinx/coroutines/internal/DispatchedContinuationKt;
-Lkotlinx/coroutines/internal/LimitedDispatcher;
-Lkotlinx/coroutines/internal/LockFreeLinkedListHead;
-Lkotlinx/coroutines/internal/LockFreeLinkedListNode$CondAddOp;
-Lkotlinx/coroutines/internal/LockFreeLinkedListNode;
-Lkotlinx/coroutines/internal/LockFreeTaskQueue;
-Lkotlinx/coroutines/internal/LockFreeTaskQueueCore;
-Lkotlinx/coroutines/internal/MainDispatcherFactory;
-Lkotlinx/coroutines/internal/MainDispatcherLoader$$ExternalSyntheticServiceLoad0;
-Lkotlinx/coroutines/internal/MainDispatcherLoader;
-Lkotlinx/coroutines/internal/OnUndeliveredElementKt$bindCancellationFun$1;
-Lkotlinx/coroutines/internal/OnUndeliveredElementKt;
-Lkotlinx/coroutines/internal/OpDescriptor;
-Lkotlinx/coroutines/internal/Removed;
-Lkotlinx/coroutines/internal/ResizableAtomicArray;
-Lkotlinx/coroutines/internal/ScopeCoroutine;
-Lkotlinx/coroutines/internal/StackTraceRecoveryKt;
-Lkotlinx/coroutines/internal/Symbol;
-Lkotlinx/coroutines/internal/SystemPropsKt__SystemPropsKt;
-Lkotlinx/coroutines/internal/ThreadContextKt$countAll$1;
-Lkotlinx/coroutines/internal/ThreadContextKt;
-Lkotlinx/coroutines/intrinsics/UndispatchedKt;
-Lkotlinx/coroutines/scheduling/CoroutineScheduler;
-Lkotlinx/coroutines/scheduling/DefaultIoScheduler;
-Lkotlinx/coroutines/scheduling/DefaultScheduler;
-Lkotlinx/coroutines/scheduling/GlobalQueue;
-Lkotlinx/coroutines/scheduling/NanoTimeSource;
-Lkotlinx/coroutines/scheduling/SchedulerCoroutineDispatcher;
-Lkotlinx/coroutines/scheduling/SchedulerTimeSource;
-Lkotlinx/coroutines/scheduling/Task;
-Lkotlinx/coroutines/scheduling/TaskContext;
-Lkotlinx/coroutines/scheduling/TaskContextImpl;
-Lkotlinx/coroutines/scheduling/TasksKt;
-Lkotlinx/coroutines/scheduling/UnlimitedIoScheduler;
-Lkotlinx/coroutines/sync/Empty;
-Lkotlinx/coroutines/sync/Mutex;
-Lkotlinx/coroutines/sync/MutexImpl$LockCont$tryResumeLockWaiter$1;
-Lkotlinx/coroutines/sync/MutexImpl$LockCont;
-Lkotlinx/coroutines/sync/MutexImpl$LockWaiter;
-Lkotlinx/coroutines/sync/MutexImpl$LockedQueue;
-Lkotlinx/coroutines/sync/MutexImpl$UnlockOp;
-Lkotlinx/coroutines/sync/MutexImpl;
-Lkotlinx/coroutines/sync/MutexKt;
-PLandroidx/arch/core/internal/SafeIterableMap$DescendingIterator;-><init>(Landroidx/arch/core/internal/SafeIterableMap$Entry;Landroidx/arch/core/internal/SafeIterableMap$Entry;)V
-PLandroidx/arch/core/internal/SafeIterableMap$DescendingIterator;->forward(Landroidx/arch/core/internal/SafeIterableMap$Entry;)Landroidx/arch/core/internal/SafeIterableMap$Entry;
-PLandroidx/arch/core/internal/SafeIterableMap$ListIterator;->next()Ljava/lang/Object;
-PLandroidx/arch/core/internal/SafeIterableMap$ListIterator;->supportRemove(Landroidx/arch/core/internal/SafeIterableMap$Entry;)V
-PLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$2;-><init>(Ljava/lang/Object;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Alignment;Lkotlin/jvm/functions/Function4;II)V
-PLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$4;-><clinit>()V
-PLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$4;-><init>()V
-PLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$4;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$5$1$3;-><init>(Ljava/lang/Object;)V
-PLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$5$1$4$1$invoke$$inlined$onDispose$1;-><init>(Landroidx/compose/runtime/snapshots/SnapshotStateList;Ljava/lang/Object;Landroidx/compose/animation/AnimatedContentScope;)V
-PLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$5$1$4$1$invoke$$inlined$onDispose$1;->dispose()V
-PLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$5$1$4$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/animation/AnimatedContentKt$AnimatedContent$5$1;-><init>(Landroidx/compose/animation/core/Transition;Ljava/lang/Object;ILkotlin/jvm/functions/Function1;Landroidx/compose/animation/AnimatedContentScope;Lkotlin/jvm/functions/Function4;Landroidx/compose/runtime/snapshots/SnapshotStateList;)V
-PLandroidx/compose/animation/AnimatedContentKt$SizeTransform$1;-><clinit>()V
-PLandroidx/compose/animation/AnimatedContentKt$SizeTransform$1;-><init>()V
-PLandroidx/compose/animation/AnimatedContentMeasurePolicy;-><init>(Landroidx/compose/animation/AnimatedContentScope;)V
-PLandroidx/compose/animation/AnimatedContentScope$ChildData;-><init>(Z)V
-PLandroidx/compose/animation/AnimatedContentScope$ChildData;->equals(Ljava/lang/Object;)Z
-PLandroidx/compose/animation/AnimatedContentScope$ChildData;->modifyParentData(Landroidx/compose/ui/unit/Density;Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/animation/AnimatedContentScope$SizeModifier$measure$1;-><init>(JLandroidx/compose/ui/layout/Placeable;)V
-PLandroidx/compose/animation/AnimatedContentScope$SizeModifier$measure$size$1;-><init>(Landroidx/compose/animation/AnimatedContentScope;Landroidx/compose/animation/AnimatedContentScope$SizeModifier;)V
-PLandroidx/compose/animation/AnimatedContentScope$SizeModifier$measure$size$2;-><init>(Landroidx/compose/animation/AnimatedContentScope;)V
-PLandroidx/compose/animation/AnimatedContentScope$SizeModifier;-><init>(Landroidx/compose/animation/AnimatedContentScope;Landroidx/compose/animation/core/Transition$DeferredAnimation;Landroidx/compose/runtime/MutableState;)V
-PLandroidx/compose/animation/AnimatedContentScope;-><init>(Landroidx/compose/animation/core/Transition;Landroidx/compose/ui/Alignment;Landroidx/compose/ui/unit/LayoutDirection;)V
-PLandroidx/compose/animation/AnimatedEnterExitMeasurePolicy$measure$1;-><init>(Ljava/util/ArrayList;)V
-PLandroidx/compose/animation/AnimatedEnterExitMeasurePolicy;-><init>(Landroidx/compose/animation/AnimatedVisibilityScopeImpl;)V
-PLandroidx/compose/animation/AnimatedVisibilityKt$AnimatedEnterExitImpl$1$1$1;-><init>(Landroidx/compose/animation/core/Transition;)V
-PLandroidx/compose/animation/AnimatedVisibilityKt$AnimatedEnterExitImpl$1$1$2;-><init>(Landroidx/compose/runtime/MutableState;)V
-PLandroidx/compose/animation/AnimatedVisibilityKt$AnimatedEnterExitImpl$1$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-PLandroidx/compose/animation/AnimatedVisibilityKt$AnimatedVisibility$7;-><clinit>()V
-PLandroidx/compose/animation/AnimatedVisibilityKt$AnimatedVisibility$7;-><init>()V
-PLandroidx/compose/animation/AnimatedVisibilityKt$AnimatedVisibility$7;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/animation/AnimatedVisibilityScopeImpl;->getTransition()Landroidx/compose/animation/core/Transition;
-PLandroidx/compose/animation/EnterExitState;-><clinit>()V
-PLandroidx/compose/animation/EnterExitState;-><init>(ILjava/lang/String;)V
-PLandroidx/compose/animation/EnterExitTransitionKt$TransformOriginVectorConverter$1;-><clinit>()V
-PLandroidx/compose/animation/EnterExitTransitionKt$TransformOriginVectorConverter$1;-><init>()V
-PLandroidx/compose/animation/EnterExitTransitionKt$TransformOriginVectorConverter$2;-><clinit>()V
-PLandroidx/compose/animation/EnterExitTransitionKt$TransformOriginVectorConverter$2;-><init>()V
-PLandroidx/compose/animation/EnterExitTransitionKt$createModifier$2$1;-><init>(Landroidx/compose/runtime/State;)V
-PLandroidx/compose/animation/EnterExitTransitionKt$slideInHorizontally$2;-><init>(Lkotlin/jvm/functions/Function1;)V
-PLandroidx/compose/animation/EnterExitTransitionKt$slideOutHorizontally$1;-><clinit>()V
-PLandroidx/compose/animation/EnterExitTransitionKt$slideOutHorizontally$1;-><init>()V
-PLandroidx/compose/animation/EnterExitTransitionKt$slideOutHorizontally$2;-><init>(Lkotlin/jvm/functions/Function1;)V
-PLandroidx/compose/animation/EnterExitTransitionKt;-><clinit>()V
-PLandroidx/compose/animation/EnterExitTransitionKt;->fadeIn$default(Landroidx/compose/animation/core/TweenSpec;I)Landroidx/compose/animation/EnterTransitionImpl;
-PLandroidx/compose/animation/EnterExitTransitionKt;->fadeOut$default(Landroidx/compose/animation/core/TweenSpec;I)Landroidx/compose/animation/ExitTransitionImpl;
-PLandroidx/compose/animation/EnterTransition;-><clinit>()V
-PLandroidx/compose/animation/EnterTransition;-><init>()V
-PLandroidx/compose/animation/EnterTransitionImpl;-><init>(Landroidx/compose/animation/TransitionData;)V
-PLandroidx/compose/animation/ExitTransition;-><clinit>()V
-PLandroidx/compose/animation/ExitTransition;-><init>()V
-PLandroidx/compose/animation/ExitTransitionImpl;-><init>(Landroidx/compose/animation/TransitionData;)V
-PLandroidx/compose/animation/Fade;-><init>(FLandroidx/compose/animation/core/FiniteAnimationSpec;)V
-PLandroidx/compose/animation/LayoutModifierWithPassThroughIntrinsics;-><init>()V
-PLandroidx/compose/animation/SizeTransformImpl;-><init>(ZLkotlin/jvm/functions/Function2;)V
-PLandroidx/compose/animation/SizeTransformImpl;->getClip()Z
-PLandroidx/compose/animation/Slide;-><init>(Landroidx/compose/animation/core/FiniteAnimationSpec;Lkotlin/jvm/functions/Function1;)V
-PLandroidx/compose/animation/Slide;->equals(Ljava/lang/Object;)Z
-PLandroidx/compose/animation/SlideModifier$measure$1;-><init>(Landroidx/compose/animation/SlideModifier;Landroidx/compose/ui/layout/Placeable;J)V
-PLandroidx/compose/animation/SlideModifier$transitionSpec$1;-><init>(Landroidx/compose/animation/SlideModifier;)V
-PLandroidx/compose/animation/TransitionData;-><init>(Landroidx/compose/animation/Fade;Landroidx/compose/animation/Slide;Landroidx/compose/animation/ChangeSize;Landroidx/compose/animation/Scale;)V
-PLandroidx/compose/animation/TransitionData;-><init>(Landroidx/compose/animation/Fade;Landroidx/compose/animation/Slide;Landroidx/compose/animation/ChangeSize;Landroidx/compose/animation/Scale;I)V
-PLandroidx/compose/animation/TransitionData;->equals(Ljava/lang/Object;)Z
-PLandroidx/compose/animation/core/Animatable;-><init>(Ljava/lang/Object;Landroidx/compose/animation/core/TwoWayConverterImpl;Ljava/lang/Float;I)V
-PLandroidx/compose/animation/core/AnimationVector2D;->newVector$animation_core_release()Landroidx/compose/animation/core/AnimationVector;
-PLandroidx/compose/animation/core/CubicBezierEasing;-><init>(F)V
-PLandroidx/compose/animation/core/CubicBezierEasing;->equals(Ljava/lang/Object;)Z
-PLandroidx/compose/animation/core/EasingKt$LinearEasing$1;-><clinit>()V
-PLandroidx/compose/animation/core/EasingKt$LinearEasing$1;-><init>()V
-PLandroidx/compose/animation/core/EasingKt;-><clinit>()V
-PLandroidx/compose/animation/core/Transition$DeferredAnimation$DeferredAnimationData;-><init>(Landroidx/compose/animation/core/Transition$DeferredAnimation;Landroidx/compose/animation/core/Transition$TransitionAnimationState;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
-PLandroidx/compose/animation/core/Transition$animateTo$1$1;-><init>(Landroidx/compose/animation/core/Transition;Lkotlin/coroutines/Continuation;)V
-PLandroidx/compose/animation/core/Transition$animateTo$1$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-PLandroidx/compose/animation/core/Transition$animateTo$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/animation/core/Transition$totalDurationNanos$2;-><init>(Landroidx/compose/animation/core/Transition;)V
-PLandroidx/compose/animation/core/Transition$updateTarget$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/animation/core/TransitionKt$createChildTransitionInternal$1$1$invoke$$inlined$onDispose$1;-><init>(Landroidx/compose/animation/core/Transition;Landroidx/compose/animation/core/Transition;)V
-PLandroidx/compose/animation/core/TransitionKt$createChildTransitionInternal$1$1;-><init>(Landroidx/compose/animation/core/Transition;Landroidx/compose/animation/core/Transition;)V
-PLandroidx/compose/animation/core/TransitionKt$createDeferredAnimation$1$invoke$$inlined$onDispose$1;-><init>(Landroidx/compose/animation/core/Transition;Landroidx/compose/animation/core/Transition$DeferredAnimation;)V
-PLandroidx/compose/animation/core/TransitionKt$createDeferredAnimation$1;-><init>(Landroidx/compose/animation/core/Transition;Landroidx/compose/animation/core/Transition$DeferredAnimation;)V
-PLandroidx/compose/animation/core/TransitionKt$createDeferredAnimation$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/animation/core/TransitionKt$createTransitionAnimation$1$1$invoke$$inlined$onDispose$1;-><init>(Landroidx/compose/animation/core/Transition;Landroidx/compose/animation/core/Transition$TransitionAnimationState;)V
-PLandroidx/compose/animation/core/TransitionKt$createTransitionAnimation$1$1$invoke$$inlined$onDispose$1;->dispose()V
-PLandroidx/compose/animation/core/TransitionKt$createTransitionAnimation$1$1;-><init>(Landroidx/compose/animation/core/Transition;Landroidx/compose/animation/core/Transition$TransitionAnimationState;)V
-PLandroidx/compose/animation/core/TransitionKt$updateTransition$1$1$invoke$$inlined$onDispose$1;-><init>(Landroidx/compose/animation/core/Transition;)V
-PLandroidx/compose/animation/core/TransitionKt$updateTransition$1$1$invoke$$inlined$onDispose$1;->dispose()V
-PLandroidx/compose/animation/core/TransitionKt$updateTransition$1$1;-><init>(Landroidx/compose/animation/core/Transition;)V
-PLandroidx/compose/animation/core/TransitionKt$updateTransition$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/animation/core/TransitionKt$updateTransition$2$1$invoke$$inlined$onDispose$1;-><init>(Landroidx/compose/animation/core/Transition;)V
-PLandroidx/compose/animation/core/TransitionKt$updateTransition$2$1$invoke$$inlined$onDispose$1;->dispose()V
-PLandroidx/compose/animation/core/TransitionKt$updateTransition$2$1;-><init>(Landroidx/compose/animation/core/Transition;)V
-PLandroidx/compose/animation/core/TransitionKt$updateTransition$2$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/animation/core/TweenSpec;-><init>(IILandroidx/compose/animation/core/Easing;)V
-PLandroidx/compose/animation/core/TweenSpec;-><init>(ILandroidx/compose/animation/core/Easing;I)V
-PLandroidx/compose/animation/core/TweenSpec;->equals(Ljava/lang/Object;)Z
-PLandroidx/compose/animation/core/VectorConvertersKt$IntOffsetToVector$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/animation/core/VectorConvertersKt$IntSizeToVector$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/animation/core/VectorConvertersKt$IntSizeToVector$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/animation/core/VectorizedFiniteAnimationSpec;->isInfinite()V
-PLandroidx/compose/animation/core/VectorizedFloatAnimationSpec$1;-><init>(Landroidx/compose/animation/core/FloatAnimationSpec;)V
-PLandroidx/compose/foundation/BorderKt;->shrink-Kibmq7A(FJ)J
-PLandroidx/compose/foundation/ClickableKt$PressedInteractionSourceDisposableEffect$1$invoke$$inlined$onDispose$1;->dispose()V
-PLandroidx/compose/foundation/HoverableKt$hoverable$2$1$1$invoke$$inlined$onDispose$1;->dispose()V
-PLandroidx/compose/foundation/gestures/DraggableKt$awaitDownAndSlop$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/foundation/gestures/DraggableKt$draggable$9$1$1$invoke$$inlined$onDispose$1;->dispose()V
-PLandroidx/compose/foundation/gestures/ScrollableKt$ModifierLocalScrollableContainer$1;->invoke()Ljava/lang/Object;
-PLandroidx/compose/foundation/gestures/ScrollableKt$awaitScrollEvent$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/foundation/gestures/TapGestureDetectorKt$awaitFirstDown$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/foundation/interaction/PressInteraction$Press;-><init>(J)V
-PLandroidx/compose/foundation/layout/BoxChildData;-><init>(Landroidx/compose/ui/BiasAlignment;)V
-PLandroidx/compose/foundation/layout/BoxChildData;->equals(Ljava/lang/Object;)Z
-PLandroidx/compose/foundation/layout/BoxChildData;->modifyParentData(Landroidx/compose/ui/unit/Density;Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/foundation/layout/BoxScopeInstance;-><clinit>()V
-PLandroidx/compose/foundation/layout/BoxScopeInstance;-><init>()V
-PLandroidx/compose/foundation/layout/BoxScopeInstance;->align()Landroidx/compose/ui/Modifier;
-PLandroidx/compose/foundation/layout/PaddingValuesImpl;->equals(Ljava/lang/Object;)Z
-PLandroidx/compose/foundation/layout/PaddingValuesModifier$measure$2;-><init>(Landroidx/compose/ui/layout/Placeable;Landroidx/compose/ui/layout/MeasureScope;Landroidx/compose/foundation/layout/PaddingValuesModifier;)V
-PLandroidx/compose/foundation/layout/PaddingValuesModifier;-><init>(Landroidx/compose/foundation/layout/PaddingValues;)V
-PLandroidx/compose/foundation/layout/PaddingValuesModifier;->equals(Ljava/lang/Object;)Z
-PLandroidx/compose/foundation/layout/SizeKt;->fillMaxSize$default()Landroidx/compose/ui/Modifier;
-PLandroidx/compose/foundation/layout/UnspecifiedConstraintsModifier$measure$1;-><init>(Landroidx/compose/ui/layout/Placeable;)V
-PLandroidx/compose/foundation/layout/UnspecifiedConstraintsModifier$measure$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/foundation/layout/UnspecifiedConstraintsModifier;-><init>(FF)V
-PLandroidx/compose/foundation/layout/UnspecifiedConstraintsModifier;->equals(Ljava/lang/Object;)Z
-PLandroidx/compose/material/ripple/AndroidRippleIndicationInstance$onInvalidateRipple$1;-><init>(Landroidx/compose/material/ripple/AndroidRippleIndicationInstance;)V
-PLandroidx/compose/material/ripple/AndroidRippleIndicationInstance;->dispose()V
-PLandroidx/compose/material/ripple/AndroidRippleIndicationInstance;->onForgotten()V
-PLandroidx/compose/material/ripple/AndroidRippleIndicationInstance;->onRemembered()V
-PLandroidx/compose/material/ripple/DebugRippleTheme;-><clinit>()V
-PLandroidx/compose/material/ripple/DebugRippleTheme;-><init>()V
-PLandroidx/compose/material/ripple/DebugRippleTheme;->defaultColor-WaAFU9c(Landroidx/compose/runtime/Composer;)J
-PLandroidx/compose/material/ripple/DebugRippleTheme;->rippleAlpha(Landroidx/compose/runtime/Composer;)Landroidx/compose/material/ripple/RippleAlpha;
-PLandroidx/compose/material/ripple/PlatformRipple;-><init>(ZFLandroidx/compose/runtime/MutableState;)V
-PLandroidx/compose/material/ripple/Ripple$rememberUpdatedInstance$1$invokeSuspend$$inlined$collect$1;-><init>(Landroidx/compose/material/ripple/RippleIndicationInstance;Lkotlinx/coroutines/CoroutineScope;)V
-PLandroidx/compose/material/ripple/Ripple$rememberUpdatedInstance$1$invokeSuspend$$inlined$collect$1;->emit(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-PLandroidx/compose/material/ripple/Ripple$rememberUpdatedInstance$1;-><init>(Landroidx/compose/foundation/interaction/InteractionSource;Landroidx/compose/material/ripple/RippleIndicationInstance;Lkotlin/coroutines/Continuation;)V
-PLandroidx/compose/material/ripple/Ripple$rememberUpdatedInstance$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-PLandroidx/compose/material/ripple/Ripple;-><init>(ZFLandroidx/compose/runtime/MutableState;)V
-PLandroidx/compose/material/ripple/Ripple;->equals(Ljava/lang/Object;)Z
-PLandroidx/compose/material/ripple/RippleAlpha;-><init>(FFFF)V
-PLandroidx/compose/material/ripple/RippleAlpha;->equals(Ljava/lang/Object;)Z
-PLandroidx/compose/material/ripple/RippleAnimationKt;-><clinit>()V
-PLandroidx/compose/material/ripple/RippleAnimationKt;->getRippleEndRadius-cSwnlzA(Landroidx/compose/ui/unit/Density;ZJ)F
-PLandroidx/compose/material/ripple/RippleContainer;-><init>(Landroid/content/Context;)V
-PLandroidx/compose/material/ripple/RippleHostMap;-><init>()V
-PLandroidx/compose/material/ripple/RippleHostView;-><clinit>()V
-PLandroidx/compose/material/ripple/RippleHostView;-><init>(Landroid/content/Context;)V
-PLandroidx/compose/material/ripple/RippleHostView;->refreshDrawableState()V
-PLandroidx/compose/material/ripple/RippleIndicationInstance;-><init>(Landroidx/compose/runtime/MutableState;Z)V
-PLandroidx/compose/material/ripple/RippleKt;-><clinit>()V
-PLandroidx/compose/material/ripple/RippleThemeKt$LocalRippleTheme$1;-><clinit>()V
-PLandroidx/compose/material/ripple/RippleThemeKt$LocalRippleTheme$1;-><init>()V
-PLandroidx/compose/material/ripple/RippleThemeKt$LocalRippleTheme$1;->invoke()Ljava/lang/Object;
-PLandroidx/compose/material/ripple/RippleThemeKt;-><clinit>()V
-PLandroidx/compose/material/ripple/StateLayer;-><init>(Landroidx/compose/runtime/MutableState;Z)V
-PLandroidx/compose/material3/ButtonColors;-><init>(JJJJ)V
-PLandroidx/compose/material3/ButtonDefaults;-><clinit>()V
-PLandroidx/compose/material3/ButtonElevation$animateElevation$1$1$1;-><init>(Landroidx/compose/runtime/snapshots/SnapshotStateList;)V
-PLandroidx/compose/material3/ButtonElevation$animateElevation$1$1$1;->emit(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-PLandroidx/compose/material3/ButtonElevation$animateElevation$1$1;-><init>(Landroidx/compose/foundation/interaction/InteractionSource;Landroidx/compose/runtime/snapshots/SnapshotStateList;Lkotlin/coroutines/Continuation;)V
-PLandroidx/compose/material3/ButtonElevation$animateElevation$1$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-PLandroidx/compose/material3/ButtonElevation$animateElevation$3;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-PLandroidx/compose/material3/ButtonElevation;-><init>(FFFFF)V
-PLandroidx/compose/material3/ButtonKt$Button$2$1$1;-><init>(Landroidx/compose/foundation/layout/PaddingValues;Lkotlin/jvm/functions/Function3;I)V
-PLandroidx/compose/material3/ButtonKt$Button$2$1;-><init>(Landroidx/compose/foundation/layout/PaddingValues;Lkotlin/jvm/functions/Function3;I)V
-PLandroidx/compose/material3/ButtonKt$Button$2;-><init>(JLandroidx/compose/foundation/layout/PaddingValues;Lkotlin/jvm/functions/Function3;I)V
-PLandroidx/compose/material3/ColorScheme;-><init>(JJJJJJJJJJJJJJJJJJJJJJJJJJJJJ)V
-PLandroidx/compose/material3/ColorScheme;->getSurface-0d7_KjU()J
-PLandroidx/compose/material3/ColorSchemeKt$LocalColorScheme$1;-><clinit>()V
-PLandroidx/compose/material3/ColorSchemeKt$LocalColorScheme$1;-><init>()V
-PLandroidx/compose/material3/ColorSchemeKt$LocalColorScheme$1;->invoke()Ljava/lang/Object;
-PLandroidx/compose/material3/ColorSchemeKt;-><clinit>()V
-PLandroidx/compose/material3/ContentColorKt$LocalContentColor$1;-><clinit>()V
-PLandroidx/compose/material3/ContentColorKt$LocalContentColor$1;-><init>()V
-PLandroidx/compose/material3/ContentColorKt;-><clinit>()V
-PLandroidx/compose/material3/ElevationKt;-><clinit>()V
-PLandroidx/compose/material3/MinimumTouchTargetModifier$measure$1;-><init>(ILandroidx/compose/ui/layout/Placeable;I)V
-PLandroidx/compose/material3/MinimumTouchTargetModifier;-><init>(J)V
-PLandroidx/compose/material3/MinimumTouchTargetModifier;->equals(Ljava/lang/Object;)Z
-PLandroidx/compose/material3/ShapeDefaults;-><clinit>()V
-PLandroidx/compose/material3/Shapes;-><init>(I)V
-PLandroidx/compose/material3/ShapesKt$LocalShapes$1;-><clinit>()V
-PLandroidx/compose/material3/ShapesKt$LocalShapes$1;-><init>()V
-PLandroidx/compose/material3/ShapesKt$LocalShapes$1;->invoke()Ljava/lang/Object;
-PLandroidx/compose/material3/ShapesKt;-><clinit>()V
-PLandroidx/compose/material3/SurfaceKt$LocalAbsoluteTonalElevation$1;-><clinit>()V
-PLandroidx/compose/material3/SurfaceKt$LocalAbsoluteTonalElevation$1;-><init>()V
-PLandroidx/compose/material3/SurfaceKt$LocalAbsoluteTonalElevation$1;->invoke()Ljava/lang/Object;
-PLandroidx/compose/material3/SurfaceKt;-><clinit>()V
-PLandroidx/compose/material3/TouchTargetKt$LocalMinimumTouchTargetEnforcement$1;-><clinit>()V
-PLandroidx/compose/material3/TouchTargetKt$LocalMinimumTouchTargetEnforcement$1;-><init>()V
-PLandroidx/compose/material3/TouchTargetKt$LocalMinimumTouchTargetEnforcement$1;->invoke()Ljava/lang/Object;
-PLandroidx/compose/material3/TouchTargetKt$minimumTouchTargetSize$2;-><clinit>()V
-PLandroidx/compose/material3/TouchTargetKt$minimumTouchTargetSize$2;-><init>()V
-PLandroidx/compose/material3/TouchTargetKt;-><clinit>()V
-PLandroidx/compose/material3/Typography;-><init>(I)V
-PLandroidx/compose/material3/TypographyKt$LocalTypography$1;-><clinit>()V
-PLandroidx/compose/material3/TypographyKt$LocalTypography$1;-><init>()V
-PLandroidx/compose/material3/TypographyKt$LocalTypography$1;->invoke()Ljava/lang/Object;
-PLandroidx/compose/material3/TypographyKt;-><clinit>()V
-PLandroidx/compose/material3/tokens/ColorLightTokens;-><clinit>()V
-PLandroidx/compose/material3/tokens/ElevationTokens;-><clinit>()V
-PLandroidx/compose/material3/tokens/FilledButtonTokens;-><clinit>()V
-PLandroidx/compose/material3/tokens/PaletteTokens;-><clinit>()V
-PLandroidx/compose/material3/tokens/ShapeTokens;-><clinit>()V
-PLandroidx/compose/material3/tokens/TypeScaleTokens;-><clinit>()V
-PLandroidx/compose/material3/tokens/TypefaceTokens;-><clinit>()V
-PLandroidx/compose/material3/tokens/TypographyTokens;-><clinit>()V
-PLandroidx/compose/runtime/ComposerImpl$CompositionContextHolder;->onForgotten()V
-PLandroidx/compose/runtime/ComposerImpl$CompositionContextImpl;->dispose()V
-PLandroidx/compose/runtime/ComposerImpl$realizeMovement$1;-><init>(II)V
-PLandroidx/compose/runtime/ComposerImpl$start$2;-><init>(I)V
-PLandroidx/compose/runtime/ComposerImpl;->reportAllMovableContent()V
-PLandroidx/compose/runtime/CompositionContext;->unregisterComposer$runtime_release(Landroidx/compose/runtime/Composer;)V
-PLandroidx/compose/runtime/JoinedKey;-><init>(Ljava/lang/Integer;Ljava/lang/Object;)V
-PLandroidx/compose/runtime/JoinedKey;->equals(Ljava/lang/Object;)Z
-PLandroidx/compose/runtime/Recomposer$effectJob$1$1$1$1;-><init>(Landroidx/compose/runtime/Recomposer;Ljava/lang/Throwable;)V
-PLandroidx/compose/runtime/Recomposer$effectJob$1$1$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/runtime/Recomposer$effectJob$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/runtime/Recomposer;->cancel()V
-PLandroidx/compose/runtime/collection/IdentityScopeMap;->clear()V
-PLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableList/AbstractPersistentList;->contains(Ljava/lang/Object;)Z
-PLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableList/SmallPersistentVector;->addAll(Ljava/util/Collection;)Landroidx/compose/runtime/external/kotlinx/collections/immutable/PersistentList;
-PLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;->remove(IILjava/lang/Object;)Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/immutableMap/TrieNode;
-PLandroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/persistentOrderedSet/PersistentOrderedSet;->remove(Ljava/lang/Object;)Landroidx/compose/runtime/external/kotlinx/collections/immutable/implementations/persistentOrderedSet/PersistentOrderedSet;
-PLandroidx/compose/runtime/internal/ComposableLambdaImpl$invoke$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/runtime/saveable/RememberSaveableKt$rememberSaveable$1$invoke$$inlined$onDispose$1;->dispose()V
-PLandroidx/compose/runtime/snapshots/SnapshotIdSet$iterator$1;-><init>(Landroidx/compose/runtime/snapshots/SnapshotIdSet;Lkotlin/coroutines/Continuation;)V
-PLandroidx/compose/runtime/snapshots/SnapshotIdSet$iterator$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-PLandroidx/compose/runtime/snapshots/SnapshotIdSet;->iterator()Ljava/util/Iterator;
-PLandroidx/compose/runtime/snapshots/SnapshotStateList;->addAll(Ljava/util/Collection;)Z
-PLandroidx/compose/runtime/snapshots/SnapshotStateList;->clear()V
-PLandroidx/compose/runtime/snapshots/SnapshotStateList;->contains(Ljava/lang/Object;)Z
-PLandroidx/compose/runtime/snapshots/SnapshotStateList;->isEmpty()Z
-PLandroidx/compose/runtime/snapshots/SnapshotStateListKt;->access$validateRange(II)V
-PLandroidx/compose/ui/Modifier$Node;->isValid()Z
-PLandroidx/compose/ui/autofill/AutofillCallback;->unregister(Landroidx/compose/ui/autofill/AndroidAutofill;)V
-PLandroidx/compose/ui/focus/FocusOwnerImpl;->clearFocus(ZZ)V
-PLandroidx/compose/ui/focus/FocusOwnerImpl;->getFocusRect()Landroidx/compose/ui/geometry/Rect;
-PLandroidx/compose/ui/focus/FocusPropertiesImpl;->getCanFocus()Z
-PLandroidx/compose/ui/focus/FocusRequester$requestFocus$2;-><clinit>()V
-PLandroidx/compose/ui/focus/FocusRequester$requestFocus$2;-><init>()V
-PLandroidx/compose/ui/focus/FocusRequester$requestFocus$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/ui/focus/FocusRequester;->requestFocus()V
-PLandroidx/compose/ui/focus/FocusTargetModifierNode$invalidateFocus$1;-><init>(Lkotlin/jvm/internal/Ref$ObjectRef;Landroidx/compose/ui/focus/FocusTargetModifierNode;)V
-PLandroidx/compose/ui/focus/FocusTargetModifierNode$invalidateFocus$1;->invoke()Ljava/lang/Object;
-PLandroidx/compose/ui/focus/FocusTargetModifierNode;->scheduleInvalidationForFocusEvents$ui_release()V
-PLandroidx/compose/ui/geometry/Offset;->getDistance-impl(J)F
-PLandroidx/compose/ui/graphics/BlockGraphicsLayerModifier$measure$1;-><init>(Landroidx/compose/ui/layout/Placeable;Landroidx/compose/ui/graphics/BlockGraphicsLayerModifier;)V
-PLandroidx/compose/ui/graphics/BlockGraphicsLayerModifier;-><init>(Lkotlin/jvm/functions/Function1;)V
-PLandroidx/compose/ui/graphics/GraphicsLayerModifierKt$graphicsLayer$$inlined$modifierElementOf$1;->create()Landroidx/compose/ui/Modifier$Node;
-PLandroidx/compose/ui/graphics/drawscope/CanvasDrawScope;->getDensity()F
-PLandroidx/compose/ui/graphics/drawscope/DrawScope;->drawRoundRect-ZuiqVtQ$default(Landroidx/compose/ui/graphics/drawscope/DrawScope;Landroidx/compose/ui/graphics/Brush;JJJLandroidx/compose/ui/graphics/drawscope/Stroke;I)V
-PLandroidx/compose/ui/input/nestedscroll/NestedScrollModifierLocal;->getValue()Ljava/lang/Object;
-PLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilter$PointerEventHandlerCoroutine;->resumeWith(Ljava/lang/Object;)V
-PLandroidx/compose/ui/input/pointer/SuspendingPointerInputFilter$awaitPointerEventScope$2$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/ui/layout/ContentScale$Companion$FillBounds$1;->computeScaleFactor-H7hwNQA(JJ)J
-PLandroidx/compose/ui/modifier/ModifierLocalManager$invalidate$1;-><init>(Landroidx/compose/ui/modifier/ModifierLocalManager;)V
-PLandroidx/compose/ui/modifier/ModifierLocalManager;->invalidate()V
-PLandroidx/compose/ui/node/BackwardsCompatNodeKt$updateModifierLocalConsumer$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/ui/node/LayoutNode;->isPlacedInLookahead()Ljava/lang/Boolean;
-PLandroidx/compose/ui/node/LayoutNodeDrawScope;->drawRoundRect-ZuiqVtQ(Landroidx/compose/ui/graphics/Brush;JJJFLandroidx/arch/core/executor/TaskExecutor;Landroidx/compose/ui/graphics/ColorFilter;I)V
-PLandroidx/compose/ui/node/LayoutNodeDrawScope;->toPx-0680j_4(F)F
-PLandroidx/compose/ui/node/LayoutNodeKt;->Animatable$default()Landroidx/compose/animation/core/Animatable;
-PLandroidx/compose/ui/node/MeasureAndLayoutDelegate;->getCanAffectParentInLookahead(Landroidx/compose/ui/node/LayoutNode;)Z
-PLandroidx/compose/ui/node/NodeChainKt$fillVector$1;-><init>(Landroidx/compose/runtime/collection/MutableVector;)V
-PLandroidx/compose/ui/node/NodeKind;->tween$default(IILandroidx/compose/animation/core/Easing;I)Landroidx/compose/animation/core/TweenSpec;
-PLandroidx/compose/ui/node/UiApplier;->remove(II)V
-PLandroidx/compose/ui/platform/AndroidComposeView;->getModifierLocalManager()Landroidx/compose/ui/modifier/ModifierLocalManager;
-PLandroidx/compose/ui/platform/AndroidComposeView;->onDetachedFromWindow()V
-PLandroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat$1;->onViewDetachedFromWindow(Landroid/view/View;)V
-PLandroidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat$boundsUpdatesEventLoop$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$2$invoke$$inlined$onDispose$1;->dispose()V
-PLandroidx/compose/ui/platform/AndroidCompositionLocals_androidKt$obtainImageVectorCache$1$invoke$$inlined$onDispose$1;->dispose()V
-PLandroidx/compose/ui/platform/DisposableSaveableStateRegistry_androidKt$DisposableSaveableStateRegistry$1;->invoke()Ljava/lang/Object;
-PLandroidx/compose/ui/platform/ViewCompositionStrategy$DisposeOnDetachedFromWindowOrReleasedFromPool$installFor$listener$1;->onViewDetachedFromWindow(Landroid/view/View;)V
-PLandroidx/compose/ui/platform/ViewConfiguration;->getMinimumTouchTargetSize-MYxV2XQ()J
-PLandroidx/compose/ui/platform/WindowRecomposerPolicy$createAndInstallWindowRecomposer$1;->onViewDetachedFromWindow(Landroid/view/View;)V
-PLandroidx/compose/ui/platform/WindowRecomposer_androidKt$createLifecycleAwareWindowRecomposer$1;->onViewDetachedFromWindow(Landroid/view/View;)V
-PLandroidx/compose/ui/platform/WrappedComposition;->dispose()V
-PLandroidx/compose/ui/text/input/TextInputServiceAndroid$textInputCommandEventLoop$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/compose/ui/unit/Constraints$Companion;->fixedHeight-OenEA2s(I)J
-PLandroidx/compose/ui/unit/DpKt;->DpSize-YgX7TsA(FF)J
-PLandroidx/compose/ui/unit/DpSize;-><clinit>()V
-PLandroidx/compose/ui/unit/DpSize;->getHeight-D9Ej5fM(J)F
-PLandroidx/compose/ui/unit/DpSize;->getWidth-D9Ej5fM(J)F
-PLandroidx/concurrent/futures/AbstractResolvableFuture$AtomicHelper;-><init>()V
-PLandroidx/concurrent/futures/AbstractResolvableFuture$Listener;-><clinit>()V
-PLandroidx/concurrent/futures/AbstractResolvableFuture$Listener;-><init>()V
-PLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper;-><init>(Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;)V
-PLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper;->casListeners(Landroidx/concurrent/futures/AbstractResolvableFuture;Landroidx/concurrent/futures/AbstractResolvableFuture$Listener;)Z
-PLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper;->casValue(Landroidx/concurrent/futures/AbstractResolvableFuture;Ljava/lang/Object;Ljava/lang/Object;)Z
-PLandroidx/concurrent/futures/AbstractResolvableFuture$SafeAtomicHelper;->casWaiters(Landroidx/concurrent/futures/AbstractResolvableFuture;Landroidx/concurrent/futures/AbstractResolvableFuture$Waiter;Landroidx/concurrent/futures/AbstractResolvableFuture$Waiter;)Z
-PLandroidx/concurrent/futures/AbstractResolvableFuture$Waiter;-><clinit>()V
-PLandroidx/concurrent/futures/AbstractResolvableFuture$Waiter;-><init>(I)V
-PLandroidx/concurrent/futures/AbstractResolvableFuture;-><clinit>()V
-PLandroidx/concurrent/futures/AbstractResolvableFuture;-><init>()V
-PLandroidx/concurrent/futures/AbstractResolvableFuture;->complete(Landroidx/concurrent/futures/AbstractResolvableFuture;)V
-PLandroidx/concurrent/futures/ResolvableFuture;-><init>()V
-PLandroidx/core/R$styleable;-><clinit>()V
-PLandroidx/core/R$styleable;->yield(Lkotlin/coroutines/jvm/internal/ContinuationImpl;)Ljava/lang/Object;
-PLandroidx/core/view/ViewKt$ancestors$1;-><clinit>()V
-PLandroidx/core/view/ViewKt$ancestors$1;-><init>()V
-PLandroidx/core/view/ViewKt$ancestors$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/lifecycle/DefaultLifecycleObserver;->onDestroy(Landroidx/lifecycle/LifecycleOwner;)V
-PLandroidx/lifecycle/DefaultLifecycleObserver;->onStop(Landroidx/lifecycle/LifecycleOwner;)V
-PLandroidx/lifecycle/EmptyActivityLifecycleCallbacks;->onActivityDestroyed(Landroid/app/Activity;)V
-PLandroidx/lifecycle/EmptyActivityLifecycleCallbacks;->onActivityPaused(Landroid/app/Activity;)V
-PLandroidx/lifecycle/EmptyActivityLifecycleCallbacks;->onActivityStopped(Landroid/app/Activity;)V
-PLandroidx/lifecycle/LifecycleDispatcher$DispatcherActivityCallback;->onActivityStopped(Landroid/app/Activity;)V
-PLandroidx/lifecycle/ProcessLifecycleOwner$3;->onActivityPaused(Landroid/app/Activity;)V
-PLandroidx/lifecycle/ProcessLifecycleOwner$3;->onActivityStopped(Landroid/app/Activity;)V
-PLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityDestroyed(Landroid/app/Activity;)V
-PLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityPaused(Landroid/app/Activity;)V
-PLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityPreDestroyed(Landroid/app/Activity;)V
-PLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityPrePaused(Landroid/app/Activity;)V
-PLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityPreStopped(Landroid/app/Activity;)V
-PLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityStopped(Landroid/app/Activity;)V
-PLandroidx/lifecycle/ReportFragment;->onDestroy()V
-PLandroidx/lifecycle/ReportFragment;->onPause()V
-PLandroidx/lifecycle/ReportFragment;->onStop()V
-PLandroidx/metrics/performance/JankStatsApi24Impl;->removeFrameMetricsListenerDelegate(Landroidx/metrics/performance/JankStatsApi24Impl$$ExternalSyntheticLambda0;Landroid/view/Window;)V
-PLandroidx/profileinstaller/ProfileInstallReceiver$$ExternalSyntheticLambda0;-><init>()V
-PLandroidx/profileinstaller/ProfileInstaller$1;-><init>()V
-PLandroidx/profileinstaller/ProfileInstaller$1;->onResultReceived(ILjava/lang/Object;)V
-PLandroidx/profileinstaller/ProfileInstaller$2;-><init>()V
-PLandroidx/profileinstaller/ProfileInstaller;-><clinit>()V
-PLandroidx/profileinstaller/ProfileInstaller;->writeProfile(Landroid/content/Context;Ljava/util/concurrent/Executor;Landroidx/profileinstaller/ProfileInstaller$DiagnosticsCallback;Z)V
-PLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda1;->run()V
-PLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda2;-><init>(Landroid/content/Context;)V
-PLandroidx/profileinstaller/ProfileInstallerInitializer$$ExternalSyntheticLambda2;->run()V
-PLandroidx/profileinstaller/ProfileVerifier$Cache;-><init>(IIJJ)V
-PLandroidx/profileinstaller/ProfileVerifier$Cache;->writeOnFile(Ljava/io/File;)V
-PLandroidx/profileinstaller/ProfileVerifier$CompilationStatus;-><init>()V
-PLandroidx/profileinstaller/ProfileVerifier;-><clinit>()V
-PLandroidx/profileinstaller/ProfileVerifier;->getPackageLastUpdateTime(Landroid/content/Context;)J
-PLandroidx/profileinstaller/ProfileVerifier;->setCompilationStatus(IZZ)Landroidx/profileinstaller/ProfileVerifier$CompilationStatus;
-PLandroidx/profileinstaller/ProfileVerifier;->writeProfileVerification(Landroid/content/Context;Z)V
-PLandroidx/tv/foundation/lazy/grid/ComposableSingletons$LazyGridItemProviderKt$lambda-1$1;-><clinit>()V
-PLandroidx/tv/foundation/lazy/grid/ComposableSingletons$LazyGridItemProviderKt$lambda-1$1;-><init>()V
-PLandroidx/tv/foundation/lazy/grid/ComposableSingletons$LazyGridItemProviderKt;-><clinit>()V
-PLandroidx/tv/foundation/lazy/grid/EmptyTvLazyGridLayoutInfo;-><clinit>()V
-PLandroidx/tv/foundation/lazy/grid/EmptyTvLazyGridLayoutInfo;-><init>()V
-PLandroidx/tv/foundation/lazy/grid/ItemIndex;-><init>(I)V
-PLandroidx/tv/foundation/lazy/grid/ItemIndex;->equals(Ljava/lang/Object;)Z
-PLandroidx/tv/foundation/lazy/grid/LazyGridDslKt$rememberColumnWidthSums$1$1;-><init>(Landroidx/compose/foundation/layout/PaddingValues;Landroidx/tv/foundation/lazy/grid/TvGridCells;Landroidx/compose/foundation/layout/Arrangement$Horizontal;)V
-PLandroidx/tv/foundation/lazy/grid/LazyGridDslKt$rememberRowHeightSums$1$1;-><init>(Landroidx/compose/foundation/layout/PaddingValues;Landroidx/tv/foundation/lazy/grid/TvGridCells;Landroidx/compose/foundation/layout/Arrangement$Vertical;)V
-PLandroidx/tv/foundation/lazy/grid/LazyGridDslKt;->TvLazyHorizontalGrid(IILandroidx/compose/foundation/layout/Arrangement$Horizontal;Landroidx/compose/foundation/layout/Arrangement$Vertical;Landroidx/compose/foundation/layout/PaddingValues;Landroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;Landroidx/tv/foundation/PivotOffsets;Landroidx/tv/foundation/lazy/grid/TvGridCells;Landroidx/tv/foundation/lazy/grid/TvLazyGridState;Lkotlin/jvm/functions/Function1;ZZ)V
-PLandroidx/tv/foundation/lazy/grid/LazyGridDslKt;->TvLazyVerticalGrid(IILandroidx/compose/foundation/layout/Arrangement$Horizontal;Landroidx/compose/foundation/layout/Arrangement$Vertical;Landroidx/compose/foundation/layout/PaddingValues;Landroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;Landroidx/tv/foundation/PivotOffsets;Landroidx/tv/foundation/lazy/grid/TvGridCells;Landroidx/tv/foundation/lazy/grid/TvLazyGridState;Lkotlin/jvm/functions/Function1;ZZ)V
-PLandroidx/tv/foundation/lazy/grid/LazyGridIntervalContent;-><init>(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/internal/ComposableLambdaImpl;)V
-PLandroidx/tv/foundation/lazy/grid/LazyGridItemPlacementAnimator;-><init>(Lkotlinx/coroutines/CoroutineScope;Z)V
-PLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderImpl;-><init>(Landroidx/compose/foundation/lazy/layout/MutableIntervalList;ZLkotlin/ranges/IntRange;)V
-PLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderImpl;->getSpanLayoutProvider()Landroidx/tv/foundation/lazy/grid/LazyGridSpanLayoutProvider;
-PLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderKt$rememberLazyGridItemProvider$1$1;-><init>(Landroidx/compose/runtime/DerivedSnapshotState;)V
-PLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderKt$rememberLazyGridItemProvider$1$itemProviderState$1;-><init>(Landroidx/compose/runtime/MutableState;Landroidx/compose/runtime/MutableState;)V
-PLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderKt$rememberLazyGridItemProvider$1$itemProviderState$1;->invoke()Ljava/lang/Object;
-PLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderKt$rememberLazyGridItemProvider$nearestItemsRangeState$1$1;-><init>(Landroidx/tv/foundation/lazy/grid/TvLazyGridState;)V
-PLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderKt$rememberLazyGridItemProvider$nearestItemsRangeState$1$1;->invoke()Ljava/lang/Object;
-PLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderKt$rememberLazyGridItemProvider$nearestItemsRangeState$2;-><clinit>()V
-PLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderKt$rememberLazyGridItemProvider$nearestItemsRangeState$2;-><init>()V
-PLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderKt$rememberLazyGridItemProvider$nearestItemsRangeState$2;->invoke()Ljava/lang/Object;
-PLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderKt$rememberLazyGridItemProvider$nearestItemsRangeState$3;-><clinit>()V
-PLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderKt$rememberLazyGridItemProvider$nearestItemsRangeState$3;-><init>()V
-PLandroidx/tv/foundation/lazy/grid/LazyGridItemProviderKt$rememberLazyGridItemProvider$nearestItemsRangeState$3;->invoke()Ljava/lang/Object;
-PLandroidx/tv/foundation/lazy/grid/LazyGridKt$ScrollPositionUpdater$1;-><init>(Landroidx/tv/foundation/lazy/grid/LazyGridItemProvider;Landroidx/tv/foundation/lazy/grid/TvLazyGridState;I)V
-PLandroidx/tv/foundation/lazy/grid/LazyGridKt$rememberLazyGridMeasurePolicy$1$1;-><init>(ZLandroidx/compose/foundation/layout/PaddingValues;ZLandroidx/tv/foundation/lazy/grid/TvLazyGridState;Landroidx/tv/foundation/lazy/grid/LazyGridItemProviderKt$rememberLazyGridItemProvider$1$1;Lkotlin/jvm/functions/Function2;Landroidx/compose/foundation/layout/Arrangement$Vertical;Landroidx/compose/foundation/layout/Arrangement$Horizontal;Landroidx/tv/foundation/lazy/grid/LazyGridItemPlacementAnimator;)V
-PLandroidx/tv/foundation/lazy/grid/LazyGridKt;->ScrollPositionUpdater(Landroidx/tv/foundation/lazy/grid/LazyGridItemProvider;Landroidx/tv/foundation/lazy/grid/TvLazyGridState;Landroidx/compose/runtime/Composer;I)V
-PLandroidx/tv/foundation/lazy/grid/LazyGridMeasureKt$measureLazyGrid$3;-><init>(Ljava/util/ArrayList;)V
-PLandroidx/tv/foundation/lazy/grid/LazyGridPositionedItem;->getColumn()I
-PLandroidx/tv/foundation/lazy/grid/LazyGridPositionedItem;->getIndex()I
-PLandroidx/tv/foundation/lazy/grid/LazyGridPositionedItem;->getRow()I
-PLandroidx/tv/foundation/lazy/grid/LazyGridScrollPosition;-><init>(II)V
-PLandroidx/tv/foundation/lazy/grid/LazyGridSpanLayoutProvider$Bucket;-><init>(II)V
-PLandroidx/tv/foundation/lazy/grid/LazyGridSpanLayoutProvider;-><init>(Landroidx/tv/foundation/lazy/grid/LazyGridItemProvider;)V
-PLandroidx/tv/foundation/lazy/grid/LazySemanticsKt$rememberLazyGridSemanticState$1$1$scrollAxisRange$1;-><init>(Landroidx/tv/foundation/lazy/grid/TvLazyGridState;)V
-PLandroidx/tv/foundation/lazy/grid/LazySemanticsKt$rememberLazyGridSemanticState$1$1$scrollAxisRange$2;-><init>(Landroidx/tv/foundation/lazy/grid/TvLazyGridState;Landroidx/compose/foundation/lazy/layout/LazyLayoutItemProvider;)V
-PLandroidx/tv/foundation/lazy/grid/LazySemanticsKt$rememberLazyGridSemanticState$1$1;-><init>(ZLandroidx/tv/foundation/lazy/grid/TvLazyGridState;Landroidx/tv/foundation/lazy/grid/LazyGridItemProviderKt$rememberLazyGridItemProvider$1$1;)V
-PLandroidx/tv/foundation/lazy/grid/LazySemanticsKt$rememberLazyGridSemanticState$1$1;->collectionInfo()Landroidx/compose/ui/semantics/CollectionInfo;
-PLandroidx/tv/foundation/lazy/grid/LazySemanticsKt$rememberLazyGridSemanticState$1$1;->scrollAxisRange()Landroidx/compose/ui/semantics/ScrollAxisRange;
-PLandroidx/tv/foundation/lazy/grid/LineIndex;-><init>(I)V
-PLandroidx/tv/foundation/lazy/grid/TvGridCells$Fixed;-><init>(I)V
-PLandroidx/tv/foundation/lazy/grid/TvGridItemSpan;-><init>(J)V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridItemScopeImpl;-><clinit>()V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridItemScopeImpl;-><init>()V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridMeasureResult;->getAlignmentLines()Ljava/util/Map;
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridMeasureResult;->getHeight()I
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridMeasureResult;->getTotalItemsCount()I
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridMeasureResult;->getVisibleItemsInfo()Ljava/util/List;
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridMeasureResult;->getWidth()I
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridMeasureResult;->placeChildren()V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridScope$items$1;-><clinit>()V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridScope$items$1;-><init>()V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridScope;->items$default(Landroidx/tv/foundation/lazy/grid/TvLazyGridScope;ILandroidx/compose/runtime/internal/ComposableLambdaImpl;)V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridScopeImpl$DefaultSpan$1;-><clinit>()V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridScopeImpl$DefaultSpan$1;-><init>()V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridScopeImpl;-><init>()V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridScopeImpl;->items(ILkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/internal/ComposableLambdaImpl;)V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridState$Companion$Saver$1;-><clinit>()V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridState$Companion$Saver$1;-><init>()V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridState$Companion$Saver$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridState$Companion$Saver$2;-><clinit>()V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridState$Companion$Saver$2;-><init>()V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridState$prefetchInfoRetriever$2;-><clinit>()V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridState$prefetchInfoRetriever$2;-><init>()V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridState$remeasurementModifier$1;-><init>(Landroidx/tv/foundation/lazy/grid/TvLazyGridState;)V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridState$remeasurementModifier$1;->onRemeasurementAvailable(Landroidx/compose/ui/layout/Remeasurement;)V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridState$scroll$1;-><init>(Landroidx/tv/foundation/lazy/grid/TvLazyGridState;Lkotlin/coroutines/Continuation;)V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridState$scroll$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridState$scrollableState$1;-><init>(Landroidx/tv/foundation/lazy/grid/TvLazyGridState;)V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridState;-><clinit>()V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridState;->getCanScrollBackward()Z
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridState;->getCanScrollForward()Z
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridStateKt$rememberTvLazyGridState$1$1;-><init>(II)V
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridStateKt$rememberTvLazyGridState$1$1;->invoke()Ljava/lang/Object;
-PLandroidx/tv/foundation/lazy/grid/TvLazyGridStateKt;->rememberTvLazyGridState(Landroidx/compose/runtime/Composer;)Landroidx/tv/foundation/lazy/grid/TvLazyGridState;
-PLandroidx/tv/foundation/lazy/list/LazyDslKt;->TvLazyColumn(Landroidx/compose/ui/Modifier;Landroidx/tv/foundation/lazy/list/TvLazyListState;Landroidx/compose/foundation/layout/PaddingValues;ZLandroidx/compose/foundation/layout/Arrangement$Vertical;Landroidx/compose/ui/Alignment$Horizontal;ZLandroidx/tv/foundation/PivotOffsets;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V
-PLandroidx/tv/foundation/lazy/list/LazyListBeyondBoundsModifierLocal$layout$2;->getHasMoreContent()Z
-PLandroidx/tv/material3/BringIntoViewIfChildrenAreFocusedKt$bringIntoViewIfChildrenAreFocused$2$1;-><init>(Lkotlin/jvm/internal/Ref$ObjectRef;)V
-PLandroidx/tv/material3/BringIntoViewIfChildrenAreFocusedKt$bringIntoViewIfChildrenAreFocused$2$2$1;-><init>(Lkotlin/jvm/internal/Ref$ObjectRef;)V
-PLandroidx/tv/material3/BringIntoViewIfChildrenAreFocusedKt$bringIntoViewIfChildrenAreFocused$2$2$1;->bringChildIntoView(Landroidx/compose/foundation/relocation/BringIntoViewResponderModifier$bringChildIntoView$2$1$1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-PLandroidx/tv/material3/BringIntoViewIfChildrenAreFocusedKt$bringIntoViewIfChildrenAreFocused$2$2$1;->calculateRectForParent(Landroidx/compose/ui/geometry/Rect;)Landroidx/compose/ui/geometry/Rect;
-PLandroidx/tv/material3/BringIntoViewIfChildrenAreFocusedKt$bringIntoViewIfChildrenAreFocused$2;-><clinit>()V
-PLandroidx/tv/material3/BringIntoViewIfChildrenAreFocusedKt$bringIntoViewIfChildrenAreFocused$2;-><init>()V
-PLandroidx/tv/material3/CarouselDefaults;-><clinit>()V
-PLandroidx/tv/material3/CarouselDefaults;-><init>()V
-PLandroidx/tv/material3/CarouselItemDefaults$OverlayEnterTransition$1;-><clinit>()V
-PLandroidx/tv/material3/CarouselItemDefaults$OverlayEnterTransition$1;-><init>()V
-PLandroidx/tv/material3/CarouselItemDefaults;-><clinit>()V
-PLandroidx/tv/material3/CarouselItemKt$CarouselItem$1$1;-><init>(Landroidx/compose/ui/focus/FocusManager;Landroidx/compose/runtime/MutableState;Lkotlin/coroutines/Continuation;)V
-PLandroidx/tv/material3/CarouselItemKt$CarouselItem$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/tv/material3/CarouselItemKt$CarouselItem$1$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/tv/material3/CarouselItemKt$CarouselItem$1;-><init>(Landroidx/compose/animation/core/MutableTransitionState;Landroidx/compose/ui/focus/FocusManager;Landroidx/compose/runtime/MutableState;Lkotlin/coroutines/Continuation;)V
-PLandroidx/tv/material3/CarouselItemKt$CarouselItem$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-PLandroidx/tv/material3/CarouselItemKt$CarouselItem$2$1;-><init>(Landroidx/compose/runtime/MutableState;)V
-PLandroidx/tv/material3/CarouselItemKt$CarouselItem$2$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/tv/material3/CarouselItemKt$CarouselItem$3;-><init>(Landroidx/compose/ui/focus/FocusManager;Landroidx/compose/animation/core/MutableTransitionState;Landroidx/compose/runtime/MutableState;Landroidx/compose/runtime/MutableState;)V
-PLandroidx/tv/material3/CarouselItemKt$CarouselItem$4$1;-><init>(JLandroidx/compose/animation/core/MutableTransitionState;Lkotlin/coroutines/Continuation;)V
-PLandroidx/tv/material3/CarouselItemKt$CarouselItem$4$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-PLandroidx/tv/material3/CarouselItemKt$CarouselItem$4$2;-><init>(ILkotlin/jvm/functions/Function2;)V
-PLandroidx/tv/material3/CarouselItemKt$onAnimationCompletion$1;-><init>(Lkotlin/coroutines/Continuation;)V
-PLandroidx/tv/material3/CarouselItemKt$onAnimationCompletion$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/tv/material3/CarouselItemKt$onAnimationCompletion$2;-><init>(Landroidx/compose/animation/core/MutableTransitionState;)V
-PLandroidx/tv/material3/CarouselItemKt$onAnimationCompletion$3;-><init>(Lkotlin/coroutines/Continuation;)V
-PLandroidx/tv/material3/CarouselItemKt$onAnimationCompletion$3;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-PLandroidx/tv/material3/CarouselItemKt$onAnimationCompletion$3;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/tv/material3/CarouselItemKt$onAnimationCompletion$3;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/tv/material3/CarouselKt$AutoScrollSideEffect$2$1;-><init>(JLandroidx/tv/material3/CarouselState;ILkotlin/coroutines/Continuation;)V
-PLandroidx/tv/material3/CarouselKt$AutoScrollSideEffect$2$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-PLandroidx/tv/material3/CarouselKt$AutoScrollSideEffect$2$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/tv/material3/CarouselKt$Carousel$3$1;-><init>(Landroidx/compose/runtime/MutableState;)V
-PLandroidx/tv/material3/CarouselKt$Carousel$3$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/tv/material3/CarouselKt$Carousel$4;-><init>(Landroidx/compose/ui/focus/FocusManager;Landroidx/compose/runtime/MutableState;Landroidx/compose/runtime/MutableState;)V
-PLandroidx/tv/material3/CarouselKt$Carousel$4;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/tv/material3/CarouselKt$Carousel$5$1$1;-><init>(Landroidx/compose/animation/EnterTransition;Landroidx/compose/animation/ExitTransition;)V
-PLandroidx/tv/material3/CarouselKt$Carousel$5$2$1$1;-><init>(Landroidx/compose/ui/focus/FocusRequester;Landroidx/compose/ui/focus/FocusManager;Landroidx/compose/runtime/MutableState;Landroidx/compose/runtime/MutableState;Lkotlin/coroutines/Continuation;)V
-PLandroidx/tv/material3/CarouselKt$Carousel$5$2$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/tv/material3/CarouselKt$Carousel$5$2$1$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/tv/material3/CarouselKt$Carousel$5$2$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-PLandroidx/tv/material3/CarouselKt$Carousel$5$2;-><init>(Lkotlin/jvm/functions/Function3;ILandroidx/compose/ui/focus/FocusRequester;Landroidx/compose/ui/focus/FocusManager;Landroidx/compose/runtime/MutableState;Landroidx/compose/runtime/MutableState;)V
-PLandroidx/tv/material3/CarouselKt$CarouselStateUpdater$1$1;-><init>(ILandroidx/tv/material3/CarouselState;Lkotlin/coroutines/Continuation;)V
-PLandroidx/tv/material3/CarouselKt$CarouselStateUpdater$1$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-PLandroidx/tv/material3/CarouselKt$CarouselStateUpdater$1$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/tv/material3/CarouselKt$handleKeyEvents$1$showPreviousSlideAndGetKeyEventPropagation$1;-><init>(Landroidx/tv/material3/CarouselState;ILandroidx/compose/ui/focus/FocusRequester;)V
-PLandroidx/tv/material3/CarouselKt$handleKeyEvents$1$showPreviousSlideAndGetKeyEventPropagation$1;->invoke()Ljava/lang/Object;
-PLandroidx/tv/material3/CarouselKt$handleKeyEvents$1;-><init>(Landroidx/compose/ui/focus/FocusManager;ZLandroidx/tv/material3/CarouselState;ILandroidx/compose/ui/focus/FocusRequester;)V
-PLandroidx/tv/material3/CarouselKt$onAnimationCompletion$1;-><init>(Lkotlin/coroutines/Continuation;)V
-PLandroidx/tv/material3/CarouselKt$onAnimationCompletion$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/tv/material3/CarouselKt$onAnimationCompletion$2;-><init>(Landroidx/compose/animation/AnimatedVisibilityScope;)V
-PLandroidx/tv/material3/CarouselKt$onAnimationCompletion$3;-><init>(Lkotlin/coroutines/Continuation;)V
-PLandroidx/tv/material3/CarouselKt$onAnimationCompletion$3;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
-PLandroidx/tv/material3/CarouselKt$onAnimationCompletion$3;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/tv/material3/CarouselKt$onAnimationCompletion$3;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-PLandroidx/tv/material3/CarouselKt;->AutoScrollSideEffect(JILandroidx/tv/material3/CarouselState;ZLkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V
-PLandroidx/tv/material3/CarouselKt;->CarouselStateUpdater(Landroidx/tv/material3/CarouselState;ILandroidx/compose/runtime/Composer;I)V
-PLandroidx/tv/material3/CarouselState;-><init>(I)V
-PLandroidx/tv/material3/CarouselState;->moveToNextSlide$tv_material_release(I)V
-PLandroidx/tv/material3/ComposableSingletons$CarouselKt$lambda-1$1;-><clinit>()V
-PLandroidx/tv/material3/ComposableSingletons$CarouselKt$lambda-1$1;-><init>()V
-PLandroidx/tv/material3/ComposableSingletons$CarouselKt;-><clinit>()V
-PLcom/example/tvcomposebasedtests/MainActivity;->onPause()V
-PLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$FeaturedCarouselKt$lambda-1$1;-><clinit>()V
-PLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$FeaturedCarouselKt$lambda-1$1;-><init>()V
-PLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$FeaturedCarouselKt$lambda-1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-PLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$FeaturedCarouselKt$lambda-2$1$1;-><init>(I)V
-PLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$FeaturedCarouselKt$lambda-2$1;-><clinit>()V
-PLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$FeaturedCarouselKt$lambda-2$1;-><init>()V
-PLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$FeaturedCarouselKt$lambda-3$1;-><clinit>()V
-PLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$FeaturedCarouselKt$lambda-3$1;-><init>()V
-PLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$FeaturedCarouselKt;-><clinit>()V
-PLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-2$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-PLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-3$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-PLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-4$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-PLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-5$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-PLcom/example/tvcomposebasedtests/tvComponents/ComposableSingletons$TopNavigationKt$lambda-6$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-PLcom/example/tvcomposebasedtests/tvComponents/FeaturedCarouselKt$FeaturedCarousel$1;-><init>(IILandroidx/tv/material3/CarouselState;)V
-PLcom/example/tvcomposebasedtests/tvComponents/FeaturedCarouselKt$OverlayButton$1;-><clinit>()V
-PLcom/example/tvcomposebasedtests/tvComponents/FeaturedCarouselKt$OverlayButton$1;-><init>()V
-PLcom/example/tvcomposebasedtests/tvComponents/FeaturedCarouselKt$OverlayButton$2$1;-><init>(Landroidx/compose/runtime/MutableState;)V
-PLcom/example/tvcomposebasedtests/tvComponents/FeaturedCarouselKt$OverlayButton$2$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLcom/example/tvcomposebasedtests/tvComponents/FeaturedCarouselKt$OverlayButton$3;-><init>(I)V
-PLcom/example/tvcomposebasedtests/tvComponents/FeaturedCarouselKt$OverlayButton$3;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-PLcom/example/tvcomposebasedtests/tvComponents/FeaturedCarouselKt;->FeaturedCarousel(ILandroidx/compose/runtime/Composer;I)V
-PLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$SampleTvLazyColumn$1$1;-><init>(I)V
-PLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$SampleTvLazyColumn$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$SampleTvLazyHorizontalGrid$1$1;-><init>(I)V
-PLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$SampleTvLazyHorizontalGrid$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$SampleTvLazyVerticalGrid$1$1;-><init>(I)V
-PLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$SampleTvLazyVerticalGrid$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$TvLazyRowsAndColumn$1$1$1$1$1$1;-><init>(II)V
-PLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$TvLazyRowsAndColumn$1$1$1$1$1;-><init>(II)V
-PLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$TvLazyRowsAndColumn$1$1$1$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$TvLazyRowsAndColumn$1$1$1;-><init>(II)V
-PLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$TvLazyRowsAndColumn$1$1;-><init>(III)V
-PLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt$TvLazyRowsAndColumn$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
-PLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt;->SampleTvLazyColumn(ILandroidx/compose/runtime/Composer;I)V
-PLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt;->SampleTvLazyHorizontalGrid(IIILandroidx/compose/runtime/Composer;)V
-PLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt;->SampleTvLazyVerticalGrid(IIILandroidx/compose/runtime/Composer;)V
-PLcom/example/tvcomposebasedtests/tvComponents/LazyContainersKt;->TvLazyRowsAndColumn(IIILandroidx/compose/runtime/Composer;)V
-PLcom/example/tvcomposebasedtests/tvComponents/TopNavigationKt$TopNavigation$4;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-PLcom/google/gson/FieldNamingPolicy$1;-><init>()V
-PLcom/google/gson/FieldNamingPolicy$1;->translateName(Ljava/lang/reflect/Field;)Ljava/lang/String;
-PLcom/google/gson/FieldNamingPolicy$2;-><init>()V
-PLcom/google/gson/FieldNamingPolicy$3;-><init>()V
-PLcom/google/gson/FieldNamingPolicy$4;-><init>()V
-PLcom/google/gson/FieldNamingPolicy$5;-><init>()V
-PLcom/google/gson/FieldNamingPolicy$6;-><init>()V
-PLcom/google/gson/FieldNamingPolicy$7;-><init>()V
-PLcom/google/gson/FieldNamingPolicy;-><clinit>()V
-PLcom/google/gson/FieldNamingPolicy;-><init>(Ljava/lang/String;I)V
-PLcom/google/gson/Gson$1;-><init>()V
-PLcom/google/gson/Gson$2;-><init>()V
-PLcom/google/gson/Gson$4;-><init>(Lcom/google/gson/TypeAdapter;)V
-PLcom/google/gson/Gson$5;-><init>(Lcom/google/gson/TypeAdapter;)V
-PLcom/google/gson/Gson$FutureTypeAdapter;-><init>()V
-PLcom/google/gson/Gson;-><init>()V
-PLcom/google/gson/Gson;->newJsonWriter(Ljava/io/Writer;)Lcom/google/gson/stream/JsonWriter;
-PLcom/google/gson/Gson;->toJson(Lcom/google/gson/JsonObject;Lcom/google/gson/stream/JsonWriter;)V
-PLcom/google/gson/JsonArray;->iterator()Ljava/util/Iterator;
-PLcom/google/gson/JsonElement;->getAsJsonPrimitive()Lcom/google/gson/JsonPrimitive;
-PLcom/google/gson/JsonNull;-><clinit>()V
-PLcom/google/gson/JsonNull;-><init>()V
-PLcom/google/gson/JsonPrimitive;-><init>(Ljava/lang/String;)V
-PLcom/google/gson/TypeAdapter$1;-><init>(Lcom/google/gson/TypeAdapter;)V
-PLcom/google/gson/internal/$Gson$Types$ParameterizedTypeImpl;-><init>(Ljava/lang/reflect/Type;Ljava/lang/reflect/Type;[Ljava/lang/reflect/Type;)V
-PLcom/google/gson/internal/$Gson$Types$ParameterizedTypeImpl;->getActualTypeArguments()[Ljava/lang/reflect/Type;
-PLcom/google/gson/internal/$Gson$Types$ParameterizedTypeImpl;->getRawType()Ljava/lang/reflect/Type;
-PLcom/google/gson/internal/$Gson$Types$ParameterizedTypeImpl;->hashCode()I
-PLcom/google/gson/internal/$Gson$Types;-><clinit>()V
-PLcom/google/gson/internal/$Gson$Types;->checkNotPrimitive(Ljava/lang/reflect/Type;)V
-PLcom/google/gson/internal/$Gson$Types;->equals(Ljava/lang/reflect/Type;Ljava/lang/reflect/Type;)Z
-PLcom/google/gson/internal/$Gson$Types;->getGenericSupertype(Ljava/lang/reflect/Type;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/reflect/Type;
-PLcom/google/gson/internal/$Gson$Types;->getRawType(Ljava/lang/reflect/Type;)Ljava/lang/Class;
-PLcom/google/gson/internal/$Gson$Types;->getSupertype(Ljava/lang/reflect/Type;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/reflect/Type;
-PLcom/google/gson/internal/$Gson$Types;->resolve(Ljava/lang/reflect/Type;Ljava/lang/Class;Ljava/lang/reflect/Type;Ljava/util/HashMap;)Ljava/lang/reflect/Type;
-PLcom/google/gson/internal/ConstructorConstructor$13;-><init>()V
-PLcom/google/gson/internal/ConstructorConstructor$19;-><init>(Ljava/lang/Class;)V
-PLcom/google/gson/internal/ConstructorConstructor;-><init>(Ljava/util/Map;Ljava/util/List;)V
-PLcom/google/gson/internal/ConstructorConstructor;->checkInstantiable(Ljava/lang/Class;)Ljava/lang/String;
-PLcom/google/gson/internal/ConstructorConstructor;->get(Lcom/google/gson/reflect/TypeToken;)Lcom/google/gson/internal/ObjectConstructor;
-PLcom/google/gson/internal/Excluder;-><clinit>()V
-PLcom/google/gson/internal/Excluder;-><init>()V
-PLcom/google/gson/internal/Excluder;->create(Lcom/google/gson/Gson;Lcom/google/gson/reflect/TypeToken;)Lcom/google/gson/TypeAdapter;
-PLcom/google/gson/internal/Excluder;->excludeClassChecks(Ljava/lang/Class;)Z
-PLcom/google/gson/internal/Excluder;->excludeClassInStrategy(Ljava/lang/Class;Z)Z
-PLcom/google/gson/internal/Excluder;->isAnonymousOrNonStaticLocal(Ljava/lang/Class;)Z
-PLcom/google/gson/internal/LinkedTreeMap$1;-><init>()V
-PLcom/google/gson/internal/LinkedTreeMap$Node;-><init>(Z)V
-PLcom/google/gson/internal/LinkedTreeMap;-><clinit>()V
-PLcom/google/gson/internal/LinkedTreeMap;->entrySet()Ljava/util/Set;
-PLcom/google/gson/internal/ReflectionAccessFilterHelper;->getFilterResult(Ljava/util/List;Ljava/lang/Class;)I
-PLcom/google/gson/internal/bind/ArrayTypeAdapter$1;-><init>()V
-PLcom/google/gson/internal/bind/ArrayTypeAdapter$1;->create(Lcom/google/gson/Gson;Lcom/google/gson/reflect/TypeToken;)Lcom/google/gson/TypeAdapter;
-PLcom/google/gson/internal/bind/ArrayTypeAdapter;-><clinit>()V
-PLcom/google/gson/internal/bind/CollectionTypeAdapterFactory$Adapter;-><init>(Lcom/google/gson/Gson;Ljava/lang/reflect/Type;Lcom/google/gson/TypeAdapter;)V
-PLcom/google/gson/internal/bind/CollectionTypeAdapterFactory;-><init>(Lcom/google/gson/internal/ConstructorConstructor;)V
-PLcom/google/gson/internal/bind/CollectionTypeAdapterFactory;->create(Lcom/google/gson/Gson;Lcom/google/gson/reflect/TypeToken;)Lcom/google/gson/TypeAdapter;
-PLcom/google/gson/internal/bind/DateTypeAdapter$1;-><init>()V
-PLcom/google/gson/internal/bind/DateTypeAdapter$1;->create(Lcom/google/gson/Gson;Lcom/google/gson/reflect/TypeToken;)Lcom/google/gson/TypeAdapter;
-PLcom/google/gson/internal/bind/DateTypeAdapter;-><clinit>()V
-PLcom/google/gson/internal/bind/DefaultDateTypeAdapter$DateType;-><clinit>()V
-PLcom/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory;-><init>(Lcom/google/gson/internal/ConstructorConstructor;)V
-PLcom/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory;->create(Lcom/google/gson/Gson;Lcom/google/gson/reflect/TypeToken;)Lcom/google/gson/TypeAdapter;
-PLcom/google/gson/internal/bind/JsonTreeWriter$1;-><init>()V
-PLcom/google/gson/internal/bind/JsonTreeWriter;-><clinit>()V
-PLcom/google/gson/internal/bind/MapTypeAdapterFactory;-><init>(Lcom/google/gson/internal/ConstructorConstructor;)V
-PLcom/google/gson/internal/bind/MapTypeAdapterFactory;->create(Lcom/google/gson/Gson;Lcom/google/gson/reflect/TypeToken;)Lcom/google/gson/TypeAdapter;
-PLcom/google/gson/internal/bind/NumberTypeAdapter$1;-><init>(Lcom/google/gson/internal/bind/NumberTypeAdapter;)V
-PLcom/google/gson/internal/bind/NumberTypeAdapter$1;->create(Lcom/google/gson/Gson;Lcom/google/gson/reflect/TypeToken;)Lcom/google/gson/TypeAdapter;
-PLcom/google/gson/internal/bind/NumberTypeAdapter;-><clinit>()V
-PLcom/google/gson/internal/bind/NumberTypeAdapter;-><init>()V
-PLcom/google/gson/internal/bind/ObjectTypeAdapter$1;-><init>()V
-PLcom/google/gson/internal/bind/ObjectTypeAdapter$1;->create(Lcom/google/gson/Gson;Lcom/google/gson/reflect/TypeToken;)Lcom/google/gson/TypeAdapter;
-PLcom/google/gson/internal/bind/ObjectTypeAdapter;-><clinit>()V
-PLcom/google/gson/internal/bind/ReflectiveTypeAdapterFactory$1;-><init>(Ljava/lang/String;Ljava/lang/reflect/Field;ZZZLjava/lang/reflect/Method;ZLcom/google/gson/TypeAdapter;Lcom/google/gson/Gson;Lcom/google/gson/reflect/TypeToken;)V
-PLcom/google/gson/internal/bind/ReflectiveTypeAdapterFactory$Adapter;-><init>(Ljava/util/LinkedHashMap;)V
-PLcom/google/gson/internal/bind/ReflectiveTypeAdapterFactory$BoundField;-><init>(Ljava/lang/String;Ljava/lang/reflect/Field;Z)V
-PLcom/google/gson/internal/bind/ReflectiveTypeAdapterFactory$FieldReflectionAdapter;-><init>(Ljava/util/LinkedHashMap;)V
-PLcom/google/gson/internal/bind/ReflectiveTypeAdapterFactory;-><init>(Lcom/google/gson/internal/ConstructorConstructor;Lcom/google/gson/internal/Excluder;Lcom/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory;Ljava/util/List;)V
-PLcom/google/gson/internal/bind/ReflectiveTypeAdapterFactory;->create(Lcom/google/gson/Gson;Lcom/google/gson/reflect/TypeToken;)Lcom/google/gson/TypeAdapter;
-PLcom/google/gson/internal/bind/ReflectiveTypeAdapterFactory;->getBoundFields(Lcom/google/gson/Gson;Lcom/google/gson/reflect/TypeToken;Ljava/lang/Class;ZZ)Ljava/util/LinkedHashMap;
-PLcom/google/gson/internal/bind/ReflectiveTypeAdapterFactory;->includeField(Ljava/lang/reflect/Field;Z)Z
-PLcom/google/gson/internal/bind/SerializationDelegatingTypeAdapter;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$10;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$11;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$12;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$13;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$14;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$15;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$16;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$17;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$18;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$19;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$1;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$20;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$21;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$22;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$23;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$24;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$25;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$26;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$27;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$28;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$28;->write(Lcom/google/gson/stream/JsonWriter;Ljava/lang/Object;)V
-PLcom/google/gson/internal/bind/TypeAdapters$29;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$29;->create(Lcom/google/gson/Gson;Lcom/google/gson/reflect/TypeToken;)Lcom/google/gson/TypeAdapter;
-PLcom/google/gson/internal/bind/TypeAdapters$2;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$31;-><init>(Ljava/lang/Class;Lcom/google/gson/TypeAdapter;)V
-PLcom/google/gson/internal/bind/TypeAdapters$31;->create(Lcom/google/gson/Gson;Lcom/google/gson/reflect/TypeToken;)Lcom/google/gson/TypeAdapter;
-PLcom/google/gson/internal/bind/TypeAdapters$32;-><init>(Ljava/lang/Class;Ljava/lang/Class;Lcom/google/gson/TypeAdapter;)V
-PLcom/google/gson/internal/bind/TypeAdapters$32;->create(Lcom/google/gson/Gson;Lcom/google/gson/reflect/TypeToken;)Lcom/google/gson/TypeAdapter;
-PLcom/google/gson/internal/bind/TypeAdapters$33;-><init>(Ljava/lang/Class;Ljava/lang/Class;Lcom/google/gson/internal/bind/TypeAdapters$26;)V
-PLcom/google/gson/internal/bind/TypeAdapters$33;->create(Lcom/google/gson/Gson;Lcom/google/gson/reflect/TypeToken;)Lcom/google/gson/TypeAdapter;
-PLcom/google/gson/internal/bind/TypeAdapters$34;-><init>(Ljava/lang/Class;Lcom/google/gson/TypeAdapter;)V
-PLcom/google/gson/internal/bind/TypeAdapters$34;->create(Lcom/google/gson/Gson;Lcom/google/gson/reflect/TypeToken;)Lcom/google/gson/TypeAdapter;
-PLcom/google/gson/internal/bind/TypeAdapters$3;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$4;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$5;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$6;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$7;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$8;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters$9;-><init>()V
-PLcom/google/gson/internal/bind/TypeAdapters;-><clinit>()V
-PLcom/google/gson/internal/reflect/ReflectionHelper$RecordHelper;-><init>()V
-PLcom/google/gson/internal/reflect/ReflectionHelper$RecordNotSupportedHelper;-><init>()V
-PLcom/google/gson/internal/reflect/ReflectionHelper$RecordNotSupportedHelper;->isRecord(Ljava/lang/Class;)Z
-PLcom/google/gson/internal/reflect/ReflectionHelper$RecordSupportedHelper;-><init>()V
-PLcom/google/gson/internal/reflect/ReflectionHelper;-><clinit>()V
-PLcom/google/gson/internal/reflect/ReflectionHelper;->makeAccessible(Ljava/lang/reflect/AccessibleObject;)V
-PLcom/google/gson/internal/sql/SqlDateTypeAdapter$1;-><init>()V
-PLcom/google/gson/internal/sql/SqlDateTypeAdapter$1;->create(Lcom/google/gson/Gson;Lcom/google/gson/reflect/TypeToken;)Lcom/google/gson/TypeAdapter;
-PLcom/google/gson/internal/sql/SqlDateTypeAdapter;-><clinit>()V
-PLcom/google/gson/internal/sql/SqlTimeTypeAdapter$1;-><init>()V
-PLcom/google/gson/internal/sql/SqlTimeTypeAdapter$1;->create(Lcom/google/gson/Gson;Lcom/google/gson/reflect/TypeToken;)Lcom/google/gson/TypeAdapter;
-PLcom/google/gson/internal/sql/SqlTimeTypeAdapter;-><clinit>()V
-PLcom/google/gson/internal/sql/SqlTimestampTypeAdapter$1;-><init>()V
-PLcom/google/gson/internal/sql/SqlTimestampTypeAdapter$1;->create(Lcom/google/gson/Gson;Lcom/google/gson/reflect/TypeToken;)Lcom/google/gson/TypeAdapter;
-PLcom/google/gson/internal/sql/SqlTimestampTypeAdapter;-><clinit>()V
-PLcom/google/gson/internal/sql/SqlTypesSupport;-><clinit>()V
-PLcom/google/gson/stream/JsonWriter;-><clinit>()V
-PLcom/google/gson/stream/JsonWriter;->endArray()V
-PLcom/google/gson/stream/JsonWriter;->endObject()V
-PLcom/google/gson/stream/JsonWriter;->newline()V
-PLcom/google/gson/stream/JsonWriter;->value(Z)V
-PLkotlin/collections/ArrayAsCollection;-><init>([Ljava/lang/Object;Z)V
-PLkotlin/collections/ArrayAsCollection;->toArray()[Ljava/lang/Object;
-PLkotlin/collections/CollectionsKt__CollectionsKt;->arrayListOf([Ljava/lang/Object;)Ljava/util/ArrayList;
-PLkotlin/coroutines/jvm/internal/BaseContinuationImpl;->releaseIntercepted()V
-PLkotlin/io/CloseableKt;->closeFinally(Ljava/io/Closeable;Ljava/lang/Throwable;)V
-PLkotlin/jvm/internal/Ref$IntRef;-><init>()V
-PLkotlin/jvm/internal/TypeIntrinsics;->asMutableCollection(Ljava/util/LinkedHashSet;)Ljava/util/Collection;
-PLkotlin/sequences/SequenceBuilderIterator;-><init>()V
-PLkotlin/sequences/SequenceBuilderIterator;->getContext()Lkotlin/coroutines/CoroutineContext;
-PLkotlin/sequences/SequenceBuilderIterator;->hasNext()Z
-PLkotlin/sequences/SequenceBuilderIterator;->next()Ljava/lang/Object;
-PLkotlin/sequences/SequenceBuilderIterator;->resumeWith(Ljava/lang/Object;)V
-PLkotlin/sequences/SequenceBuilderIterator;->yield(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)V
-PLkotlin/sequences/SequenceScope;-><init>()V
-PLkotlin/text/Charsets;-><clinit>()V
-PLkotlinx/coroutines/CancellableContinuationImpl;->resumeUndispatched(Lkotlinx/coroutines/CoroutineDispatcher;Lkotlin/Unit;)V
-PLkotlinx/coroutines/ChildHandleNode;->invoke(Ljava/lang/Throwable;)V
-PLkotlinx/coroutines/CompletedContinuation;->copy$default(Lkotlinx/coroutines/CompletedContinuation;Lkotlinx/coroutines/CancelHandler;Ljava/util/concurrent/CancellationException;I)Lkotlinx/coroutines/CompletedContinuation;
-PLkotlinx/coroutines/CoroutineDispatcher;->dispatchYield(Lkotlin/coroutines/CoroutineContext;Ljava/lang/Runnable;)V
-PLkotlinx/coroutines/DefaultExecutor;->acknowledgeShutdownIfNeeded()V
-PLkotlinx/coroutines/DefaultExecutor;->getThread()Ljava/lang/Thread;
-PLkotlinx/coroutines/DefaultExecutor;->run()V
-PLkotlinx/coroutines/DelayKt;->delay(JLkotlin/coroutines/Continuation;)Ljava/lang/Object;
-PLkotlinx/coroutines/DisposeOnCancel;-><init>(Lkotlinx/coroutines/DisposableHandle;)V
-PLkotlinx/coroutines/DisposeOnCancel;->invoke(Ljava/lang/Throwable;)V
-PLkotlinx/coroutines/EventLoopImplBase$DelayedResumeTask;-><init>(Lkotlinx/coroutines/EventLoopImplBase;JLkotlinx/coroutines/CancellableContinuationImpl;)V
-PLkotlinx/coroutines/EventLoopImplBase$DelayedResumeTask;->run()V
-PLkotlinx/coroutines/EventLoopImplBase$DelayedTask;-><init>(J)V
-PLkotlinx/coroutines/EventLoopImplBase$DelayedTask;->dispose()V
-PLkotlinx/coroutines/EventLoopImplBase$DelayedTask;->scheduleTask(JLkotlinx/coroutines/EventLoopImplBase$DelayedTaskQueue;Lkotlinx/coroutines/EventLoopImplBase;)I
-PLkotlinx/coroutines/EventLoopImplBase$DelayedTask;->setHeap(Lkotlinx/coroutines/EventLoopImplBase$DelayedTaskQueue;)V
-PLkotlinx/coroutines/EventLoopImplBase$DelayedTask;->setIndex(I)V
-PLkotlinx/coroutines/EventLoopImplBase$DelayedTaskQueue;-><init>(J)V
-PLkotlinx/coroutines/EventLoopImplBase;->access$isCompleted(Lkotlinx/coroutines/EventLoopImplBase;)Z
-PLkotlinx/coroutines/EventLoopImplBase;->enqueueImpl(Ljava/lang/Runnable;)Z
-PLkotlinx/coroutines/EventLoopImplBase;->isEmpty()Z
-PLkotlinx/coroutines/EventLoopImplBase;->processNextEvent()J
-PLkotlinx/coroutines/EventLoopImplBase;->schedule(JLkotlinx/coroutines/EventLoopImplBase$DelayedTask;)V
-PLkotlinx/coroutines/EventLoopImplBase;->scheduleResumeAfterDelay(JLkotlinx/coroutines/CancellableContinuationImpl;)V
-PLkotlinx/coroutines/EventLoop_commonKt;-><clinit>()V
-PLkotlinx/coroutines/InvokeOnCompletion;->invoke(Ljava/lang/Throwable;)V
-PLkotlinx/coroutines/JobKt;->access$removeEntryAtIndex([Ljava/lang/Object;I)[Ljava/lang/Object;
-PLkotlinx/coroutines/JobSupport$Finishing;->isSealed()Z
-PLkotlinx/coroutines/JobSupport;->getChildJobCancellationCause()Ljava/util/concurrent/CancellationException;
-PLkotlinx/coroutines/JobSupport;->join(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-PLkotlinx/coroutines/JobSupport;->parentCancelled(Lkotlinx/coroutines/JobSupport;)V
-PLkotlinx/coroutines/UndispatchedCoroutine;->afterResume(Ljava/lang/Object;)V
-PLkotlinx/coroutines/flow/FlowKt__ContextKt;->checkArgument(Z)V
-PLkotlinx/coroutines/flow/FlowKt__DistinctKt$defaultAreEquivalent$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-PLkotlinx/coroutines/flow/FlowKt__ReduceKt$first$3;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-PLkotlinx/coroutines/flow/SharedFlowImpl$collect$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
-PLkotlinx/coroutines/flow/SharedFlowSlot;->freeLocked(Lkotlinx/coroutines/flow/internal/AbstractSharedFlow;)[Lkotlin/coroutines/Continuation;
-PLkotlinx/coroutines/flow/StateFlowSlot;->freeLocked(Lkotlinx/coroutines/flow/internal/AbstractSharedFlow;)[Lkotlin/coroutines/Continuation;
-PLkotlinx/coroutines/flow/internal/AbortFlowException;-><init>(Lkotlinx/coroutines/flow/FlowKt__ReduceKt$first$$inlined$collectWhile$2;)V
-PLkotlinx/coroutines/flow/internal/AbortFlowException;->fillInStackTrace()Ljava/lang/Throwable;
-PLkotlinx/coroutines/flow/internal/AbstractSharedFlow;->freeSlot(Lkotlinx/coroutines/flow/internal/AbstractSharedFlowSlot;)V
-PLkotlinx/coroutines/flow/internal/ChildCancelledException;-><init>()V
-PLkotlinx/coroutines/flow/internal/ChildCancelledException;->fillInStackTrace()Ljava/lang/Throwable;
-PLkotlinx/coroutines/flow/internal/DownstreamExceptionContext;-><init>(Lkotlin/coroutines/CoroutineContext;Ljava/lang/Throwable;)V
-PLkotlinx/coroutines/flow/internal/SafeCollector;->getContext()Lkotlin/coroutines/CoroutineContext;
-PLkotlinx/coroutines/flow/internal/SafeCollector;->releaseIntercepted()V
-PLkotlinx/coroutines/internal/ThreadSafeHeap;-><init>()V
-PLkotlinx/coroutines/internal/ThreadSafeHeap;->addImpl(Lkotlinx/coroutines/EventLoopImplBase$DelayedTask;)V
-PLkotlinx/coroutines/internal/ThreadSafeHeap;->isEmpty()Z
-PLkotlinx/coroutines/internal/ThreadSafeHeap;->removeAtImpl(I)Lkotlinx/coroutines/internal/ThreadSafeHeapNode;
-PLkotlinx/coroutines/internal/ThreadSafeHeap;->siftUpFrom(I)V
\ No newline at end of file
diff --git a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/App.kt b/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/App.kt
deleted file mode 100644
index 00ea81e..0000000
--- a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/App.kt
+++ /dev/null
@@ -1,49 +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.tv.integration.demos
-
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.padding
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-import androidx.tv.material3.ExperimentalTvMaterial3Api
-import androidx.tv.material3.MaterialTheme
-import androidx.tv.material3.darkColorScheme
-
-@OptIn(ExperimentalTvMaterial3Api::class)
-@Composable
-fun App() {
-    var selectedTab by remember { mutableStateOf(Navigation.FeaturedCarousel) }
-
-    MaterialTheme(
-        colorScheme = darkColorScheme()
-    ) {
-        Column(
-            modifier = Modifier.padding(20.dp),
-            verticalArrangement = Arrangement.spacedBy(20.dp)
-        ) {
-            TopNavigation(updateSelectedTab = { selectedTab = it })
-            selectedTab.action.invoke()
-        }
-    }
-}
diff --git a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/FeaturedCarousel.kt b/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/FeaturedCarousel.kt
deleted file mode 100644
index deeafa0..0000000
--- a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/FeaturedCarousel.kt
+++ /dev/null
@@ -1,166 +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.tv.integration.demos
-
-import androidx.compose.animation.ExperimentalAnimationApi
-import androidx.compose.foundation.background
-import androidx.compose.foundation.border
-import androidx.compose.foundation.focusable
-import androidx.compose.foundation.layout.Arrangement
-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.layout.width
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material3.Button
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.onFocusChanged
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
-import androidx.tv.material3.Carousel
-import androidx.tv.material3.CarouselDefaults
-import androidx.tv.material3.CarouselState
-import androidx.tv.material3.ExperimentalTvMaterial3Api
-
-@Composable
-fun FeaturedCarouselContent() {
-    LazyColumn(verticalArrangement = Arrangement.spacedBy(20.dp)) {
-        items(3) { SampleLazyRow() }
-        item {
-            Row(horizontalArrangement = Arrangement.spacedBy(20.dp)) {
-                Column(verticalArrangement = Arrangement.spacedBy(20.dp)) {
-                    repeat(3) {
-                        Box(
-                            modifier = Modifier
-                                .background(Color.Magenta.copy(alpha = 0.3f))
-                                .width(50.dp)
-                                .height(50.dp)
-                                .drawBorderOnFocus()
-                                .focusable()
-                        )
-                    }
-                }
-
-                FeaturedCarousel(Modifier.weight(1f))
-
-                Column(verticalArrangement = Arrangement.spacedBy(20.dp)) {
-                    repeat(3) {
-                        Box(
-                            modifier = Modifier
-                                .background(Color.Magenta.copy(alpha = 0.3f))
-                                .width(50.dp)
-                                .height(50.dp)
-                                .drawBorderOnFocus()
-                                .focusable()
-                        )
-                    }
-                }
-            }
-        }
-        items(2) { SampleLazyRow() }
-    }
-}
-
-@Composable
-fun Modifier.drawBorderOnFocus(borderColor: Color = Color.White, width: Dp = 5.dp): Modifier {
-    var isFocused by remember { mutableStateOf(false) }
-    return this
-        .border(width, borderColor.copy(alpha = if (isFocused) 1f else 0.2f))
-        .onFocusChanged { isFocused = it.isFocused }
-}
-
-@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
-@Composable
-internal fun FeaturedCarousel(modifier: Modifier = Modifier) {
-    val backgrounds = listOf(
-        Color.Red.copy(alpha = 0.3f),
-        Color.Yellow.copy(alpha = 0.3f),
-        Color.Green.copy(alpha = 0.3f),
-        Color.Blue.copy(alpha = 0.3f),
-        Color.LightGray.copy(alpha = 0.3f),
-        Color.Magenta.copy(alpha = 0.3f),
-        Color.DarkGray.copy(alpha = 0.3f),
-        Color.LightGray.copy(alpha = 0.3f),
-    )
-
-    val carouselState = remember { CarouselState() }
-    Carousel(
-        slideCount = backgrounds.size,
-        carouselState = carouselState,
-        modifier = modifier
-            .height(300.dp)
-            .fillMaxWidth(),
-        carouselIndicator = {
-            CarouselDefaults.IndicatorRow(
-                slideCount = backgrounds.size,
-                activeSlideIndex = carouselState.activeSlideIndex,
-                modifier = Modifier
-                    .align(Alignment.BottomEnd)
-                    .padding(16.dp),
-            )
-        }
-    ) { itemIndex ->
-        CarouselSlide(
-            background = {
-                Box(
-                    modifier = Modifier
-                        .background(backgrounds[itemIndex])
-                        .fillMaxSize()
-                )
-            }
-        ) {
-            Box(modifier = Modifier) {
-                OverlayButton(
-                    modifier = Modifier
-                )
-            }
-        }
-    }
-}
-
-@Composable
-private fun OverlayButton(modifier: Modifier = Modifier) {
-    var isFocused by remember { mutableStateOf(false) }
-
-    Button(
-        onClick = { },
-        modifier = modifier
-            .onFocusChanged { isFocused = it.isFocused }
-            .padding(40.dp)
-            .border(
-                width = 2.dp,
-                color = if (isFocused) Color.Red else Color.Transparent,
-                shape = RoundedCornerShape(50)
-            )
-            .padding(vertical = 2.dp, horizontal = 5.dp)
-    ) {
-        Text(text = "Play")
-    }
-}
diff --git a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/ImmersiveList.kt b/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/ImmersiveList.kt
deleted file mode 100644
index a690908..0000000
--- a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/ImmersiveList.kt
+++ /dev/null
@@ -1,96 +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.tv.integration.demos
-
-import android.util.Log
-import androidx.compose.foundation.background
-import androidx.compose.foundation.border
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.width
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.onFocusChanged
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.unit.dp
-import androidx.tv.foundation.lazy.list.TvLazyColumn
-import androidx.tv.material3.ExperimentalTvMaterial3Api
-import androidx.tv.material3.ImmersiveList
-
-@Composable
-fun ImmersiveListContent() {
-    TvLazyColumn(verticalArrangement = Arrangement.spacedBy(20.dp)) {
-        items(3) { SampleLazyRow() }
-        item { SampleImmersiveList() }
-        items(3) { SampleLazyRow() }
-    }
-}
-
-@OptIn(ExperimentalTvMaterial3Api::class)
-@Composable
-private fun SampleImmersiveList() {
-    val immersiveListHeight = 300.dp
-    val cardSpacing = 10.dp
-    val cardWidth = 200.dp
-    val cardHeight = 150.dp
-    val backgrounds = listOf(
-        Color.Red,
-        Color.Blue,
-        Color.Magenta,
-    )
-
-    ImmersiveList(
-        modifier = Modifier
-            .height(immersiveListHeight + cardHeight / 2)
-            .fillMaxWidth(),
-        background = { index, _ ->
-            Box(
-                modifier = Modifier
-                    .background(backgrounds[index].copy(alpha = 0.3f))
-                    .height(immersiveListHeight)
-                    .fillMaxWidth()
-            )
-        }
-    ) {
-        Row(horizontalArrangement = Arrangement.spacedBy(cardSpacing)) {
-            backgrounds.forEachIndexed { index, backgroundColor ->
-                var isFocused by remember { mutableStateOf(false) }
-
-                Box(
-                    modifier = Modifier
-                        .background(backgroundColor)
-                        .width(cardWidth)
-                        .height(cardHeight)
-                        .border(5.dp, Color.White.copy(alpha = if (isFocused) 1f else 0.3f))
-                        .onFocusChanged { isFocused = it.isFocused }
-                        .immersiveListItem(index)
-                        .clickable {
-                            Log.d("ImmersiveList", "Item $index was clicked")
-                        }
-                )
-            }
-        }
-    }
-}
diff --git a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/LazyRowsAndColumns.kt b/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/LazyRowsAndColumns.kt
deleted file mode 100644
index 72f62aa..0000000
--- a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/LazyRowsAndColumns.kt
+++ /dev/null
@@ -1,61 +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.tv.integration.demos
-
-import androidx.compose.foundation.background
-import androidx.compose.foundation.focusable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.width
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.unit.dp
-import androidx.tv.foundation.lazy.list.TvLazyColumn
-import androidx.tv.foundation.lazy.list.TvLazyRow
-
-const val rowsCount = 20
-const val columnsCount = 100
-
-@Composable
-fun LazyRowsAndColumns() {
-    TvLazyColumn(verticalArrangement = Arrangement.spacedBy(20.dp)) {
-        items(rowsCount) { SampleLazyRow() }
-    }
-}
-
-@Composable
-fun SampleLazyRow() {
-    val colors = listOf(Color.Red, Color.Magenta, Color.Green, Color.Yellow, Color.Blue, Color.Cyan)
-    val backgroundColors = List(columnsCount) { colors.random() }
-
-    TvLazyRow(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
-        backgroundColors.forEach { backgroundColor ->
-            item {
-                Box(
-                    modifier = Modifier
-                        .background(backgroundColor.copy(alpha = 0.3f))
-                        .width(200.dp)
-                        .height(150.dp)
-                        .drawBorderOnFocus()
-                        .focusable()
-                )
-            }
-        }
-    }
-}
diff --git a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/MainActivity.kt b/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/MainActivity.kt
deleted file mode 100644
index fd0f6da..0000000
--- a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/MainActivity.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.tv.integration.demos
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-
-class MainActivity : ComponentActivity() {
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        setContent {
-            App()
-        }
-    }
-}
\ No newline at end of file
diff --git a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/SampleModalNavDrawer.kt b/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/SampleModalNavDrawer.kt
deleted file mode 100644
index e36b1df..0000000
--- a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/SampleModalNavDrawer.kt
+++ /dev/null
@@ -1,114 +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.tv.integration.demos
-
-import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.core.tween
-import androidx.compose.animation.shrinkHorizontally
-import androidx.compose.foundation.background
-import androidx.compose.foundation.border
-import androidx.compose.foundation.focusable
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
-import androidx.compose.material3.Button
-import androidx.compose.material3.Text
-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.platform.LocalLayoutDirection
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.dp
-import androidx.tv.material3.DrawerValue
-import androidx.tv.material3.ExperimentalTvMaterial3Api
-import androidx.tv.material3.ModalNavigationDrawer
-
-@OptIn(ExperimentalTvMaterial3Api::class)
-@Composable
-fun SampleModalDrawer() {
-    Row(Modifier.fillMaxSize()) {
-        Box(modifier = Modifier
-            .height(400.dp)
-            .width(400.dp)
-            .border(2.dp, Color.Magenta)) {
-            ModalNavigationDrawer(drawerContent = drawerContent()) {
-                Button(modifier = Modifier
-                    .height(100.dp)
-                    .fillMaxWidth(), onClick = {}) {
-                    Text("BUTTON")
-                }
-            }
-        }
-
-        CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
-            Box(
-                modifier = Modifier
-                    .height(400.dp)
-                    .width(400.dp)
-                    .border(2.dp, Color.Magenta)
-            ) {
-                ModalNavigationDrawer(drawerContent = drawerContent()) {
-                    Button(modifier = Modifier
-                        .height(100.dp)
-                        .fillMaxWidth(), onClick = {}) {
-                        Text("BUTTON")
-                    }
-                }
-            }
-        }
-    }
-}
-
-@Composable
-@OptIn(ExperimentalTvMaterial3Api::class)
-internal fun drawerContent(): @Composable (DrawerValue) -> Unit =
-    {
-        Column(Modifier.background(Color.Gray).fillMaxHeight()) {
-            NavigationRow(it, Color.Red, "Red")
-            NavigationRow(it, Color.Blue, "Blue")
-            NavigationRow(it, Color.Yellow, "Yellow")
-        }
-    }
-
-@OptIn(ExperimentalTvMaterial3Api::class)
-@Composable
-private fun NavigationRow(drawerValue: DrawerValue, color: Color, text: String) {
-    Row(Modifier.padding(10.dp).drawBorderOnFocus(width = 2.dp).focusable()) {
-        Box(Modifier.size(50.dp).background(color).padding(end = 20.dp))
-        AnimatedVisibility(
-            drawerValue == DrawerValue.Open,
-            // intentionally slow to test animation
-            exit = shrinkHorizontally(tween(2000))
-        ) {
-            Text(
-                text = text,
-                softWrap = false,
-                modifier = Modifier.padding(15.dp).width(50.dp),
-                textAlign = TextAlign.Center
-            )
-        }
-    }
-}
diff --git a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/SampleNavDrawer.kt b/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/SampleNavDrawer.kt
deleted file mode 100644
index cf1ed6a..0000000
--- a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/SampleNavDrawer.kt
+++ /dev/null
@@ -1,72 +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.tv.integration.demos
-
-import androidx.compose.foundation.border
-import androidx.compose.foundation.layout.Box
-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.width
-import androidx.compose.material3.Button
-import androidx.compose.material3.Text
-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.platform.LocalLayoutDirection
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.dp
-import androidx.tv.material3.ExperimentalTvMaterial3Api
-import androidx.tv.material3.NavigationDrawer
-
-@OptIn(ExperimentalTvMaterial3Api::class)
-@Composable
-fun SampleDrawer() {
-    Row(Modifier.fillMaxSize()) {
-        Box(modifier = Modifier
-            .height(400.dp)
-            .width(400.dp)
-            .border(2.dp, Color.Magenta)) {
-            NavigationDrawer(drawerContent = drawerContent()) {
-                Button(modifier = Modifier
-                    .height(100.dp)
-                    .fillMaxWidth(), onClick = {}) {
-                    Text("BUTTON")
-                }
-            }
-        }
-
-        CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
-            Box(
-                modifier = Modifier
-                    .height(400.dp)
-                    .width(400.dp)
-                    .border(2.dp, Color.Magenta)
-            ) {
-                NavigationDrawer(drawerContent = drawerContent()) {
-                    Button(modifier = Modifier
-                        .height(100.dp)
-                        .fillMaxWidth(), onClick = {}) {
-                        Text("BUTTON")
-                    }
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/StickyHeader.kt b/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/StickyHeader.kt
deleted file mode 100644
index 1b58b32..0000000
--- a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/StickyHeader.kt
+++ /dev/null
@@ -1,147 +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.tv.integration.demos
-
-import androidx.compose.foundation.background
-import androidx.compose.foundation.border
-import androidx.compose.foundation.focusable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.BoxScope
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.onFocusChanged
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
-import androidx.tv.foundation.ExperimentalTvFoundationApi
-import androidx.tv.foundation.lazy.list.TvLazyColumn
-
-data class MonthActivity(
-    val month: String,
-    val activities: List<String>,
-)
-
-val monthActivities = listOf(
-    MonthActivity(
-        month = "October 2022",
-        activities = buildActivities(),
-    ),
-    MonthActivity(
-        month = "September 2022",
-        activities = buildActivities(),
-    ),
-    MonthActivity(
-        month = "August 2022",
-        activities = buildActivities(),
-    ),
-)
-
-@OptIn(ExperimentalTvFoundationApi::class)
-@Composable
-fun StickyHeaderContent() {
-    TvLazyColumn(modifier = Modifier.fillMaxWidth()) {
-        monthActivities.forEachIndexed { monthIndex, monthActivity ->
-            val isLastMonth = monthIndex == monthActivities.lastIndex
-
-            stickyHeader {
-                MonthHeader(month = monthActivity.month)
-            }
-
-            items(monthActivity.activities.size) { activityIndex ->
-                val activity = monthActivity.activities[activityIndex]
-                val isLastActivity = activityIndex == monthActivity.activities.lastIndex
-
-                Box(modifier = Modifier.fillMaxWidth()) {
-                    Column(
-                        verticalArrangement = Arrangement.spacedBy(20.dp),
-                        modifier = Modifier
-                            .fillMaxWidth()
-                            .padding(bottom = 20.dp),
-                    ) {
-                        Box(modifier = Modifier.fillMaxWidth()) {
-                            MonthActivityComponent(this, activity)
-                        }
-
-                        if (isLastActivity && isLastMonth.not()) {
-                            MonthDivider()
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
-@Composable
-private fun MonthActivityComponent(boxScope: BoxScope, activity: String) {
-    var isFocused by remember { mutableStateOf(false) }
-
-    boxScope.apply {
-        Box(
-            modifier = Modifier
-                .align(Alignment.CenterEnd)
-                .fillMaxWidth(0.5f)
-                .height(70.dp)
-                .onFocusChanged { isFocused = it.isFocused }
-                .border(
-                    width = 2.dp,
-                    color = if (isFocused) Color.Red else Color.White,
-                    shape = RoundedCornerShape(10.dp),
-                )
-                .focusable(),
-            contentAlignment = Alignment.Center
-        ) {
-            Text(text = activity, color = Color.White)
-        }
-    }
-}
-
-@Composable
-private fun MonthHeader(month: String) {
-    Text(
-        text = month,
-        fontSize = 20.sp,
-        color = Color.White,
-    )
-}
-
-@Composable
-private fun MonthDivider() {
-    Box(
-        modifier = Modifier
-            .fillMaxWidth()
-            .height(2.dp)
-            .background(Color.White, RoundedCornerShape(50))
-    )
-}
-
-private fun buildActivities(
-    count: Int = 10,
-    buildActivity: (index: Int) -> String = { "Activity $it" }
-): List<String> = (0..count).map(buildActivity)
\ No newline at end of file
diff --git a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/TextField.kt b/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/TextField.kt
deleted file mode 100644
index ff5793b..0000000
--- a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/TextField.kt
+++ /dev/null
@@ -1,107 +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.tv.integration.demos
-
-import androidx.compose.foundation.background
-import androidx.compose.foundation.focusable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.LazyRow
-import androidx.compose.material3.Button
-import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.OutlinedTextField
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextFieldDefaults
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.unit.dp
-
-@Composable
-fun TextFieldContent() {
-    LazyRow(horizontalArrangement = Arrangement.spacedBy(20.dp)) {
-        item {
-            Column(verticalArrangement = Arrangement.spacedBy(20.dp)) {
-                repeat(4) { SampleCardItem() }
-            }
-        }
-        item {
-            LazyColumn(verticalArrangement = Arrangement.spacedBy(20.dp)) {
-                item { SampleTextField(label = "Email") }
-                item { SampleTextField(label = "Password") }
-                item { SampleButton(text = "Submit") }
-            }
-        }
-        item {
-            Column(verticalArrangement = Arrangement.spacedBy(20.dp)) {
-                repeat(4) { SampleCardItem() }
-            }
-        }
-    }
-}
-
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
-fun SampleTextField(label: String) {
-    var text by remember { mutableStateOf("") }
-
-    OutlinedTextField(
-        value = text,
-        onValueChange = { text = it },
-        label = {
-            Text(label)
-        },
-        singleLine = true,
-        placeholder = {
-            Text("$label...")
-        },
-        colors = TextFieldDefaults.outlinedTextFieldColors(
-            focusedBorderColor = Color.Cyan,
-            focusedLabelColor = Color.Cyan,
-            cursorColor = Color.White
-        )
-    )
-}
-
-@Composable
-fun SampleButton(text: String) {
-    Button(
-        onClick = { }
-    ) {
-        Text(text)
-    }
-}
-
-@Composable
-private fun SampleCardItem() {
-    Box(
-        modifier = Modifier
-            .background(Color.Magenta.copy(alpha = 0.3f))
-            .width(50.dp)
-            .height(50.dp)
-            .drawBorderOnFocus()
-            .focusable()
-    )
-}
\ No newline at end of file
diff --git a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/TopNavigation.kt b/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/TopNavigation.kt
deleted file mode 100644
index 95a6f7e..0000000
--- a/tv/integration-tests/demos/src/main/java/androidx/tv/integration/demos/TopNavigation.kt
+++ /dev/null
@@ -1,96 +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.tv.integration.demos
-
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
-import androidx.tv.material3.ExperimentalTvMaterial3Api
-import androidx.tv.material3.Tab
-import androidx.tv.material3.TabRow
-import androidx.tv.material3.Text
-import kotlinx.coroutines.delay
-
-enum class Navigation(val displayName: String, val action: @Composable () -> Unit) {
-  Drawer("Drawer", { SampleDrawer() }),
-  ModalDrawer("Modal Drawer", { SampleModalDrawer() }),
-  LazyRowsAndColumns("Lazy Rows and Columns", { LazyRowsAndColumns() }),
-  FeaturedCarousel("Featured Carousel", { FeaturedCarouselContent() }),
-  ImmersiveList("Immersive List", { ImmersiveListContent() }),
-  StickyHeader("Sticky Header", { StickyHeaderContent() }),
-  TextField("Text Field", { TextFieldContent() }),
-}
-
-@Composable
-internal fun TopNavigation(
-  updateSelectedTab: (Navigation) -> Unit = {},
-) {
-  var selectedTabIndex by remember { mutableStateOf(0) }
-  val tabs = Navigation.values().map { it.displayName }
-
-  // Pill indicator
-  PillIndicatorTabRow(
-    tabs = tabs,
-    selectedTabIndex = selectedTabIndex,
-    updateSelectedTab = { selectedTabIndex = it }
-  )
-
-  LaunchedEffect(selectedTabIndex) {
-    // Only update the tab after 250 milliseconds to avoid loading intermediate tabs while
-    // fast scrolling in the TabRow
-    delay(250)
-    updateSelectedTab(Navigation.values()[selectedTabIndex])
-  }
-}
-
-/**
- * Pill indicator tab row for reference
- */
-@OptIn(ExperimentalTvMaterial3Api::class)
-@Composable
-fun PillIndicatorTabRow(
-  tabs: List<String>,
-  selectedTabIndex: Int,
-  updateSelectedTab: (Int) -> Unit
-) {
-  TabRow(
-    selectedTabIndex = selectedTabIndex,
-    separator = { Spacer(modifier = Modifier.width(12.dp)) },
-  ) {
-    tabs.forEachIndexed { index, tab ->
-      Tab(
-        selected = index == selectedTabIndex,
-        onFocus = { updateSelectedTab(index) },
-      ) {
-        Text(
-          text = tab,
-          fontSize = 12.sp,
-          modifier = Modifier.padding(horizontal = 16.dp, vertical = 6.dp)
-        )
-      }
-    }
-  }
-}
diff --git a/tv/integration-tests/demos/src/main/res/values/strings.xml b/tv/integration-tests/demos/src/main/res/values/strings.xml
deleted file mode 100644
index 2d22ec3..0000000
--- a/tv/integration-tests/demos/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,35 +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.
-  -->
-
-<resources>
-    <string name="app_name">Samples</string>
-    <string name="browse_title">Videos by Your Company</string>
-    <string name="related_movies">Related Videos</string>
-    <string name="grid_view">Grid View</string>
-    <string name="error_fragment">Error Fragment</string>
-    <string name="personal_settings">Personal Settings</string>
-    <string name="watch_trailer_1">Watch trailer</string>
-    <string name="watch_trailer_2">FREE</string>
-    <string name="rent_1">Rent By Day</string>
-    <string name="rent_2">From $1.99</string>
-    <string name="buy_1">Buy and Own</string>
-    <string name="buy_2">AT $9.99</string>
-    <string name="movie">Movie</string>
-
-    <!-- Error messages -->
-    <string name="error_fragment_message">An error occurred</string>
-    <string name="dismiss_error">Dismiss</string>
-</resources>
\ No newline at end of file
diff --git a/tv/integration-tests/demos/src/main/res/values/themes.xml b/tv/integration-tests/demos/src/main/res/values/themes.xml
deleted file mode 100644
index 402d3ea..0000000
--- a/tv/integration-tests/demos/src/main/res/values/themes.xml
+++ /dev/null
@@ -1,19 +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.
-  -->
-
-<resources>
-    <style name="Theme.Androidx" parent="@style/Theme.AppCompat" />
-</resources>
\ No newline at end of file
diff --git a/tv/integration-tests/playground/build.gradle b/tv/integration-tests/playground/build.gradle
new file mode 100644
index 0000000..3ea0172
--- /dev/null
+++ b/tv/integration-tests/playground/build.gradle
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryType
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.application")
+    id("AndroidXComposePlugin")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    implementation(libs.kotlinStdlib)
+
+    implementation("androidx.appcompat:appcompat:1.6.1")
+
+    implementation(project(":activity:activity-compose"))
+    implementation(project(":compose:material3:material3"))
+    implementation(project(":navigation:navigation-runtime"))
+    implementation(project(":profileinstaller:profileinstaller"))
+
+    implementation(project(":tv:tv-foundation"))
+    implementation(project(":tv:tv-material"))
+}
+
+androidx {
+    name = "TV-Compose Test App"
+    type = LibraryType.SAMPLES
+    inceptionYear = "2022"
+    description = "Test application for TV libraries"
+}
+
+android {
+    defaultConfig {
+        minSdkVersion 28
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled true
+            shrinkResources true
+            proguardFiles getDefaultProguardFile('proguard-android.txt')
+            signingConfig signingConfigs.debug
+        }
+    }
+    namespace "androidx.tv.integration.playground"
+}
+
+// Workaround for https://github.com/gradle/gradle/issues/19882
+configurations.all {
+    resolutionStrategy.dependencySubstitution {
+        substitute(module("androidx.lifecycle:lifecycle-common-java8:")).
+                using project(":lifecycle:lifecycle-common-java8")
+        substitute(module("androidx.lifecycle:lifecycle-livedata-core:")).
+                using project(":lifecycle:lifecycle-livedata-core")
+        substitute(module("androidx.lifecycle:lifecycle-runtime:")).
+                using project(":lifecycle:lifecycle-runtime")
+        substitute(module("androidx.lifecycle:lifecycle-viewmodel:")).
+                using project(":lifecycle:lifecycle-viewmodel")
+        substitute(module("androidx.lifecycle:lifecycle-viewmodel-savedstate:")).
+                using project(":lifecycle:lifecycle-viewmodel-savedstate")
+    }
+}
diff --git a/tv/integration-tests/playground/src/main/AndroidManifest.xml b/tv/integration-tests/playground/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..3a078f4
--- /dev/null
+++ b/tv/integration-tests/playground/src/main/AndroidManifest.xml
@@ -0,0 +1,53 @@
+<?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"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:supportsRtl="true"
+        android:theme="@style/Theme.Androidx">
+        <activity
+            android:name=".MainActivity"
+            android:banner="@drawable/app_icon_your_company"
+            android:exported="true"
+            android:icon="@drawable/app_icon_your_company"
+            android:label="@string/app_name"
+            android:logo="@drawable/app_icon_your_company"
+            android:screenOrientation="landscape">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+    </application>
+
+    <uses-feature
+        android:name="android.software.leanback"
+        android:required="true" />
+    <uses-feature
+        android:name="android.hardware.touchscreen"
+        android:required="false" />
+
+    <uses-permission android:name="android.permission.INTERNET" />
+
+</manifest>
\ No newline at end of file
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/App.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/App.kt
new file mode 100644
index 0000000..20ad0ae
--- /dev/null
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/App.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.integration.playground
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.tv.material3.ExperimentalTvMaterial3Api
+import androidx.tv.material3.MaterialTheme
+import androidx.tv.material3.darkColorScheme
+
+val pageColor = Color.Black
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+@Composable
+fun App() {
+    var selectedTab by remember { mutableStateOf(Navigation.FeaturedCarousel) }
+
+    MaterialTheme(colorScheme = darkColorScheme()) {
+        Column(
+            modifier = Modifier
+                .background(pageColor)
+                .fillMaxSize()
+                .padding(20.dp),
+            verticalArrangement = Arrangement.spacedBy(20.dp),
+        ) {
+            TopNavigation(updateSelectedTab = { selectedTab = it })
+            selectedTab.action.invoke()
+        }
+    }
+}
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/Card.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/Card.kt
new file mode 100644
index 0000000..c517d99
--- /dev/null
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/Card.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.integration.playground
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.width
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun Card(
+    modifier: Modifier = Modifier,
+    backgroundColor: Color = Color.Transparent,
+) {
+    Box(
+        modifier = modifier
+            .background(backgroundColor.copy(alpha = 0.3f))
+            .width(200.dp)
+            .height(150.dp)
+            .drawBorderOnFocus()
+            .focusable()
+    )
+}
\ No newline at end of file
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FeaturedCarousel.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FeaturedCarousel.kt
new file mode 100644
index 0000000..64f92b9
--- /dev/null
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FeaturedCarousel.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.integration.playground
+
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.layout.Arrangement
+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.layout.width
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Button
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.tv.foundation.ExperimentalTvFoundationApi
+import androidx.tv.material3.Carousel
+import androidx.tv.material3.CarouselDefaults
+import androidx.tv.material3.CarouselState
+import androidx.tv.material3.ExperimentalTvMaterial3Api
+
+@Composable
+fun FeaturedCarouselContent() {
+    LazyColumn(verticalArrangement = Arrangement.spacedBy(20.dp)) {
+        items(3) { SampleLazyRow() }
+        item {
+            Row(horizontalArrangement = Arrangement.spacedBy(20.dp)) {
+                Column(verticalArrangement = Arrangement.spacedBy(20.dp)) {
+                    repeat(3) {
+                        Box(
+                            modifier = Modifier
+                                .background(Color.Magenta.copy(alpha = 0.3f))
+                                .width(50.dp)
+                                .height(50.dp)
+                                .drawBorderOnFocus()
+                                .focusable()
+                        )
+                    }
+                }
+
+                FeaturedCarousel(Modifier.weight(1f))
+
+                Column(verticalArrangement = Arrangement.spacedBy(20.dp)) {
+                    repeat(3) {
+                        Box(
+                            modifier = Modifier
+                                .background(Color.Magenta.copy(alpha = 0.3f))
+                                .width(50.dp)
+                                .height(50.dp)
+                                .drawBorderOnFocus()
+                                .focusable()
+                        )
+                    }
+                }
+            }
+        }
+        items(2) { SampleLazyRow() }
+    }
+}
+
+@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class,
+    ExperimentalTvFoundationApi::class
+)
+@Composable
+internal fun FeaturedCarousel(modifier: Modifier = Modifier) {
+    val backgrounds = listOf(
+        Color.Red.copy(alpha = 0.3f),
+        Color.Yellow.copy(alpha = 0.3f),
+        Color.Green.copy(alpha = 0.3f),
+        Color.Blue.copy(alpha = 0.3f),
+        Color.LightGray.copy(alpha = 0.3f),
+        Color.Magenta.copy(alpha = 0.3f),
+        Color.DarkGray.copy(alpha = 0.3f),
+        Color.LightGray.copy(alpha = 0.3f),
+    )
+
+    val carouselState = remember { CarouselState() }
+    FocusGroup {
+        Carousel(
+            itemCount = backgrounds.size,
+            carouselState = carouselState,
+            modifier = modifier
+                .height(300.dp)
+                .fillMaxWidth(),
+            carouselIndicator = {
+                CarouselDefaults.IndicatorRow(
+                    itemCount = backgrounds.size,
+                    activeItemIndex = carouselState.activeItemIndex,
+                    modifier = Modifier
+                        .align(Alignment.BottomEnd)
+                        .padding(16.dp),
+                )
+            }
+        ) { itemIndex ->
+            CarouselItem(
+                background = {
+                    Box(
+                        modifier = Modifier
+                            .background(backgrounds[itemIndex])
+                            .fillMaxSize()
+                    )
+                },
+                modifier =
+                if (itemIndex == 0)
+                    Modifier.initiallyFocused()
+                else
+                    Modifier.restorableFocus()
+            ) {
+                Box(modifier = Modifier) {
+                    OverlayButton(
+                        modifier = Modifier
+                    )
+                }
+            }
+        }
+    }
+}
+
+@Composable
+private fun OverlayButton(modifier: Modifier = Modifier) {
+    var isFocused by remember { mutableStateOf(false) }
+
+    Button(
+        onClick = { },
+        modifier = modifier
+            .onFocusChanged { isFocused = it.isFocused }
+            .padding(40.dp)
+            .border(
+                width = 2.dp,
+                color = if (isFocused) Color.Red else Color.Transparent,
+                shape = RoundedCornerShape(50)
+            )
+            .padding(vertical = 2.dp, horizontal = 5.dp)
+    ) {
+        Text(text = "Play")
+    }
+}
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FocusGroup.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FocusGroup.kt
new file mode 100644
index 0000000..98a9e38
--- /dev/null
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FocusGroup.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.integration.playground
+
+import android.annotation.SuppressLint
+import android.util.Log
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.currentCompositeKeyHash
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusDirection
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.tv.foundation.ExperimentalTvFoundationApi
+
+/**
+ * Composable container that provides modifier extensions to allow focus to be restored to the
+ * element that was previously focused within the TvFocusGroup.
+ *
+ * @param modifier the modifier to apply to this group.
+ * @param content the content that is present within the group and can use focus-group modifier
+ * extensions.
+ */
+@OptIn(ExperimentalComposeUiApi::class)
+@ExperimentalTvFoundationApi
+@Composable
+fun FocusGroup(
+    modifier: Modifier = Modifier,
+    content: @Composable FocusGroupScope.() -> Unit
+) {
+    val focusManager = LocalFocusManager.current
+    val focusGroupKeyHash = currentCompositeKeyHash
+
+    // TODO: Is this the intended way to call rememberSaveable
+    //  with key set to parentHash?
+    val previousFocusedItemHash: MutableState<Int?> = rememberSaveable(
+        key = focusGroupKeyHash.toString()
+    ) {
+        mutableStateOf(null)
+    }
+
+    val state = FocusGroupState(previousFocusedItemHash = previousFocusedItemHash)
+
+    Box(
+        modifier = modifier
+            .onFocusChanged {
+                if (it.isFocused) {
+                    if (state.noRecordedState()) {
+                        focusManager.moveFocus(FocusDirection.Enter)
+                    } else {
+                        if (state.focusRequester != FocusRequester.Default) {
+                            try {
+                                state.focusRequester.requestFocus()
+                            } catch (e: Exception) {
+                                Log.w("TvFocusGroup", "TvFocusGroup: Failed to request focus", e)
+                            }
+                        } else {
+                            focusManager.moveFocus(FocusDirection.Enter)
+                        }
+                    }
+                }
+            }
+            .focusable(),
+        content = { FocusGroupScope(state).content() }
+    )
+}
+
+/**
+ * Scope containing the modifier extensions to be used within [FocusGroup].
+ */
+@ExperimentalTvFoundationApi
+class FocusGroupScope internal constructor(private val state: FocusGroupState) {
+    private var currentFocusableIdIndex = 0
+
+    private fun generateUniqueFocusableId(): Int = currentFocusableIdIndex++
+
+    /**
+     * Modifier that records if the item was in focus before it moved out of the group. When focus
+     * enters the [FocusGroup], the item will be returned focus.
+     */
+    @SuppressLint("ComposableModifierFactory")
+    @Composable
+    fun Modifier.restorableFocus(): Modifier =
+        this.restorableFocus(focusId = rememberSaveable { generateUniqueFocusableId() })
+
+    /**
+     * Modifier that marks the current composable as the item to gain focus initially when focus
+     * enters the [FocusGroup]. When focus enters the [FocusGroup], the item will be returned focus.
+     */
+    @SuppressLint("ComposableModifierFactory")
+    @Composable
+    fun Modifier.initiallyFocused(): Modifier {
+        val focusId = rememberSaveable { generateUniqueFocusableId() }
+        if (state.noRecordedState()) {
+            state.recordFocusedItemHash(focusId)
+        }
+        return this.restorableFocus(focusId)
+    }
+
+    @SuppressLint("ComposableModifierFactory")
+    @OptIn(ExperimentalComposeUiApi::class)
+    @Composable
+    private fun Modifier.restorableFocus(focusId: Int): Modifier {
+        val focusRequester = remember { FocusRequester() }
+        var isFocused = remember { false }
+        val isCurrentlyFocused by rememberUpdatedState(isFocused)
+        val focusManager = LocalFocusManager.current
+        state.associatedWith(focusId, focusRequester)
+        DisposableEffect(Unit) {
+            onDispose {
+                state.clearDisposedFocusRequester(focusId)
+                if (isCurrentlyFocused) {
+                    focusManager.moveFocus(FocusDirection.Exit)
+                    focusManager.moveFocus(FocusDirection.Enter)
+                }
+            }
+        }
+
+        return this
+            .focusRequester(focusRequester)
+            .onFocusChanged {
+                isFocused = it.isFocused || it.hasFocus
+                if (isFocused) {
+                    state.recordFocusedItemHash(focusId)
+                    state.associatedWith(focusId, focusRequester)
+                }
+            }
+    }
+}
+
+@Stable
+@ExperimentalTvFoundationApi
+internal class FocusGroupState(
+    private var previousFocusedItemHash: MutableState<Int?>
+) {
+    internal var focusRequester: FocusRequester = FocusRequester.Default
+        private set
+
+    internal fun recordFocusedItemHash(itemHash: Int) {
+        previousFocusedItemHash.value = itemHash
+    }
+
+    internal fun clearDisposedFocusRequester(itemHash: Int) {
+        if (previousFocusedItemHash.value == itemHash) {
+            focusRequester = FocusRequester.Default
+        }
+    }
+
+    internal fun associatedWith(itemHash: Int, focusRequester: FocusRequester) {
+        if (previousFocusedItemHash.value == itemHash) {
+            this.focusRequester = focusRequester
+        }
+    }
+
+    internal fun noRecordedState(): Boolean =
+        previousFocusedItemHash.value == null && focusRequester == FocusRequester.Default
+}
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/ImmersiveList.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/ImmersiveList.kt
new file mode 100644
index 0000000..b8ed37f
--- /dev/null
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/ImmersiveList.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.integration.playground
+
+import android.util.Log
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.width
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.tv.foundation.ExperimentalTvFoundationApi
+import androidx.tv.foundation.lazy.list.TvLazyColumn
+import androidx.tv.material3.ExperimentalTvMaterial3Api
+import androidx.tv.material3.ImmersiveList
+
+@Composable
+fun ImmersiveListContent() {
+    TvLazyColumn(verticalArrangement = Arrangement.spacedBy(20.dp)) {
+        items(3) { SampleLazyRow() }
+        item { SampleImmersiveList() }
+        items(3) { SampleLazyRow() }
+    }
+}
+
+@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalTvFoundationApi::class)
+@Composable
+private fun SampleImmersiveList() {
+    val immersiveListHeight = 300.dp
+    val cardSpacing = 10.dp
+    val cardWidth = 200.dp
+    val cardHeight = 150.dp
+    val backgrounds = listOf(
+        Color.Red,
+        Color.Blue,
+        Color.Magenta,
+    )
+
+    FocusGroup {
+        ImmersiveList(
+            modifier = Modifier
+                .height(immersiveListHeight + cardHeight / 2)
+                .fillMaxWidth(),
+            background = { index, _ ->
+                Box(
+                    modifier = Modifier
+                        .background(backgrounds[index].copy(alpha = 0.3f))
+                        .height(immersiveListHeight)
+                        .fillMaxWidth()
+                )
+            }
+        ) {
+            Row(horizontalArrangement = Arrangement.spacedBy(cardSpacing)) {
+                backgrounds.forEachIndexed { index, backgroundColor ->
+                    var isFocused by remember { mutableStateOf(false) }
+
+                    Box(
+                        modifier = Modifier
+                            .background(backgroundColor)
+                            .width(cardWidth)
+                            .height(cardHeight)
+                            .border(5.dp, Color.White.copy(alpha = if (isFocused) 1f else 0.3f))
+                            .then(
+                                if (index == 0)
+                                    Modifier.initiallyFocused()
+                                else
+                                    Modifier.restorableFocus()
+                            )
+                            .onFocusChanged { isFocused = it.isFocused }
+                            .immersiveListItem(index)
+                            .clickable {
+                                Log.d("ImmersiveList", "Item $index was clicked")
+                            }
+                    )
+                }
+            }
+        }
+    }
+}
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/LazyRowsAndColumns.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/LazyRowsAndColumns.kt
new file mode 100644
index 0000000..29b1866
--- /dev/null
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/LazyRowsAndColumns.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.integration.playground
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.tv.foundation.ExperimentalTvFoundationApi
+import androidx.tv.foundation.lazy.list.TvLazyColumn
+import androidx.tv.foundation.lazy.list.TvLazyRow
+
+const val rowsCount = 20
+const val columnsCount = 100
+
+@Composable
+fun LazyRowsAndColumns() {
+    TvLazyColumn(verticalArrangement = Arrangement.spacedBy(20.dp)) {
+        items(rowsCount) { SampleLazyRow() }
+    }
+}
+
+@OptIn(ExperimentalTvFoundationApi::class)
+@Composable
+fun SampleLazyRow() {
+    val colors = listOf(Color.Red, Color.Magenta, Color.Green, Color.Yellow, Color.Blue, Color.Cyan)
+    val backgroundColors = List(columnsCount) { colors.random() }
+
+    FocusGroup {
+        TvLazyRow(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
+            backgroundColors.forEachIndexed { index, backgroundColor ->
+                item {
+                    Card(
+                        backgroundColor = backgroundColor,
+                        modifier =
+                        if (index == 0)
+                            Modifier.initiallyFocused()
+                        else
+                            Modifier.restorableFocus()
+                    )
+                }
+            }
+        }
+    }
+}
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/MainActivity.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/MainActivity.kt
new file mode 100644
index 0000000..1c8fc89
--- /dev/null
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/MainActivity.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.integration.playground
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+
+class MainActivity : ComponentActivity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContent {
+            App()
+        }
+    }
+}
\ No newline at end of file
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/NavigationDrawer.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/NavigationDrawer.kt
new file mode 100644
index 0000000..cbdffe8
--- /dev/null
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/NavigationDrawer.kt
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.integration.playground
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.foundation.background
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.KeyboardArrowLeft
+import androidx.compose.material.icons.filled.KeyboardArrowRight
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
+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.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.zIndex
+import androidx.tv.foundation.ExperimentalTvFoundationApi
+import androidx.tv.material3.DrawerValue
+import androidx.tv.material3.ExperimentalTvMaterial3Api
+import androidx.tv.material3.Icon
+import androidx.tv.material3.NavigationDrawer
+import androidx.tv.material3.Text
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+@Composable
+fun StandardNavigationDrawer() {
+    val direction = remember { mutableStateOf(LayoutDirection.Ltr) }
+
+    CompositionLocalProvider(LocalLayoutDirection provides direction.value) {
+        Row(Modifier.fillMaxSize()) {
+            Box(modifier = Modifier.height(400.dp)) {
+                NavigationDrawer(
+                    drawerContent = { drawerValue ->
+                        Sidebar(
+                            drawerValue = drawerValue,
+                            direction = direction,
+                        )
+                    }
+                ) {
+                    CommonBackground()
+                }
+            }
+        }
+    }
+}
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+@Composable
+fun ModalNavigationDrawer() {
+    val direction = remember { mutableStateOf(LayoutDirection.Ltr) }
+
+    CompositionLocalProvider(LocalLayoutDirection provides direction.value) {
+        Row(Modifier.fillMaxSize()) {
+            Box(modifier = Modifier.height(400.dp)) {
+                androidx.tv.material3.ModalNavigationDrawer(
+                    drawerContent = { drawerValue ->
+                        Sidebar(
+                            drawerValue = drawerValue,
+                            direction = direction,
+                        )
+                    }
+                ) {
+                    CommonBackground()
+                }
+            }
+        }
+    }
+}
+
+@Composable
+private fun CommonBackground() {
+    Row(modifier = Modifier.padding(start = 10.dp)) {
+        Card(backgroundColor = Color.Red)
+    }
+}
+
+@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalTvFoundationApi::class)
+@Composable
+private fun Sidebar(
+    drawerValue: DrawerValue,
+    direction: MutableState<LayoutDirection>,
+) {
+    val selectedIndex = remember { mutableStateOf(0) }
+
+    LaunchedEffect(selectedIndex.value) {
+        direction.value = when (selectedIndex.value) {
+            0 -> LayoutDirection.Ltr
+            else -> LayoutDirection.Rtl
+        }
+    }
+
+    FocusGroup {
+        Column(
+            modifier = Modifier
+                .fillMaxHeight()
+                .background(pageColor)
+                .focusable(false),
+            horizontalAlignment = Alignment.CenterHorizontally,
+        ) {
+            NavigationItem(
+                imageVector = Icons.Default.KeyboardArrowRight,
+                text = "LTR",
+                drawerValue = drawerValue,
+                selectedIndex = selectedIndex,
+                index = 0,
+                modifier = Modifier.initiallyFocused(),
+            )
+            NavigationItem(
+                imageVector = Icons.Default.KeyboardArrowLeft,
+                text = "RTL",
+                drawerValue = drawerValue,
+                selectedIndex = selectedIndex,
+                index = 1,
+                modifier = Modifier.restorableFocus(),
+            )
+        }
+    }
+}
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+@Composable
+private fun NavigationItem(
+    imageVector: ImageVector,
+    text: String,
+    drawerValue: DrawerValue,
+    selectedIndex: MutableState<Int>,
+    index: Int,
+    modifier: Modifier = Modifier,
+) {
+    var isFocused by remember { mutableStateOf(false) }
+
+    Button(
+        onClick = { selectedIndex.value = index },
+        modifier = modifier
+            .onFocusChanged { isFocused = it.isFocused },
+        colors = ButtonDefaults.filledTonalButtonColors(
+            containerColor = if (isFocused) Color.White else Color.Transparent,
+        )
+    ) {
+        Box(modifier = Modifier) {
+            Row(
+                verticalAlignment = Alignment.CenterVertically,
+                horizontalArrangement = Arrangement.spacedBy(5.dp),
+            ) {
+                Icon(
+                    imageVector = imageVector,
+                    tint = if (isFocused) pageColor else Color.White,
+                    contentDescription = null,
+                )
+                AnimatedVisibility(visible = drawerValue == DrawerValue.Open) {
+                    Text(
+                        text = text,
+                        modifier = Modifier,
+                        softWrap = false,
+                        color = if (isFocused) pageColor else Color.White,
+                    )
+                }
+            }
+            if (selectedIndex.value == index) {
+                Box(
+                    modifier = Modifier
+                        .width(10.dp)
+                        .height(3.dp)
+                        .offset(y = 5.dp)
+                        .align(Alignment.BottomCenter)
+                        .background(Color.Red)
+                        .zIndex(10f)
+                )
+            }
+        }
+    }
+}
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/StickyHeader.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/StickyHeader.kt
new file mode 100644
index 0000000..f1af42e
--- /dev/null
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/StickyHeader.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.integration.playground
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.tv.foundation.ExperimentalTvFoundationApi
+import androidx.tv.foundation.lazy.list.TvLazyColumn
+
+data class MonthActivity(
+    val month: String,
+    val activities: List<String>,
+)
+
+val monthActivities = listOf(
+    MonthActivity(
+        month = "October 2022",
+        activities = buildActivities(),
+    ),
+    MonthActivity(
+        month = "September 2022",
+        activities = buildActivities(),
+    ),
+    MonthActivity(
+        month = "August 2022",
+        activities = buildActivities(),
+    ),
+)
+
+@OptIn(ExperimentalTvFoundationApi::class)
+@Composable
+fun StickyHeaderContent() {
+    TvLazyColumn(modifier = Modifier.fillMaxWidth()) {
+        monthActivities.forEachIndexed { monthIndex, monthActivity ->
+            val isLastMonth = monthIndex == monthActivities.lastIndex
+
+            stickyHeader {
+                MonthHeader(month = monthActivity.month)
+            }
+
+            items(monthActivity.activities.size) { activityIndex ->
+                val activity = monthActivity.activities[activityIndex]
+                val isLastActivity = activityIndex == monthActivity.activities.lastIndex
+
+                Box(modifier = Modifier.fillMaxWidth()) {
+                    Column(
+                        verticalArrangement = Arrangement.spacedBy(20.dp),
+                        modifier = Modifier
+                            .fillMaxWidth()
+                            .padding(bottom = 20.dp),
+                    ) {
+                        Box(modifier = Modifier.fillMaxWidth()) {
+                            MonthActivityComponent(this, activity)
+                        }
+
+                        if (isLastActivity && isLastMonth.not()) {
+                            MonthDivider()
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+@Composable
+private fun MonthActivityComponent(boxScope: BoxScope, activity: String) {
+    var isFocused by remember { mutableStateOf(false) }
+
+    boxScope.apply {
+        Box(
+            modifier = Modifier
+                .align(Alignment.CenterEnd)
+                .fillMaxWidth(0.5f)
+                .height(70.dp)
+                .onFocusChanged { isFocused = it.isFocused }
+                .border(
+                    width = 2.dp,
+                    color = if (isFocused) Color.Red else Color.White,
+                    shape = RoundedCornerShape(10.dp),
+                )
+                .focusable(),
+            contentAlignment = Alignment.Center
+        ) {
+            Text(text = activity, color = Color.White)
+        }
+    }
+}
+
+@Composable
+private fun MonthHeader(month: String) {
+    Text(
+        text = month,
+        fontSize = 20.sp,
+        color = Color.White,
+    )
+}
+
+@Composable
+private fun MonthDivider() {
+    Box(
+        modifier = Modifier
+            .fillMaxWidth()
+            .height(2.dp)
+            .background(Color.White, RoundedCornerShape(50))
+    )
+}
+
+private fun buildActivities(
+    count: Int = 10,
+    buildActivity: (index: Int) -> String = { "Activity $it" }
+): List<String> = (0..count).map(buildActivity)
\ No newline at end of file
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/TextField.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/TextField.kt
new file mode 100644
index 0000000..4a55634
--- /dev/null
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/TextField.kt
@@ -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.tv.integration.playground
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.material3.Button
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextFieldDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun TextFieldContent() {
+    LazyRow(horizontalArrangement = Arrangement.spacedBy(20.dp)) {
+        item {
+            Column(verticalArrangement = Arrangement.spacedBy(20.dp)) {
+                repeat(4) { SampleCardItem() }
+            }
+        }
+        item {
+            LazyColumn(verticalArrangement = Arrangement.spacedBy(20.dp)) {
+                item { SampleTextField(label = "Email") }
+                item { SampleTextField(label = "Password") }
+                item { SampleButton(text = "Submit") }
+            }
+        }
+        item {
+            Column(verticalArrangement = Arrangement.spacedBy(20.dp)) {
+                repeat(4) { SampleCardItem() }
+            }
+        }
+    }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun SampleTextField(label: String) {
+    var text by remember { mutableStateOf("") }
+
+    OutlinedTextField(
+        value = text,
+        onValueChange = { text = it },
+        label = {
+            Text(label)
+        },
+        singleLine = true,
+        placeholder = {
+            Text("$label...")
+        },
+        colors = TextFieldDefaults.outlinedTextFieldColors(
+            focusedBorderColor = Color.Cyan,
+            focusedLabelColor = Color.Cyan,
+            cursorColor = Color.White
+        )
+    )
+}
+
+@Composable
+fun SampleButton(text: String) {
+    Button(
+        onClick = { }
+    ) {
+        Text(text)
+    }
+}
+
+@Composable
+private fun SampleCardItem() {
+    Box(
+        modifier = Modifier
+            .background(Color.Magenta.copy(alpha = 0.3f))
+            .width(50.dp)
+            .height(50.dp)
+            .drawBorderOnFocus()
+            .focusable()
+    )
+}
\ No newline at end of file
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/TopNavigation.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/TopNavigation.kt
new file mode 100644
index 0000000..30b8f83
--- /dev/null
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/TopNavigation.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.tv.integration.playground
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.tv.foundation.ExperimentalTvFoundationApi
+import androidx.tv.material3.ExperimentalTvMaterial3Api
+import androidx.tv.material3.Tab
+import androidx.tv.material3.TabRow
+import androidx.tv.material3.Text
+import kotlinx.coroutines.delay
+
+enum class Navigation(val displayName: String, val action: @Composable () -> Unit) {
+  StandardNavigationDrawer("Standard Navigation Drawer", { StandardNavigationDrawer() }),
+  ModalNavigationDrawer("Modal Navigation Drawer", { ModalNavigationDrawer() }),
+  LazyRowsAndColumns("Lazy Rows and Columns", { LazyRowsAndColumns() }),
+  FeaturedCarousel("Featured Carousel", { FeaturedCarouselContent() }),
+  ImmersiveList("Immersive List", { ImmersiveListContent() }),
+  TextField("Text Field", { TextFieldContent() }),
+  StickyHeader("Sticky Header", { StickyHeaderContent() }),
+}
+
+@Composable
+internal fun TopNavigation(
+  updateSelectedTab: (Navigation) -> Unit = {},
+) {
+  var selectedTabIndex by remember { mutableStateOf(0) }
+  val tabs = Navigation.values().map { it.displayName }
+
+  // Pill indicator
+  PillIndicatorTabRow(
+    tabs = tabs,
+    selectedTabIndex = selectedTabIndex,
+    updateSelectedTab = { selectedTabIndex = it }
+  )
+
+  LaunchedEffect(selectedTabIndex) {
+    // Only update the tab after 250 milliseconds to avoid loading intermediate tabs while
+    // fast scrolling in the TabRow
+    delay(250)
+    updateSelectedTab(Navigation.values()[selectedTabIndex])
+  }
+}
+
+/**
+ * Pill indicator tab row for reference
+ */
+@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalTvFoundationApi::class)
+@Composable
+fun PillIndicatorTabRow(
+  tabs: List<String>,
+  selectedTabIndex: Int,
+  updateSelectedTab: (Int) -> Unit
+) {
+  FocusGroup {
+    TabRow(selectedTabIndex = selectedTabIndex) {
+      tabs.forEachIndexed { index, tab ->
+        Tab(
+          selected = index == selectedTabIndex,
+          onFocus = { updateSelectedTab(index) },
+          modifier =
+          if (tab == Navigation.StandardNavigationDrawer.displayName)
+            Modifier.initiallyFocused()
+          else
+            Modifier.restorableFocus()
+        ) {
+          Text(
+            text = tab,
+            fontSize = 12.sp,
+            modifier = Modifier.padding(horizontal = 16.dp, vertical = 6.dp)
+          )
+        }
+      }
+    }
+  }
+}
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/drawBorderOnFocus.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/drawBorderOnFocus.kt
new file mode 100644
index 0000000..fff347e
--- /dev/null
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/drawBorderOnFocus.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.tv.integration.playground
+
+import androidx.compose.foundation.border
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun Modifier.drawBorderOnFocus(borderColor: Color = Color.White, width: Dp = 5.dp): Modifier {
+    var isFocused by remember { mutableStateOf(false) }
+    return this
+        .border(width, borderColor.copy(alpha = if (isFocused) 1f else 0.2f))
+        .onFocusChanged { isFocused = it.isFocused }
+}
diff --git a/tv/integration-tests/demos/src/main/res/drawable/app_icon_your_company.png b/tv/integration-tests/playground/src/main/res/drawable/app_icon_your_company.png
similarity index 100%
rename from tv/integration-tests/demos/src/main/res/drawable/app_icon_your_company.png
rename to tv/integration-tests/playground/src/main/res/drawable/app_icon_your_company.png
Binary files differ
diff --git a/tv/integration-tests/demos/src/main/res/layout/activity_main.xml b/tv/integration-tests/playground/src/main/res/layout/activity_main.xml
similarity index 100%
rename from tv/integration-tests/demos/src/main/res/layout/activity_main.xml
rename to tv/integration-tests/playground/src/main/res/layout/activity_main.xml
diff --git a/tv/integration-tests/demos/src/main/res/mipmap-hdpi/ic_launcher.webp b/tv/integration-tests/playground/src/main/res/mipmap-hdpi/ic_launcher.webp
similarity index 100%
rename from tv/integration-tests/demos/src/main/res/mipmap-hdpi/ic_launcher.webp
rename to tv/integration-tests/playground/src/main/res/mipmap-hdpi/ic_launcher.webp
Binary files differ
diff --git a/tv/integration-tests/demos/src/main/res/mipmap-mdpi/ic_launcher.webp b/tv/integration-tests/playground/src/main/res/mipmap-mdpi/ic_launcher.webp
similarity index 100%
rename from tv/integration-tests/demos/src/main/res/mipmap-mdpi/ic_launcher.webp
rename to tv/integration-tests/playground/src/main/res/mipmap-mdpi/ic_launcher.webp
Binary files differ
diff --git a/tv/integration-tests/demos/src/main/res/mipmap-xhdpi/ic_launcher.webp b/tv/integration-tests/playground/src/main/res/mipmap-xhdpi/ic_launcher.webp
similarity index 100%
rename from tv/integration-tests/demos/src/main/res/mipmap-xhdpi/ic_launcher.webp
rename to tv/integration-tests/playground/src/main/res/mipmap-xhdpi/ic_launcher.webp
Binary files differ
diff --git a/tv/integration-tests/demos/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/tv/integration-tests/playground/src/main/res/mipmap-xxhdpi/ic_launcher.webp
similarity index 100%
rename from tv/integration-tests/demos/src/main/res/mipmap-xxhdpi/ic_launcher.webp
rename to tv/integration-tests/playground/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Binary files differ
diff --git a/tv/integration-tests/demos/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/tv/integration-tests/playground/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
similarity index 100%
rename from tv/integration-tests/demos/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
rename to tv/integration-tests/playground/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Binary files differ
diff --git a/tv/integration-tests/demos/src/main/res/values/colors.xml b/tv/integration-tests/playground/src/main/res/values/colors.xml
similarity index 100%
rename from tv/integration-tests/demos/src/main/res/values/colors.xml
rename to tv/integration-tests/playground/src/main/res/values/colors.xml
diff --git a/tv/integration-tests/playground/src/main/res/values/strings.xml b/tv/integration-tests/playground/src/main/res/values/strings.xml
new file mode 100644
index 0000000..5beced7
--- /dev/null
+++ b/tv/integration-tests/playground/src/main/res/values/strings.xml
@@ -0,0 +1,35 @@
+<!--
+  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>
+    <string name="app_name">DevX Playground</string>
+    <string name="browse_title">Playground app of DevX engineers</string>
+    <string name="related_movies">Related Videos</string>
+    <string name="grid_view">Grid View</string>
+    <string name="error_fragment">Error Fragment</string>
+    <string name="personal_settings">Personal Settings</string>
+    <string name="watch_trailer_1">Watch trailer</string>
+    <string name="watch_trailer_2">FREE</string>
+    <string name="rent_1">Rent By Day</string>
+    <string name="rent_2">From $1.99</string>
+    <string name="buy_1">Buy and Own</string>
+    <string name="buy_2">AT $9.99</string>
+    <string name="movie">Movie</string>
+
+    <!-- Error messages -->
+    <string name="error_fragment_message">An error occurred</string>
+    <string name="dismiss_error">Dismiss</string>
+</resources>
\ No newline at end of file
diff --git a/tv/integration-tests/playground/src/main/res/values/themes.xml b/tv/integration-tests/playground/src/main/res/values/themes.xml
new file mode 100644
index 0000000..8df2c77
--- /dev/null
+++ b/tv/integration-tests/playground/src/main/res/values/themes.xml
@@ -0,0 +1,19 @@
+<!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <style name="Theme.Androidx" parent="@style/Theme.AppCompat" />
+</resources>
\ No newline at end of file
diff --git a/tv/integration-tests/presentation/build.gradle b/tv/integration-tests/presentation/build.gradle
new file mode 100644
index 0000000..b712865
--- /dev/null
+++ b/tv/integration-tests/presentation/build.gradle
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryType
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.application")
+    id("AndroidXComposePlugin")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    implementation(libs.kotlinStdlib)
+
+    implementation("androidx.appcompat:appcompat:1.6.1")
+
+    implementation(project(":activity:activity-compose"))
+    implementation(project(":compose:material3:material3"))
+    implementation(project(":navigation:navigation-runtime"))
+    implementation(project(":profileinstaller:profileinstaller"))
+
+    implementation(project(":tv:tv-foundation"))
+    implementation(project(":tv:tv-material"))
+}
+
+androidx {
+    name = "TV-Compose Test App"
+    type = LibraryType.SAMPLES
+    inceptionYear = "2022"
+    description = "Test application for TV libraries"
+}
+
+android {
+    defaultConfig {
+        minSdkVersion 28
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled true
+            shrinkResources true
+            proguardFiles getDefaultProguardFile('proguard-android.txt')
+            signingConfig signingConfigs.debug
+        }
+    }
+    namespace "androidx.tv.integration.presentation"
+}
+
+// Workaround for https://github.com/gradle/gradle/issues/19882
+configurations.all {
+    resolutionStrategy.dependencySubstitution {
+        substitute(module("androidx.lifecycle:lifecycle-common-java8:")).
+                using project(":lifecycle:lifecycle-common-java8")
+        substitute(module("androidx.lifecycle:lifecycle-livedata-core:")).
+                using project(":lifecycle:lifecycle-livedata-core")
+        substitute(module("androidx.lifecycle:lifecycle-runtime:")).
+                using project(":lifecycle:lifecycle-runtime")
+        substitute(module("androidx.lifecycle:lifecycle-viewmodel:")).
+                using project(":lifecycle:lifecycle-viewmodel")
+        substitute(module("androidx.lifecycle:lifecycle-viewmodel-savedstate:")).
+                using project(":lifecycle:lifecycle-viewmodel-savedstate")
+    }
+}
diff --git a/tv/integration-tests/presentation/src/main/AndroidManifest.xml b/tv/integration-tests/presentation/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..3a078f4
--- /dev/null
+++ b/tv/integration-tests/presentation/src/main/AndroidManifest.xml
@@ -0,0 +1,53 @@
+<?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"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:supportsRtl="true"
+        android:theme="@style/Theme.Androidx">
+        <activity
+            android:name=".MainActivity"
+            android:banner="@drawable/app_icon_your_company"
+            android:exported="true"
+            android:icon="@drawable/app_icon_your_company"
+            android:label="@string/app_name"
+            android:logo="@drawable/app_icon_your_company"
+            android:screenOrientation="landscape">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+    </application>
+
+    <uses-feature
+        android:name="android.software.leanback"
+        android:required="true" />
+    <uses-feature
+        android:name="android.hardware.touchscreen"
+        android:required="false" />
+
+    <uses-permission android:name="android.permission.INTERNET" />
+
+</manifest>
\ No newline at end of file
diff --git a/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/MainActivity.kt b/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/MainActivity.kt
new file mode 100644
index 0000000..6306306
--- /dev/null
+++ b/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/MainActivity.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.tv.integration.presentation
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.text.BasicText
+
+class MainActivity : ComponentActivity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContent {
+            BasicText("Hi")
+        }
+    }
+}
\ No newline at end of file
diff --git a/tv/integration-tests/demos/src/main/res/drawable/app_icon_your_company.png b/tv/integration-tests/presentation/src/main/res/drawable/app_icon_your_company.png
similarity index 100%
copy from tv/integration-tests/demos/src/main/res/drawable/app_icon_your_company.png
copy to tv/integration-tests/presentation/src/main/res/drawable/app_icon_your_company.png
Binary files differ
diff --git a/tv/integration-tests/demos/src/main/res/layout/activity_main.xml b/tv/integration-tests/presentation/src/main/res/layout/activity_main.xml
similarity index 100%
copy from tv/integration-tests/demos/src/main/res/layout/activity_main.xml
copy to tv/integration-tests/presentation/src/main/res/layout/activity_main.xml
diff --git a/tv/integration-tests/demos/src/main/res/mipmap-hdpi/ic_launcher.webp b/tv/integration-tests/presentation/src/main/res/mipmap-hdpi/ic_launcher.webp
similarity index 100%
copy from tv/integration-tests/demos/src/main/res/mipmap-hdpi/ic_launcher.webp
copy to tv/integration-tests/presentation/src/main/res/mipmap-hdpi/ic_launcher.webp
Binary files differ
diff --git a/tv/integration-tests/demos/src/main/res/mipmap-mdpi/ic_launcher.webp b/tv/integration-tests/presentation/src/main/res/mipmap-mdpi/ic_launcher.webp
similarity index 100%
copy from tv/integration-tests/demos/src/main/res/mipmap-mdpi/ic_launcher.webp
copy to tv/integration-tests/presentation/src/main/res/mipmap-mdpi/ic_launcher.webp
Binary files differ
diff --git a/tv/integration-tests/demos/src/main/res/mipmap-xhdpi/ic_launcher.webp b/tv/integration-tests/presentation/src/main/res/mipmap-xhdpi/ic_launcher.webp
similarity index 100%
copy from tv/integration-tests/demos/src/main/res/mipmap-xhdpi/ic_launcher.webp
copy to tv/integration-tests/presentation/src/main/res/mipmap-xhdpi/ic_launcher.webp
Binary files differ
diff --git a/tv/integration-tests/demos/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/tv/integration-tests/presentation/src/main/res/mipmap-xxhdpi/ic_launcher.webp
similarity index 100%
copy from tv/integration-tests/demos/src/main/res/mipmap-xxhdpi/ic_launcher.webp
copy to tv/integration-tests/presentation/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Binary files differ
diff --git a/tv/integration-tests/demos/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/tv/integration-tests/presentation/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
similarity index 100%
copy from tv/integration-tests/demos/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
copy to tv/integration-tests/presentation/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Binary files differ
diff --git a/tv/integration-tests/demos/src/main/res/values/colors.xml b/tv/integration-tests/presentation/src/main/res/values/colors.xml
similarity index 100%
copy from tv/integration-tests/demos/src/main/res/values/colors.xml
copy to tv/integration-tests/presentation/src/main/res/values/colors.xml
diff --git a/tv/integration-tests/presentation/src/main/res/values/strings.xml b/tv/integration-tests/presentation/src/main/res/values/strings.xml
new file mode 100644
index 0000000..38b8b29
--- /dev/null
+++ b/tv/integration-tests/presentation/src/main/res/values/strings.xml
@@ -0,0 +1,35 @@
+<!--
+  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>
+    <string name="app_name">Presentation app</string>
+    <string name="browse_title">Presentation app for visitors</string>
+    <string name="related_movies">Related Videos</string>
+    <string name="grid_view">Grid View</string>
+    <string name="error_fragment">Error Fragment</string>
+    <string name="personal_settings">Personal Settings</string>
+    <string name="watch_trailer_1">Watch trailer</string>
+    <string name="watch_trailer_2">FREE</string>
+    <string name="rent_1">Rent By Day</string>
+    <string name="rent_2">From $1.99</string>
+    <string name="buy_1">Buy and Own</string>
+    <string name="buy_2">AT $9.99</string>
+    <string name="movie">Movie</string>
+
+    <!-- Error messages -->
+    <string name="error_fragment_message">An error occurred</string>
+    <string name="dismiss_error">Dismiss</string>
+</resources>
\ No newline at end of file
diff --git a/tv/integration-tests/presentation/src/main/res/values/themes.xml b/tv/integration-tests/presentation/src/main/res/values/themes.xml
new file mode 100644
index 0000000..8df2c77
--- /dev/null
+++ b/tv/integration-tests/presentation/src/main/res/values/themes.xml
@@ -0,0 +1,19 @@
+<!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <style name="Theme.Androidx" parent="@style/Theme.AppCompat" />
+</resources>
\ No newline at end of file
diff --git a/tv/samples/src/main/java/androidx/tv/samples/CarouselSamples.kt b/tv/samples/src/main/java/androidx/tv/samples/CarouselSamples.kt
index ab964bd..aba7109 100644
--- a/tv/samples/src/main/java/androidx/tv/samples/CarouselSamples.kt
+++ b/tv/samples/src/main/java/androidx/tv/samples/CarouselSamples.kt
@@ -17,7 +17,6 @@
 package androidx.tv.samples
 
 import androidx.annotation.Sampled
-import androidx.compose.animation.ExperimentalAnimationApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
 import androidx.compose.foundation.layout.Box
@@ -45,7 +44,7 @@
 import androidx.tv.material3.CarouselState
 import androidx.tv.material3.ExperimentalTvMaterial3Api
 
-@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
+@OptIn(ExperimentalTvMaterial3Api::class)
 @Sampled
 @Composable
 fun SimpleCarousel() {
@@ -56,12 +55,12 @@
     )
 
     Carousel(
-        slideCount = backgrounds.size,
+        itemCount = backgrounds.size,
         modifier = Modifier
             .height(300.dp)
             .fillMaxWidth(),
     ) { itemIndex ->
-        CarouselSlide(
+        CarouselItem(
             background = {
                 Box(
                     modifier = Modifier
@@ -91,7 +90,7 @@
     }
 }
 
-@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
+@OptIn(ExperimentalTvMaterial3Api::class)
 @Sampled
 @Composable
 fun CarouselIndicatorWithRectangleShape() {
@@ -103,15 +102,15 @@
     val carouselState = remember { CarouselState() }
 
     Carousel(
-        slideCount = backgrounds.size,
+        itemCount = backgrounds.size,
         modifier = Modifier
             .height(300.dp)
             .fillMaxWidth(),
         carouselState = carouselState,
         carouselIndicator = {
             CarouselDefaults.IndicatorRow(
-                slideCount = backgrounds.size,
-                activeSlideIndex = carouselState.activeSlideIndex,
+                itemCount = backgrounds.size,
+                activeItemIndex = carouselState.activeItemIndex,
                 modifier = Modifier
                     .align(Alignment.BottomEnd)
                     .padding(16.dp),
@@ -130,7 +129,7 @@
             )
         }
     ) { itemIndex ->
-        CarouselSlide(
+        CarouselItem(
             background = {
                 Box(
                     modifier = Modifier
diff --git a/tv/samples/src/main/java/androidx/tv/samples/NavigationDrawerSamples.kt b/tv/samples/src/main/java/androidx/tv/samples/NavigationDrawerSamples.kt
index a61b2f6..345adc6 100644
--- a/tv/samples/src/main/java/androidx/tv/samples/NavigationDrawerSamples.kt
+++ b/tv/samples/src/main/java/androidx/tv/samples/NavigationDrawerSamples.kt
@@ -16,6 +16,7 @@
 
 package androidx.tv.samples
 
+import androidx.annotation.Sampled
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.foundation.background
 import androidx.compose.foundation.focusable
@@ -41,53 +42,73 @@
 import androidx.tv.material3.Text
 
 @OptIn(ExperimentalTvMaterial3Api::class)
+@Sampled
 @Composable
 fun SampleNavigationDrawer() {
-    NavigationDrawer(drawerContent = drawerContent()) {
-        NonDrawerContent()
+    val navigationRow: @Composable (drawerValue: DrawerValue, color: Color, text: String) -> Unit =
+        { drawerValue, color, text ->
+            Row(Modifier.padding(10.dp).focusable()) {
+                Box(Modifier.size(50.dp).background(color).padding(end = 20.dp))
+                AnimatedVisibility(visible = drawerValue == DrawerValue.Open) {
+                    Text(
+                        text = text,
+                        softWrap = false,
+                        modifier = Modifier.padding(15.dp).width(50.dp),
+                        textAlign = TextAlign.Center
+                    )
+                }
+            }
+        }
+
+    NavigationDrawer(
+        drawerContent = {
+            Column(Modifier.background(Color.Gray).fillMaxHeight()) {
+                navigationRow(it, Color.Red, "Red")
+                navigationRow(it, Color.Blue, "Blue")
+                navigationRow(it, Color.Yellow, "Yellow")
+            }
+        }
+    ) {
+        Button(modifier = Modifier
+            .height(100.dp)
+            .fillMaxWidth(), onClick = {}) {
+            Text("BUTTON")
+        }
     }
 }
 
 @OptIn(ExperimentalTvMaterial3Api::class)
+@Sampled
 @Composable
 fun SampleModalNavigationDrawer() {
-    ModalNavigationDrawer(drawerContent = drawerContent()) {
-        NonDrawerContent()
-    }
-}
+    val navigationRow: @Composable (drawerValue: DrawerValue, color: Color, text: String) -> Unit =
+        { drawerValue, color, text ->
+            Row(Modifier.padding(10.dp).focusable()) {
+                Box(Modifier.size(50.dp).background(color).padding(end = 20.dp))
+                AnimatedVisibility(visible = drawerValue == DrawerValue.Open) {
+                    Text(
+                        text = text,
+                        softWrap = false,
+                        modifier = Modifier.padding(15.dp).width(50.dp),
+                        textAlign = TextAlign.Center
+                    )
+                }
+            }
+        }
 
-@Composable
-private fun NonDrawerContent() {
-    Button(modifier = Modifier
-        .height(100.dp)
-        .fillMaxWidth(), onClick = {}) {
-        Text("BUTTON")
-    }
-}
-
-@Composable
-@OptIn(ExperimentalTvMaterial3Api::class)
-private fun drawerContent(): @Composable (DrawerValue) -> Unit =
-    {
-        Column(Modifier.background(Color.Gray).fillMaxHeight()) {
-            NavigationRow(it, Color.Red, "Red")
-            NavigationRow(it, Color.Blue, "Blue")
-            NavigationRow(it, Color.Yellow, "Yellow")
+    ModalNavigationDrawer(
+        drawerContent = {
+            Column(Modifier.background(Color.Gray).fillMaxHeight()) {
+                navigationRow(it, Color.Red, "Red")
+                navigationRow(it, Color.Blue, "Blue")
+                navigationRow(it, Color.Yellow, "Yellow")
+            }
+        }
+    ) {
+        Button(modifier = Modifier
+            .height(100.dp)
+            .fillMaxWidth(), onClick = {}) {
+            Text("BUTTON")
         }
     }
-
-@OptIn(ExperimentalTvMaterial3Api::class)
-@Composable
-private fun NavigationRow(drawerValue: DrawerValue, color: Color, text: String) {
-    Row(Modifier.padding(10.dp).focusable()) {
-        Box(Modifier.size(50.dp).background(color).padding(end = 20.dp))
-        AnimatedVisibility(visible = drawerValue == DrawerValue.Open) {
-            Text(
-                text = text,
-                softWrap = false,
-                modifier = Modifier.padding(15.dp).width(50.dp),
-                textAlign = TextAlign.Center
-            )
-        }
-    }
-}
\ No newline at end of file
+}
diff --git a/tv/tv-foundation/build.gradle b/tv/tv-foundation/build.gradle
index 76fef0d..c193d0d 100644
--- a/tv/tv-foundation/build.gradle
+++ b/tv/tv-foundation/build.gradle
@@ -30,17 +30,17 @@
 dependencies {
     api(libs.kotlinStdlib)
 
-    def composeVersion = '1.4.0-alpha04'
+    def composeVersion = '1.4.0-rc01'
 
     implementation(libs.kotlinStdlibCommon)
-    implementation("androidx.profileinstaller:profileinstaller:1.2.0")
-    api(project(":compose:ui:ui"))
+    implementation("androidx.profileinstaller:profileinstaller:1.3.0")
 
     api("androidx.annotation:annotation:1.5.0")
     api("androidx.compose.animation:animation:$composeVersion")
     api("androidx.compose.runtime:runtime:$composeVersion")
 
     api(project(":compose:foundation:foundation"))
+    api("androidx.compose.ui:ui:$composeVersion")
     api("androidx.compose.foundation:foundation-layout:$composeVersion")
     api("androidx.compose.ui:ui-graphics:$composeVersion")
     api("androidx.compose.ui:ui-text:$composeVersion")
diff --git a/tv/tv-material/api/public_plus_experimental_current.txt b/tv/tv-material/api/public_plus_experimental_current.txt
index 38f62bb..beb7c7f 100644
--- a/tv/tv-material/api/public_plus_experimental_current.txt
+++ b/tv/tv-material/api/public_plus_experimental_current.txt
@@ -24,39 +24,104 @@
     method @androidx.compose.runtime.Composable public androidx.compose.foundation.IndicationInstance rememberUpdatedInstance(androidx.compose.foundation.interaction.InteractionSource interactionSource);
   }
 
+  @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ButtonBorder {
+  }
+
+  @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ButtonColors {
+  }
+
+  @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ButtonDefaults {
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ButtonBorder border(optional androidx.tv.material3.Border border, optional androidx.tv.material3.Border focusedBorder, optional androidx.tv.material3.Border pressedBorder, optional androidx.tv.material3.Border disabledBorder, optional androidx.tv.material3.Border focusedDisabledBorder);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ButtonColors colors(optional long containerColor, optional long contentColor, optional long focusedContainerColor, optional long focusedContentColor, optional long pressedContainerColor, optional long pressedContentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method public androidx.compose.foundation.layout.PaddingValues getButtonWithIconContentPadding();
+    method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    method public float getIconSize();
+    method public float getIconSpacing();
+    method public androidx.tv.material3.ButtonGlow glow(optional androidx.tv.material3.Glow glow, optional androidx.tv.material3.Glow focusedGlow, optional androidx.tv.material3.Glow pressedGlow);
+    method public androidx.tv.material3.ButtonScale scale(optional @FloatRange(from=0.0) float scale, optional @FloatRange(from=0.0) float focusedScale, optional @FloatRange(from=0.0) float pressedScale, optional @FloatRange(from=0.0) float disabledScale, optional @FloatRange(from=0.0) float focusedDisabledScale);
+    method public androidx.tv.material3.ButtonShape shape(optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.ui.graphics.Shape focusedShape, optional androidx.compose.ui.graphics.Shape pressedShape, optional androidx.compose.ui.graphics.Shape disabledShape, optional androidx.compose.ui.graphics.Shape focusedDisabledShape);
+    property public final androidx.compose.foundation.layout.PaddingValues ButtonWithIconContentPadding;
+    property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
+    property public final float IconSize;
+    property public final float IconSpacing;
+    field public static final androidx.tv.material3.ButtonDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ButtonGlow {
+  }
+
+  public final class ButtonKt {
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Button(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.tv.material3.ButtonScale scale, optional androidx.tv.material3.ButtonGlow glow, optional androidx.tv.material3.ButtonShape shape, optional androidx.tv.material3.ButtonColors colors, optional float tonalElevation, optional androidx.tv.material3.ButtonBorder border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void OutlinedButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.tv.material3.ButtonScale scale, optional androidx.tv.material3.ButtonGlow glow, optional androidx.tv.material3.ButtonShape shape, optional androidx.tv.material3.ButtonColors colors, optional float tonalElevation, optional androidx.tv.material3.ButtonBorder border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+  }
+
+  @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ButtonScale {
+  }
+
+  @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ButtonShape {
+  }
+
+  @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CardBorder {
+  }
+
+  @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CardColors {
+  }
+
+  @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CardDefaults {
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.CardBorder border(optional androidx.tv.material3.Border border, optional androidx.tv.material3.Border focusedBorder, optional androidx.tv.material3.Border pressedBorder);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.CardColors colors(optional long containerColor, optional long contentColor, optional long focusedContainerColor, optional long focusedContentColor, optional long pressedContainerColor, optional long pressedContentColor);
+    method public androidx.tv.material3.CardGlow glow(optional androidx.tv.material3.Glow glow, optional androidx.tv.material3.Glow focusedGlow, optional androidx.tv.material3.Glow pressedGlow);
+    method public androidx.tv.material3.CardScale scale(optional @FloatRange(from=0.0) float scale, optional @FloatRange(from=0.0) float focusedScale, optional @FloatRange(from=0.0) float pressedScale);
+    method public androidx.tv.material3.CardShape shape(optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.ui.graphics.Shape focusedShape, optional androidx.compose.ui.graphics.Shape pressedShape);
+    field public static final androidx.tv.material3.CardDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CardGlow {
+  }
+
+  public final class CardKt {
+    method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Card(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.tv.material3.CardShape shape, optional androidx.tv.material3.CardColors colors, optional androidx.tv.material3.CardScale scale, optional androidx.tv.material3.CardBorder border, optional androidx.tv.material3.CardGlow glow, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+  }
+
+  @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CardScale {
+  }
+
+  @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CardShape {
+  }
+
   @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CarouselDefaults {
-    method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public void IndicatorRow(int slideCount, int activeSlideIndex, optional androidx.compose.ui.Modifier modifier, optional float spacing, optional kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> indicator);
+    method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public void IndicatorRow(int itemCount, int activeItemIndex, optional androidx.compose.ui.Modifier modifier, optional float spacing, optional kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> indicator);
     method @androidx.compose.runtime.Composable public androidx.compose.animation.ContentTransform getContentTransform();
     property @androidx.compose.runtime.Composable public final androidx.compose.animation.ContentTransform contentTransform;
     field public static final androidx.tv.material3.CarouselDefaults INSTANCE;
-    field public static final long TimeToDisplaySlideMillis = 5000L; // 0x1388L
+    field public static final long TimeToDisplayItemMillis = 5000L; // 0x1388L
+  }
+
+  @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CarouselItemDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.animation.ContentTransform getContentTransformEndToStart();
+    method @androidx.compose.runtime.Composable public androidx.compose.animation.ContentTransform getContentTransformLeftToRight();
+    method @androidx.compose.runtime.Composable public androidx.compose.animation.ContentTransform getContentTransformRightToLeft();
+    method @androidx.compose.runtime.Composable public androidx.compose.animation.ContentTransform getContentTransformStartToEnd();
+    property @androidx.compose.runtime.Composable public final androidx.compose.animation.ContentTransform contentTransformEndToStart;
+    property @androidx.compose.runtime.Composable public final androidx.compose.animation.ContentTransform contentTransformLeftToRight;
+    property @androidx.compose.runtime.Composable public final androidx.compose.animation.ContentTransform contentTransformRightToLeft;
+    property @androidx.compose.runtime.Composable public final androidx.compose.animation.ContentTransform contentTransformStartToEnd;
+    field public static final androidx.tv.material3.CarouselItemDefaults INSTANCE;
   }
 
   public final class CarouselKt {
-    method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Carousel(int slideCount, optional androidx.compose.ui.Modifier modifier, optional androidx.tv.material3.CarouselState carouselState, optional long autoScrollDurationMillis, optional androidx.compose.animation.ContentTransform contentTransformForward, optional androidx.compose.animation.ContentTransform contentTransformBackward, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> carouselIndicator, kotlin.jvm.functions.Function2<? super androidx.tv.material3.CarouselScope,? super java.lang.Integer,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Carousel(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.tv.material3.CarouselState carouselState, optional long autoScrollDurationMillis, optional androidx.compose.animation.ContentTransform contentTransformStartToEnd, optional androidx.compose.animation.ContentTransform contentTransformEndToStart, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> carouselIndicator, kotlin.jvm.functions.Function2<? super androidx.tv.material3.CarouselScope,? super java.lang.Integer,kotlin.Unit> content);
   }
 
   @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CarouselScope {
-    method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public void CarouselSlide(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> background, optional androidx.compose.animation.ContentTransform contentTransformForward, optional androidx.compose.animation.ContentTransform contentTransformBackward, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-  }
-
-  @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CarouselSlideDefaults {
-    method @androidx.compose.runtime.Composable public androidx.compose.animation.ContentTransform getContentTransformBackward();
-    method @androidx.compose.runtime.Composable public androidx.compose.animation.ContentTransform getContentTransformForward();
-    method @androidx.compose.runtime.Composable public androidx.compose.animation.ContentTransform getContentTransformLeftToRight();
-    method @androidx.compose.runtime.Composable public androidx.compose.animation.ContentTransform getContentTransformRightToLeft();
-    property @androidx.compose.runtime.Composable public final androidx.compose.animation.ContentTransform contentTransformBackward;
-    property @androidx.compose.runtime.Composable public final androidx.compose.animation.ContentTransform contentTransformForward;
-    property @androidx.compose.runtime.Composable public final androidx.compose.animation.ContentTransform contentTransformLeftToRight;
-    property @androidx.compose.runtime.Composable public final androidx.compose.animation.ContentTransform contentTransformRightToLeft;
-    field public static final androidx.tv.material3.CarouselSlideDefaults INSTANCE;
+    method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public void CarouselItem(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> background, optional androidx.compose.animation.ContentTransform contentTransformStartToEnd, optional androidx.compose.animation.ContentTransform contentTransformEndToStart, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
   @androidx.compose.runtime.Stable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CarouselState {
-    ctor public CarouselState(optional int initialActiveSlideIndex);
-    method public int getActiveSlideIndex();
-    method public androidx.tv.material3.ScrollPauseHandle pauseAutoScroll(int slideIndex);
-    property public final int activeSlideIndex;
+    ctor public CarouselState(optional int initialActiveItemIndex);
+    method public int getActiveItemIndex();
+    method public androidx.tv.material3.ScrollPauseHandle pauseAutoScroll(int itemIndex);
+    property public final int activeItemIndex;
   }
 
   @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ClickableSurfaceBorder {
@@ -260,6 +325,23 @@
     method @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static androidx.tv.material3.DrawerState rememberDrawerState(androidx.tv.material3.DrawerValue initialValue);
   }
 
+  @androidx.tv.material3.ExperimentalTvMaterial3Api public final class OutlinedButtonDefaults {
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ButtonBorder border(optional androidx.tv.material3.Border border, optional androidx.tv.material3.Border focusedBorder, optional androidx.tv.material3.Border pressedBorder, optional androidx.tv.material3.Border disabledBorder, optional androidx.tv.material3.Border focusedDisabledBorder);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ButtonColors colors(optional long containerColor, optional long contentColor, optional long focusedContainerColor, optional long focusedContentColor, optional long pressedContainerColor, optional long pressedContentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method public androidx.compose.foundation.layout.PaddingValues getButtonWithIconContentPadding();
+    method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    method public float getIconSize();
+    method public float getIconSpacing();
+    method public androidx.tv.material3.ButtonGlow glow(optional androidx.tv.material3.Glow glow, optional androidx.tv.material3.Glow focusedGlow, optional androidx.tv.material3.Glow pressedGlow);
+    method public androidx.tv.material3.ButtonScale scale(optional @FloatRange(from=0.0) float scale, optional @FloatRange(from=0.0) float focusedScale, optional @FloatRange(from=0.0) float pressedScale, optional @FloatRange(from=0.0) float disabledScale, optional @FloatRange(from=0.0) float focusedDisabledScale);
+    method public androidx.tv.material3.ButtonShape shape(optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.ui.graphics.Shape focusedShape, optional androidx.compose.ui.graphics.Shape pressedShape, optional androidx.compose.ui.graphics.Shape disabledShape, optional androidx.compose.ui.graphics.Shape focusedDisabledShape);
+    property public final androidx.compose.foundation.layout.PaddingValues ButtonWithIconContentPadding;
+    property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
+    property public final float IconSize;
+    property public final float IconSpacing;
+    field public static final androidx.tv.material3.OutlinedButtonDefaults INSTANCE;
+  }
+
   @androidx.compose.runtime.Stable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ScaleIndication implements androidx.compose.foundation.Indication {
     ctor public ScaleIndication(float scale);
     method @androidx.compose.runtime.Composable public androidx.compose.foundation.IndicationInstance rememberUpdatedInstance(androidx.compose.foundation.interaction.InteractionSource interactionSource);
diff --git a/tv/tv-material/build.gradle b/tv/tv-material/build.gradle
index 6b59668..7b7119b 100644
--- a/tv/tv-material/build.gradle
+++ b/tv/tv-material/build.gradle
@@ -27,13 +27,13 @@
 dependencies {
     api(libs.kotlinStdlib)
 
-    def composeVersion = '1.3.0-rc01'
+    def composeVersion = '1.4.0-rc01'
 
     api("androidx.annotation:annotation:1.5.0")
     api("androidx.compose.runtime:runtime:$composeVersion")
 
-    api(project(":compose:ui:ui"))
     api(project(":compose:animation:animation"))
+    api("androidx.compose.ui:ui:$composeVersion")
     api("androidx.compose.foundation:foundation:$composeVersion")
     api("androidx.compose.foundation:foundation-layout:$composeVersion")
     api("androidx.compose.material:material-icons-core:$composeVersion")
@@ -43,13 +43,14 @@
     api(project(":tv:tv-foundation"))
 
     implementation(libs.kotlinStdlibCommon)
-    implementation("androidx.profileinstaller:profileinstaller:1.2.0")
+    implementation("androidx.profileinstaller:profileinstaller:1.3.0")
 
     androidTestImplementation(libs.truth)
 
     androidTestImplementation(project(":compose:ui:ui-test"))
     androidTestImplementation(project(":compose:ui:ui-test-junit4"))
     androidTestImplementation(project(":compose:test-utils"))
+    androidTestImplementation(project(":test:screenshot:screenshot"))
     androidTestImplementation(libs.testRunner)
 
     samples(project(":tv:tv-samples"))
@@ -60,6 +61,8 @@
     defaultConfig {
         minSdkVersion 21
     }
+    sourceSets.androidTest.assets.srcDirs +=
+             project.rootDir.absolutePath + "/../../golden/tv/compose/material3"
 }
 
 androidx {
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/ButtonScreenshotTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/ButtonScreenshotTest.kt
new file mode 100644
index 0000000..35873cd
--- /dev/null
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/ButtonScreenshotTest.kt
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.material3
+
+import android.os.Build
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.hasClickAction
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onNodeWithText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+class ButtonScreenshotTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @get:Rule
+    val screenshotRule = AndroidXScreenshotTestRule(TV_GOLDEN_MATERIAL3)
+
+    @Test
+    fun default_button_light_theme() {
+        rule.setContent {
+            LightMaterialTheme {
+                Button(onClick = { }) {
+                    Text("Button")
+                }
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "button_light_theme")
+    }
+
+    @Test
+    fun default_button_dark_theme() {
+        rule.setContent {
+            DarkMaterialTheme {
+                Button(onClick = { }) {
+                    Text("Button")
+                }
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "button_dark_theme")
+    }
+
+    @Test
+    fun disabled_button_light_theme() {
+        rule.setContent {
+            LightMaterialTheme {
+                Button(onClick = { }, enabled = false) {
+                    Text("Button")
+                }
+            }
+        }
+
+        rule.onNodeWithText("Button")
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "button_disabled_light_theme")
+    }
+
+    @Test
+    fun disabled_button_dark_theme() {
+        rule.setContent {
+            DarkMaterialTheme {
+                Button(onClick = { }, enabled = false) {
+                    Text("Button")
+                }
+            }
+        }
+
+        rule.onNodeWithText("Button")
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "button_disabled_dark_theme")
+    }
+
+    @Test
+    fun outlined_button_lightTheme() {
+        rule.setContent {
+            LightMaterialTheme {
+                OutlinedButton(onClick = {}) {
+                    Text("Outlined Button")
+                }
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "outlined_button_light_theme")
+    }
+
+    @Test
+    fun outlined_button_darkTheme() {
+        rule.setContent {
+            DarkMaterialTheme {
+                OutlinedButton(onClick = {}) {
+                    Text("Outlined Button")
+                }
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "outlined_button_dark_theme")
+    }
+
+    @Test
+    fun disabled_outlined_button_lightTheme() {
+        rule.setContent {
+            LightMaterialTheme {
+                OutlinedButton(
+                    onClick = {},
+                    enabled = false,
+                    modifier = Modifier.testTag("button")
+                ) {
+                    Text("Outlined Button")
+                }
+            }
+        }
+
+        rule.onNodeWithTag("button")
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "outlined_button_disabled_light_theme")
+    }
+
+    @Test
+    fun disabled_outlined_button_darkTheme() {
+        rule.setContent {
+            DarkMaterialTheme {
+                OutlinedButton(
+                    onClick = {},
+                    enabled = false,
+                    modifier = Modifier.testTag("button")
+                ) {
+                    Text("Outlined Button")
+                }
+            }
+        }
+
+        rule.onNodeWithTag("button")
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "outlined_button_disabled_dark_theme")
+    }
+
+    @Test
+    fun button_withIcon_lightTheme() {
+        rule.setContent {
+            LightMaterialTheme {
+                Button(
+                    onClick = { /* Do something! */ },
+                    contentPadding = ButtonDefaults.ButtonWithIconContentPadding
+                ) {
+                    Icon(
+                        Icons.Filled.Favorite,
+                        contentDescription = "Localized description",
+                        modifier = Modifier.size(ButtonDefaults.IconSize)
+                    )
+                    Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                    Text("Like")
+                }
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "button_withIcon_lightTheme")
+    }
+
+    @Test
+    fun button_withIcon_darkTheme() {
+        rule.setContent {
+            DarkMaterialTheme {
+                Button(
+                    onClick = { /* Do something! */ },
+                    contentPadding = ButtonDefaults.ButtonWithIconContentPadding
+                ) {
+                    Icon(
+                        Icons.Filled.Favorite,
+                        contentDescription = "Localized description",
+                        modifier = Modifier.size(ButtonDefaults.IconSize)
+                    )
+                    Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                    Text("Like")
+                }
+            }
+        }
+
+        rule.onNode(hasClickAction())
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "button_withIcon_darkTheme")
+    }
+
+    @Test
+    fun disabled_button_withIcon_lightTheme() {
+        rule.setContent {
+            LightMaterialTheme {
+                Button(
+                    onClick = { /* Do something! */ },
+                    contentPadding = ButtonDefaults.ButtonWithIconContentPadding,
+                    enabled = false,
+                    modifier = Modifier.testTag("button")
+                ) {
+                    Icon(
+                        Icons.Filled.Favorite,
+                        contentDescription = "Localized description",
+                        modifier = Modifier.size(ButtonDefaults.IconSize)
+                    )
+                    Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                    Text("Like")
+                }
+            }
+        }
+
+        rule.onNodeWithTag("button")
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "button_withIcon_disabled_lightTheme")
+    }
+
+    @Test
+    fun disabled_button_withIcon_darkTheme() {
+        rule.setContent {
+            DarkMaterialTheme {
+                Button(
+                    onClick = { /* Do something! */ },
+                    contentPadding = ButtonDefaults.ButtonWithIconContentPadding,
+                    enabled = false,
+                    modifier = Modifier.testTag("button")
+                ) {
+                    Icon(
+                        Icons.Filled.Favorite,
+                        contentDescription = "Localized description",
+                        modifier = Modifier.size(ButtonDefaults.IconSize)
+                    )
+                    Spacer(Modifier.size(ButtonDefaults.IconSpacing))
+                    Text("Like")
+                }
+            }
+        }
+
+        rule.onNodeWithTag("button")
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "button_withIcon_disabled_darkTheme")
+    }
+}
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/ButtonTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/ButtonTest.kt
new file mode 100644
index 0000000..70ba16f
--- /dev/null
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/ButtonTest.kt
@@ -0,0 +1,480 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.material3
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.SemanticsActions
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsEqualTo
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.getUnclippedBoundsInRoot
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performKeyInput
+import androidx.compose.ui.test.performSemanticsAction
+import androidx.compose.ui.test.pressKey
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@OptIn(
+    ExperimentalTestApi::class,
+    ExperimentalComposeUiApi::class,
+    ExperimentalTvMaterial3Api::class
+)
+@RunWith(AndroidJUnit4::class)
+class ButtonTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun filledButton_defaultSemantics() {
+        rule.setContent {
+            Box {
+                Button(modifier = Modifier.testTag(FilledButtonTag), onClick = {}) {
+                    Text("FilledButton")
+                }
+            }
+        }
+
+        rule.onNodeWithTag(FilledButtonTag)
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Button))
+            .assertIsEnabled()
+    }
+
+    @Test
+    fun filledButton_disabledSemantics() {
+        rule.setContent {
+            Box {
+                Button(
+                    modifier = Modifier.testTag(FilledButtonTag),
+                    onClick = {},
+                    enabled = false
+                ) {
+                    Text("FilledButton")
+                }
+            }
+        }
+
+        rule.onNodeWithTag(FilledButtonTag)
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Button))
+            .assertIsNotEnabled()
+    }
+
+    @Test
+    fun filledButton_findByTag_andClick() {
+        var counter = 0
+        val onClick: () -> Unit = { ++counter }
+        val text = "FilledButtonText"
+
+        rule.setContent {
+            Box {
+                Button(modifier = Modifier.testTag(FilledButtonTag), onClick = onClick) {
+                    Text(text)
+                }
+            }
+        }
+        rule.onNodeWithTag(FilledButtonTag)
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+        rule.runOnIdle {
+            Truth.assertThat(counter).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun filledButton_canBeDisabled() {
+        rule.setContent {
+            var enabled by remember { mutableStateOf(true) }
+            Box {
+                Button(
+                    modifier = Modifier.testTag(FilledButtonTag),
+                    onClick = { enabled = false },
+                    enabled = enabled
+                ) {
+                    Text("Hello")
+                }
+            }
+        }
+        rule.onNodeWithTag(FilledButtonTag)
+            // Confirm the button starts off enabled, with a click action
+            .assertHasClickAction()
+            .assertIsEnabled()
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+            // Then confirm it's disabled with click action after clicking it
+            .assertHasClickAction()
+            .assertIsNotEnabled()
+    }
+
+    @Test
+    fun filledButton_clickIs_independent_betweenButtons() {
+        var watchButtonCounter = 0
+        val watchButtonOnClick: () -> Unit = { ++watchButtonCounter }
+        val watchButtonTag = "WatchButton"
+
+        var playButtonCounter = 0
+        val playButtonOnClick: () -> Unit = { ++playButtonCounter }
+        val playButtonTag = "PlayButton"
+
+        rule.setContent {
+            Column {
+                Button(
+                    modifier = Modifier.testTag(watchButtonTag),
+                    onClick = watchButtonOnClick
+                ) {
+                    Text("Watch")
+                }
+                Button(
+                    modifier = Modifier.testTag(playButtonTag),
+                    onClick = playButtonOnClick
+                ) {
+                    Text("Play")
+                }
+            }
+        }
+
+        rule.onNodeWithTag(watchButtonTag)
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+
+        rule.runOnIdle {
+            Truth.assertThat(watchButtonCounter).isEqualTo(1)
+            Truth.assertThat(playButtonCounter).isEqualTo(0)
+        }
+
+        rule.onNodeWithTag(playButtonTag)
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+
+        rule.runOnIdle {
+            Truth.assertThat(watchButtonCounter).isEqualTo(1)
+            Truth.assertThat(playButtonCounter).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun filledButton_buttonPositioning() {
+        rule.setContent {
+            Button(
+                onClick = {},
+                modifier = Modifier.testTag(FilledButtonTag)
+            ) {
+                Text(
+                    "FilledButton",
+                    modifier = Modifier
+                        .testTag(FilledButtonTextTag)
+                        .semantics(mergeDescendants = true) {}
+                )
+            }
+        }
+
+        val buttonBounds = rule.onNodeWithTag(FilledButtonTag).getUnclippedBoundsInRoot()
+        val textBounds = rule.onNodeWithTag(FilledButtonTextTag).getUnclippedBoundsInRoot()
+
+        (textBounds.left - buttonBounds.left).assertIsEqualTo(
+            16.dp,
+            "padding between the start of the button and the start of the text."
+        )
+
+        (buttonBounds.right - textBounds.right).assertIsEqualTo(
+            16.dp,
+            "padding between the end of the text and the end of the button."
+        )
+    }
+
+    @Test
+    fun filledButtonWithIcon_positioning() {
+        rule.setContent {
+            Button(
+                onClick = {},
+                contentPadding = ButtonDefaults.ButtonWithIconContentPadding,
+                modifier = Modifier
+                    .testTag(FilledButtonTag)
+            ) {
+                Box(
+                    modifier = Modifier
+                        .size(FilledButtonIconSize)
+                        .testTag(FilledButtonIconTag)
+                        .semantics(mergeDescendants = true) {}
+                )
+                Spacer(Modifier.size(FilledButtonIconSpacing))
+                Text(
+                    "Liked it",
+                    modifier = Modifier
+                        .testTag(FilledButtonTextTag)
+                        .semantics(mergeDescendants = true) {}
+                )
+            }
+        }
+
+        val textBounds = rule.onNodeWithTag(FilledButtonTextTag).getUnclippedBoundsInRoot()
+        val iconBounds = rule.onNodeWithTag(FilledButtonIconTag).getUnclippedBoundsInRoot()
+        val buttonBounds = rule.onNodeWithTag(FilledButtonTag).getUnclippedBoundsInRoot()
+
+        (iconBounds.left - buttonBounds.left).assertIsEqualTo(
+            expected = 12.dp,
+            subject = "Padding between start of button and start of icon."
+        )
+
+        (textBounds.left - iconBounds.right).assertIsEqualTo(
+            expected = FilledButtonIconSpacing,
+            subject = "Padding between end of icon and start of text."
+        )
+
+        (buttonBounds.right - textBounds.right).assertIsEqualTo(
+            expected = 16.dp,
+            subject = "padding between end of text and end of button."
+        )
+    }
+
+    @Test
+    fun outlinedButton_defaultSemantics() {
+        rule.setContent {
+            Box {
+                OutlinedButton(modifier = Modifier.testTag(OutlinedButtonTag), onClick = {}) {
+                    Text("OutlinedButton")
+                }
+            }
+        }
+
+        rule.onNodeWithTag(OutlinedButtonTag)
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Button))
+            .assertIsEnabled()
+    }
+
+    @Test
+    fun outlinedButton_disabledSemantics() {
+        rule.setContent {
+            Box {
+                OutlinedButton(
+                    modifier = Modifier.testTag(OutlinedButtonTag),
+                    onClick = {},
+                    enabled = false
+                ) {
+                    Text("OutlinedButton")
+                }
+            }
+        }
+
+        rule.onNodeWithTag(OutlinedButtonTag)
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Button))
+            .assertIsNotEnabled()
+    }
+
+    @Test
+    fun outlinedButton_findByTag_andClick() {
+        var counter = 0
+        val onClick: () -> Unit = { ++counter }
+        val text = "OutlinedButtonText"
+
+        rule.setContent {
+            Box {
+                OutlinedButton(modifier = Modifier.testTag(OutlinedButtonTag), onClick = onClick) {
+                    Text(text)
+                }
+            }
+        }
+        rule.onNodeWithTag(OutlinedButtonTag)
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+        rule.runOnIdle {
+            Truth.assertThat(counter).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun outlinedButton_canBeDisabled() {
+        rule.setContent {
+            var enabled by remember { mutableStateOf(true) }
+            Box {
+                OutlinedButton(
+                    modifier = Modifier.testTag(OutlinedButtonTag),
+                    onClick = { enabled = false },
+                    enabled = enabled
+                ) {
+                    Text("Hello")
+                }
+            }
+        }
+        rule.onNodeWithTag(OutlinedButtonTag)
+            // Confirm the button starts off enabled, with a click action
+            .assertHasClickAction()
+            .assertIsEnabled()
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+            // Then confirm it's disabled with click action after clicking it
+            .assertHasClickAction()
+            .assertIsNotEnabled()
+    }
+
+    @Test
+    fun outlinedButton_clickIs_independent_betweenButtons() {
+        var watchButtonCounter = 0
+        val watchButtonOnClick: () -> Unit = { ++watchButtonCounter }
+        val watchButtonTag = "WatchButton"
+
+        var playButtonCounter = 0
+        val playButtonOnClick: () -> Unit = { ++playButtonCounter }
+        val playButtonTag = "PlayButton"
+
+        rule.setContent {
+            Column {
+                OutlinedButton(
+                    modifier = Modifier.testTag(watchButtonTag),
+                    onClick = watchButtonOnClick
+                ) {
+                    Text("Watch")
+                }
+                OutlinedButton(
+                    modifier = Modifier.testTag(playButtonTag),
+                    onClick = playButtonOnClick
+                ) {
+                    Text("Play")
+                }
+            }
+        }
+
+        rule.onNodeWithTag(watchButtonTag)
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+
+        rule.runOnIdle {
+            Truth.assertThat(watchButtonCounter).isEqualTo(1)
+            Truth.assertThat(playButtonCounter).isEqualTo(0)
+        }
+
+        rule.onNodeWithTag(playButtonTag)
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+
+        rule.runOnIdle {
+            Truth.assertThat(watchButtonCounter).isEqualTo(1)
+            Truth.assertThat(playButtonCounter).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun outlinedButton_buttonPositioning() {
+        rule.setContent {
+            OutlinedButton(
+                onClick = {},
+                modifier = Modifier.testTag(OutlinedButtonTag)
+            ) {
+                Text(
+                    "OutlinedButton",
+                    modifier = Modifier
+                        .testTag(OutlinedButtonTextTag)
+                        .semantics(mergeDescendants = true) {}
+                )
+            }
+        }
+
+        val buttonBounds = rule.onNodeWithTag(OutlinedButtonTag).getUnclippedBoundsInRoot()
+        val textBounds = rule.onNodeWithTag(OutlinedButtonTextTag).getUnclippedBoundsInRoot()
+
+        (textBounds.left - buttonBounds.left).assertIsEqualTo(
+            16.dp,
+            "padding between the start of the button and the start of the text."
+        )
+
+        (buttonBounds.right - textBounds.right).assertIsEqualTo(
+            16.dp,
+            "padding between the end of the text and the end of the button."
+        )
+    }
+
+    @Test
+    fun outlinedButton_buttonWithIcon_positioning() {
+        rule.setContent {
+            OutlinedButton(
+                onClick = {},
+                contentPadding = OutlinedButtonDefaults.ButtonWithIconContentPadding,
+                modifier = Modifier
+                    .testTag(OutlinedButtonTag)
+            ) {
+                Box(
+                    modifier = Modifier
+                        .size(OutlinedButtonIconSize)
+                        .testTag(OutlinedButtonIconTag)
+                        .semantics(mergeDescendants = true) {}
+                )
+                Spacer(Modifier.size(OutlinedButtonIconSpacing))
+                Text(
+                    "Liked it",
+                    modifier = Modifier
+                        .testTag(OutlinedButtonTextTag)
+                        .semantics(mergeDescendants = true) {}
+                )
+            }
+        }
+
+        val textBounds = rule.onNodeWithTag(OutlinedButtonTextTag).getUnclippedBoundsInRoot()
+        val iconBounds = rule.onNodeWithTag(OutlinedButtonIconTag).getUnclippedBoundsInRoot()
+        val buttonBounds = rule.onNodeWithTag(OutlinedButtonTag).getUnclippedBoundsInRoot()
+
+        (iconBounds.left - buttonBounds.left).assertIsEqualTo(
+            expected = 12.dp,
+            subject = "Padding between start of button and start of icon."
+        )
+
+        (textBounds.left - iconBounds.right).assertIsEqualTo(
+            expected = OutlinedButtonIconSpacing,
+            subject = "Padding between end of icon and start of text."
+        )
+
+        (buttonBounds.right - textBounds.right).assertIsEqualTo(
+            expected = 16.dp,
+            subject = "padding between end of text and end of button."
+        )
+    }
+}
+
+private const val FilledButtonTag = "FilledButton"
+private const val FilledButtonTextTag = "FilledButtonText"
+private const val FilledButtonIconTag = "FilledButtonIcon"
+private val FilledButtonIconSize = 18.0.dp
+private val FilledButtonIconSpacing = 8.dp
+
+private const val OutlinedButtonTag = "OutlinedButton"
+private const val OutlinedButtonTextTag = "OutlinedButtonText"
+private const val OutlinedButtonIconTag = "OutlinedButtonIcon"
+private val OutlinedButtonIconSize = 18.0.dp
+private val OutlinedButtonIconSpacing = 8.dp
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CardTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CardTest.kt
new file mode 100644
index 0000000..d4437c7
--- /dev/null
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CardTest.kt
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.material3
+
+import android.os.Build
+import androidx.compose.foundation.background
+import androidx.compose.foundation.interaction.FocusInteraction
+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.Spacer
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CutCornerShape
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.testutils.assertShape
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.SemanticsActions
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertTextEquals
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performKeyInput
+import androidx.compose.ui.test.performSemanticsAction
+import androidx.compose.ui.test.pressKey
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(
+    ExperimentalTestApi::class,
+    ExperimentalComposeUiApi::class,
+    ExperimentalTvMaterial3Api::class
+)
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class CardTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun card_customShapeAndColorIsUsed() {
+        val shape = CutCornerShape(8.dp)
+        val background = Color.Yellow
+        val cardColor = Color.Blue
+        rule.setContent {
+            Box(modifier = Modifier.background(background)) {
+                Card(
+                    modifier = Modifier
+                        .semantics(mergeDescendants = true) {}
+                        .testTag("card"),
+                    shape = CardDefaults.shape(shape = shape),
+                    colors = CardDefaults.colors(containerColor = cardColor),
+                    onClick = {}
+                ) {
+                    Box(Modifier.size(50.dp, 50.dp))
+                }
+            }
+        }
+
+        rule
+            .onNodeWithTag("card")
+            .captureToImage()
+            .assertShape(
+                density = rule.density,
+                shape = shape,
+                shapeColor = cardColor,
+                backgroundColor = background,
+                shapeOverlapPixelCount = with(rule.density) { 1.dp.toPx() }
+            )
+    }
+
+    @Test
+    fun card_semantics() {
+        val count = mutableStateOf(0)
+        rule.setContent {
+            Card(
+                modifier = Modifier.testTag("card"),
+                onClick = { count.value += 1 },
+            ) {
+                Text("${count.value}")
+                Spacer(Modifier.size(30.dp))
+            }
+        }
+        rule.onNodeWithTag("card")
+            .assertHasClickAction()
+            .assert(SemanticsMatcher.keyNotDefined(SemanticsProperties.Role))
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .assertIsEnabled()
+            .assertTextEquals("0")
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+            .assertTextEquals("1")
+    }
+
+    @Test
+    fun card_clickAction() {
+        val count = mutableStateOf(0f)
+        rule.setContent {
+            Card(
+                modifier = Modifier.testTag("card"),
+                onClick = { count.value += 1 },
+            ) {
+                Text("${count.value}")
+                Spacer(Modifier.size(30.dp))
+            }
+        }
+
+        rule.onNodeWithTag("card")
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+        Truth.assertThat(count.value).isEqualTo(1)
+
+        rule.onNodeWithTag("card")
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+            .performKeyInput { pressKey(Key.DirectionCenter) }
+        Truth.assertThat(count.value).isEqualTo(3)
+    }
+
+    @Test
+    fun card_interactionSource() {
+        val interactionSource = MutableInteractionSource()
+
+        lateinit var scope: CoroutineScope
+
+        rule.setContent {
+            scope = rememberCoroutineScope()
+            Card(
+                onClick = {},
+                modifier = Modifier.testTag("card"),
+                interactionSource = interactionSource
+            ) {
+                Spacer(Modifier.size(30.dp))
+            }
+        }
+
+        val interactions = mutableListOf<Interaction>()
+
+        scope.launch { interactionSource.interactions.collect { interactions.add(it) } }
+
+        rule.runOnIdle { Truth.assertThat(interactions).isEmpty() }
+
+        rule.onNodeWithTag("card").performSemanticsAction(SemanticsActions.RequestFocus)
+
+        rule.runOnIdle {
+            Truth.assertThat(interactions).hasSize(1)
+            Truth.assertThat(interactions.first()).isInstanceOf(FocusInteraction.Focus::class.java)
+        }
+
+        rule.onNodeWithTag("card").performKeyInput { pressKey(Key.DirectionCenter) }
+
+        rule.runOnIdle {
+            Truth.assertThat(interactions).hasSize(3)
+            Truth.assertThat(interactions.first()).isInstanceOf(FocusInteraction.Focus::class.java)
+            Truth.assertThat(interactions[1]).isInstanceOf(PressInteraction.Press::class.java)
+            Truth.assertThat(interactions[2]).isInstanceOf(PressInteraction.Release::class.java)
+        }
+    }
+}
\ No newline at end of file
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselScopeTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselScopeTest.kt
index 6fdeaa9..10080e0 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselScopeTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselScopeTest.kt
@@ -52,9 +52,9 @@
 
     @OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
     @Test
-    fun carouselSlide_parentContainerGainsFocused_onBackPress() {
+    fun carouselItem_parentContainerGainsFocused_onBackPress() {
         val containerBoxTag = "container-box"
-        val carouselSlideTag = "carousel-slide"
+        val carouselItemTag = "carousel-item"
 
         rule.setContent {
             val carouselState = remember { CarouselState() }
@@ -68,8 +68,8 @@
                     .focusable()
             ) {
                 CarouselScope(carouselState = carouselState)
-                    .CarouselSlide(
-                        modifier = Modifier.testTag(carouselSlideTag),
+                    .CarouselItem(
+                        modifier = Modifier.testTag(carouselItemTag),
                         background = {
                             Box(
                                 modifier = Modifier
@@ -82,7 +82,7 @@
         }
 
         // Request focus for Carousel Item on start
-        rule.onNodeWithTag(carouselSlideTag)
+        rule.onNodeWithTag(carouselItemTag)
             .performSemanticsAction(SemanticsActions.RequestFocus)
         rule.waitForIdle()
 
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt
index d9c5a2f..7582b2c 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt
@@ -71,7 +71,7 @@
 import org.junit.Rule
 import org.junit.Test
 
-private const val delayBetweenSlides = 2500L
+private const val delayBetweenItems = 2500L
 private const val animationTime = 900L
 
 @OptIn(ExperimentalTvMaterial3Api::class)
@@ -89,10 +89,10 @@
 
         rule.onNodeWithText("Text 1").assertIsDisplayed()
 
-        rule.mainClock.advanceTimeBy(delayBetweenSlides)
+        rule.mainClock.advanceTimeBy(delayBetweenItems)
         rule.onNodeWithText("Text 2").assertIsDisplayed()
 
-        rule.mainClock.advanceTimeBy(delayBetweenSlides)
+        rule.mainClock.advanceTimeBy(delayBetweenItems)
         rule.onNodeWithText("Text 3").assertIsDisplayed()
     }
 
@@ -111,7 +111,7 @@
             .onParent()
             .performSemanticsAction(SemanticsActions.RequestFocus)
 
-        rule.mainClock.advanceTimeBy(delayBetweenSlides)
+        rule.mainClock.advanceTimeBy(delayBetweenItems)
 
         rule.onNodeWithText("Text 2").assertDoesNotExist()
         rule.onNodeWithText("Text 1").onParent().assertIsFocused()
@@ -130,7 +130,7 @@
         rule.onNodeWithText("Text 1").assertIsDisplayed()
         rule.onNodeWithText("Text 1").onParent().assertIsNotFocused()
 
-        rule.mainClock.advanceTimeBy(delayBetweenSlides)
+        rule.mainClock.advanceTimeBy(delayBetweenItems)
 
         rule.onNodeWithText("Text 2").assertDoesNotExist()
         rule.onNodeWithText("Text 1").assertIsDisplayed()
@@ -154,17 +154,17 @@
         rule.onNodeWithText("Text 1").assertIsDisplayed()
         rule.onNodeWithText("Text 1").onParent().assertIsNotFocused()
 
-        rule.mainClock.advanceTimeBy(delayBetweenSlides)
+        rule.mainClock.advanceTimeBy(delayBetweenItems)
 
         // pause handle has not been resumed, so Text 1 should still be on the screen.
         rule.onNodeWithText("Text 2").assertDoesNotExist()
         rule.onNodeWithText("Text 1").assertIsDisplayed()
 
         rule.runOnIdle { pauseHandle?.resumeAutoScroll() }
-        rule.mainClock.advanceTimeBy(delayBetweenSlides)
+        rule.mainClock.advanceTimeBy(delayBetweenItems)
 
         // pause handle has been resumed, so Text 2 should be on the screen after
-        // delayBetweenSlides + animationTime
+        // delayBetweenItems + animationTime
         rule.onNodeWithText("Text 1").assertDoesNotExist()
         rule.onNodeWithText("Text 2").assertIsDisplayed()
     }
@@ -192,23 +192,23 @@
         rule.onNodeWithText("Text 1").assertIsDisplayed()
         rule.onNodeWithText("Text 1").onParent().assertIsNotFocused()
 
-        rule.mainClock.advanceTimeBy(delayBetweenSlides)
+        rule.mainClock.advanceTimeBy(delayBetweenItems)
 
         // pause handles have not been resumed, so Text 1 should still be on the screen.
         rule.onNodeWithText("Text 2").assertDoesNotExist()
         rule.onNodeWithText("Text 1").assertIsDisplayed()
 
         rule.runOnIdle { pauseHandle1?.resumeAutoScroll() }
-        rule.mainClock.advanceTimeBy(delayBetweenSlides)
+        rule.mainClock.advanceTimeBy(delayBetweenItems)
 
         // Second pause handle has not been resumed, so Text 1 should still be on the screen.
         rule.onNodeWithText("Text 2").assertDoesNotExist()
         rule.onNodeWithText("Text 1").assertIsDisplayed()
 
         rule.runOnIdle { pauseHandle2?.resumeAutoScroll() }
-        rule.mainClock.advanceTimeBy(delayBetweenSlides)
+        rule.mainClock.advanceTimeBy(delayBetweenItems)
         // All pause handles have been resumed, so Text 2 should be on the screen after
-        // delayBetweenSlides + animationTime
+        // delayBetweenItems + animationTime
         rule.onNodeWithText("Text 1").assertDoesNotExist()
         rule.onNodeWithText("Text 2").assertIsDisplayed()
     }
@@ -236,7 +236,7 @@
         rule.onNodeWithText("Text 1").assertIsDisplayed()
         rule.onNodeWithText("Text 1").onParent().assertIsNotFocused()
 
-        rule.mainClock.advanceTimeBy(delayBetweenSlides)
+        rule.mainClock.advanceTimeBy(delayBetweenItems)
 
         // pause handles have not been resumed, so Text 1 should still be on the screen.
         rule.onNodeWithText("Text 2").assertDoesNotExist()
@@ -245,7 +245,7 @@
         rule.runOnIdle { pauseHandle1?.resumeAutoScroll() }
         // subsequent call to resume should be ignored
         rule.runOnIdle { pauseHandle1?.resumeAutoScroll() }
-        rule.mainClock.advanceTimeBy(delayBetweenSlides)
+        rule.mainClock.advanceTimeBy(delayBetweenItems)
 
         // Second pause handle has not been resumed, so Text 1 should still be on the screen.
         rule.onNodeWithText("Text 2").assertDoesNotExist()
@@ -270,7 +270,7 @@
         rule.onNodeWithText("Card").performSemanticsAction(SemanticsActions.RequestFocus)
         rule.onNodeWithText("Card").assertIsFocused()
 
-        rule.mainClock.advanceTimeBy(delayBetweenSlides)
+        rule.mainClock.advanceTimeBy(delayBetweenItems)
         rule.onNodeWithText("Text 1").assertDoesNotExist()
         rule.onNodeWithText("Text 2").assertIsDisplayed()
     }
@@ -280,7 +280,7 @@
     fun carousel_pagerIndicatorDisplayed() {
         rule.setContent {
             SampleCarousel {
-                SampleCarouselSlide(index = it)
+                SampleCarouselItem(index = it)
             }
         }
 
@@ -292,7 +292,7 @@
     fun carousel_withAnimatedContent_successfulTransition() {
         rule.setContent {
             SampleCarousel {
-                SampleCarouselSlide(index = it) {
+                SampleCarouselItem(index = it) {
                     Column {
                         BasicText(text = "Text ${it + 1}")
                         BasicText(text = "PLAY")
@@ -313,7 +313,7 @@
     fun carousel_withAnimatedContent_successfulFocusIn() {
         rule.setContent {
             SampleCarousel {
-                SampleCarouselSlide(index = it)
+                SampleCarouselItem(index = it)
             }
         }
 
@@ -321,7 +321,7 @@
         rule.onNodeWithTag("pager")
             .performSemanticsAction(SemanticsActions.RequestFocus)
 
-        // current slide overlay render delay
+        // current item overlay render delay
         rule.mainClock.advanceTimeBy(animationTime, false)
         rule.mainClock.advanceTimeBy(animationTime, false)
         rule.mainClock.advanceTimeByFrame()
@@ -367,14 +367,14 @@
 
     @OptIn(ExperimentalAnimationApi::class)
     @Test
-    fun carousel_withCarouselSlide_parentContainerGainsFocus_onBackPress() {
+    fun carousel_withCarouselItem_parentContainerGainsFocus_onBackPress() {
         rule.setContent {
             Box(modifier = Modifier
                 .testTag("box-container")
                 .fillMaxSize()
                 .focusable()) {
                 SampleCarousel {
-                    SampleCarouselSlide(index = it)
+                    SampleCarouselItem(index = it)
                 }
             }
         }
@@ -432,10 +432,10 @@
                             .testTag("featured-carousel")
                             .border(2.dp, Color.Black),
                         carouselState = remember { CarouselState() },
-                        slideCount = 3,
-                        autoScrollDurationMillis = delayBetweenSlides
+                        itemCount = 3,
+                        autoScrollDurationMillis = delayBetweenItems
                     ) {
-                        SampleCarouselSlide(index = it) {
+                        SampleCarouselItem(index = it) {
                             Box {
                                 Column(modifier = Modifier.align(Alignment.BottomStart)) {
                                     BasicText(text = "carousel-frame")
@@ -493,10 +493,10 @@
 
     @OptIn(ExperimentalAnimationApi::class)
     @Test
-    fun carousel_zeroSlideCount_shouldNotCrash() {
+    fun carousel_zeroItemCount_shouldNotCrash() {
         val testTag = "emptyCarousel"
         rule.setContent {
-            Carousel(slideCount = 0, modifier = Modifier.testTag(testTag)) {}
+            Carousel(itemCount = 0, modifier = Modifier.testTag(testTag)) {}
         }
 
         rule.onNodeWithTag(testTag).assertExists()
@@ -504,10 +504,10 @@
 
     @OptIn(ExperimentalAnimationApi::class)
     @Test
-    fun carousel_oneSlideCount_shouldNotCrash() {
+    fun carousel_oneItemCount_shouldNotCrash() {
         val testTag = "emptyCarousel"
         rule.setContent {
-            Carousel(slideCount = 1, modifier = Modifier.testTag(testTag)) {}
+            Carousel(itemCount = 1, modifier = Modifier.testTag(testTag)) {}
         }
 
         rule.onNodeWithTag(testTag).assertExists()
@@ -536,20 +536,20 @@
         rule.mainClock.advanceTimeByFrame()
         rule.waitForIdle()
 
-        // Check that slide 1 is in view and button 1 has focus
+        // Check that item 1 is in view and button 1 has focus
         rule.onNodeWithText("Button-1").assertIsDisplayed()
         rule.onNodeWithText("Button-1").assertIsFocused()
 
-        // press dpad right to scroll to next slide
+        // press dpad right to scroll to next item
         performKeyPress(NativeKeyEvent.KEYCODE_DPAD_RIGHT)
 
-        // Wait for slide to load
+        // Wait for item to load
         rule.mainClock.advanceTimeByFrame()
         rule.waitForIdle()
         rule.mainClock.advanceTimeBy(animationTime, false)
         rule.waitForIdle()
 
-        // Check that slide 2 is in view and button 2 has focus
+        // Check that item 2 is in view and button 2 has focus
         rule.onNodeWithText("Button-2").assertIsDisplayed()
         // TODO: Fix button 2 isn't gaining focus
         // rule.onNodeWithText("Button-2").assertIsFocused()
@@ -557,16 +557,16 @@
         // Check if the first focusable element in parent has focus
         rule.onNodeWithText("Row-button-1").assertIsNotFocused()
 
-        // press dpad left to scroll to previous slide
+        // press dpad left to scroll to previous item
         performKeyPress(NativeKeyEvent.KEYCODE_DPAD_LEFT)
 
-        // Wait for slide to load
+        // Wait for item to load
         rule.mainClock.advanceTimeByFrame()
         rule.waitForIdle()
         rule.mainClock.advanceTimeBy(animationTime, false)
         rule.waitForIdle()
 
-        // Check that slide 1 is in view and button 1 has focus
+        // Check that item 1 is in view and button 1 has focus
         rule.onNodeWithText("Button-1").assertIsDisplayed()
         rule.onNodeWithText("Button-1").assertIsFocused()
     }
@@ -592,8 +592,8 @@
                     }
                 }
 
-                SampleCarousel(carouselState = carouselState, slideCount = 20) {
-                    SampleCarouselSlide(modifier = Modifier.testTag("slide-$it"), index = it)
+                SampleCarousel(carouselState = carouselState, itemCount = 20) {
+                    SampleCarouselItem(modifier = Modifier.testTag("item-$it"), index = it)
                 }
             }
         }
@@ -602,9 +602,9 @@
         rule.onNodeWithTag("pager").performSemanticsAction(SemanticsActions.RequestFocus)
         rule.waitForIdle()
 
-        val slideProgression = listOf(6, 3, -4, 3, -6, 5, 3)
+        val itemProgression = listOf(6, 3, -4, 3, -6, 5, 3)
 
-        slideProgression.forEach {
+        itemProgression.forEach {
             if (it < 0) {
                 performKeyPress(NativeKeyEvent.KEYCODE_DPAD_LEFT, it * -1)
             } else {
@@ -614,20 +614,20 @@
 
         rule.mainClock.advanceTimeBy(animationTime)
 
-        val finalSlide = slideProgression.sum()
-        rule.onNodeWithText("Play $finalSlide").assertIsFocused()
+        val finalItem = itemProgression.sum()
+        rule.onNodeWithText("Play $finalItem").assertIsFocused()
 
         performKeyPress(NativeKeyEvent.KEYCODE_DPAD_RIGHT, 3)
 
         rule.mainClock.advanceTimeBy((animationTime) * 3)
 
-        rule.onNodeWithText("Play ${finalSlide + 3}").assertIsFocused()
+        rule.onNodeWithText("Play ${finalItem + 3}").assertIsFocused()
     }
 
     @Test
     fun carousel_manualScrolling_onDpadLongPress() {
         rule.setContent {
-            SampleCarousel(slideCount = 6) { index ->
+            SampleCarousel(itemCount = 6) { index ->
                 SampleButton("Button ${index + 1}")
             }
         }
@@ -641,31 +641,31 @@
         rule.mainClock.advanceTimeByFrame()
         rule.waitForIdle()
 
-        // Assert that Button 1 from first slide is focused
+        // Assert that Button 1 from first item is focused
         rule.onNodeWithText("Button 1").assertIsFocused()
 
         // Trigger dpad right key long press
         performLongKeyPress(rule, NativeKeyEvent.KEYCODE_DPAD_RIGHT)
 
-        // Advance time and trigger recomposition to switch to next slide
+        // Advance time and trigger recomposition to switch to next item
         rule.mainClock.advanceTimeByFrame()
         rule.waitForIdle()
-        rule.mainClock.advanceTimeBy(delayBetweenSlides, false)
+        rule.mainClock.advanceTimeBy(delayBetweenItems, false)
         rule.waitForIdle()
 
-        // Assert that Button 2 from second slide is focused
+        // Assert that Button 2 from second item is focused
         rule.onNodeWithText("Button 2").assertIsFocused()
 
         // Trigger dpad left key long press
         performLongKeyPress(rule, NativeKeyEvent.KEYCODE_DPAD_LEFT)
 
-        // Advance time and trigger recomposition to switch to previous slide
-        rule.mainClock.advanceTimeBy(delayBetweenSlides, false)
+        // Advance time and trigger recomposition to switch to previous item
+        rule.mainClock.advanceTimeBy(delayBetweenItems, false)
         rule.waitForIdle()
         rule.mainClock.advanceTimeByFrame()
         rule.waitForIdle()
 
-        // Assert that Button 1 from first slide is focused
+        // Assert that Button 1 from first item is focused
         rule.onNodeWithText("Button 1").assertIsFocused()
     }
 
@@ -681,37 +681,37 @@
         rule.onNodeWithTag("pager")
             .performSemanticsAction(SemanticsActions.RequestFocus)
 
-        // current slide overlay render delay
+        // current item overlay render delay
         rule.mainClock.advanceTimeBy(animationTime, false)
         rule.mainClock.advanceTimeBy(animationTime, false)
         rule.mainClock.advanceTimeByFrame()
 
-        // Assert that slide 1 is in view
+        // Assert that item 1 is in view
         rule.onNodeWithText("Button 1").assertIsDisplayed()
 
         // advance time
-        rule.mainClock.advanceTimeBy(delayBetweenSlides + animationTime, false)
+        rule.mainClock.advanceTimeBy(delayBetweenItems + animationTime, false)
         rule.mainClock.advanceTimeByFrame()
 
         // go right once
         performKeyPress(NativeKeyEvent.KEYCODE_DPAD_RIGHT)
 
-        // Wait for slide to load
+        // Wait for item to load
         rule.mainClock.advanceTimeBy(animationTime)
         rule.mainClock.advanceTimeByFrame()
 
-        // Assert that slide 2 is in view
+        // Assert that item 2 is in view
         rule.onNodeWithText("Button 2").assertIsDisplayed()
 
         // go left once
         performKeyPress(NativeKeyEvent.KEYCODE_DPAD_LEFT)
 
-        // Wait for slide to load
-        rule.mainClock.advanceTimeBy(delayBetweenSlides)
+        // Wait for item to load
+        rule.mainClock.advanceTimeBy(delayBetweenItems)
         rule.mainClock.advanceTimeBy(animationTime)
         rule.mainClock.advanceTimeByFrame()
 
-        // Assert that slide 1 is in view
+        // Assert that item 1 is in view
         rule.onNodeWithText("Button 1").assertIsDisplayed()
     }
 
@@ -731,70 +731,70 @@
         rule.onNodeWithTag("pager")
             .performSemanticsAction(SemanticsActions.RequestFocus)
 
-        // current slide overlay render delay
+        // current item overlay render delay
         rule.mainClock.advanceTimeBy(animationTime, false)
         rule.mainClock.advanceTimeBy(animationTime, false)
         rule.mainClock.advanceTimeByFrame()
 
-        // Assert that slide 1 is in view
+        // Assert that item 1 is in view
         rule.onNodeWithText("Button 1").assertIsDisplayed()
 
         // advance time
-        rule.mainClock.advanceTimeBy(delayBetweenSlides + animationTime, false)
+        rule.mainClock.advanceTimeBy(delayBetweenItems + animationTime, false)
         rule.mainClock.advanceTimeByFrame()
 
         // go right once
         performKeyPress(NativeKeyEvent.KEYCODE_DPAD_LEFT)
 
-        // Wait for slide to load
+        // Wait for item to load
         rule.mainClock.advanceTimeBy(animationTime)
         rule.mainClock.advanceTimeByFrame()
 
-        // Assert that slide 2 is in view
+        // Assert that item 2 is in view
         rule.onNodeWithText("Button 2").assertIsDisplayed()
 
         // go left once
         performKeyPress(NativeKeyEvent.KEYCODE_DPAD_RIGHT)
 
-        // Wait for slide to load
-        rule.mainClock.advanceTimeBy(delayBetweenSlides + animationTime, false)
+        // Wait for item to load
+        rule.mainClock.advanceTimeBy(delayBetweenItems + animationTime, false)
         rule.mainClock.advanceTimeByFrame()
 
-        // Assert that slide 1 is in view
+        // Assert that item 1 is in view
         rule.onNodeWithText("Button 1").assertIsDisplayed()
     }
 
     @Test
-    fun carousel_slideCountChangesDuringAnimation_shouldNotCrash() {
-        val slideDisplayDurationMs: Long = 100
-        var slideChanges = 0
-        // number of slides will fall from 4 to 2, but 4 slide transitions should happen without a
+    fun carousel_itemCountChangesDuringAnimation_shouldNotCrash() {
+        val itemDisplayDurationMs: Long = 100
+        var itemChanges = 0
+        // number of items will fall from 4 to 2, but 4 item transitions should happen without a
         // crash
-        val minSuccessfulSlideChanges = 4
+        val minSuccessfulItemChanges = 4
         rule.setContent {
-            var slideCount by remember { mutableStateOf(4) }
+            var itemCount by remember { mutableStateOf(4) }
             LaunchedEffect(Unit) {
-                while (slideCount >= 2) {
-                    delay(slideDisplayDurationMs)
-                    slideCount--
+                while (itemCount >= 2) {
+                    delay(itemDisplayDurationMs)
+                    itemCount--
                 }
             }
             SampleCarousel(
-                slideCount = slideCount,
-                timeToDisplaySlideMillis = slideDisplayDurationMs
+                itemCount = itemCount,
+                timeToDisplayItemMillis = itemDisplayDurationMs
             ) { index ->
-                if (index >= slideCount) {
-                    // slideIndex requested should not be greater than slideCount. User could be
+                if (index >= itemCount) {
+                    // itemIndex requested should not be greater than itemCount. User could be
                     // using a data-structure that could throw an IndexOutOfBoundsException.
-                    // This can happen when the slideCount changes during the transition between
-                    // slides.
-                    throw Exception("Index is larger, index=$index, slideCount=$slideCount")
+                    // This can happen when the itemCount changes during the transition between
+                    // items.
+                    throw Exception("Index is larger, index=$index, itemCount=$itemCount")
                 }
-                slideChanges++
+                itemChanges++
             }
         }
 
-        rule.waitUntil(timeoutMillis = 5000) { slideChanges > minSuccessfulSlideChanges }
+        rule.waitUntil(timeoutMillis = 5000) { itemChanges > minSuccessfulItemChanges }
     }
 }
 
@@ -802,8 +802,8 @@
 @Composable
 private fun SampleCarousel(
     carouselState: CarouselState = remember { CarouselState() },
-    slideCount: Int = 3,
-    timeToDisplaySlideMillis: Long = delayBetweenSlides,
+    itemCount: Int = 3,
+    timeToDisplayItemMillis: Long = delayBetweenItems,
     content: @Composable CarouselScope.(index: Int) -> Unit
 ) {
     Carousel(
@@ -813,16 +813,16 @@
             .height(200.dp)
             .testTag("pager"),
         carouselState = carouselState,
-        slideCount = slideCount,
-        autoScrollDurationMillis = timeToDisplaySlideMillis,
+        itemCount = itemCount,
+        autoScrollDurationMillis = timeToDisplayItemMillis,
         carouselIndicator = {
             CarouselDefaults.IndicatorRow(
                 modifier = Modifier
                     .align(Alignment.BottomEnd)
                     .padding(16.dp)
                     .testTag("indicator"),
-                activeSlideIndex = carouselState.activeSlideIndex,
-                slideCount = slideCount
+                activeItemIndex = carouselState.activeItemIndex,
+                itemCount = itemCount
             )
         },
         content = { content(it) },
@@ -831,16 +831,16 @@
 
 @OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
 @Composable
-private fun CarouselScope.SampleCarouselSlide(
+private fun CarouselScope.SampleCarouselItem(
     index: Int,
     modifier: Modifier = Modifier,
-    contentTransformForward: ContentTransform =
-        CarouselSlideDefaults.contentTransformForward,
+    contentTransformStartToEnd: ContentTransform =
+        CarouselItemDefaults.contentTransformStartToEnd,
     content: (@Composable () -> Unit) = { SampleButton("Play $index") },
 ) {
-    CarouselSlide(
+    CarouselItem(
         modifier = modifier,
-        contentTransformForward = contentTransformForward,
+        contentTransformStartToEnd = contentTransformStartToEnd,
         background = {
             Box(
                 modifier = Modifier
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/GoldenCommon.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/GoldenCommon.kt
new file mode 100644
index 0000000..49b8130
--- /dev/null
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/GoldenCommon.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.material3
+
+internal const val TV_GOLDEN_MATERIAL3 = "tv/compose/material3"
\ No newline at end of file
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/MaterialThemeCommon.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/MaterialThemeCommon.kt
new file mode 100644
index 0000000..9267ce5
--- /dev/null
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/MaterialThemeCommon.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.material3
+
+import androidx.compose.runtime.Composable
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+@Composable
+fun LightMaterialTheme(content: @Composable () -> Unit) {
+    MaterialTheme(lightColorScheme()) {
+        content()
+    }
+}
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+@Composable
+fun DarkMaterialTheme(content: @Composable () -> Unit) {
+    MaterialTheme(darkColorScheme()) {
+        content()
+    }
+}
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/ModalNavigationDrawerTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/ModalNavigationDrawerTest.kt
index d0f0fda..f221066 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/ModalNavigationDrawerTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/ModalNavigationDrawerTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.tv.material3
 
+import androidx.compose.foundation.background
 import androidx.compose.foundation.border
 import androidx.compose.foundation.focusable
 import androidx.compose.foundation.layout.Box
@@ -25,11 +26,15 @@
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.text.BasicText
 import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.input.key.Key
@@ -41,6 +46,7 @@
 import androidx.compose.ui.test.SemanticsNodeInteractionCollection
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertIsEqualTo
+import androidx.compose.ui.test.assertIsFocused
 import androidx.compose.ui.test.assertWidthIsEqualTo
 import androidx.compose.ui.test.getUnclippedBoundsInRoot
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -125,7 +131,9 @@
             val navigationDrawerValue = remember { DrawerState(DrawerValue.Closed) }
             Row {
                 ModalNavigationDrawer(
-                    modifier = Modifier.focusRequester(drawerFocusRequester).focusable(false),
+                    modifier = Modifier
+                        .focusRequester(drawerFocusRequester)
+                        .focusable(false),
                     drawerState = navigationDrawerValue,
                     drawerContent = {
                         BasicText(text = if (it == DrawerValue.Open) "Opened" else "Closed")
@@ -155,7 +163,17 @@
                 ModalNavigationDrawer(
                     drawerState = navigationDrawerValue,
                     drawerContent = {
-                        BasicText(text = if (it == DrawerValue.Open) "Opened" else "Closed")
+                        var isFocused by remember { mutableStateOf(false) }
+                        BasicText(
+                            text = if (it == DrawerValue.Open) "Opened" else "Closed",
+                            modifier = Modifier
+                                .onFocusChanged { focusState ->
+                                    isFocused = focusState.isFocused
+                                }
+                                .background(if (isFocused) Color.Green else Color.Yellow)
+                                .focusable()
+                                .testTag("drawerItem")
+                        )
                     }) {
                     Box(
                         modifier = Modifier
@@ -172,7 +190,9 @@
         }
         rule.onAllNodesWithText("Closed").assertAnyAreDisplayed()
         rule.onRoot().performKeyInput { pressKey(Key.DirectionLeft) }
+        rule.waitForIdle()
         rule.onAllNodesWithText("Opened").assertAnyAreDisplayed()
+        rule.onNodeWithTag("drawerItem").assertIsFocused()
     }
 
     @Test
@@ -191,7 +211,10 @@
                             Box(Modifier.width(closedDrawerContentWidth * 10))
                         }
                     }
-                ) { Box(Modifier.fillMaxWidth().testTag(contentWidthBoxTag)) }
+                ) { Box(
+                    Modifier
+                        .fillMaxWidth()
+                        .testTag(contentWidthBoxTag)) }
             }
         }
 
@@ -213,7 +236,10 @@
                             Box(Modifier.width(openDrawerContentWidth * 10))
                         }
                     }
-                ) { Box(Modifier.fillMaxWidth().testTag(contentWidthBoxTag)) }
+                ) { Box(
+                    Modifier
+                        .fillMaxWidth()
+                        .testTag(contentWidthBoxTag)) }
             }
         }
 
@@ -229,11 +255,17 @@
                 ModalNavigationDrawer(
                     drawerState = remember { DrawerState(DrawerValue.Closed) },
                     drawerContent = {
-                        Box(Modifier.testTag(drawerContentBoxTag).border(2.dp, Color.Red)) {
+                        Box(
+                            Modifier
+                                .testTag(drawerContentBoxTag)
+                                .border(2.dp, Color.Red)) {
                             BasicText(text = if (it == DrawerValue.Open) "Opened" else "Closed")
                         }
                     }
-                ) { Box(Modifier.fillMaxWidth().testTag(contentWidthBoxTag)) }
+                ) { Box(
+                    Modifier
+                        .fillMaxWidth()
+                        .testTag(contentWidthBoxTag)) }
             }
         }
 
@@ -252,11 +284,17 @@
                 ModalNavigationDrawer(
                     drawerState = drawerState!!,
                     drawerContent = {
-                        Box(Modifier.testTag(drawerContentBoxTag).border(2.dp, Color.Red)) {
+                        Box(
+                            Modifier
+                                .testTag(drawerContentBoxTag)
+                                .border(2.dp, Color.Red)) {
                             BasicText(text = if (it == DrawerValue.Open) "Opened" else "Closed")
                         }
                     }
-                ) { Box(Modifier.fillMaxWidth().testTag(contentWidthBoxTag)) }
+                ) { Box(
+                    Modifier
+                        .fillMaxWidth()
+                        .testTag(contentWidthBoxTag)) }
             }
         }
 
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/NavigationDrawerTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/NavigationDrawerTest.kt
index f414f64..e3c8177 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/NavigationDrawerTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/NavigationDrawerTest.kt
@@ -41,6 +41,7 @@
 import androidx.compose.ui.test.SemanticsNodeInteractionCollection
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertIsEqualTo
+import androidx.compose.ui.test.assertIsFocused
 import androidx.compose.ui.test.assertWidthIsEqualTo
 import androidx.compose.ui.test.getUnclippedBoundsInRoot
 import androidx.compose.ui.test.junit4.StateRestorationTester
@@ -156,7 +157,12 @@
                 NavigationDrawer(
                     drawerState = navigationDrawerValue,
                     drawerContent = {
-                        BasicText(text = if (it == DrawerValue.Open) "Opened" else "Closed")
+                        BasicText(
+                            text = if (it == DrawerValue.Open) "Opened" else "Closed",
+                            modifier = Modifier
+                                .focusable()
+                                .testTag("drawerItem")
+                        )
                     }) {
                     Box(
                         modifier = Modifier
@@ -174,6 +180,7 @@
         rule.onAllNodesWithText("Closed").assertAnyAreDisplayed()
         rule.onRoot().performKeyInput { pressKey(Key.DirectionLeft) }
         rule.onAllNodesWithText("Opened").assertAnyAreDisplayed()
+        rule.onNodeWithTag("drawerItem").assertIsFocused()
     }
 
     @Test
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/SurfaceTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/SurfaceTest.kt
index 392192f..9f25017 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/SurfaceTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/SurfaceTest.kt
@@ -19,7 +19,6 @@
 import android.os.Build
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.background
-import androidx.compose.foundation.gestures.awaitEachGesture
 import androidx.compose.foundation.interaction.FocusInteraction
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -44,8 +43,6 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.input.key.Key
-import androidx.compose.ui.input.pointer.PointerEventPass
-import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.platform.LocalFocusManager
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.Role
@@ -346,41 +343,6 @@
     }
 
     @Test
-    fun clickableSurface_allowsFinalPassChildren() {
-        val hitTested = mutableStateOf(false)
-
-        rule.setContent {
-            Box(Modifier.fillMaxSize()) {
-                Surface(
-                    modifier = Modifier
-                        .fillMaxSize()
-                        .testTag("surface"),
-                    onClick = {}
-                ) {
-                    Box(
-                        Modifier
-                            .fillMaxSize()
-                            .testTag("pressable")
-                            .pointerInput(Unit) {
-                                awaitEachGesture {
-                                    hitTested.value = true
-                                    val event = awaitPointerEvent(PointerEventPass.Final)
-                                    Truth
-                                        .assertThat(event.changes[0].isConsumed)
-                                        .isFalse()
-                                }
-                            }
-                    )
-                }
-            }
-        }
-        rule.onNodeWithTag("surface").performSemanticsAction(SemanticsActions.RequestFocus)
-        rule.onNodeWithTag("pressable", true)
-            .performKeyInput { pressKey(Key.DirectionCenter) }
-        Truth.assertThat(hitTested.value).isTrue()
-    }
-
-    @Test
     fun clickableSurface_reactsToStateChange() {
         val interactionSource = MutableInteractionSource()
         var isPressed by mutableStateOf(false)
@@ -658,42 +620,6 @@
     }
 
     @Test
-    fun toggleableSurface_allowsFinalPassChildren() {
-        val hitTested = mutableStateOf(false)
-
-        rule.setContent {
-            Box(Modifier.fillMaxSize()) {
-                Surface(
-                    checked = false,
-                    modifier = Modifier
-                        .fillMaxSize()
-                        .testTag("surface"),
-                    onCheckedChange = {}
-                ) {
-                    Box(
-                        Modifier
-                            .fillMaxSize()
-                            .testTag("pressable")
-                            .pointerInput(Unit) {
-                                awaitEachGesture {
-                                    hitTested.value = true
-                                    val event = awaitPointerEvent(PointerEventPass.Final)
-                                    Truth
-                                        .assertThat(event.changes[0].isConsumed)
-                                        .isFalse()
-                                }
-                            }
-                    )
-                }
-            }
-        }
-        rule.onNodeWithTag("surface").performSemanticsAction(SemanticsActions.RequestFocus)
-        rule.onNodeWithTag("pressable", true)
-            .performKeyInput { pressKey(Key.DirectionCenter) }
-        Truth.assertThat(hitTested.value).isTrue()
-    }
-
-    @Test
     fun toggleableSurface_reactsToStateChange() {
         val interactionSource = MutableInteractionSource()
         var isPressed by mutableStateOf(false)
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/TabRowTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/TabRowTest.kt
index fd811f2..198e493 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/TabRowTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/TabRowTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.tv.material3
 
+import android.os.Build
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
 import androidx.compose.foundation.focusable
@@ -34,6 +35,7 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
@@ -42,20 +44,52 @@
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertIsFocused
+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.DpRect
 import androidx.compose.ui.unit.dp
+import androidx.test.filters.SdkSuppress
 import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.screenshot.AndroidXScreenshotTestRule
 import org.junit.Rule
 import org.junit.Test
 
 class TabRowTest {
-
     @get:Rule
     val rule = createComposeRule()
 
+    @get:Rule
+    val screenshotRule = AndroidXScreenshotTestRule(TV_GOLDEN_MATERIAL3)
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun tabRow_pillIndicatorScreenshot() {
+        val tabs = constructTabs(count = 3)
+        val testTag = "TabRowTestTag"
+
+        setContent(
+            tabs = tabs,
+            contentBuilder = {
+                Box {
+                    var selectedTabIndex by remember { mutableStateOf(0) }
+                    TabRowSample(
+                        tabs = tabs,
+                        modifier = Modifier.testTag(testTag),
+                        selectedTabIndex = selectedTabIndex,
+                        onFocus = { selectedTabIndex = it }
+                    )
+                }
+            }
+        )
+
+        rule
+            .onNodeWithTag(testTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "tab_row_pill_indicator_default")
+    }
+
     @Test
     fun tabRow_shouldNotCrashWithOnly1Tab() {
         val tabs = constructTabs(count = 1)
@@ -233,6 +267,7 @@
 private fun TabRowSample(
     tabs: List<String>,
     selectedTabIndex: Int,
+    modifier: Modifier = Modifier,
     onFocus: (index: Int) -> Unit = {},
     onClick: (index: Int) -> Unit = onFocus,
     buildTab: @Composable ((index: Int, tab: String) -> Unit) = @Composable { index, tab ->
@@ -272,6 +307,7 @@
         if (indicator != null) {
             TabRow(
                 selectedTabIndex = selectedTabIndex,
+                modifier = modifier,
                 indicator = indicator,
                 separator = { Spacer(modifier = Modifier.width(12.dp)) },
             ) {
@@ -280,6 +316,7 @@
         } else {
             TabRow(
                 selectedTabIndex = selectedTabIndex,
+                modifier = modifier,
                 separator = { Spacer(modifier = Modifier.width(12.dp)) },
             ) {
                 tabs.forEachIndexed { index, tab -> buildTab(index, tab) }
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Button.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Button.kt
new file mode 100644
index 0000000..cf59b79
--- /dev/null
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Button.kt
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.material3
+
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.defaultMinSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.Dp
+import androidx.tv.material3.tokens.Elevation
+
+/**
+ * Material Design filled button for TV.
+ *
+ * Filled buttons are for high emphasis (important, final actions that complete a flow).
+ *
+ * Choose the best button for an action based on the amount of emphasis it needs. The more important
+ * an action is, the higher emphasis its button should be.
+ *
+ * - See [Button] for high emphasis (important, final actions that complete a flow).
+ * - See [OutlinedButton] for a medium-emphasis button with a border.
+ *
+ * The default text style for internal [Text] components will be set to [Typography.labelLarge].
+ *
+ * @param onClick called when this button is clicked
+ * @param modifier the [Modifier] to be applied to this button
+ * @param enabled controls the enabled state of this button. When `false`, this component will not
+ * respond to user input, and it will appear visually disabled and disabled to accessibility
+ * services.
+ * @param scale Defines size of the Button relative to its original size.
+ * @param glow Shadow to be shown behind the Button.
+ * @param shape Defines the Button's shape.
+ * @param colors Color to be used for background and content of the Button
+ * @param tonalElevation tonal elevation used to apply a color shift to the button to give the it
+ * higher emphasis
+ * @param border Defines a border around the Button.
+ * @param contentPadding the spacing values to apply internally between the container and the
+ * content
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this button. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this button in different states.
+ * @param content the content of the button
+ */
+@ExperimentalTvMaterial3Api
+@NonRestartableComposable
+@Composable
+fun Button(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    scale: ButtonScale = ButtonDefaults.scale(),
+    glow: ButtonGlow = ButtonDefaults.glow(),
+    shape: ButtonShape = ButtonDefaults.shape(),
+    colors: ButtonColors = ButtonDefaults.colors(),
+    tonalElevation: Dp = Elevation.Level0,
+    border: ButtonBorder = ButtonDefaults.border(),
+    contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    content: @Composable RowScope.() -> Unit
+) {
+    ButtonImpl(
+        onClick = onClick,
+        modifier = modifier,
+        enabled = enabled,
+        scale = scale,
+        glow = glow,
+        shape = shape,
+        colors = colors,
+        tonalElevation = tonalElevation,
+        border = border,
+        contentPadding = contentPadding,
+        interactionSource = interactionSource,
+        content = content
+    )
+}
+
+/**
+ * Material Design outlined button for TV.
+ *
+ * Outlined buttons are medium-emphasis buttons. They contain actions that are important, but are
+ * not the primary action in an app. Outlined buttons pair well with [Button]s to indicate an
+ * alternative, secondary action.
+ *
+ * Choose the best button for an action based on the amount of emphasis it needs. The more important
+ * an action is, the higher emphasis its button should be.
+ *
+ * - See [Button] for high emphasis (important, final actions that complete a flow).
+ * - See [OutlinedButton] for a medium-emphasis button with a border.
+ *
+ * The default text style for internal [Text] components will be set to [Typography.labelLarge].
+ *
+ * @param onClick called when this button is clicked
+ * @param modifier the [Modifier] to be applied to this button
+ * @param enabled controls the enabled state of this button. When `false`, this component will not
+ * respond to user input, and it will appear visually disabled and disabled to accessibility
+ * services.
+ * @param scale Defines size of the Button relative to its original size.
+ * @param glow Shadow to be shown behind the Button.
+ * @param shape Defines the Button's shape.
+ * @param colors Color to be used for background and content of the Button
+ * @param tonalElevation tonal elevation used to apply a color shift to the button to give the it
+ * higher emphasis
+ * @param border Defines a border around the Button.
+ * @param contentPadding the spacing values to apply internally between the container and the
+ * content
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this button. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this button in different states.
+ * @param content the content of the button
+ */
+@ExperimentalTvMaterial3Api
+@NonRestartableComposable
+@Composable
+fun OutlinedButton(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    scale: ButtonScale = OutlinedButtonDefaults.scale(),
+    glow: ButtonGlow = OutlinedButtonDefaults.glow(),
+    shape: ButtonShape = OutlinedButtonDefaults.shape(),
+    colors: ButtonColors = OutlinedButtonDefaults.colors(),
+    tonalElevation: Dp = Elevation.Level0,
+    border: ButtonBorder = OutlinedButtonDefaults.border(),
+    contentPadding: PaddingValues = OutlinedButtonDefaults.ContentPadding,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    content: @Composable RowScope.() -> Unit
+) {
+    ButtonImpl(
+        onClick = onClick,
+        modifier = modifier,
+        enabled = enabled,
+        scale = scale,
+        glow = glow,
+        shape = shape,
+        colors = colors,
+        tonalElevation = tonalElevation,
+        border = border,
+        contentPadding = contentPadding,
+        interactionSource = interactionSource,
+        content = content
+    )
+}
+
+@ExperimentalTvMaterial3Api
+@Composable
+private fun ButtonImpl(
+    onClick: () -> Unit,
+    modifier: Modifier,
+    enabled: Boolean,
+    scale: ButtonScale,
+    glow: ButtonGlow,
+    shape: ButtonShape,
+    colors: ButtonColors,
+    tonalElevation: Dp,
+    border: ButtonBorder,
+    contentPadding: PaddingValues,
+    interactionSource: MutableInteractionSource,
+    content: @Composable RowScope.() -> Unit
+) {
+    Surface(
+        modifier = modifier.semantics { role = Role.Button },
+        onClick = onClick,
+        enabled = enabled,
+        scale = scale.toClickableSurfaceScale(),
+        glow = glow.toClickableSurfaceGlow(),
+        shape = shape.toClickableSurfaceShape(),
+        color = colors.toClickableSurfaceContainerColor(),
+        contentColor = colors.toClickableSurfaceContentColor(),
+        tonalElevation = tonalElevation,
+        border = border.toClickableSurfaceBorder(),
+        interactionSource = interactionSource
+    ) {
+        ProvideTextStyle(value = MaterialTheme.typography.labelLarge) {
+            Row(
+                modifier = Modifier
+                    .defaultMinSize(
+                        minWidth = BaseButtonDefaults.MinWidth,
+                        minHeight = BaseButtonDefaults.MinHeight
+                    )
+                    .padding(contentPadding),
+                horizontalArrangement = Arrangement.Center,
+                verticalAlignment = Alignment.CenterVertically,
+                content = content
+            )
+        }
+    }
+}
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+private fun ButtonShape.toClickableSurfaceShape(): ClickableSurfaceShape = ClickableSurfaceShape(
+    shape = shape,
+    focusedShape = focusedShape,
+    pressedShape = pressedShape,
+    disabledShape = disabledShape,
+    focusedDisabledShape = focusedDisabledShape
+)
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+private fun ButtonColors.toClickableSurfaceContainerColor(): ClickableSurfaceColor =
+    ClickableSurfaceColor(
+        color = containerColor,
+        focusedColor = focusedContainerColor,
+        pressedColor = pressedContainerColor,
+        disabledColor = disabledContainerColor,
+    )
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+private fun ButtonColors.toClickableSurfaceContentColor(): ClickableSurfaceColor =
+    ClickableSurfaceColor(
+        color = contentColor,
+        focusedColor = focusedContentColor,
+        pressedColor = pressedContentColor,
+        disabledColor = disabledContentColor,
+    )
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+private fun ButtonScale.toClickableSurfaceScale() = ClickableSurfaceScale(
+    scale = scale,
+    focusedScale = focusedScale,
+    pressedScale = pressedScale,
+    disabledScale = disabledScale,
+    focusedDisabledScale = focusedDisabledScale
+)
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+private fun ButtonBorder.toClickableSurfaceBorder() = ClickableSurfaceBorder(
+    border = border,
+    focusedBorder = focusedBorder,
+    pressedBorder = pressedBorder,
+    disabledBorder = disabledBorder,
+    focusedDisabledBorder = focusedDisabledBorder
+)
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+private fun ButtonGlow.toClickableSurfaceGlow() = ClickableSurfaceGlow(
+    glow = glow,
+    focusedGlow = focusedGlow,
+    pressedGlow = pressedGlow
+)
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/ButtonDefaults.kt b/tv/tv-material/src/main/java/androidx/tv/material3/ButtonDefaults.kt
new file mode 100644
index 0000000..e147851
--- /dev/null
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/ButtonDefaults.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.tv.material3
+
+import androidx.annotation.FloatRange
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.unit.dp
+
+internal object BaseButtonDefaults {
+    val MinWidth = 58.dp
+    val MinHeight = 40.dp
+}
+
+@ExperimentalTvMaterial3Api
+object ButtonDefaults {
+    private val ContainerShape = CircleShape
+    private val ButtonHorizontalPadding = 16.dp
+    private val ButtonVerticalPadding = 10.dp
+    private val ButtonWithIconHorizontalStartPadding = 12.dp
+
+    val ContentPadding = PaddingValues(
+        start = ButtonHorizontalPadding,
+        top = ButtonVerticalPadding,
+        end = ButtonHorizontalPadding,
+        bottom = ButtonVerticalPadding
+    )
+
+    val ButtonWithIconContentPadding = PaddingValues(
+        start = ButtonWithIconHorizontalStartPadding,
+        top = ButtonVerticalPadding,
+        end = ButtonHorizontalPadding,
+        bottom = ButtonVerticalPadding
+    )
+
+    /** The default size of the icon when used inside any button. */
+    val IconSize = 18.dp
+
+    /**
+     * The default size of the spacing between an icon and a text when they used inside any button.
+     */
+    val IconSpacing = 8.dp
+
+    /**
+     * Creates a [ButtonShape] that represents the default container shapes used in a FilledButton.
+     *
+     * @param shape the shape used when the Button is enabled, and has no other [Interaction]s.
+     * @param focusedShape the shape used when the Button is enabled and focused.
+     * @param pressedShape the shape used when the Button is enabled pressed.
+     * @param disabledShape the shape used when the Button is not enabled.
+     * @param focusedDisabledShape the shape used when the Button is not enabled and focused.
+     */
+    fun shape(
+        shape: Shape = ContainerShape,
+        focusedShape: Shape = shape,
+        pressedShape: Shape = shape,
+        disabledShape: Shape = shape,
+        focusedDisabledShape: Shape = disabledShape
+    ) = ButtonShape(
+        shape = shape,
+        focusedShape = focusedShape,
+        pressedShape = pressedShape,
+        disabledShape = disabledShape,
+        focusedDisabledShape = focusedDisabledShape
+    )
+
+    /**
+     * Creates a [ButtonColors] that represents the default colors used in a FilledButton.
+     *
+     * @param containerColor the container color of this Button when enabled
+     * @param contentColor the content color of this Button when enabled
+     * @param focusedContainerColor the container color of this Button when enabled and focused
+     * @param focusedContentColor the content color of this Button when enabled and focused
+     * @param pressedContainerColor the container color of this Button when enabled and pressed
+     * @param pressedContentColor the content color of this Button when enabled and pressed
+     * @param disabledContainerColor the container color of this Button when not enabled
+     * @param disabledContentColor the content color of this Button when not enabled
+     */
+    @ReadOnlyComposable
+    @Composable
+    fun colors(
+        containerColor: Color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.8f),
+        contentColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f),
+        focusedContainerColor: Color = MaterialTheme.colorScheme.onSurface,
+        focusedContentColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
+        pressedContainerColor: Color = focusedContainerColor,
+        pressedContentColor: Color = focusedContentColor,
+        disabledContainerColor: Color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.4f),
+        disabledContentColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.4f),
+    ) = ButtonColors(
+        containerColor = containerColor,
+        contentColor = contentColor,
+        focusedContainerColor = focusedContainerColor,
+        focusedContentColor = focusedContentColor,
+        pressedContainerColor = pressedContainerColor,
+        pressedContentColor = pressedContentColor,
+        disabledContainerColor = disabledContainerColor,
+        disabledContentColor = disabledContentColor,
+    )
+
+    /**
+     * Creates a [ButtonScale] that represents the default scales used in a FilledButton.
+     * scales are used to modify the size of a composable in different [Interaction]
+     * states e.g. 1f (original) in default state, 1.2f (scaled up) in focused state,
+     * 0.8f (scaled down) in pressed state, etc.
+     *
+     * @param scale the scale to be used for this Button when enabled
+     * @param focusedScale the scale to be used for this Button when focused
+     * @param pressedScale the scale to be used for this Button when pressed
+     * @param disabledScale the scale to be used for this Button when disabled
+     * @param focusedDisabledScale the scale to be used for this Button when disabled and
+     * focused
+     */
+    fun scale(
+        @FloatRange(from = 0.0) scale: Float = 1f,
+        @FloatRange(from = 0.0) focusedScale: Float = 1.1f,
+        @FloatRange(from = 0.0) pressedScale: Float = scale,
+        @FloatRange(from = 0.0) disabledScale: Float = scale,
+        @FloatRange(from = 0.0) focusedDisabledScale: Float = disabledScale
+    ) = ButtonScale(
+        scale = scale,
+        focusedScale = focusedScale,
+        pressedScale = pressedScale,
+        disabledScale = disabledScale,
+        focusedDisabledScale = focusedDisabledScale
+    )
+
+    /**
+     * Creates a [ButtonBorder] that represents the default [Border]s applied on a
+     * FilledButton in different [Interaction] states.
+     *
+     * @param border the [Border] to be used for this Button when enabled
+     * @param focusedBorder the [Border] to be used for this Button when focused
+     * @param pressedBorder the [Border] to be used for this Button when pressed
+     * @param disabledBorder the [Border] to be used for this Button when disabled
+     * @param focusedDisabledBorder the [Border] to be used for this Button when disabled and
+     * focused
+     */
+    @ReadOnlyComposable
+    @Composable
+    fun border(
+        border: Border = Border.None,
+        focusedBorder: Border = border,
+        pressedBorder: Border = focusedBorder,
+        disabledBorder: Border = border,
+        focusedDisabledBorder: Border = Border(
+            border = BorderStroke(
+                width = 1.5.dp,
+                color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.2f)
+            ),
+            shape = ContainerShape
+        )
+    ) = ButtonBorder(
+        border = border,
+        focusedBorder = focusedBorder,
+        pressedBorder = pressedBorder,
+        disabledBorder = disabledBorder,
+        focusedDisabledBorder = focusedDisabledBorder
+    )
+
+    /**
+     * Creates a [ButtonGlow] that represents the default [Glow]s used in a FilledButton.
+     *
+     * @param glow the Glow behind this Button when enabled
+     * @param focusedGlow the Glow behind this Button when focused
+     * @param pressedGlow the Glow behind this Button when pressed
+     */
+    fun glow(
+        glow: Glow = Glow.None,
+        focusedGlow: Glow = glow,
+        pressedGlow: Glow = glow
+    ) = ButtonGlow(
+        glow = glow,
+        focusedGlow = focusedGlow,
+        pressedGlow = pressedGlow
+    )
+}
+
+@ExperimentalTvMaterial3Api
+object OutlinedButtonDefaults {
+    private val ContainerShape = CircleShape
+    private val ButtonHorizontalPadding = 16.dp
+    private val ButtonVerticalPadding = 10.dp
+    private val ButtonWithIconHorizontalStartPadding = 12.dp
+
+    val ContentPadding = PaddingValues(
+        start = ButtonHorizontalPadding,
+        top = ButtonVerticalPadding,
+        end = ButtonHorizontalPadding,
+        bottom = ButtonVerticalPadding
+    )
+
+    /** The default size of the icon when used inside any button. */
+    val IconSize = 18.dp
+
+    /**
+     * The default size of the spacing between an icon and a text when they used inside any button.
+     */
+    val IconSpacing = 8.dp
+
+    /** The default content padding used by [OutlinedButton] that contains an [Icon]. */
+    val ButtonWithIconContentPadding = PaddingValues(
+        start = ButtonWithIconHorizontalStartPadding,
+        top = ButtonVerticalPadding,
+        end = ButtonHorizontalPadding,
+        bottom = ButtonVerticalPadding
+    )
+
+    /**
+     * Creates a [ButtonShape] that represents the default container shapes used in an
+     * OutlinedButton.
+     *
+     * @param shape the shape used when the Button is enabled, and has no other [Interaction]s.
+     * @param focusedShape the shape used when the Button is enabled and focused.
+     * @param pressedShape the shape used when the Button is enabled pressed.
+     * @param disabledShape the shape used when the Button is not enabled.
+     * @param focusedDisabledShape the shape used when the Button is not enabled and focused.
+     */
+    fun shape(
+        shape: Shape = ContainerShape,
+        focusedShape: Shape = shape,
+        pressedShape: Shape = shape,
+        disabledShape: Shape = shape,
+        focusedDisabledShape: Shape = disabledShape
+    ) = ButtonShape(
+        shape = shape,
+        focusedShape = focusedShape,
+        pressedShape = pressedShape,
+        disabledShape = disabledShape,
+        focusedDisabledShape = focusedDisabledShape
+    )
+
+    /**
+     * Creates a [ButtonColors] that represents the default colors used in a OutlinedButton.
+     *
+     * @param containerColor the container color of this Button when enabled
+     * @param contentColor the content color of this Button when enabled
+     * @param focusedContainerColor the container color of this Button when enabled and focused
+     * @param focusedContentColor the content color of this Button when enabled and focused
+     * @param pressedContainerColor the container color of this Button when enabled and pressed
+     * @param pressedContentColor the content color of this Button when enabled and pressed
+     * @param disabledContainerColor the container color of this Button when not enabled
+     * @param disabledContentColor the content color of this Button when not enabled
+     */
+    @ReadOnlyComposable
+    @Composable
+    fun colors(
+        containerColor: Color = Color.Transparent,
+        contentColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f),
+        focusedContainerColor: Color = MaterialTheme.colorScheme.onSurface,
+        focusedContentColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
+        pressedContainerColor: Color = focusedContainerColor,
+        pressedContentColor: Color = focusedContentColor,
+        disabledContainerColor: Color = containerColor,
+        disabledContentColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.4f),
+    ) = ButtonColors(
+        containerColor = containerColor,
+        contentColor = contentColor,
+        focusedContainerColor = focusedContainerColor,
+        focusedContentColor = focusedContentColor,
+        pressedContainerColor = pressedContainerColor,
+        pressedContentColor = pressedContentColor,
+        disabledContainerColor = disabledContainerColor,
+        disabledContentColor = disabledContentColor,
+    )
+
+    /**
+     * Creates a [ButtonScale] that represents the default scales used in an OutlinedButton.
+     * scales are used to modify the size of a composable in different [Interaction]
+     * states e.g. 1f (original) in default state, 1.2f (scaled up) in focused state,
+     * 0.8f (scaled down) in pressed state, etc.
+     *
+     * @param scale the scale to be used for this Button when enabled
+     * @param focusedScale the scale to be used for this Button when focused
+     * @param pressedScale the scale to be used for this Button when pressed
+     * @param disabledScale the scale to be used for this Button when disabled
+     * @param focusedDisabledScale the scale to be used for this Button when disabled and
+     * focused
+     */
+    fun scale(
+        @FloatRange(from = 0.0) scale: Float = 1f,
+        @FloatRange(from = 0.0) focusedScale: Float = 1.1f,
+        @FloatRange(from = 0.0) pressedScale: Float = scale,
+        @FloatRange(from = 0.0) disabledScale: Float = scale,
+        @FloatRange(from = 0.0) focusedDisabledScale: Float = disabledScale
+    ) = ButtonScale(
+        scale = scale,
+        focusedScale = focusedScale,
+        pressedScale = pressedScale,
+        disabledScale = disabledScale,
+        focusedDisabledScale = focusedDisabledScale
+    )
+
+    /**
+     * Creates a [ButtonBorder] that represents the default [Border]s applied on an
+     * OutlinedButton in different [Interaction] states.
+     *
+     * @param border the [Border] to be used for this Button when enabled
+     * @param focusedBorder the [Border] to be used for this Button when focused
+     * @param pressedBorder the [Border] to be used for this Button when pressed
+     * @param disabledBorder the [Border] to be used for this Button when disabled
+     * @param focusedDisabledBorder the [Border] to be used for this Button when disabled and
+     * focused
+     */
+    @ReadOnlyComposable
+    @Composable
+    fun border(
+        border: Border = Border(
+            border = BorderStroke(
+                width = 1.5.dp,
+                color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.4f)
+            ),
+            shape = ContainerShape
+        ),
+        focusedBorder: Border = Border(
+            border = BorderStroke(
+                width = 1.65.dp,
+                color = MaterialTheme.colorScheme.onSurfaceVariant
+            ),
+            shape = ContainerShape
+        ),
+        pressedBorder: Border = Border(
+            border = BorderStroke(
+                width = 1.5.dp,
+                color = MaterialTheme.colorScheme.onSurfaceVariant
+            ),
+            shape = ContainerShape
+        ),
+        disabledBorder: Border = Border(
+            border = BorderStroke(
+                width = 1.5.dp,
+                color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.2f)
+            ),
+            shape = ContainerShape
+        ),
+        focusedDisabledBorder: Border = disabledBorder
+    ) = ButtonBorder(
+        border = border,
+        focusedBorder = focusedBorder,
+        pressedBorder = pressedBorder,
+        disabledBorder = disabledBorder,
+        focusedDisabledBorder = focusedDisabledBorder
+    )
+
+    /**
+     * Creates a [ButtonGlow] that represents the default [Glow]s used in an OutlinedButton.
+     *
+     * @param glow the Glow behind this Button when enabled
+     * @param focusedGlow the Glow behind this Button when focused
+     * @param pressedGlow the Glow behind this Button when pressed
+     */
+    fun glow(
+        glow: Glow = Glow.None,
+        focusedGlow: Glow = glow,
+        pressedGlow: Glow = glow
+    ) = ButtonGlow(
+        glow = glow,
+        focusedGlow = focusedGlow,
+        pressedGlow = pressedGlow
+    )
+}
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/ButtonStyles.kt b/tv/tv-material/src/main/java/androidx/tv/material3/ButtonStyles.kt
new file mode 100644
index 0000000..89fe0a7
--- /dev/null
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/ButtonStyles.kt
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.material3
+
+import androidx.annotation.FloatRange
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.runtime.Immutable
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+
+/**
+ * Defines [Shape] for all TV [Interaction] states of Button.
+ */
+@ExperimentalTvMaterial3Api
+@Immutable
+class ButtonShape internal constructor(
+    internal val shape: Shape,
+    internal val focusedShape: Shape,
+    internal val pressedShape: Shape,
+    internal val disabledShape: Shape,
+    internal val focusedDisabledShape: Shape
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as ButtonShape
+
+        if (shape != other.shape) return false
+        if (focusedShape != other.focusedShape) return false
+        if (pressedShape != other.pressedShape) return false
+        if (disabledShape != other.disabledShape) return false
+        if (focusedDisabledShape != other.focusedDisabledShape) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = shape.hashCode()
+        result = 31 * result + focusedShape.hashCode()
+        result = 31 * result + pressedShape.hashCode()
+        result = 31 * result + disabledShape.hashCode()
+        result = 31 * result + focusedDisabledShape.hashCode()
+
+        return result
+    }
+
+    override fun toString(): String {
+        return "ButtonShape(shape=$shape, focusedShape=$focusedShape, pressedShape=$pressedShape," +
+            " disabledShape=$disabledShape, focusedDisabledShape=$focusedDisabledShape)"
+    }
+}
+
+/**
+ * Defines [Color]s for all TV [Interaction] states of Button.
+ */
+@ExperimentalTvMaterial3Api
+@Immutable
+class ButtonColors internal constructor(
+    internal val containerColor: Color,
+    internal val contentColor: Color,
+    internal val focusedContainerColor: Color,
+    internal val focusedContentColor: Color,
+    internal val pressedContainerColor: Color,
+    internal val pressedContentColor: Color,
+    internal val disabledContainerColor: Color,
+    internal val disabledContentColor: Color,
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as ButtonColors
+
+        if (containerColor != other.containerColor) return false
+        if (contentColor != other.contentColor) return false
+        if (focusedContainerColor != other.focusedContainerColor) return false
+        if (focusedContentColor != other.focusedContentColor) return false
+        if (pressedContainerColor != other.pressedContainerColor) return false
+        if (pressedContentColor != other.pressedContentColor) return false
+        if (disabledContainerColor != other.disabledContainerColor) return false
+        if (disabledContentColor != other.disabledContentColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = containerColor.hashCode()
+        result = 31 * result + contentColor.hashCode()
+        result = 31 * result + focusedContainerColor.hashCode()
+        result = 31 * result + focusedContentColor.hashCode()
+        result = 31 * result + pressedContainerColor.hashCode()
+        result = 31 * result + pressedContentColor.hashCode()
+        result = 31 * result + disabledContainerColor.hashCode()
+        result = 31 * result + disabledContentColor.hashCode()
+        return result
+    }
+
+    override fun toString(): String {
+        return "ButtonColors(containerColor=$containerColor, contentColor=$contentColor, " +
+            "focusedContainerColor=$focusedContainerColor, " +
+            "focusedContentColor=$focusedContentColor, " +
+            "pressedContainerColor=$pressedContainerColor, " +
+            "pressedContentColor=$pressedContentColor, " +
+            "disabledContainerColor=$disabledContainerColor, " +
+            "disabledContentColor=$disabledContentColor)"
+    }
+}
+
+/**
+ * Defines the scale for all TV [Interaction] states of Button.
+ */
+@ExperimentalTvMaterial3Api
+@Immutable
+class ButtonScale internal constructor(
+    @FloatRange(from = 0.0) internal val scale: Float,
+    @FloatRange(from = 0.0) internal val focusedScale: Float,
+    @FloatRange(from = 0.0) internal val pressedScale: Float,
+    @FloatRange(from = 0.0) internal val disabledScale: Float,
+    @FloatRange(from = 0.0) internal val focusedDisabledScale: Float
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as ButtonScale
+
+        if (scale != other.scale) return false
+        if (focusedScale != other.focusedScale) return false
+        if (pressedScale != other.pressedScale) return false
+        if (disabledScale != other.disabledScale) return false
+        if (focusedDisabledScale != other.focusedDisabledScale) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = scale.hashCode()
+        result = 31 * result + focusedScale.hashCode()
+        result = 31 * result + pressedScale.hashCode()
+        result = 31 * result + disabledScale.hashCode()
+        result = 31 * result + focusedDisabledScale.hashCode()
+
+        return result
+    }
+
+    override fun toString(): String {
+        return "ButtonScale(scale=$scale, focusedScale=$focusedScale, pressedScale=$pressedScale," +
+            " disabledScale=$disabledScale, focusedDisabledScale=$focusedDisabledScale)"
+    }
+}
+
+/**
+ * Defines [Border] for all TV [Interaction] states of Button.
+ */
+@ExperimentalTvMaterial3Api
+@Immutable
+class ButtonBorder internal constructor(
+    internal val border: Border,
+    internal val focusedBorder: Border,
+    internal val pressedBorder: Border,
+    internal val disabledBorder: Border,
+    internal val focusedDisabledBorder: Border
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as ButtonBorder
+
+        if (border != other.border) return false
+        if (focusedBorder != other.focusedBorder) return false
+        if (pressedBorder != other.pressedBorder) return false
+        if (disabledBorder != other.disabledBorder) return false
+        if (focusedDisabledBorder != other.focusedDisabledBorder) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = border.hashCode()
+        result = 31 * result + focusedBorder.hashCode()
+        result = 31 * result + pressedBorder.hashCode()
+        result = 31 * result + disabledBorder.hashCode()
+        result = 31 * result + focusedDisabledBorder.hashCode()
+
+        return result
+    }
+
+    override fun toString(): String {
+        return "ButtonBorder(border=$border, focusedBorder=$focusedBorder," +
+            "pressedBorder=$pressedBorder, disabledBorder=$disabledBorder, " +
+            "focusedDisabledBorder=$focusedDisabledBorder)"
+    }
+}
+
+/**
+ * Defines [Glow] for all TV [Interaction] states of Button.
+ */
+@ExperimentalTvMaterial3Api
+@Immutable
+class ButtonGlow internal constructor(
+    internal val glow: Glow,
+    internal val focusedGlow: Glow,
+    internal val pressedGlow: Glow
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as ButtonGlow
+
+        if (glow != other.glow) return false
+        if (focusedGlow != other.focusedGlow) return false
+        if (pressedGlow != other.pressedGlow) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = glow.hashCode()
+        result = 31 * result + focusedGlow.hashCode()
+        result = 31 * result + pressedGlow.hashCode()
+
+        return result
+    }
+
+    override fun toString(): String {
+        return "ButtonGlow(glow=$glow, focusedGlow=$focusedGlow, pressedGlow=$pressedGlow)"
+    }
+}
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Card.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Card.kt
new file mode 100644
index 0000000..1a5d6c7
--- /dev/null
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Card.kt
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.material3
+
+import androidx.annotation.FloatRange
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.unit.dp
+
+/**
+ * Cards contain content and actions that relate information about a subject.
+ *
+ * This Card handles click events, calling its [onClick] lambda.
+ *
+ * @param onClick called when this card is clicked
+ * @param modifier the [Modifier] to be applied to this card
+ * @param shape [CardShape] defines the shape of this card's container in different interaction
+ * states. See [CardDefaults.shape].
+ * @param colors [CardColors] defines the background & content colors used in this card for
+ * different interaction states. See [CardDefaults.colors].
+ * @param scale [CardScale] defines size of the card relative to its original size for different
+ * interaction states. See [CardDefaults.scale].
+ * @param border [CardBorder] defines a border around the card for different interaction states.
+ * See [CardDefaults.border].
+ * @param glow [CardGlow] defines a shadow to be shown behind the card for different interaction
+ * states. See [CardDefaults.glow].
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this card. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this card in different states.
+ * @param content defines the [Composable] content inside the Card.
+ */
+@ExperimentalTvMaterial3Api
+@Composable
+fun Card(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    shape: CardShape = CardDefaults.shape(),
+    colors: CardColors = CardDefaults.colors(),
+    scale: CardScale = CardDefaults.scale(),
+    border: CardBorder = CardDefaults.border(),
+    glow: CardGlow = CardDefaults.glow(),
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    content: @Composable ColumnScope.() -> Unit
+) {
+    Surface(
+        onClick = onClick,
+        modifier = modifier,
+        shape = shape.toClickableSurfaceShape(),
+        color = colors.toClickableSurfaceContainerColor(),
+        contentColor = colors.toClickableSurfaceContentColor(),
+        scale = scale.toClickableSurfaceScale(),
+        border = border.toClickableSurfaceBorder(),
+        glow = glow.toClickableSurfaceGlow(),
+        interactionSource = interactionSource,
+    ) {
+        Column(content = content)
+    }
+}
+
+/**
+ * Contains the default values used by all card types.
+ */
+@ExperimentalTvMaterial3Api
+object CardDefaults {
+    /**
+    * The default [Shape] used by Cards.
+    */
+    private val ContainerShape = RoundedCornerShape(8.dp)
+
+    /**
+     * Creates a [CardShape] that represents the default container shapes used in a Card.
+     *
+     * @param shape the default shape used when the Card has no other [Interaction]s.
+     * @param focusedShape the shape used when the Card is focused.
+     * @param pressedShape the shape used when the Card is pressed.
+     */
+    fun shape(
+        shape: Shape = ContainerShape,
+        focusedShape: Shape = shape,
+        pressedShape: Shape = shape
+    ) = CardShape(
+        shape = shape,
+        focusedShape = focusedShape,
+        pressedShape = pressedShape
+    )
+
+    /**
+     * Creates [CardColors] that represents the default container & content colors used in a Card.
+     *
+     * @param containerColor the default container color of this Card.
+     * @param contentColor the default content color of this Card.
+     * @param focusedContainerColor the container color of this Card when focused.
+     * @param focusedContentColor the content color of this Card when focused.
+     * @param pressedContainerColor the container color of this Card when pressed.
+     * @param pressedContentColor the content color of this Card when pressed.
+     */
+    @ReadOnlyComposable
+    @Composable
+    fun colors(
+        containerColor: Color = MaterialTheme.colorScheme.surface,
+        contentColor: Color = contentColorFor(containerColor),
+        focusedContainerColor: Color = containerColor,
+        focusedContentColor: Color = contentColorFor(focusedContainerColor),
+        pressedContainerColor: Color = focusedContainerColor,
+        pressedContentColor: Color = contentColorFor(pressedContainerColor)
+    ) = CardColors(
+        containerColor = containerColor,
+        contentColor = contentColor,
+        focusedContainerColor = focusedContainerColor,
+        focusedContentColor = focusedContentColor,
+        pressedContainerColor = pressedContainerColor,
+        pressedContentColor = pressedContentColor
+    )
+
+    /**
+     * Creates a [CardScale] that represents the default scales used in a Card.
+     * Scales are used to modify the size of a composable in different [Interaction] states
+     * e.g. 1f (original) in default state, 1.1f (scaled up) in focused state, 0.8f (scaled down)
+     * in pressed state, etc.
+     *
+     * @param scale the default scale to be used for this Card.
+     * @param focusedScale the scale to be used for this Card when focused.
+     * @param pressedScale the scale to be used for this Card when pressed.
+     */
+    fun scale(
+        @FloatRange(from = 0.0) scale: Float = 1f,
+        @FloatRange(from = 0.0) focusedScale: Float = 1.1f,
+        @FloatRange(from = 0.0) pressedScale: Float = scale
+    ) = CardScale(
+        scale = scale,
+        focusedScale = focusedScale,
+        pressedScale = pressedScale
+    )
+
+    /**
+     * Creates a [CardBorder] that represents the border [Border]s applied on a Card in
+     * different [Interaction] states.
+     *
+     * @param border the default [Border] to be used for this Card.
+     * @param focusedBorder the [Border] to be used for this Card when focused.
+     * @param pressedBorder the [Border] to be used for this Card when pressed.
+     */
+    @ReadOnlyComposable
+    @Composable
+    fun border(
+        border: Border = Border.None,
+        focusedBorder: Border = Border(
+            border = BorderStroke(
+                width = 3.dp,
+                color = MaterialTheme.colorScheme.border
+            ),
+            shape = ContainerShape
+        ),
+        pressedBorder: Border = focusedBorder
+    ) = CardBorder(
+        border = border,
+        focusedBorder = focusedBorder,
+        pressedBorder = pressedBorder
+    )
+
+    /**
+     * Creates a [CardGlow] that represents the default [Glow]s used in a card.
+     *
+     * @param glow the default [Glow] behind this Card.
+     * @param focusedGlow the [Glow] behind this Card when focused.
+     * @param pressedGlow the [Glow] behind this Card when pressed.
+     */
+    fun glow(
+        glow: Glow = Glow.None,
+        focusedGlow: Glow = glow,
+        pressedGlow: Glow = glow
+    ) = CardGlow(
+        glow = glow,
+        focusedGlow = focusedGlow,
+        pressedGlow = pressedGlow
+    )
+}
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+private fun CardColors.toClickableSurfaceContainerColor() =
+    ClickableSurfaceColor(
+        color = containerColor,
+        focusedColor = focusedContainerColor,
+        pressedColor = pressedContainerColor,
+        disabledColor = containerColor
+    )
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+private fun CardColors.toClickableSurfaceContentColor() =
+    ClickableSurfaceColor(
+        color = contentColor,
+        focusedColor = focusedContentColor,
+        pressedColor = pressedContentColor,
+        disabledColor = contentColor
+    )
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+private fun CardShape.toClickableSurfaceShape() =
+    ClickableSurfaceShape(
+        shape = shape,
+        focusedShape = focusedShape,
+        pressedShape = pressedShape,
+        disabledShape = shape,
+        focusedDisabledShape = shape
+    )
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+private fun CardScale.toClickableSurfaceScale() =
+    ClickableSurfaceScale(
+        scale = scale,
+        focusedScale = focusedScale,
+        pressedScale = pressedScale,
+        disabledScale = scale,
+        focusedDisabledScale = scale
+    )
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+private fun CardBorder.toClickableSurfaceBorder() =
+    ClickableSurfaceBorder(
+        border = border,
+        focusedBorder = focusedBorder,
+        pressedBorder = pressedBorder,
+        disabledBorder = border,
+        focusedDisabledBorder = border
+    )
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+private fun CardGlow.toClickableSurfaceGlow() =
+    ClickableSurfaceGlow(
+        glow = glow,
+        focusedGlow = focusedGlow,
+        pressedGlow = pressedGlow
+    )
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/CardStyles.kt b/tv/tv-material/src/main/java/androidx/tv/material3/CardStyles.kt
new file mode 100644
index 0000000..6df86c2
--- /dev/null
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/CardStyles.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.tv.material3
+
+import androidx.annotation.FloatRange
+import androidx.compose.runtime.Immutable
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+
+/**
+ * Represents the [Color] of Card in different interaction states.
+ */
+@ExperimentalTvMaterial3Api
+@Immutable
+class CardColors internal constructor(
+    internal val containerColor: Color,
+    internal val contentColor: Color,
+    internal val focusedContainerColor: Color,
+    internal val focusedContentColor: Color,
+    internal val pressedContainerColor: Color,
+    internal val pressedContentColor: Color
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as CardColors
+
+        if (containerColor != other.containerColor) return false
+        if (contentColor != other.contentColor) return false
+        if (focusedContainerColor != other.focusedContainerColor) return false
+        if (focusedContentColor != other.focusedContentColor) return false
+        if (pressedContainerColor != other.pressedContainerColor) return false
+        if (pressedContentColor != other.pressedContentColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = containerColor.hashCode()
+        result = 31 * result + contentColor.hashCode()
+        result = 31 * result + focusedContainerColor.hashCode()
+        result = 31 * result + focusedContentColor.hashCode()
+        result = 31 * result + pressedContainerColor.hashCode()
+        result = 31 * result + pressedContentColor.hashCode()
+
+        return result
+    }
+
+    override fun toString(): String {
+        return "CardColors(" +
+            "containerColor=$containerColor, " +
+            "contentColor=$contentColor, " +
+            "focusedContainerColor=$focusedContainerColor, " +
+            "focusedContentColor=$focusedContentColor, " +
+            "pressedContainerColor=$pressedContainerColor, " +
+            "pressedContentColor=$pressedContentColor)"
+    }
+}
+
+/**
+ * Represents the [Shape] of Card in different interaction states.
+ */
+@ExperimentalTvMaterial3Api
+@Immutable
+class CardShape internal constructor(
+    internal val shape: Shape,
+    internal val focusedShape: Shape,
+    internal val pressedShape: Shape
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as CardShape
+
+        if (shape != other.shape) return false
+        if (focusedShape != other.focusedShape) return false
+        if (pressedShape != other.pressedShape) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = shape.hashCode()
+        result = 31 * result + focusedShape.hashCode()
+        result = 31 * result + pressedShape.hashCode()
+
+        return result
+    }
+
+    override fun toString(): String {
+        return "CardShape(shape=$shape, focusedShape=$focusedShape, pressedShape=$pressedShape)"
+    }
+}
+
+/**
+ * Represents the scaleFactor of Card in different interaction states.
+ * Note: This scaleFactor must always be a non-negative float.
+ */
+@ExperimentalTvMaterial3Api
+@Immutable
+class CardScale internal constructor(
+    @FloatRange(from = 0.0) internal val scale: Float,
+    @FloatRange(from = 0.0) internal val focusedScale: Float,
+    @FloatRange(from = 0.0) internal val pressedScale: Float
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as CardScale
+
+        if (scale != other.scale) return false
+        if (focusedScale != other.focusedScale) return false
+        if (pressedScale != other.pressedScale) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = scale.hashCode()
+        result = 31 * result + focusedScale.hashCode()
+        result = 31 * result + pressedScale.hashCode()
+
+        return result
+    }
+
+    override fun toString(): String {
+        return "CardScale(scale=$scale, focusedScale=$focusedScale, pressedScale=$pressedScale)"
+    }
+}
+
+/**
+ * Represents the [Border] of Card in different interaction states.
+ */
+@ExperimentalTvMaterial3Api
+@Immutable
+class CardBorder internal constructor(
+    internal val border: Border,
+    internal val focusedBorder: Border,
+    internal val pressedBorder: Border
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as CardBorder
+
+        if (border != other.border) return false
+        if (focusedBorder != other.focusedBorder) return false
+        if (pressedBorder != other.pressedBorder) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = border.hashCode()
+        result = 31 * result + focusedBorder.hashCode()
+        result = 31 * result + pressedBorder.hashCode()
+
+        return result
+    }
+
+    override fun toString(): String {
+        return "CardBorder(border=$border, focusedBorder=$focusedBorder, " +
+            "pressedBorder=$pressedBorder)"
+    }
+}
+
+/**
+ * Represents the [Glow] of Card in different interaction states.
+ */
+@ExperimentalTvMaterial3Api
+@Immutable
+class CardGlow internal constructor(
+    internal val glow: Glow,
+    internal val focusedGlow: Glow,
+    internal val pressedGlow: Glow
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as CardGlow
+
+        if (glow != other.glow) return false
+        if (focusedGlow != other.focusedGlow) return false
+        if (pressedGlow != other.pressedGlow) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = glow.hashCode()
+        result = 31 * result + focusedGlow.hashCode()
+        result = 31 * result + pressedGlow.hashCode()
+
+        return result
+    }
+
+    override fun toString(): String {
+        return "CardGlow(glow=$glow, focusedGlow=$focusedGlow, pressedGlow=$pressedGlow)"
+    }
+}
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
index 442c810..8f5661b 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
@@ -78,34 +78,33 @@
  * @sample androidx.tv.samples.CarouselIndicatorWithRectangleShape
  *
  * @param modifier Modifier applied to the Carousel.
- * @param slideCount total number of slides present in the carousel.
+ * @param itemCount total number of items present in the carousel.
  * @param carouselState state associated with this carousel.
- * @param autoScrollDurationMillis duration for which slide should be visible before moving to
- * the next slide.
- * @param contentTransformForward animation transform applied when we are moving forward in the
- * carousel while scrolling
- * @param contentTransformBackward animation transform applied when we are moving backward in the
- * carousel while scrolling
- * in the next slide
- * @param carouselIndicator indicator showing the position of the current slide among all slides.
- * @param content defines the slides for a given index.
+ * @param autoScrollDurationMillis duration for which item should be visible before moving to
+ * the next item.
+ * @param contentTransformStartToEnd animation transform applied when we are moving from start to
+ * end in the carousel while scrolling to the next item
+ * @param contentTransformEndToStart animation transform applied when we are moving from end to
+ * start in the carousel while scrolling to the next item
+ * @param carouselIndicator indicator showing the position of the current item among all items.
+ * @param content defines the items for a given index.
  */
 @Suppress("IllegalExperimentalApiUsage")
-@OptIn(ExperimentalComposeUiApi::class, ExperimentalAnimationApi::class)
+@OptIn(ExperimentalComposeUiApi::class)
 @ExperimentalTvMaterial3Api
 @Composable
 fun Carousel(
-    slideCount: Int,
+    itemCount: Int,
     modifier: Modifier = Modifier,
     carouselState: CarouselState = remember { CarouselState() },
-    autoScrollDurationMillis: Long = CarouselDefaults.TimeToDisplaySlideMillis,
-    contentTransformForward: ContentTransform = CarouselDefaults.contentTransform,
-    contentTransformBackward: ContentTransform = CarouselDefaults.contentTransform,
+    autoScrollDurationMillis: Long = CarouselDefaults.TimeToDisplayItemMillis,
+    contentTransformStartToEnd: ContentTransform = CarouselDefaults.contentTransform,
+    contentTransformEndToStart: ContentTransform = CarouselDefaults.contentTransform,
     carouselIndicator:
     @Composable BoxScope.() -> Unit = {
         CarouselDefaults.IndicatorRow(
-            slideCount = slideCount,
-            activeSlideIndex = carouselState.activeSlideIndex,
+            itemCount = itemCount,
+            activeItemIndex = carouselState.activeItemIndex,
             modifier = Modifier
                 .align(Alignment.BottomEnd)
                 .padding(16.dp),
@@ -113,7 +112,7 @@
     },
     content: @Composable CarouselScope.(index: Int) -> Unit
 ) {
-    CarouselStateUpdater(carouselState, slideCount)
+    CarouselStateUpdater(carouselState, itemCount)
     var focusState: FocusState? by remember { mutableStateOf(null) }
     val focusManager = LocalFocusManager.current
     val isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr
@@ -122,7 +121,7 @@
 
     AutoScrollSideEffect(
         autoScrollDurationMillis = autoScrollDurationMillis,
-        slideCount = slideCount,
+        itemCount = itemCount,
         carouselState = carouselState,
         doAutoScroll = shouldPerformAutoScroll(focusState),
         onAutoScrollChange = { isAutoScrollActive = it })
@@ -142,21 +141,21 @@
             carouselState = carouselState,
             outerBoxFocusRequester = carouselOuterBoxFocusRequester,
             focusManager = focusManager,
-            slideCount = slideCount,
+            itemCount = itemCount,
             isLtr = isLtr,
         )
         .focusable()
     ) {
         AnimatedContent(
-            targetState = carouselState.activeSlideIndex,
+            targetState = carouselState.activeItemIndex,
             transitionSpec = {
                 if (carouselState.isMovingBackward) {
-                    contentTransformBackward
+                    contentTransformEndToStart
                 } else {
-                    contentTransformForward
+                    contentTransformStartToEnd
                 }
             }
-        ) { activeSlideIndex ->
+        ) { activeItemIndex ->
             LaunchedEffect(Unit) {
                 this@AnimatedContent.onAnimationCompletion {
                     // Outer box is focused
@@ -166,13 +165,13 @@
                     }
                 }
             }
-            // it is possible for the slideCount to have changed during the transition.
-            // This can cause the slideIndex to be greater than or equal to slideCount and cause
-            // IndexOutOfBoundsException. Guarding against this by checking against slideCount
+            // it is possible for the itemCount to have changed during the transition.
+            // This can cause the itemIndex to be greater than or equal to itemCount and cause
+            // IndexOutOfBoundsException. Guarding against this by checking against itemCount
             // before invoking.
-            if (slideCount > 0) {
+            if (itemCount > 0) {
                 CarouselScope(carouselState = carouselState)
-                    .content(if (activeSlideIndex < slideCount) activeSlideIndex else 0)
+                    .content(if (activeItemIndex < itemCount) activeItemIndex else 0)
             }
         }
         this.carouselIndicator()
@@ -197,13 +196,13 @@
 @Composable
 private fun AutoScrollSideEffect(
     autoScrollDurationMillis: Long,
-    slideCount: Int,
+    itemCount: Int,
     carouselState: CarouselState,
     doAutoScroll: Boolean,
     onAutoScrollChange: (isAutoScrollActive: Boolean) -> Unit = {},
 ) {
-    // Needed to ensure that the code within LaunchedEffect receives updates to the slideCount.
-    val updatedSlideCount by rememberUpdatedState(newValue = slideCount)
+    // Needed to ensure that the code within LaunchedEffect receives updates to the itemCount.
+    val updatedItemCount by rememberUpdatedState(newValue = itemCount)
     if (doAutoScroll) {
         LaunchedEffect(carouselState) {
             while (true) {
@@ -213,7 +212,7 @@
                     snapshotFlow { carouselState.activePauseHandlesCount }
                         .first { pauseHandleCount -> pauseHandleCount == 0 }
                 }
-                carouselState.moveToNextSlide(updatedSlideCount)
+                carouselState.moveToNextItem(updatedItemCount)
             }
         }
     }
@@ -226,7 +225,7 @@
     carouselState: CarouselState,
     outerBoxFocusRequester: FocusRequester,
     focusManager: FocusManager,
-    slideCount: Int,
+    itemCount: Int,
     isLtr: Boolean
 ): Modifier = onKeyEvent {
     // Ignore KeyUp action type
@@ -234,20 +233,20 @@
         return@onKeyEvent KeyEventPropagation.ContinuePropagation
     }
 
-    val showPreviousSlideAndGetKeyEventPropagation = {
-        if (carouselState.isFirstSlide()) {
+    val showPreviousItemAndGetKeyEventPropagation = {
+        if (carouselState.isFirstItem()) {
             KeyEventPropagation.ContinuePropagation
         } else {
-            carouselState.moveToPreviousSlide(slideCount)
+            carouselState.moveToPreviousItem(itemCount)
             outerBoxFocusRequester.requestFocus()
             KeyEventPropagation.StopPropagation
         }
     }
-    val showNextSlideAndGetKeyEventPropagation = {
-        if (carouselState.isLastSlide(slideCount)) {
+    val showNextItemAndGetKeyEventPropagation = {
+        if (carouselState.isLastItem(itemCount)) {
             KeyEventPropagation.ContinuePropagation
         } else {
-            carouselState.moveToNextSlide(slideCount)
+            carouselState.moveToNextItem(itemCount)
             outerBoxFocusRequester.requestFocus()
             KeyEventPropagation.StopPropagation
         }
@@ -266,9 +265,9 @@
             }
 
             if (isLtr) {
-                showPreviousSlideAndGetKeyEventPropagation()
+                showPreviousItemAndGetKeyEventPropagation()
             } else {
-                showNextSlideAndGetKeyEventPropagation()
+                showNextItemAndGetKeyEventPropagation()
             }
         }
 
@@ -279,9 +278,9 @@
             }
 
             if (isLtr) {
-                showNextSlideAndGetKeyEventPropagation()
+                showNextItemAndGetKeyEventPropagation()
             } else {
-                showPreviousSlideAndGetKeyEventPropagation()
+                showPreviousItemAndGetKeyEventPropagation()
             }
         }
 
@@ -291,31 +290,31 @@
 
 @OptIn(ExperimentalTvMaterial3Api::class)
 @Composable
-private fun CarouselStateUpdater(carouselState: CarouselState, slideCount: Int) {
-    LaunchedEffect(carouselState, slideCount) {
-        if (slideCount != 0) {
-            carouselState.activeSlideIndex = floorMod(carouselState.activeSlideIndex, slideCount)
+private fun CarouselStateUpdater(carouselState: CarouselState, itemCount: Int) {
+    LaunchedEffect(carouselState, itemCount) {
+        if (itemCount != 0) {
+            carouselState.activeItemIndex = floorMod(carouselState.activeItemIndex, itemCount)
         }
     }
 }
 
 /**
- * State of the Carousel which allows the user to specify the first slide that is shown when the
+ * State of the Carousel which allows the user to specify the first item that is shown when the
  * Carousel is instantiated in the constructor.
  *
  * It also provides the user with support to pause and resume the auto-scroll behaviour of the
  * Carousel.
- * @param initialActiveSlideIndex the index of the first active slide
+ * @param initialActiveItemIndex the index of the first active item
  */
 @Stable
 @ExperimentalTvMaterial3Api
-class CarouselState(initialActiveSlideIndex: Int = 0) {
+class CarouselState(initialActiveItemIndex: Int = 0) {
     internal var activePauseHandlesCount by mutableStateOf(0)
 
     /**
-     * The index of the slide that is currently displayed by the carousel
+     * The index of the item that is currently displayed by the carousel
      */
-    var activeSlideIndex by mutableStateOf(initialActiveSlideIndex)
+    var activeItemIndex by mutableStateOf(initialActiveItemIndex)
         internal set
 
     /**
@@ -327,38 +326,38 @@
 
     /**
      * Pauses the auto-scrolling behaviour of Carousel.
-     * The pause request is ignored if [slideIndex] is not the current slide that is visible.
+     * The pause request is ignored if [itemIndex] is not the current item that is visible.
      * Returns a [ScrollPauseHandle] that can be used to resume
      */
-    fun pauseAutoScroll(slideIndex: Int): ScrollPauseHandle {
-        if (this.activeSlideIndex != slideIndex) {
+    fun pauseAutoScroll(itemIndex: Int): ScrollPauseHandle {
+        if (this.activeItemIndex != itemIndex) {
             return NoOpScrollPauseHandle
         }
         return ScrollPauseHandleImpl(this)
     }
 
-    internal fun isFirstSlide() = activeSlideIndex == 0
+    internal fun isFirstItem() = activeItemIndex == 0
 
-    internal fun isLastSlide(slideCount: Int) = activeSlideIndex == slideCount - 1
+    internal fun isLastItem(itemCount: Int) = activeItemIndex == itemCount - 1
 
-    internal fun moveToPreviousSlide(slideCount: Int) {
-        // No slides available for carousel
-        if (slideCount == 0) return
+    internal fun moveToPreviousItem(itemCount: Int) {
+        // No items available for carousel
+        if (itemCount == 0) return
 
         isMovingBackward = true
 
-        // Go to previous slide
-        activeSlideIndex = floorMod(activeSlideIndex - 1, slideCount)
+        // Go to previous item
+        activeItemIndex = floorMod(activeItemIndex - 1, itemCount)
     }
 
-    internal fun moveToNextSlide(slideCount: Int) {
-        // No slides available for carousel
-        if (slideCount == 0) return
+    internal fun moveToNextItem(itemCount: Int) {
+        // No items available for carousel
+        if (itemCount == 0) return
 
         isMovingBackward = false
 
-        // Go to next slide
-        activeSlideIndex = floorMod(activeSlideIndex + 1, slideCount)
+        // Go to next item
+        activeItemIndex = floorMod(activeItemIndex + 1, itemCount)
     }
 }
 
@@ -403,34 +402,33 @@
 @ExperimentalTvMaterial3Api
 object CarouselDefaults {
     /**
-     * Default time for which the slide is visible to the user.
+     * Default time for which the item is visible to the user.
      */
-    const val TimeToDisplaySlideMillis: Long = 5000
+    const val TimeToDisplayItemMillis: Long = 5000
 
     /**
      * Transition applied when bringing it into view and removing it from the view
      */
-    @OptIn(ExperimentalAnimationApi::class)
     val contentTransform: ContentTransform
     @Composable get() =
         fadeIn(animationSpec = tween(100))
             .with(fadeOut(animationSpec = tween(100)))
 
     /**
-     * An indicator showing the position of the current active slide among the slides of the
+     * An indicator showing the position of the current active item among the items of the
      * carousel.
      *
-     * @param slideCount total number of slides in the carousel
-     * @param activeSlideIndex the current active slide index
+     * @param itemCount total number of items in the carousel
+     * @param activeItemIndex the current active item index
      * @param modifier Modifier applied to the indicators' container
      * @param spacing spacing between the indicator dots
-     * @param indicator indicator dot representing each slide in the carousel
+     * @param indicator indicator dot representing each item in the carousel
      */
     @ExperimentalTvMaterial3Api
     @Composable
     fun IndicatorRow(
-        slideCount: Int,
-        activeSlideIndex: Int,
+        itemCount: Int,
+        activeItemIndex: Int,
         modifier: Modifier = Modifier,
         spacing: Dp = 8.dp,
         indicator: @Composable (isActive: Boolean) -> Unit = { isActive ->
@@ -451,8 +449,8 @@
             verticalAlignment = Alignment.CenterVertically,
             modifier = modifier,
         ) {
-            repeat(slideCount) {
-                val isActive = it == activeSlideIndex
+            repeat(itemCount) {
+                val isActive = it == activeItemIndex
                 indicator(isActive = isActive)
             }
         }
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/CarouselItem.kt b/tv/tv-material/src/main/java/androidx/tv/material3/CarouselItem.kt
new file mode 100644
index 0000000..14650f7
--- /dev/null
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/CarouselItem.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.material3
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.ContentTransform
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.slideInHorizontally
+import androidx.compose.animation.slideOutHorizontally
+import androidx.compose.animation.with
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusDirection
+import androidx.compose.ui.focus.FocusState
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.input.key.onKeyEvent
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.tv.material3.KeyEventPropagation.ContinuePropagation
+
+/**
+ * This composable is intended for use in Carousel.
+ * A composable that has
+ * - a [background] layer that is rendered as soon as the composable is visible.
+ * - a [content] layer that is rendered on top of the [background]
+ *
+ * @param background composable defining the background of the item
+ * @param itemIndex current active item index of the carousel
+ * @param modifier modifier applied to the CarouselItem
+ * @param contentTransform content transform to be applied to the content of the item when
+ * scrolling
+ * @param content composable defining the content displayed on top of the background
+ */
+@Suppress("IllegalExperimentalApiUsage")
+@OptIn(ExperimentalAnimationApi::class, ExperimentalComposeUiApi::class)
+@ExperimentalTvMaterial3Api
+@Composable
+internal fun CarouselItem(
+    itemIndex: Int,
+    modifier: Modifier = Modifier,
+    background: @Composable () -> Unit = {},
+    contentTransform: ContentTransform =
+        CarouselItemDefaults.contentTransformStartToEnd,
+    content: @Composable () -> Unit,
+) {
+    var containerBoxFocusState: FocusState? by remember { mutableStateOf(null) }
+    val focusManager = LocalFocusManager.current
+    var exitFocus by remember { mutableStateOf(false) }
+
+    var isVisible by remember { mutableStateOf(false) }
+
+    DisposableEffect(itemIndex) {
+        isVisible = true
+        onDispose { isVisible = false }
+    }
+
+    // This box holds the focus until the overlay animation completes
+    Box(
+        modifier = modifier
+            .onKeyEvent {
+                exitFocus = it.isBackPress() && it.isTypeKeyDown()
+                ContinuePropagation
+            }
+            .onFocusChanged {
+                containerBoxFocusState = it
+                if (it.isFocused && exitFocus) {
+                    focusManager.moveFocus(FocusDirection.Exit)
+                    exitFocus = false
+                }
+            }
+            .focusable()
+    ) {
+        background()
+
+        AnimatedVisibility(
+            visible = isVisible,
+            enter = contentTransform.targetContentEnter,
+            exit = contentTransform.initialContentExit,
+        ) {
+            LaunchedEffect(transition.isRunning, containerBoxFocusState?.isFocused) {
+                if (!transition.isRunning && containerBoxFocusState?.isFocused == true) {
+                    focusManager.moveFocus(FocusDirection.Enter)
+                }
+            }
+            content.invoke()
+        }
+    }
+}
+
+@ExperimentalTvMaterial3Api
+object CarouselItemDefaults {
+    /**
+     * Transform the content from right to left
+     */
+    // Keeping this as public so that users can access it directly without the isLTR helper
+    val contentTransformRightToLeft: ContentTransform
+        @Composable get() =
+            slideInHorizontally { it * 4 }
+                .with(slideOutHorizontally { it * 4 })
+
+    /**
+     * Transform the content from left to right
+     */
+    // Keeping this as public so that users can access it directly without the isLTR helper
+    val contentTransformLeftToRight: ContentTransform
+        @Composable get() =
+            slideInHorizontally()
+                .with(slideOutHorizontally())
+
+    /**
+     * Content transform applied when moving forward taking isLTR into account
+     */
+    val contentTransformStartToEnd
+        @Composable get() =
+            if (isLtr())
+                contentTransformRightToLeft
+            else
+                contentTransformLeftToRight
+
+    /**
+     * Content transform applied when moving backward taking isLTR into account
+     */
+    val contentTransformEndToStart
+        @Composable get() =
+            if (isLtr())
+                contentTransformLeftToRight
+            else
+                contentTransformRightToLeft
+}
+
+@Composable
+private fun isLtr() = LocalLayoutDirection.current == LayoutDirection.Ltr
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/CarouselScope.kt b/tv/tv-material/src/main/java/androidx/tv/material3/CarouselScope.kt
index 838e1e6..665ceb9 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/CarouselScope.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/CarouselScope.kt
@@ -17,51 +17,48 @@
 package androidx.tv.material3
 
 import androidx.compose.animation.ContentTransform
-import androidx.compose.animation.ExperimentalAnimationApi
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 
 /**
- * CarouselScope provides a [CarouselScope.CarouselSlide] function which you can use to
- * provide the slide's animation, background and the inner content.
+ * CarouselScope provides a [CarouselScope.CarouselItem] function which you can use to
+ * provide the carousel item's animation, background and the inner content.
  */
 @ExperimentalTvMaterial3Api
 class CarouselScope @OptIn(ExperimentalTvMaterial3Api::class)
 internal constructor(private val carouselState: CarouselState) {
     /**
-     * [CarouselScope.CarouselSlide] can be used to define a slide's animation, background, and
-     * content. Using this is optional and you can choose to define your own CarouselSlide from
+     * [CarouselScope.CarouselItem] can be used to define a item's animation, background, and
+     * content. Using this is optional and you can choose to define your own CarouselItem from
      * scratch
      *
-     * @param modifier modifier applied to the CarouselSlide
-     * @param background composable defining the background of the slide
-     * @param contentTransformForward content transform to be applied to the content of the slide
+     * @param modifier modifier applied to the CarouselItem
+     * @param background composable defining the background of the item
+     * @param contentTransformStartToEnd content transform to be applied to the content of the item
      * when scrolling forward in the carousel
-     * @param contentTransformBackward content transform to be applied to the content of the slide
+     * @param contentTransformEndToStart content transform to be applied to the content of the item
      * when scrolling backward in the carousel
      * @param content composable defining the content displayed on top of the background
      */
     @Composable
-    @Suppress("IllegalExperimentalApiUsage")
-    @OptIn(ExperimentalAnimationApi::class)
     @ExperimentalTvMaterial3Api
-    fun CarouselSlide(
+    fun CarouselItem(
         modifier: Modifier = Modifier,
         background: @Composable () -> Unit = {},
-        contentTransformForward: ContentTransform =
-            CarouselSlideDefaults.contentTransformForward,
-        contentTransformBackward: ContentTransform =
-            CarouselSlideDefaults.contentTransformBackward,
+        contentTransformStartToEnd: ContentTransform =
+            CarouselItemDefaults.contentTransformStartToEnd,
+        contentTransformEndToStart: ContentTransform =
+            CarouselItemDefaults.contentTransformEndToStart,
         content: @Composable () -> Unit
     ) {
-        CarouselSlide(
+        CarouselItem(
             background = background,
-            slideIndex = carouselState.activeSlideIndex,
+            itemIndex = carouselState.activeItemIndex,
             contentTransform =
             if (carouselState.isMovingBackward)
-                contentTransformBackward
+                contentTransformEndToStart
             else
-                contentTransformForward,
+                contentTransformStartToEnd,
             modifier = modifier,
             content = content,
         )
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/CarouselSlide.kt b/tv/tv-material/src/main/java/androidx/tv/material3/CarouselSlide.kt
deleted file mode 100644
index 162f167..0000000
--- a/tv/tv-material/src/main/java/androidx/tv/material3/CarouselSlide.kt
+++ /dev/null
@@ -1,164 +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.tv.material3
-
-import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.ContentTransform
-import androidx.compose.animation.ExperimentalAnimationApi
-import androidx.compose.animation.slideInHorizontally
-import androidx.compose.animation.slideOutHorizontally
-import androidx.compose.animation.with
-import androidx.compose.foundation.focusable
-import androidx.compose.foundation.layout.Box
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.ExperimentalComposeUiApi
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusDirection
-import androidx.compose.ui.focus.FocusState
-import androidx.compose.ui.focus.onFocusChanged
-import androidx.compose.ui.input.key.onKeyEvent
-import androidx.compose.ui.platform.LocalFocusManager
-import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.tv.material3.KeyEventPropagation.ContinuePropagation
-
-/**
- * This composable is intended for use in Carousel.
- * A composable that has
- * - a [background] layer that is rendered as soon as the composable is visible.
- * - a [content] layer that is rendered on top of the [background]
- *
- * @param background composable defining the background of the slide
- * @param slideIndex current active slide index of the carousel
- * @param modifier modifier applied to the CarouselSlide
- * @param contentTransform content transform to be applied to the content of the slide when
- * scrolling
- * @param content composable defining the content displayed on top of the background
- */
-@Suppress("IllegalExperimentalApiUsage")
-@OptIn(ExperimentalAnimationApi::class, ExperimentalComposeUiApi::class)
-@ExperimentalTvMaterial3Api
-@Composable
-internal fun CarouselSlide(
-    slideIndex: Int,
-    modifier: Modifier = Modifier,
-    background: @Composable () -> Unit = {},
-    contentTransform: ContentTransform =
-        CarouselSlideDefaults.contentTransformForward,
-    content: @Composable () -> Unit,
-) {
-    var containerBoxFocusState: FocusState? by remember { mutableStateOf(null) }
-    val focusManager = LocalFocusManager.current
-    var exitFocus by remember { mutableStateOf(false) }
-
-    var isVisible by remember { mutableStateOf(false) }
-
-    DisposableEffect(slideIndex) {
-        isVisible = true
-        onDispose { isVisible = false }
-    }
-
-    // This box holds the focus until the overlay animation completes
-    Box(
-        modifier = modifier
-            .onKeyEvent {
-                exitFocus = it.isBackPress() && it.isTypeKeyDown()
-                ContinuePropagation
-            }
-            .onFocusChanged {
-                containerBoxFocusState = it
-                if (it.isFocused && exitFocus) {
-                    focusManager.moveFocus(FocusDirection.Exit)
-                    exitFocus = false
-                }
-            }
-            .focusable()
-    ) {
-        background()
-
-        AnimatedVisibility(
-            visible = isVisible,
-            enter = contentTransform.targetContentEnter,
-            exit = contentTransform.initialContentExit,
-        ) {
-            LaunchedEffect(transition.isRunning, containerBoxFocusState?.isFocused) {
-                if (!transition.isRunning && containerBoxFocusState?.isFocused == true) {
-                    focusManager.moveFocus(FocusDirection.Enter)
-                }
-            }
-            content.invoke()
-        }
-    }
-}
-
-@ExperimentalTvMaterial3Api
-object CarouselSlideDefaults {
-    /**
-     * Transform the content from right to left
-     */
-    // Keeping this as public so that users can access it directly without the isLTR helper
-    @Suppress("IllegalExperimentalApiUsage")
-    @OptIn(ExperimentalAnimationApi::class)
-    val contentTransformRightToLeft: ContentTransform
-        @Composable get() =
-            slideInHorizontally { it * 4 }
-                .with(slideOutHorizontally { it * 4 })
-
-    /**
-     * Transform the content from left to right
-     */
-    // Keeping this as public so that users can access it directly without the isLTR helper
-    @Suppress("IllegalExperimentalApiUsage")
-    @OptIn(ExperimentalAnimationApi::class)
-    val contentTransformLeftToRight: ContentTransform
-        @Composable get() =
-            slideInHorizontally()
-                .with(slideOutHorizontally())
-
-    /**
-     * Content transform applied when moving forward taking isLTR into account
-     */
-    @Suppress("IllegalExperimentalApiUsage")
-    @OptIn(ExperimentalAnimationApi::class)
-    val contentTransformForward
-        @Composable get() =
-            if (isLtr())
-                contentTransformRightToLeft
-            else
-                contentTransformLeftToRight
-
-    /**
-     * Content transform applied when moving backward taking isLTR into account
-     */
-    @Suppress("IllegalExperimentalApiUsage")
-    @OptIn(ExperimentalAnimationApi::class)
-    val contentTransformBackward
-        @Composable get() =
-            if (isLtr())
-                contentTransformLeftToRight
-            else
-                contentTransformRightToLeft
-}
-
-@Composable
-private fun isLtr() = LocalLayoutDirection.current == LayoutDirection.Ltr
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/NavigationDrawer.kt b/tv/tv-material/src/main/java/androidx/tv/material3/NavigationDrawer.kt
index 8cba520..293f84a 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/NavigationDrawer.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/NavigationDrawer.kt
@@ -39,11 +39,13 @@
 import androidx.compose.ui.focus.FocusDirection
 import androidx.compose.ui.focus.FocusManager
 import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.FocusState
 import androidx.compose.ui.focus.focusProperties
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalFocusManager
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.unit.Dp
@@ -70,7 +72,6 @@
  * Drawer-entries can be animated when the drawer moves from Closed to Open state and vice-versa.
  * For, e.g., the entry could show only an icon in the Closed state and slide in text to form
  * (icon + text) when in the Open state.
- * @sample androidx.tv.samples.NavigationRow
  *
  * To limit the width of the drawer in the open or closed state, wrap the content in a box with the
  * required width.
@@ -90,6 +91,7 @@
     content: @Composable () -> Unit
 ) {
     val layoutDirection = LocalLayoutDirection.current
+    val localDensity = LocalDensity.current
     val exitDirection =
         if (layoutDirection == Ltr) FocusDirection.Right else FocusDirection.Left
     val drawerFocusRequester = remember { FocusRequester() }
@@ -107,7 +109,9 @@
                 if (closedDrawerWidth.value == null &&
                     drawerState.currentValue == DrawerValue.Closed
                 ) {
-                    closedDrawerWidth.value = it.width.dp
+                    with(localDensity) {
+                        closedDrawerWidth.value = it.width.toDp()
+                    }
                 }
             }
 
@@ -153,7 +157,6 @@
  * Drawer-entries can be animated when the drawer moves from Closed to Open state and vice-versa.
  * For, e.g., the entry could show only an icon in the Closed state and slide in text to form
  * (icon + text) when in the Open state.
- * @sample androidx.tv.samples.NavigationRow
  *
  * To limit the width of the drawer in the open or closed state, wrap the content in a box with the
  * required width.
@@ -276,17 +279,21 @@
     // indicates that the drawer has been set to its initial state and has grabbed focus if
     // necessary. Controls whether focus is used to decide the state of the drawer going forward.
     var initializationComplete: Boolean = remember { false }
+    val focusManager = LocalFocusManager.current
+    var focusState by remember { mutableStateOf<FocusState?>(null) }
+
+    val isDrawerOpen = drawerState.currentValue == DrawerValue.Open
+    val isDrawerClosed = drawerState.currentValue == DrawerValue.Closed
 
     val focusRequester = remember { FocusRequester() }
     LaunchedEffect(key1 = drawerState.currentValue) {
-        if (drawerState.currentValue == DrawerValue.Open) {
+        if (drawerState.currentValue == DrawerValue.Open && focusState?.hasFocus == false) {
             // used to grab focus if the drawer state is set to Open on start.
             focusRequester.requestFocus()
         }
         initializationComplete = true
     }
 
-    val focusManager = LocalFocusManager.current
     val internalModifier =
         Modifier
             .focusRequester(focusRequester)
@@ -296,14 +303,14 @@
             // size based modifiers.
             .then(modifier)
             .onFocusChanged {
+                focusState = it
                 when {
-                    it.isFocused && drawerState.currentValue == DrawerValue.Closed -> {
+                    it.isFocused && isDrawerClosed -> {
                         drawerState.setValue(DrawerValue.Open)
                         focusManager.moveFocus(FocusDirection.Enter)
                     }
 
-                    !it.hasFocus && drawerState.currentValue == DrawerValue.Open &&
-                        initializationComplete -> {
+                    !it.hasFocus && isDrawerOpen && initializationComplete -> {
                         drawerState.setValue(DrawerValue.Closed)
                     }
                 }
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/TabRow.kt b/tv/tv-material/src/main/java/androidx/tv/material3/TabRow.kt
index a1af7e7..1b49193 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/TabRow.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/TabRow.kt
@@ -170,7 +170,7 @@
   /** Space between tabs in the tab row */
   @Composable
   fun TabSeparator() {
-    Spacer(modifier = Modifier.width(20.dp))
+    Spacer(modifier = Modifier.width(8.dp))
   }
 
   /** Default accent color for the TabRow */
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 a328f50..6e8f5e1 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
@@ -48,7 +48,7 @@
 import java.lang.reflect.Field
 
 @LargeTest
-@SdkSuppress(minSdkVersion = 21)
+@SdkSuppress(minSdkVersion = 30) // TODO(b/273945673): fix test on API 21..30
 @RunWith(Parameterized::class)
 class OnApplyWindowInsetsListenerTest(private val config: TestConfig) {
     data class TestConfig(
@@ -223,7 +223,7 @@
     }
 
     private fun createWindowInsets(): WindowInsetsCompat {
-        val insets = Insets.of(10, 10, 10, 10)
+        val insets = Insets.of(10, 11, 12, 13)
         @Suppress("DEPRECATION")
         val windowInsets = WindowInsetsCompat.Builder().setSystemWindowInsets(insets).build()
         if (Build.VERSION.SDK_INT < 29) {
diff --git a/viewpager2/viewpager2/build.gradle b/viewpager2/viewpager2/build.gradle
index c05941a..be941a2 100644
--- a/viewpager2/viewpager2/build.gradle
+++ b/viewpager2/viewpager2/build.gradle
@@ -26,9 +26,10 @@
     api("androidx.annotation:annotation:1.1.0")
     implementation("androidx.core:core:1.3.2")
     api("androidx.fragment:fragment:1.1.0")
-    api("androidx.recyclerview:recyclerview:1.2.0")
+    api(project(":recyclerview:recyclerview"))
     implementation("androidx.collection:collection:1.1.0")
 
+    androidTestImplementation(libs.multidex)
     androidTestImplementation(libs.testUiautomator)
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
@@ -58,4 +59,7 @@
 
 android {
     namespace "androidx.viewpager2"
+    defaultConfig {
+        multiDexEnabled = true
+    }
 }
diff --git a/viewpager2/viewpager2/src/androidTest/AndroidManifest.xml b/viewpager2/viewpager2/src/androidTest/AndroidManifest.xml
index 9183dc0..70a62cfe 100755
--- a/viewpager2/viewpager2/src/androidTest/AndroidManifest.xml
+++ b/viewpager2/viewpager2/src/androidTest/AndroidManifest.xml
@@ -18,7 +18,9 @@
     xmlns:tools="http://schemas.android.com/tools">
 
     <uses-sdk tools:overrideLibrary="android_libs.ub_uiautomator, androidx.test.uiautomator" />
-    <application android:supportsRtl="true">
+    <application
+        android:name="androidx.multidex.MultiDexApplication"
+        android:supportsRtl="true">
         <activity android:name="androidx.viewpager2.widget.swipe.TestActivity"
                   android:theme="@style/Theme.AppCompat"/>
     </application>
diff --git a/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt
index 1075f45..1ac998c 100644
--- a/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt
@@ -161,19 +161,18 @@
                 field = value
             }
 
-        fun runOnUiThreadSync(f: () -> Unit) {
+        fun <T> runOnUiThreadSync(f: () -> T): T {
             var thrownError: Throwable? = null
+            var result: T? = null
             activityTestRule.runOnUiThread {
                 try {
-                    f()
+                    result = f()
                 } catch (t: Throwable) {
                     thrownError = t
                 }
             }
-            val caughtError = thrownError
-            if (caughtError != null) {
-                throw caughtError
-            }
+            thrownError?.let { throw it }
+            return result!!
         }
 
         val viewPager: ViewPager2 get() = activity.findViewById(R.id.view_pager)
diff --git a/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FragmentTransactionCallbackTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FragmentTransactionCallbackTest.kt
index 5b49e68..7bc0900 100644
--- a/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FragmentTransactionCallbackTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FragmentTransactionCallbackTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.viewpager2.widget
 
+import android.os.Build
 import android.os.Bundle
 import android.view.View
 import androidx.fragment.app.Fragment
@@ -181,30 +182,57 @@
             assertThat(
                 log.consume().filter { !it.contains("onFragmentSaveInstanceState") },
                 equalTo(
-                    listOf(
-                        "Lifecycle:onFragmentPaused(f1)",
-                        "Lifecycle:onFragmentStopped(f0)",
-                        "Lifecycle:onFragmentStopped(f1)",
-                        // "Lifecycle:onFragmentSaveInstanceState(f0)", # unstable ordering
-                        // "Lifecycle:onFragmentSaveInstanceState(f1)", # unstable ordering
-                        "Lifecycle:onFragmentViewDestroyed(f0)",
-                        "Lifecycle:onFragmentDestroyed(f0)",
-                        "Lifecycle:onFragmentDetached(f0)",
-                        "Lifecycle:onFragmentViewDestroyed(f1)",
-                        "Lifecycle:onFragmentDestroyed(f1)",
-                        "Lifecycle:onFragmentDetached(f1)",
-                        "Lifecycle:onFragmentViewCreated(f0)",
-                        "Lifecycle:onFragmentActivityCreated(f0)",
-                        "Lifecycle:onFragmentViewCreated(f1)",
-                        "Lifecycle:onFragmentActivityCreated(f1)",
-                        "Lifecycle:onFragmentStarted(f0)",
-                        "Lifecycle:onFragmentStarted(f1)",
-                        "Adapter:onFragmentMaxLifecyclePreUpdated(f0 at STARTED)",
-                        "Adapter:onFragmentMaxLifecyclePreUpdated(f1 at RESUMED)",
-                        "Adapter:onFragmentMaxLifecycleUpdated(f1 at RESUMED)",
-                        "Adapter:onFragmentMaxLifecycleUpdated(f0 at STARTED)",
-                        "Lifecycle:onFragmentResumed(f1)"
-                    )
+                    when (Build.VERSION.SDK_INT) {
+                        in 1..28 -> listOf(
+                            "Lifecycle:onFragmentPaused(f1)",
+                            "Lifecycle:onFragmentStopped(f0)",
+                            "Lifecycle:onFragmentStopped(f1)",
+                            // "Lifecycle:onFragmentSaveInstanceState(f0)", # unstable ordering
+                            // "Lifecycle:onFragmentSaveInstanceState(f1)", # unstable ordering
+                            "Lifecycle:onFragmentViewDestroyed(f0)",
+                            "Lifecycle:onFragmentDestroyed(f0)",
+                            "Lifecycle:onFragmentDetached(f0)",
+                            "Lifecycle:onFragmentViewDestroyed(f1)",
+                            "Lifecycle:onFragmentDestroyed(f1)",
+                            "Lifecycle:onFragmentDetached(f1)",
+                            "Lifecycle:onFragmentViewCreated(f0)",
+                            "Lifecycle:onFragmentActivityCreated(f0)",
+                            "Lifecycle:onFragmentViewCreated(f1)",
+                            "Lifecycle:onFragmentActivityCreated(f1)",
+                            "Lifecycle:onFragmentStarted(f0)",
+                            "Lifecycle:onFragmentStarted(f1)",
+                            "Adapter:onFragmentMaxLifecyclePreUpdated(f0 at STARTED)",
+                            "Adapter:onFragmentMaxLifecyclePreUpdated(f1 at RESUMED)",
+                            "Adapter:onFragmentMaxLifecycleUpdated(f1 at RESUMED)",
+                            "Adapter:onFragmentMaxLifecycleUpdated(f0 at STARTED)",
+                            "Lifecycle:onFragmentResumed(f1)"
+                        )
+                        // TODO(b/266975014): investigate change in behaviour on API 29+
+                        else -> listOf(
+                            "Lifecycle:onFragmentPaused(f1)",
+                            "Lifecycle:onFragmentStopped(f0)",
+                            "Lifecycle:onFragmentStopped(f1)",
+                            // "Lifecycle:onFragmentSaveInstanceState(f0)", # unstable ordering
+                            // "Lifecycle:onFragmentSaveInstanceState(f1)", # unstable ordering
+                            "Lifecycle:onFragmentViewDestroyed(f0)",
+                            "Lifecycle:onFragmentDestroyed(f0)",
+                            "Lifecycle:onFragmentDetached(f0)",
+                            "Lifecycle:onFragmentViewDestroyed(f1)",
+                            "Lifecycle:onFragmentDestroyed(f1)",
+                            "Lifecycle:onFragmentDetached(f1)",
+                            "Lifecycle:onFragmentViewCreated(f0)",
+                            "Lifecycle:onFragmentActivityCreated(f0)",
+                            "Lifecycle:onFragmentViewCreated(f1)",
+                            "Lifecycle:onFragmentActivityCreated(f1)",
+                            "Lifecycle:onFragmentStarted(f0)",
+                            "Lifecycle:onFragmentStarted(f1)",
+                            "Lifecycle:onFragmentResumed(f1)",
+                            "Adapter:onFragmentMaxLifecyclePreUpdated(f0 at STARTED)",
+                            "Adapter:onFragmentMaxLifecyclePreUpdated(f1 at RESUMED)",
+                            "Adapter:onFragmentMaxLifecycleUpdated(f1 at RESUMED)",
+                            "Adapter:onFragmentMaxLifecycleUpdated(f0 at STARTED)"
+                        )
+                    }
                 )
             )
 
diff --git a/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/HostFragmentBackStackTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/HostFragmentBackStackTest.kt
index 92bed54..2d17809 100644
--- a/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/HostFragmentBackStackTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/HostFragmentBackStackTest.kt
@@ -64,8 +64,8 @@
                 activity.setContentView(container)
             }
 
-            val viewPagerFragment = ViewPagerFragment()
-            val blankFragment = Fragment()
+            val viewPagerFragment = runOnUiThreadSync { ViewPagerFragment() }
+            val blankFragment = runOnUiThreadSync { Fragment() }
 
             fun setActiveFragment(f: Fragment, targetPage: Int? = null) {
                 // set new active fragment
diff --git a/viewpager2/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java b/viewpager2/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java
index a0ec29c..9bfcf04 100644
--- a/viewpager2/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java
+++ b/viewpager2/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java
@@ -183,22 +183,10 @@
         ensureFragment(position);
 
         /** Special case when {@link RecyclerView} decides to keep the {@link container}
-         * attached to the window, but not to the view hierarchy (i.e. parent is null) */
+         * attached to the window, resulting in no {@link `onViewAttachedToWindow} callback later */
         final FrameLayout container = holder.getContainer();
         if (ViewCompat.isAttachedToWindow(container)) {
-            if (container.getParent() != null) {
-                throw new IllegalStateException("Design assumption violated.");
-            }
-            container.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
-                @Override
-                public void onLayoutChange(View v, int left, int top, int right, int bottom,
-                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
-                    if (container.getParent() != null) {
-                        container.removeOnLayoutChangeListener(this);
-                        placeFragmentInViewHolder(holder);
-                    }
-                }
-            });
+            placeFragmentInViewHolder(holder);
         }
 
         gcFragments();
diff --git a/viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/WindowInsetsApplier.java b/viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/WindowInsetsApplier.java
index 2d58541..5527cc0 100644
--- a/viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/WindowInsetsApplier.java
+++ b/viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/WindowInsetsApplier.java
@@ -111,6 +111,7 @@
         return consumeAllInsets(applied);
     }
 
+    @SuppressWarnings("deprecation") // consumeSystemWindowInsets, consumeStableInsets
     private WindowInsetsCompat consumeAllInsets(@NonNull WindowInsetsCompat insets) {
         if (Build.VERSION.SDK_INT >= 21) {
             if (WindowInsetsCompat.CONSUMED.toWindowInsets() != null) {
diff --git a/wear/compose/compose-foundation/api/current.txt b/wear/compose/compose-foundation/api/current.txt
index 384b168..117b84c 100644
--- a/wear/compose/compose-foundation/api/current.txt
+++ b/wear/compose/compose-foundation/api/current.txt
@@ -171,6 +171,30 @@
     property public final androidx.compose.ui.text.font.FontWeight? fontWeight;
   }
 
+  public final class ExpandableItemsDefaults {
+    method @androidx.compose.runtime.Composable public void Chevron(float progress, long color, optional androidx.compose.ui.Modifier modifier, optional float strokeWidth);
+    method public androidx.compose.animation.core.AnimationSpec<java.lang.Float> getCollapseAnimationSpec();
+    method public androidx.compose.animation.core.AnimationSpec<java.lang.Float> getExpandAnimationSpec();
+    property public final androidx.compose.animation.core.AnimationSpec<java.lang.Float> collapseAnimationSpec;
+    property public final androidx.compose.animation.core.AnimationSpec<java.lang.Float> expandAnimationSpec;
+    field public static final androidx.wear.compose.foundation.ExpandableItemsDefaults INSTANCE;
+  }
+
+  public final class ExpandableItemsState {
+    method public float getExpandProgress();
+    method public boolean isExpanded();
+    method public void setExpanded(boolean);
+    method public void toggle();
+    property public final float expandProgress;
+    property public final boolean expanded;
+  }
+
+  public final class ExpandableKt {
+    method public static void expandableItem(androidx.wear.compose.foundation.lazy.ScalingLazyListScope, androidx.wear.compose.foundation.ExpandableItemsState state, optional Object? key, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> content);
+    method public static void expandableItems(androidx.wear.compose.foundation.lazy.ScalingLazyListScope, androidx.wear.compose.foundation.ExpandableItemsState state, int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.BoxScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+    method @androidx.compose.runtime.Composable public static androidx.wear.compose.foundation.ExpandableItemsState rememberExpandableItemsState(optional boolean initiallyExpanded, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> expandAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> collapseAnimationSpec);
+  }
+
 }
 
 package androidx.wear.compose.foundation.lazy {
diff --git a/wear/compose/compose-foundation/api/public_plus_experimental_current.txt b/wear/compose/compose-foundation/api/public_plus_experimental_current.txt
index eda89f7..abaaf5d 100644
--- a/wear/compose/compose-foundation/api/public_plus_experimental_current.txt
+++ b/wear/compose/compose-foundation/api/public_plus_experimental_current.txt
@@ -171,6 +171,30 @@
     property public final androidx.compose.ui.text.font.FontWeight? fontWeight;
   }
 
+  public final class ExpandableItemsDefaults {
+    method @androidx.compose.runtime.Composable public void Chevron(float progress, long color, optional androidx.compose.ui.Modifier modifier, optional float strokeWidth);
+    method public androidx.compose.animation.core.AnimationSpec<java.lang.Float> getCollapseAnimationSpec();
+    method public androidx.compose.animation.core.AnimationSpec<java.lang.Float> getExpandAnimationSpec();
+    property public final androidx.compose.animation.core.AnimationSpec<java.lang.Float> collapseAnimationSpec;
+    property public final androidx.compose.animation.core.AnimationSpec<java.lang.Float> expandAnimationSpec;
+    field public static final androidx.wear.compose.foundation.ExpandableItemsDefaults INSTANCE;
+  }
+
+  public final class ExpandableItemsState {
+    method public float getExpandProgress();
+    method public boolean isExpanded();
+    method public void setExpanded(boolean);
+    method public void toggle();
+    property public final float expandProgress;
+    property public final boolean expanded;
+  }
+
+  public final class ExpandableKt {
+    method public static void expandableItem(androidx.wear.compose.foundation.lazy.ScalingLazyListScope, androidx.wear.compose.foundation.ExpandableItemsState state, optional Object? key, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> content);
+    method public static void expandableItems(androidx.wear.compose.foundation.lazy.ScalingLazyListScope, androidx.wear.compose.foundation.ExpandableItemsState state, int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.BoxScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+    method @androidx.compose.runtime.Composable public static androidx.wear.compose.foundation.ExpandableItemsState rememberExpandableItemsState(optional boolean initiallyExpanded, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> expandAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> collapseAnimationSpec);
+  }
+
   @kotlin.RequiresOptIn(message="This Wear Foundation API is experimental and is likely to change or to be removed in" + " the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalWearFoundationApi {
   }
 
diff --git a/wear/compose/compose-foundation/api/restricted_current.txt b/wear/compose/compose-foundation/api/restricted_current.txt
index 384b168..117b84c 100644
--- a/wear/compose/compose-foundation/api/restricted_current.txt
+++ b/wear/compose/compose-foundation/api/restricted_current.txt
@@ -171,6 +171,30 @@
     property public final androidx.compose.ui.text.font.FontWeight? fontWeight;
   }
 
+  public final class ExpandableItemsDefaults {
+    method @androidx.compose.runtime.Composable public void Chevron(float progress, long color, optional androidx.compose.ui.Modifier modifier, optional float strokeWidth);
+    method public androidx.compose.animation.core.AnimationSpec<java.lang.Float> getCollapseAnimationSpec();
+    method public androidx.compose.animation.core.AnimationSpec<java.lang.Float> getExpandAnimationSpec();
+    property public final androidx.compose.animation.core.AnimationSpec<java.lang.Float> collapseAnimationSpec;
+    property public final androidx.compose.animation.core.AnimationSpec<java.lang.Float> expandAnimationSpec;
+    field public static final androidx.wear.compose.foundation.ExpandableItemsDefaults INSTANCE;
+  }
+
+  public final class ExpandableItemsState {
+    method public float getExpandProgress();
+    method public boolean isExpanded();
+    method public void setExpanded(boolean);
+    method public void toggle();
+    property public final float expandProgress;
+    property public final boolean expanded;
+  }
+
+  public final class ExpandableKt {
+    method public static void expandableItem(androidx.wear.compose.foundation.lazy.ScalingLazyListScope, androidx.wear.compose.foundation.ExpandableItemsState state, optional Object? key, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> content);
+    method public static void expandableItems(androidx.wear.compose.foundation.lazy.ScalingLazyListScope, androidx.wear.compose.foundation.ExpandableItemsState state, int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.BoxScope,? super java.lang.Integer,kotlin.Unit> itemContent);
+    method @androidx.compose.runtime.Composable public static androidx.wear.compose.foundation.ExpandableItemsState rememberExpandableItemsState(optional boolean initiallyExpanded, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> expandAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> collapseAnimationSpec);
+  }
+
 }
 
 package androidx.wear.compose.foundation.lazy {
diff --git a/wear/compose/compose-foundation/build.gradle b/wear/compose/compose-foundation/build.gradle
index 5a85f33..81bb66b5 100644
--- a/wear/compose/compose-foundation/build.gradle
+++ b/wear/compose/compose-foundation/build.gradle
@@ -35,7 +35,7 @@
     implementation(libs.kotlinStdlib)
     implementation(project(":compose:foundation:foundation-layout"))
     implementation(project(":compose:ui:ui-util"))
-    implementation("androidx.profileinstaller:profileinstaller:1.2.0")
+    implementation("androidx.profileinstaller:profileinstaller:1.3.0")
 
     testImplementation(libs.testRules)
     testImplementation(libs.testRunner)
diff --git a/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/ExpandableSample.kt b/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/ExpandableSample.kt
new file mode 100644
index 0000000..2ba6d1e
--- /dev/null
+++ b/wear/compose/compose-foundation/samples/src/main/java/androidx/wear/compose/foundation/samples/ExpandableSample.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.foundation.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.ExpandableItemsDefaults
+import androidx.wear.compose.foundation.expandableItem
+import androidx.wear.compose.foundation.expandableItems
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
+import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
+import androidx.wear.compose.foundation.rememberExpandableItemsState
+import androidx.wear.compose.material.Chip
+import androidx.wear.compose.material.MaterialTheme
+import androidx.wear.compose.material.OutlinedCompactChip
+import androidx.wear.compose.material.Text
+import kotlinx.coroutines.launch
+
+@Sampled
+@Composable
+fun ExpandableWithItemsSample() {
+    val expandableState = rememberExpandableItemsState()
+
+    val sampleItem: @Composable (String) -> Unit = { label ->
+        Chip(
+            label = { Text(label) },
+            onClick = { },
+            secondaryLabel = { Text("line 2 - Secondary") }
+        )
+    }
+
+    val items = List(10) { "Item $it" }
+    val top = items.take(3)
+    val rest = items.drop(3)
+
+    val state = rememberScalingLazyListState()
+    val scope = rememberCoroutineScope()
+    ScalingLazyColumn(
+        modifier = Modifier.fillMaxSize(),
+        state = state
+    ) {
+        items(top.size) {
+            sampleItem(top[it])
+        }
+        expandableItems(expandableState, rest.size) {
+            sampleItem(rest[it])
+        }
+
+        item {
+            OutlinedCompactChip(
+                label = {
+                    Text(if (expandableState.expanded) "Show Less" else "Show More")
+                    Spacer(Modifier.size(6.dp))
+                    ExpandableItemsDefaults.Chevron(
+                        expandableState.expandProgress,
+                        color = MaterialTheme.colors.primary,
+                        modifier = Modifier
+                            .size(15.dp, 11.dp)
+                            .align(Alignment.CenterVertically)
+                    )
+                },
+                onClick = {
+                    if (expandableState.expanded) {
+                        scope.launch { state.animateScrollToItem(top.size) }
+                    }
+                    expandableState.toggle()
+                }
+            )
+        }
+    }
+}
+
+@Sampled
+@Composable
+fun ExpandableTextSample() {
+    val expandableState = rememberExpandableItemsState()
+
+    ScalingLazyColumn(
+        modifier = Modifier.fillMaxSize()
+    ) {
+        expandableItem(expandableState) { expanded ->
+            Text(
+                "Account Alert: you have made a large purchase.\n" +
+                    "We have noticed that a large purchase was charged to " +
+                    "your credit card account. " +
+                    "Please contact us if you did not perform this purchase. " +
+                    "Our Customer Service team is available 24 hours a day, " +
+                    "7 days a week to answer your account or product support question.",
+                maxLines = if (expanded) 20 else 3,
+                modifier = Modifier.padding(horizontal = 10.dp)
+            )
+        }
+
+        item {
+            OutlinedCompactChip(
+                label = {
+                    Text(if (expandableState.expanded) "Show Less" else "Show More")
+                    Spacer(Modifier.size(6.dp))
+                    ExpandableItemsDefaults.Chevron(
+                        expandableState.expandProgress,
+                        color = MaterialTheme.colors.primary,
+                        modifier = Modifier
+                            .size(15.dp, 11.dp)
+                            .align(Alignment.CenterVertically)
+                    )
+                },
+                onClick = { expandableState.toggle() }
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/wear/compose/compose-foundation/src/androidAndroidTest/kotlin/androidx/wear/compose/foundation/CurvedScreenshotTest.kt b/wear/compose/compose-foundation/src/androidAndroidTest/kotlin/androidx/wear/compose/foundation/CurvedScreenshotTest.kt
index 7dcec87..170be37 100644
--- a/wear/compose/compose-foundation/src/androidAndroidTest/kotlin/androidx/wear/compose/foundation/CurvedScreenshotTest.kt
+++ b/wear/compose/compose-foundation/src/androidAndroidTest/kotlin/androidx/wear/compose/foundation/CurvedScreenshotTest.kt
@@ -34,6 +34,8 @@
 import androidx.compose.ui.test.captureToImage
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.text.PlatformTextStyle
+import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
@@ -125,7 +127,13 @@
             curvedComposable {
                 Column {
                     Box(Modifier.size(15.dp).background(Color.Red))
-                    BasicText("Text")
+                    @Suppress("DEPRECATION")
+                    BasicText(
+                        text = "Text",
+                        style = TextStyle(
+                            platformStyle = PlatformTextStyle(includeFontPadding = true)
+                        )
+                    )
                     Box(Modifier.size(15.dp).background(Color.Red))
                 }
             }
diff --git a/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/Expandable.kt b/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/Expandable.kt
new file mode 100644
index 0000000..9d931c7
--- /dev/null
+++ b/wear/compose/compose-foundation/src/commonMain/kotlin/androidx/wear/compose/foundation/Expandable.kt
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.foundation
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.TweenSpec
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clipToBounds
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.lerp
+import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
+import kotlin.math.roundToInt
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * Create and [remember] an [ExpandableItemsState]
+ *
+ * Example of an expandable list:
+ * @sample androidx.wear.compose.foundation.samples.ExpandableWithItemsSample
+ *
+ * Example of an expandable text:
+ * @sample androidx.wear.compose.foundation.samples.ExpandableTextSample
+ *
+ * @param initiallyExpanded The initial value of the state.
+ * @param expandAnimationSpec The [AnimationSpec] to use when showing the extra information.
+ * @param collapseAnimationSpec The [AnimationSpec] to use when hiding the extra information.
+ */
+@Composable
+public fun rememberExpandableItemsState(
+    initiallyExpanded: Boolean = false,
+    expandAnimationSpec: AnimationSpec<Float> = ExpandableItemsDefaults.expandAnimationSpec,
+    collapseAnimationSpec: AnimationSpec<Float> = ExpandableItemsDefaults.collapseAnimationSpec,
+): ExpandableItemsState {
+    val scope = rememberCoroutineScope()
+    return remember {
+        ExpandableItemsState(initiallyExpanded, scope, expandAnimationSpec, collapseAnimationSpec)
+    }
+}
+
+/**
+ * Adds a series of items, that will be expanded/collapsed according to the [ExpandableItemsState]
+ *
+ * Example of an expandable list:
+ * @sample androidx.wear.compose.foundation.samples.ExpandableWithItemsSample
+ *
+ * @param state The [ExpandableItemsState] connected to these items to.
+ * @param count The number of items
+ * @param key a factory of stable and unique keys representing the item. Using the same key
+ * for multiple items in the list 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 itemContent the content displayed by a single item
+ */
+public fun ScalingLazyListScope.expandableItems(
+    state: ExpandableItemsState,
+    count: Int,
+    key: ((index: Int) -> Any)? = null,
+    itemContent: @Composable BoxScope.(index: Int) -> Unit
+) {
+    repeat(count) { itemIndex ->
+        // Animations for each item start in inverse order, the first item animates last.
+        val animationStart = count - 1 - itemIndex
+        val animationProgress =
+            (state.expandProgress * count - animationStart).coerceIn(0f, 1f)
+        if (animationProgress > 0) {
+            item(key = key?.invoke(itemIndex)) {
+                Layout(
+                    modifier = Modifier.clipToBounds(),
+                    content = { Box(content = { itemContent(itemIndex) }) }
+                ) { measurables, constraints ->
+                    val placeable = measurables.first().measure(constraints)
+                    val shownHeight = (placeable.height * animationProgress).roundToInt()
+                    layout(placeable.width, shownHeight) {
+                        val y = (placeable.height * (animationProgress - 1)).roundToInt()
+                        placeable.placeWithLayer(0, y)
+                    }
+                }
+            }
+        }
+    }
+}
+
+/**
+ * Adds a single item, that will be expanded/collapsed according to the [ExpandableItemsState].
+ *
+ * Example of an expandable text:
+ * @sample androidx.wear.compose.foundation.samples.ExpandableTextSample
+ *
+ * The item should support two levels of information display (for example, a text showing a few
+ * lines in the collapsed state, and more in the expanded state)
+ *
+ * @param state The [ExpandableItemsState] to connect this items to.
+ * @param key A stable and unique key representing the item. Using the same key
+ * for multiple items in the list 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 content the content displayed by the item, according to its expanded/collapsed state.
+ */
+public fun ScalingLazyListScope.expandableItem(
+    state: ExpandableItemsState,
+    key: Any? = null,
+    content: @Composable (expanded: Boolean) -> Unit
+) {
+    val progress = state.expandProgress
+
+    item(key = key) {
+        Layout(
+            content = {
+                Box { content(false) }
+                Box { content(true) }
+            },
+            modifier = Modifier.clipToBounds()
+        ) { measurables, constraints ->
+            val placeables = measurables.map { it.measure(constraints) }
+
+            val width = lerp(placeables[0].width, placeables[1].width, progress)
+            val height = lerp(placeables[0].height, placeables[1].height, progress)
+
+            // Keep the items horizontally centered.
+            val off0 = (width - placeables[0].width) / 2
+            val off1 = (width - placeables[1].width) / 2
+
+            layout(width, height) {
+                placeables[0].placeWithLayer(off0, 0) { alpha = 1 - progress }
+                placeables[1].placeWithLayer(off1, 0) { alpha = progress }
+            }
+        }
+    }
+}
+
+/**
+ * State of the Expandable composables.
+ *
+ * It's used to control the showing/hiding of extra information either directly or connecting it
+ * with something like a button.
+ */
+public class ExpandableItemsState internal constructor(
+    initiallyExpanded: Boolean,
+    private val coroutineScope: CoroutineScope,
+    private val expandAnimationSpec: AnimationSpec<Float>,
+    private val collapseAnimationSpec: AnimationSpec<Float>,
+) {
+    private val _expandProgress = Animatable(if (initiallyExpanded) 1f else 0f)
+
+    /**
+     * While in the middle of the animation, this represents the progress from 0f (collapsed) to
+     * 1f (expanded), or the other way around.
+     * If no animation is running, it's either 0f if the extra content is not showing, or 1f if
+     * the extra content is showing.
+     */
+    val expandProgress
+        get() = _expandProgress.value
+
+    /**
+     * Represents the current state of the component, true means it's showing the extra information.
+     * If its in the middle of an animation, the value of this field takes into account only the
+     * target of that animation.
+     *
+     * Modifying this value triggers a change to show/hide the extra information.
+     */
+    var expanded
+        @JvmName("isExpanded")
+        get() = _expandProgress.targetValue == 1f
+        set(newValue) {
+            if (expanded != newValue) {
+                coroutineScope.launch {
+                    if (newValue) {
+                        _expandProgress.animateTo(1f, expandAnimationSpec)
+                    } else {
+                        _expandProgress.animateTo(0f, collapseAnimationSpec)
+                    }
+                }
+            }
+        }
+
+    /**
+     * Trigger a change to expand/collapse to the inverse of the current state.
+     */
+    fun toggle() {
+        expanded = !expanded
+    }
+}
+
+/**
+ * Contains the default values used by Expandable components.
+ */
+public object ExpandableItemsDefaults {
+    /**
+     * Default animation used to show extra information.
+     */
+    val expandAnimationSpec: AnimationSpec<Float> = TweenSpec(1000)
+
+    /**
+     * Default animation used to hide extra information.
+     */
+    val collapseAnimationSpec: AnimationSpec<Float> = TweenSpec(1000)
+
+    /**
+     * [Chevron] provides an animatable chevron, to use in an expand button.
+     *
+     * @param progress The point in the animation we are displaying this chevron in. 0f means pointing
+     * downward, 1f means pointing upward.
+     * @param color The color to draw this chevron on.
+     * @param modifier Modifier to be applied to the AnimatableChevron. This can be used to provide a
+     * content description for accessibility.
+     * @param strokeWidth The stroke width used to draw this chevron.
+     */
+    @Composable
+    public fun Chevron(
+        progress: Float,
+        color: Color,
+        modifier: Modifier = Modifier,
+        strokeWidth: Dp = 3.dp
+    ) {
+        Box(
+            modifier.drawBehind {
+                val strokeWidthPx = strokeWidth.toPx()
+
+                val halfStrokeWidthPx = strokeWidthPx / 2f
+
+                val animatedY = lerp(halfStrokeWidthPx, size.height - halfStrokeWidthPx, progress)
+
+                val path = Path().apply {
+                    moveTo(halfStrokeWidthPx, animatedY)
+                    lineTo(size.width / 2, size.height - animatedY)
+                    lineTo(size.width - halfStrokeWidthPx, animatedY)
+                }
+
+                drawPath(
+                    path,
+                    color,
+                    style = Stroke(
+                        width = strokeWidthPx,
+                        cap = StrokeCap.Round,
+                        join = StrokeJoin.Round
+                    )
+                )
+            }
+        )
+    }
+}
diff --git a/wear/compose/compose-material-core/build.gradle b/wear/compose/compose-material-core/build.gradle
index 84995a0..fe566204 100644
--- a/wear/compose/compose-material-core/build.gradle
+++ b/wear/compose/compose-material-core/build.gradle
@@ -39,7 +39,7 @@
     implementation(project(":compose:material:material-ripple"))
     implementation(project(":compose:ui:ui-util"))
     implementation(project(":wear:compose:compose-foundation"))
-    implementation("androidx.profileinstaller:profileinstaller:1.2.0")
+    implementation("androidx.profileinstaller:profileinstaller:1.3.0")
 
     androidTestImplementation(project(":compose:ui:ui-test"))
     androidTestImplementation(project(":compose:ui:ui-test-junit4"))
diff --git a/wear/compose/compose-material/api/current.txt b/wear/compose/compose-material/api/current.txt
index 1be733d..761dc1e8 100644
--- a/wear/compose/compose-material/api/current.txt
+++ b/wear/compose/compose-material/api/current.txt
@@ -184,30 +184,6 @@
     method @Deprecated public static void curvedText(androidx.wear.compose.foundation.CurvedScope, String text, optional androidx.wear.compose.foundation.CurvedModifier modifier, optional long background, optional long color, optional long fontSize, optional androidx.wear.compose.foundation.CurvedTextStyle? style, optional androidx.wear.compose.foundation.CurvedDirection.Angular? angularDirection, optional int overflow);
   }
 
-  public final class ExpandableItemsDefaults {
-    method @androidx.compose.runtime.Composable public void Chevron(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth);
-    method public androidx.compose.animation.core.AnimationSpec<java.lang.Float> getCollapseAnimationSpec();
-    method public androidx.compose.animation.core.AnimationSpec<java.lang.Float> getExpandAnimationSpec();
-    property public final androidx.compose.animation.core.AnimationSpec<java.lang.Float> collapseAnimationSpec;
-    property public final androidx.compose.animation.core.AnimationSpec<java.lang.Float> expandAnimationSpec;
-    field public static final androidx.wear.compose.material.ExpandableItemsDefaults INSTANCE;
-  }
-
-  public final class ExpandableItemsState {
-    method public float getExpandProgress();
-    method public boolean isExpanded();
-    method public void setExpanded(boolean);
-    method public void toggle();
-    property public final float expandProgress;
-    property public final boolean expanded;
-  }
-
-  public final class ExpandableKt {
-    method public static void expandableItem(androidx.wear.compose.foundation.lazy.ScalingLazyListScope, androidx.wear.compose.material.ExpandableItemsState state, optional Object? key, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> content);
-    method public static void expandableItems(androidx.wear.compose.foundation.lazy.ScalingLazyListScope, androidx.wear.compose.material.ExpandableItemsState state, int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.BoxScope,? super java.lang.Integer,kotlin.Unit> itemContent);
-    method @androidx.compose.runtime.Composable public static androidx.wear.compose.material.ExpandableItemsState rememberExpandableItemsState(optional boolean initiallyExpanded, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> expandAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> collapseAnimationSpec);
-  }
-
   public final class HorizontalPageIndicatorKt {
     method @androidx.compose.runtime.Composable public static void HorizontalPageIndicator(androidx.wear.compose.material.PageIndicatorState pageIndicatorState, optional androidx.compose.ui.Modifier modifier, optional int indicatorStyle, optional long selectedColor, optional long unselectedColor, optional float indicatorSize, optional float spacing, optional androidx.compose.ui.graphics.Shape indicatorShape);
   }
diff --git a/wear/compose/compose-material/api/public_plus_experimental_current.txt b/wear/compose/compose-material/api/public_plus_experimental_current.txt
index c1255f3..3dd2192 100644
--- a/wear/compose/compose-material/api/public_plus_experimental_current.txt
+++ b/wear/compose/compose-material/api/public_plus_experimental_current.txt
@@ -185,30 +185,6 @@
     method @Deprecated public static void curvedText(androidx.wear.compose.foundation.CurvedScope, String text, optional androidx.wear.compose.foundation.CurvedModifier modifier, optional long background, optional long color, optional long fontSize, optional androidx.wear.compose.foundation.CurvedTextStyle? style, optional androidx.wear.compose.foundation.CurvedDirection.Angular? angularDirection, optional int overflow);
   }
 
-  public final class ExpandableItemsDefaults {
-    method @androidx.compose.runtime.Composable public void Chevron(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth);
-    method public androidx.compose.animation.core.AnimationSpec<java.lang.Float> getCollapseAnimationSpec();
-    method public androidx.compose.animation.core.AnimationSpec<java.lang.Float> getExpandAnimationSpec();
-    property public final androidx.compose.animation.core.AnimationSpec<java.lang.Float> collapseAnimationSpec;
-    property public final androidx.compose.animation.core.AnimationSpec<java.lang.Float> expandAnimationSpec;
-    field public static final androidx.wear.compose.material.ExpandableItemsDefaults INSTANCE;
-  }
-
-  public final class ExpandableItemsState {
-    method public float getExpandProgress();
-    method public boolean isExpanded();
-    method public void setExpanded(boolean);
-    method public void toggle();
-    property public final float expandProgress;
-    property public final boolean expanded;
-  }
-
-  public final class ExpandableKt {
-    method public static void expandableItem(androidx.wear.compose.foundation.lazy.ScalingLazyListScope, androidx.wear.compose.material.ExpandableItemsState state, optional Object? key, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> content);
-    method public static void expandableItems(androidx.wear.compose.foundation.lazy.ScalingLazyListScope, androidx.wear.compose.material.ExpandableItemsState state, int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.BoxScope,? super java.lang.Integer,kotlin.Unit> itemContent);
-    method @androidx.compose.runtime.Composable public static androidx.wear.compose.material.ExpandableItemsState rememberExpandableItemsState(optional boolean initiallyExpanded, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> expandAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> collapseAnimationSpec);
-  }
-
   @kotlin.RequiresOptIn(message="This Wear Material API is experimental and is likely to change or to be removed in" + " the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalWearMaterialApi {
   }
 
diff --git a/wear/compose/compose-material/api/restricted_current.txt b/wear/compose/compose-material/api/restricted_current.txt
index 1be733d..761dc1e8 100644
--- a/wear/compose/compose-material/api/restricted_current.txt
+++ b/wear/compose/compose-material/api/restricted_current.txt
@@ -184,30 +184,6 @@
     method @Deprecated public static void curvedText(androidx.wear.compose.foundation.CurvedScope, String text, optional androidx.wear.compose.foundation.CurvedModifier modifier, optional long background, optional long color, optional long fontSize, optional androidx.wear.compose.foundation.CurvedTextStyle? style, optional androidx.wear.compose.foundation.CurvedDirection.Angular? angularDirection, optional int overflow);
   }
 
-  public final class ExpandableItemsDefaults {
-    method @androidx.compose.runtime.Composable public void Chevron(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth);
-    method public androidx.compose.animation.core.AnimationSpec<java.lang.Float> getCollapseAnimationSpec();
-    method public androidx.compose.animation.core.AnimationSpec<java.lang.Float> getExpandAnimationSpec();
-    property public final androidx.compose.animation.core.AnimationSpec<java.lang.Float> collapseAnimationSpec;
-    property public final androidx.compose.animation.core.AnimationSpec<java.lang.Float> expandAnimationSpec;
-    field public static final androidx.wear.compose.material.ExpandableItemsDefaults INSTANCE;
-  }
-
-  public final class ExpandableItemsState {
-    method public float getExpandProgress();
-    method public boolean isExpanded();
-    method public void setExpanded(boolean);
-    method public void toggle();
-    property public final float expandProgress;
-    property public final boolean expanded;
-  }
-
-  public final class ExpandableKt {
-    method public static void expandableItem(androidx.wear.compose.foundation.lazy.ScalingLazyListScope, androidx.wear.compose.material.ExpandableItemsState state, optional Object? key, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> content);
-    method public static void expandableItems(androidx.wear.compose.foundation.lazy.ScalingLazyListScope, androidx.wear.compose.material.ExpandableItemsState state, int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.BoxScope,? super java.lang.Integer,kotlin.Unit> itemContent);
-    method @androidx.compose.runtime.Composable public static androidx.wear.compose.material.ExpandableItemsState rememberExpandableItemsState(optional boolean initiallyExpanded, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> expandAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> collapseAnimationSpec);
-  }
-
   public final class HorizontalPageIndicatorKt {
     method @androidx.compose.runtime.Composable public static void HorizontalPageIndicator(androidx.wear.compose.material.PageIndicatorState pageIndicatorState, optional androidx.compose.ui.Modifier modifier, optional int indicatorStyle, optional long selectedColor, optional long unselectedColor, optional float indicatorSize, optional float spacing, optional androidx.compose.ui.graphics.Shape indicatorShape);
   }
diff --git a/wear/compose/compose-material/build.gradle b/wear/compose/compose-material/build.gradle
index e1354a3..03e7a68 100644
--- a/wear/compose/compose-material/build.gradle
+++ b/wear/compose/compose-material/build.gradle
@@ -40,7 +40,7 @@
     implementation(project(":compose:ui:ui-util"))
     implementation(project(":wear:compose:compose-foundation"))
     implementation(project(":wear:compose:compose-material-core"))
-    implementation("androidx.profileinstaller:profileinstaller:1.2.0")
+    implementation("androidx.profileinstaller:profileinstaller:1.3.0")
     implementation("androidx.lifecycle:lifecycle-common:2.5.1")
 
     androidTestImplementation(project(":compose:ui:ui-test"))
diff --git a/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/ExpandableSample.kt b/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/ExpandableSample.kt
deleted file mode 100644
index afa936f..0000000
--- a/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/ExpandableSample.kt
+++ /dev/null
@@ -1,122 +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.wear.compose.material.samples
-
-import androidx.annotation.Sampled
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
-import androidx.wear.compose.material.Chip
-import androidx.wear.compose.material.ExpandableItemsDefaults
-import androidx.wear.compose.material.MaterialTheme
-import androidx.wear.compose.material.OutlinedChip
-import androidx.wear.compose.material.Text
-import androidx.wear.compose.material.expandableItem
-import androidx.wear.compose.material.expandableItems
-import androidx.wear.compose.material.rememberExpandableItemsState
-
-@Sampled
-@Composable
-fun ExpandableWithItemsSample() {
-    val expandableState = rememberExpandableItemsState()
-
-    val sampleItem: @Composable (String) -> Unit = { label ->
-        Chip(
-            label = { Text(label) },
-            onClick = { },
-            secondaryLabel = { Text("line 2 - Secondary") }
-        )
-    }
-
-    val items = List(10) { "Item $it" }
-    val top = items.take(3)
-    val rest = items.drop(3)
-
-    ScalingLazyColumn(
-        modifier = Modifier.fillMaxSize()
-    ) {
-        items(top.size) {
-            sampleItem(top[it])
-        }
-        expandableItems(expandableState, rest.size) {
-            sampleItem(rest[it])
-        }
-
-        item {
-            OutlinedChip(
-                label = {
-                    Text(if (expandableState.expanded) "Show Less" else "Show More")
-                    Spacer(Modifier.size(10.dp))
-                    ExpandableItemsDefaults.Chevron(
-                        expandableState.expandProgress,
-                        color = MaterialTheme.colors.primary,
-                        modifier = Modifier
-                            .size(15.dp, 11.dp)
-                            .align(Alignment.CenterVertically)
-                    )
-                },
-                onClick = { expandableState.toggle() }
-            )
-        }
-    }
-}
-
-@Sampled
-@Composable
-fun ExpandableTextSample() {
-    val expandableState = rememberExpandableItemsState()
-
-    ScalingLazyColumn(
-        modifier = Modifier.fillMaxSize()
-    ) {
-        expandableItem(expandableState) { expanded ->
-            Text(
-                "Account Alert: you have made a large purchase.\n" +
-                    "We have noticed that a large purchase was charged to " +
-                    "your credit card account. " +
-                    "Please contact us if you did not perform this purchase. " +
-                    "Our Customer Service team is available 24 hours a day, " +
-                    "7 days a week to answer your account or product support question.",
-                maxLines = if (expanded) 20 else 3,
-                modifier = Modifier.padding(horizontal = 10.dp)
-            )
-        }
-
-        item {
-            OutlinedChip(
-                label = {
-                    Text(if (expandableState.expanded) "Show Less" else "Show More")
-                    Spacer(Modifier.size(10.dp))
-                    ExpandableItemsDefaults.Chevron(
-                        expandableState.expandProgress,
-                        color = MaterialTheme.colors.primary,
-                        modifier = Modifier
-                            .size(15.dp, 11.dp)
-                            .align(Alignment.CenterVertically)
-                    )
-                },
-                onClick = { expandableState.toggle() }
-            )
-        }
-    }
-}
\ No newline at end of file
diff --git a/wear/compose/compose-material/src/androidMain/kotlin/androidx/wear/compose/material/DefaultPlatformTextStyle.android.kt b/wear/compose/compose-material/src/androidMain/kotlin/androidx/wear/compose/material/DefaultPlatformTextStyle.android.kt
new file mode 100644
index 0000000..5365a53
--- /dev/null
+++ b/wear/compose/compose-material/src/androidMain/kotlin/androidx/wear/compose/material/DefaultPlatformTextStyle.android.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.material
+
+import androidx.compose.ui.text.PlatformTextStyle
+
+private const val DefaultIncludeFontPadding = true
+
+@Suppress("DEPRECATION")
+private val DefaultPlatformTextStyle = PlatformTextStyle(
+    includeFontPadding = DefaultIncludeFontPadding
+)
+internal actual fun defaultPlatformTextStyle(): PlatformTextStyle? = DefaultPlatformTextStyle
\ No newline at end of file
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Expandable.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Expandable.kt
deleted file mode 100644
index 47cffe6..0000000
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Expandable.kt
+++ /dev/null
@@ -1,270 +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.wear.compose.material
-
-import androidx.compose.animation.core.Animatable
-import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.animation.core.TweenSpec
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.BoxScope
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clipToBounds
-import androidx.compose.ui.draw.drawBehind
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.StrokeCap
-import androidx.compose.ui.graphics.Path
-import androidx.compose.ui.graphics.StrokeJoin
-import androidx.compose.ui.graphics.drawscope.Stroke
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.util.lerp
-import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
-import kotlin.math.roundToInt
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
-
-/**
- * Create and [remember] an [ExpandableItemsState]
- *
- * Example of an expandable list:
- * @sample androidx.wear.compose.material.samples.ExpandableWithItemsSample
- *
- * Example of an expandable text:
- * @sample androidx.wear.compose.material.samples.ExpandableTextSample
- *
- * @param initiallyExpanded The initial value of the state.
- * @param expandAnimationSpec The [AnimationSpec] to use when showing the extra information.
- * @param collapseAnimationSpec The [AnimationSpec] to use when hiding the extra information.
- */
-@Composable
-public fun rememberExpandableItemsState(
-    initiallyExpanded: Boolean = false,
-    expandAnimationSpec: AnimationSpec<Float> = ExpandableItemsDefaults.expandAnimationSpec,
-    collapseAnimationSpec: AnimationSpec<Float> = ExpandableItemsDefaults.collapseAnimationSpec,
-): ExpandableItemsState {
-    val scope = rememberCoroutineScope()
-    return remember {
-        ExpandableItemsState(initiallyExpanded, scope, expandAnimationSpec, collapseAnimationSpec)
-    }
-}
-
-/**
- * Adds a series of items, that will be expanded/collapsed according to the [ExpandableItemsState]
- *
- * Example of an expandable list:
- * @sample androidx.wear.compose.material.samples.ExpandableWithItemsSample
- *
- * @param state The [ExpandableItemsState] connected to these items to.
- * @param count The number of items
- * @param key a factory of stable and unique keys representing the item. Using the same key
- * for multiple items in the list 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 itemContent the content displayed by a single item
- */
-public fun ScalingLazyListScope.expandableItems(
-    state: ExpandableItemsState,
-    count: Int,
-    key: ((index: Int) -> Any)? = null,
-    itemContent: @Composable BoxScope.(index: Int) -> Unit
-) {
-    repeat(count) { itemIndex ->
-        // Animations for each item start in inverse order, the first item animates last.
-        val animationStart = count - 1 - itemIndex
-        val animationProgress =
-            (state.expandProgress * count - animationStart).coerceIn(0f, 1f)
-        if (animationProgress > 0) {
-            item(key = key?.invoke(itemIndex)) {
-                Layout(
-                    modifier = Modifier.clipToBounds(),
-                    content = { Box(content = { itemContent(itemIndex) }) }
-                ) { measurables, constraints ->
-                    val placeable = measurables.first().measure(constraints)
-                    val shownHeight = (placeable.height * animationProgress).roundToInt()
-                    layout(placeable.width, shownHeight) {
-                        val y = (placeable.height * (animationProgress - 1)).roundToInt()
-                        placeable.placeWithLayer(0, y)
-                    }
-                }
-            }
-        }
-    }
-}
-
-/**
- * Adds a single item, that will be expanded/collapsed according to the [ExpandableItemsState].
- *
- * Example of an expandable text:
- * @sample androidx.wear.compose.material.samples.ExpandableTextSample
- *
- * The item should support two levels of information display (for example, a text showing a few
- * lines in the collapsed state, and more in the expanded state)
- *
- * @param state The [ExpandableItemsState] to connect this items to.
- * @param key A stable and unique key representing the item. Using the same key
- * for multiple items in the list 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 content the content displayed by the item, according to its expanded/collapsed state.
- */
-public fun ScalingLazyListScope.expandableItem(
-    state: ExpandableItemsState,
-    key: Any? = null,
-    content: @Composable (expanded: Boolean) -> Unit
-) {
-    val progress = state.expandProgress
-
-    item(key = key) {
-        Layout(
-            content = {
-                Box { content(false) }
-                Box { content(true) }
-            },
-            modifier = Modifier.clipToBounds()
-        ) { measurables, constraints ->
-            val placeables = measurables.map { it.measure(constraints) }
-
-            val width = lerp(placeables[0].width, placeables[1].width, progress)
-            val height = lerp(placeables[0].height, placeables[1].height, progress)
-
-            layout(width, height) {
-                placeables[0].placeWithLayer(0, 0) { alpha = 1 - progress }
-                placeables[1].placeWithLayer(0, 0) { alpha = progress }
-            }
-        }
-    }
-}
-
-/**
- * State of the Expandable composables.
- *
- * It's used to control the showing/hiding of extra information either directly or connecting it
- * with something like a button.
- */
-public class ExpandableItemsState internal constructor(
-    initiallyExpanded: Boolean,
-    private val coroutineScope: CoroutineScope,
-    private val expandAnimationSpec: AnimationSpec<Float>,
-    private val collapseAnimationSpec: AnimationSpec<Float>,
-) {
-    private val _expandProgress = Animatable(if (initiallyExpanded) 1f else 0f)
-
-    /**
-     * While in the middle of the animation, this represents the progress from 0f (collapsed) to
-     * 1f (expanded), or the other way around.
-     * If no animation is running, it's either 0f if the extra content is not showing, or 1f if
-     * the extra content is showing.
-     */
-    val expandProgress
-        get() = _expandProgress.value
-
-    /**
-     * Represents the current state of the component, true means it's showing the extra information.
-     * If its in the middle of an animation, the value of this field takes into account only the
-     * target of that animation.
-     *
-     * Modifying this value triggers a change to show/hide the extra information.
-     */
-    var expanded
-        @JvmName("isExpanded")
-        get() = _expandProgress.targetValue == 1f
-        set(newValue) {
-            if (expanded != newValue) {
-                coroutineScope.launch {
-                    if (newValue) {
-                        _expandProgress.animateTo(1f, expandAnimationSpec)
-                    } else {
-                        _expandProgress.animateTo(0f, collapseAnimationSpec)
-                    }
-                }
-            }
-        }
-
-    /**
-     * Trigger a change to expand/collapse to the inverse of the current state.
-     */
-    fun toggle() {
-        expanded = !expanded
-    }
-}
-
-/**
- * Contains the default values used by Expandable components.
- */
-public object ExpandableItemsDefaults {
-    /**
-     * Default animation used to show extra information.
-     */
-    val expandAnimationSpec: AnimationSpec<Float> = TweenSpec(1000)
-
-    /**
-     * Default animation used to hide extra information.
-     */
-    val collapseAnimationSpec: AnimationSpec<Float> = TweenSpec(1000)
-
-    /**
-     * [Chevron] provides an animatable chevron, to use in an expand button.
-     *
-     * @param progress The point in the animation we are displaying this chevron in. 0f means pointing
-     * downward, 1f means pointing upward.
-     * @param modifier Modifier to be applied to the AnimatableChevron. This can be used to provide a
-     * content description for accessibility.
-     * @param color The color to draw this chevron on.
-     * @param strokeWidth The stroke width used to draw this chevron.
-     */
-    @Composable
-    public fun Chevron(
-        progress: Float,
-        modifier: Modifier = Modifier,
-        color: Color = MaterialTheme.colors.onBackground,
-        strokeWidth: Dp = 3.dp
-    ) {
-        Box(
-            modifier.drawBehind {
-                val strokeWidthPx = strokeWidth.toPx()
-
-                val halfStrokeWidthPx = strokeWidthPx / 2f
-
-                val animatedY = lerp(halfStrokeWidthPx, size.height - halfStrokeWidthPx, progress)
-
-                val path = Path().apply {
-                    moveTo(halfStrokeWidthPx, animatedY)
-                    lineTo(size.width / 2, size.height - animatedY)
-                    lineTo(size.width - halfStrokeWidthPx, animatedY)
-                }
-
-                drawPath(
-                    path,
-                    color,
-                    style = Stroke(
-                        width = strokeWidthPx,
-                        cap = StrokeCap.Round,
-                        join = StrokeJoin.Round
-                    )
-                )
-            }
-        )
-    }
-}
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/PickerGroup.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/PickerGroup.kt
index a9be263..d31497f 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/PickerGroup.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/PickerGroup.kt
@@ -21,6 +21,7 @@
 import androidx.compose.foundation.gestures.awaitFirstDown
 import androidx.compose.foundation.gestures.scrollable
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.focusable
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
@@ -112,12 +113,16 @@
             ),
         propagateMinConstraints = propagateMinConstraints
     ) {
+        // When no Picker is selected, provide an empty composable as a placeholder
+        // and tell the HierarchicalFocusCoordinator to clear the focus.
+        HierarchicalFocusCoordinator(requiresFocus = {
+            !pickers.indices.contains(pickerGroupState.selectedIndex)
+        }) {}
         pickers.forEachIndexed { index, pickerData ->
             val pickerSelected = index == pickerGroupState.selectedIndex
             val flingBehavior = PickerDefaults.flingBehavior(state = pickerData.pickerState)
             HierarchicalFocusCoordinator(requiresFocus = { pickerSelected }) {
-                val focusRequester =
-                    pickerData.focusRequester ?: rememberActiveFocusRequester()
+                val focusRequester = pickerData.focusRequester ?: rememberActiveFocusRequester()
                 Picker(
                     state = pickerData.pickerState,
                     contentDescription = pickerData.contentDescription,
@@ -129,7 +134,8 @@
                             if (pickerSelected && autoCenter) Modifier.autoCenteringTarget()
                             else Modifier
                         )
-                        .focusRequester(focusRequester),
+                        .focusRequester(focusRequester)
+                        .focusable(),
                     readOnlyLabel = pickerData.readOnlyLabel,
                     flingBehavior = flingBehavior,
                     onSelected = pickerData.onSelected,
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Text.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Text.kt
index 297bc4a..7991332 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Text.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Text.kt
@@ -429,7 +429,7 @@
  * @see ProvideTextStyle
  */
 public val LocalTextStyle: ProvidableCompositionLocal<TextStyle> =
-    compositionLocalOf(structuralEqualityPolicy()) { TextStyle.Default }
+    compositionLocalOf(structuralEqualityPolicy()) { DefaultTextStyle }
 
 // TODO: b/156598010 remove this and replace with fold definition on the backing CompositionLocal
 /**
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Typography.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Typography.kt
index ca17826..1c7c2a3 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Typography.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Typography.kt
@@ -17,6 +17,7 @@
 
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.staticCompositionLocalOf
+import androidx.compose.ui.text.PlatformTextStyle
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.font.FontFamily
 import androidx.compose.ui.text.font.FontWeight
@@ -87,79 +88,78 @@
 ) {
     public constructor (
         defaultFontFamily: FontFamily = FontFamily.Default,
-        display1: TextStyle = TextStyle(
+        display1: TextStyle = DefaultTextStyle.copy(
             fontWeight = FontWeight.Medium,
             fontSize = 40.sp,
             lineHeight = 46.sp,
             letterSpacing = 0.5.sp
         ),
-        display2: TextStyle = TextStyle(
+        display2: TextStyle = DefaultTextStyle.copy(
             fontWeight = FontWeight.Medium,
             fontSize = 34.sp,
             lineHeight = 40.sp,
             letterSpacing = 1.sp
         ),
-        display3: TextStyle = TextStyle(
+        display3: TextStyle = DefaultTextStyle.copy(
             fontWeight = FontWeight.Medium,
             fontSize = 30.sp,
             lineHeight = 36.sp,
             letterSpacing = 0.8.sp,
         ),
-        title1: TextStyle = TextStyle(
+        title1: TextStyle = DefaultTextStyle.copy(
             fontWeight = FontWeight.Medium,
             fontSize = 24.sp,
             lineHeight = 28.sp,
             letterSpacing = 0.2.sp
         ),
-        title2: TextStyle = TextStyle(
+        title2: TextStyle = DefaultTextStyle.copy(
             fontWeight = FontWeight.Medium,
             fontSize = 20.sp,
             lineHeight = 24.sp,
             letterSpacing = 0.2.sp
         ),
-        title3: TextStyle = TextStyle(
+        title3: TextStyle = DefaultTextStyle.copy(
             fontWeight = FontWeight.Medium,
             fontSize = 16.sp,
             lineHeight = 20.sp,
             letterSpacing = 0.2.sp
         ),
-        body1: TextStyle = TextStyle(
+        body1: TextStyle = DefaultTextStyle.copy(
             fontWeight = FontWeight.Normal,
             fontSize = 16.sp,
             lineHeight = 20.sp,
             letterSpacing = 0.18.sp
         ),
-        body2: TextStyle = TextStyle(
+        body2: TextStyle = DefaultTextStyle.copy(
             fontWeight = FontWeight.Normal,
             fontSize = 14.sp,
             lineHeight = 18.sp,
             letterSpacing = 0.2.sp
         ),
-        button: TextStyle = TextStyle(
+        button: TextStyle = DefaultTextStyle.copy(
             fontWeight = FontWeight.Bold,
             fontSize = 15.sp,
             lineHeight = 19.sp,
             letterSpacing = 0.38.sp
         ),
-        caption1: TextStyle = TextStyle(
+        caption1: TextStyle = DefaultTextStyle.copy(
             fontWeight = FontWeight.Medium,
             fontSize = 14.sp,
             lineHeight = 18.sp,
             letterSpacing = 0.1.sp
         ),
-        caption2: TextStyle = TextStyle(
+        caption2: TextStyle = DefaultTextStyle.copy(
             fontWeight = FontWeight.Medium,
             fontSize = 12.sp,
             lineHeight = 16.sp,
             letterSpacing = 0.1.sp
         ),
-        caption3: TextStyle = TextStyle(
+        caption3: TextStyle = DefaultTextStyle.copy(
             fontWeight = FontWeight.Medium,
             fontSize = 10.sp,
             lineHeight = 14.sp,
             letterSpacing = 0.1.sp
         )
-
     ) : this(
         display1 = display1.withDefaultFontFamily(defaultFontFamily),
         display2 = display2.withDefaultFontFamily(defaultFontFamily),
@@ -257,6 +257,15 @@
     return if (fontFamily != null) this else copy(fontFamily = default)
 }
 
+internal val DefaultTextStyle = TextStyle.Default.copy(
+    platformStyle = defaultPlatformTextStyle()
+)
+
+/**
+ * Returns Default [PlatformTextStyle].
+ */
+internal expect fun defaultPlatformTextStyle(): PlatformTextStyle?
+
 /**
  * This Ambient holds on to the current definition of typography for this application as described
  * by the Wear Material spec. You can read the values in it when creating custom components that
diff --git a/wear/compose/compose-material/src/desktopMain/kotlin/androidx/wear/compose/material/DefaultPlatformTextStyle.desktop.kt b/wear/compose/compose-material/src/desktopMain/kotlin/androidx/wear/compose/material/DefaultPlatformTextStyle.desktop.kt
new file mode 100644
index 0000000..fd5d994
--- /dev/null
+++ b/wear/compose/compose-material/src/desktopMain/kotlin/androidx/wear/compose/material/DefaultPlatformTextStyle.desktop.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.material
+
+import androidx.compose.ui.text.PlatformTextStyle
+
+internal actual fun defaultPlatformTextStyle(): PlatformTextStyle? = null
\ No newline at end of file
diff --git a/wear/compose/compose-material3/api/current.txt b/wear/compose/compose-material3/api/current.txt
index e6f50d0..2dbfac5 100644
--- a/wear/compose/compose-material3/api/current.txt
+++ b/wear/compose/compose-material3/api/current.txt
@@ -1 +1,186 @@
 // Signature format: 4.0
+package androidx.wear.compose.material3 {
+
+  @androidx.compose.runtime.Stable public final class 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 surfaceDim, optional long surface, optional long surfaceBright, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError);
+    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 surfaceDim, optional long surface, optional long surfaceBright, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError);
+    method public long getBackground();
+    method public long getError();
+    method public long getOnBackground();
+    method public long getOnError();
+    method public long getOnPrimary();
+    method public long getOnPrimaryContainer();
+    method public long getOnSecondary();
+    method public long getOnSecondaryContainer();
+    method public long getOnSurface();
+    method public long getOnSurfaceVariant();
+    method public long getOnTertiary();
+    method public long getOnTertiaryContainer();
+    method public long getOutline();
+    method public long getOutlineVariant();
+    method public long getPrimary();
+    method public long getPrimaryContainer();
+    method public long getPrimaryDim();
+    method public long getSecondary();
+    method public long getSecondaryContainer();
+    method public long getSecondaryDim();
+    method public long getSurface();
+    method public long getSurfaceBright();
+    method public long getSurfaceDim();
+    method public long getTertiary();
+    method public long getTertiaryContainer();
+    method public long getTertiaryDim();
+    method public void setSecondaryDim(long);
+    property public final long background;
+    property public final long error;
+    property public final long onBackground;
+    property public final long onError;
+    property public final long onPrimary;
+    property public final long onPrimaryContainer;
+    property public final long onSecondary;
+    property public final long onSecondaryContainer;
+    property public final long onSurface;
+    property public final long onSurfaceVariant;
+    property public final long onTertiary;
+    property public final long onTertiaryContainer;
+    property public final long outline;
+    property public final long outlineVariant;
+    property public final long primary;
+    property public final long primaryContainer;
+    property public final long primaryDim;
+    property public final long secondary;
+    property public final long secondaryContainer;
+    property public final long secondaryDim;
+    property public final long surface;
+    property public final long surfaceBright;
+    property public final long surfaceDim;
+    property public final long tertiary;
+    property public final long tertiaryContainer;
+    property public final long tertiaryDim;
+  }
+
+  public final class ColorSchemeKt {
+    method public static long contentColorFor(androidx.wear.compose.material3.ColorScheme, long backgroundColor);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static long contentColorFor(long backgroundColor);
+  }
+
+  public final class ContentAlpha {
+    method @androidx.compose.runtime.Composable public float getDisabled();
+    method @androidx.compose.runtime.Composable public float getHigh();
+    method @androidx.compose.runtime.Composable public float getMedium();
+    property @androidx.compose.runtime.Composable public final float disabled;
+    property @androidx.compose.runtime.Composable public final float high;
+    property @androidx.compose.runtime.Composable public final float medium;
+    field public static final androidx.wear.compose.material3.ContentAlpha INSTANCE;
+  }
+
+  public final class ContentAlphaKt {
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Float> getLocalContentAlpha();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Float> LocalContentAlpha;
+  }
+
+  public final class ContentColorKt {
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> getLocalContentColor();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
+  }
+
+  public final class IconKt {
+    method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.vector.ImageVector imageVector, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.ImageBitmap bitmap, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.painter.Painter painter, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+  }
+
+  public final class MaterialTheme {
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.wear.compose.material3.ColorScheme getColorScheme();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.wear.compose.material3.Shapes getShapes();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.wear.compose.material3.Typography getTypography();
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.wear.compose.material3.ColorScheme colorScheme;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.wear.compose.material3.Shapes shapes;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.wear.compose.material3.Typography typography;
+    field public static final androidx.wear.compose.material3.MaterialTheme INSTANCE;
+  }
+
+  public final class MaterialThemeKt {
+    method @androidx.compose.runtime.Composable public static void MaterialTheme(optional androidx.wear.compose.material3.ColorScheme colorScheme, optional androidx.wear.compose.material3.Typography typography, optional androidx.wear.compose.material3.Shapes shapes, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class ShapeDefaults {
+    method public androidx.compose.foundation.shape.RoundedCornerShape getExtraLarge();
+    method public androidx.compose.foundation.shape.RoundedCornerShape getExtraSmall();
+    method public androidx.compose.foundation.shape.RoundedCornerShape getFull();
+    method public androidx.compose.foundation.shape.RoundedCornerShape getLarge();
+    method public androidx.compose.foundation.shape.RoundedCornerShape getMedium();
+    method public androidx.compose.ui.graphics.Shape getNone();
+    method public androidx.compose.foundation.shape.RoundedCornerShape getSmall();
+    property public final androidx.compose.foundation.shape.RoundedCornerShape ExtraLarge;
+    property public final androidx.compose.foundation.shape.RoundedCornerShape ExtraSmall;
+    property public final androidx.compose.foundation.shape.RoundedCornerShape Full;
+    property public final androidx.compose.foundation.shape.RoundedCornerShape Large;
+    property public final androidx.compose.foundation.shape.RoundedCornerShape Medium;
+    property public final androidx.compose.ui.graphics.Shape None;
+    property public final androidx.compose.foundation.shape.RoundedCornerShape Small;
+    field public static final androidx.wear.compose.material3.ShapeDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Immutable public final class Shapes {
+    ctor public Shapes(optional androidx.compose.ui.graphics.Shape none, optional androidx.compose.ui.graphics.Shape extraSmall, optional androidx.compose.ui.graphics.Shape small, optional androidx.compose.ui.graphics.Shape medium, optional androidx.compose.ui.graphics.Shape large, optional androidx.compose.ui.graphics.Shape extraLarge, optional androidx.compose.ui.graphics.Shape full);
+    method public androidx.wear.compose.material3.Shapes copy(optional androidx.compose.ui.graphics.Shape none, optional androidx.compose.ui.graphics.Shape extraSmall, optional androidx.compose.ui.graphics.Shape small, optional androidx.compose.ui.graphics.Shape medium, optional androidx.compose.ui.graphics.Shape large, optional androidx.compose.ui.graphics.Shape extraLarge, optional androidx.compose.ui.graphics.Shape full);
+    method public androidx.compose.ui.graphics.Shape getExtraLarge();
+    method public androidx.compose.ui.graphics.Shape getExtraSmall();
+    method public androidx.compose.ui.graphics.Shape getFull();
+    method public androidx.compose.ui.graphics.Shape getLarge();
+    method public androidx.compose.ui.graphics.Shape getMedium();
+    method public androidx.compose.ui.graphics.Shape getNone();
+    method public androidx.compose.ui.graphics.Shape getSmall();
+    property public final androidx.compose.ui.graphics.Shape extraLarge;
+    property public final androidx.compose.ui.graphics.Shape extraSmall;
+    property public final androidx.compose.ui.graphics.Shape full;
+    property public final androidx.compose.ui.graphics.Shape large;
+    property public final androidx.compose.ui.graphics.Shape medium;
+    property public final androidx.compose.ui.graphics.Shape none;
+    property public final androidx.compose.ui.graphics.Shape small;
+  }
+
+  public final class TextKt {
+    method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> getLocalTextStyle();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
+  }
+
+  @androidx.compose.runtime.Immutable public final class Typography {
+    ctor public Typography(optional androidx.compose.ui.text.font.FontFamily defaultFontFamily, optional androidx.compose.ui.text.TextStyle displayExtraLarge, optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle buttonMedium, optional androidx.compose.ui.text.TextStyle captionLarge, optional androidx.compose.ui.text.TextStyle captionMedium, optional androidx.compose.ui.text.TextStyle captionSmall);
+    method public androidx.wear.compose.material3.Typography copy(optional androidx.compose.ui.text.TextStyle displayExtraLarge, optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle buttonMedium, optional androidx.compose.ui.text.TextStyle captionLarge, optional androidx.compose.ui.text.TextStyle captionMedium, optional androidx.compose.ui.text.TextStyle captionSmall);
+    method public androidx.compose.ui.text.TextStyle getBodyLarge();
+    method public androidx.compose.ui.text.TextStyle getBodyMedium();
+    method public androidx.compose.ui.text.TextStyle getBodySmall();
+    method public androidx.compose.ui.text.TextStyle getButtonMedium();
+    method public androidx.compose.ui.text.TextStyle getCaptionLarge();
+    method public androidx.compose.ui.text.TextStyle getCaptionMedium();
+    method public androidx.compose.ui.text.TextStyle getCaptionSmall();
+    method public androidx.compose.ui.text.TextStyle getDisplayExtraLarge();
+    method public androidx.compose.ui.text.TextStyle getDisplayLarge();
+    method public androidx.compose.ui.text.TextStyle getDisplayMedium();
+    method public androidx.compose.ui.text.TextStyle getDisplaySmall();
+    method public androidx.compose.ui.text.TextStyle getTitleLarge();
+    method public androidx.compose.ui.text.TextStyle getTitleMedium();
+    method public androidx.compose.ui.text.TextStyle getTitleSmall();
+    property public final androidx.compose.ui.text.TextStyle bodyLarge;
+    property public final androidx.compose.ui.text.TextStyle bodyMedium;
+    property public final androidx.compose.ui.text.TextStyle bodySmall;
+    property public final androidx.compose.ui.text.TextStyle buttonMedium;
+    property public final androidx.compose.ui.text.TextStyle captionLarge;
+    property public final androidx.compose.ui.text.TextStyle captionMedium;
+    property public final androidx.compose.ui.text.TextStyle captionSmall;
+    property public final androidx.compose.ui.text.TextStyle displayExtraLarge;
+    property public final androidx.compose.ui.text.TextStyle displayLarge;
+    property public final androidx.compose.ui.text.TextStyle displayMedium;
+    property public final androidx.compose.ui.text.TextStyle displaySmall;
+    property public final androidx.compose.ui.text.TextStyle titleLarge;
+    property public final androidx.compose.ui.text.TextStyle titleMedium;
+    property public final androidx.compose.ui.text.TextStyle titleSmall;
+  }
+
+}
+
diff --git a/wear/compose/compose-material3/api/public_plus_experimental_current.txt b/wear/compose/compose-material3/api/public_plus_experimental_current.txt
index e6f50d0..2dbfac5 100644
--- a/wear/compose/compose-material3/api/public_plus_experimental_current.txt
+++ b/wear/compose/compose-material3/api/public_plus_experimental_current.txt
@@ -1 +1,186 @@
 // Signature format: 4.0
+package androidx.wear.compose.material3 {
+
+  @androidx.compose.runtime.Stable public final class 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 surfaceDim, optional long surface, optional long surfaceBright, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError);
+    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 surfaceDim, optional long surface, optional long surfaceBright, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError);
+    method public long getBackground();
+    method public long getError();
+    method public long getOnBackground();
+    method public long getOnError();
+    method public long getOnPrimary();
+    method public long getOnPrimaryContainer();
+    method public long getOnSecondary();
+    method public long getOnSecondaryContainer();
+    method public long getOnSurface();
+    method public long getOnSurfaceVariant();
+    method public long getOnTertiary();
+    method public long getOnTertiaryContainer();
+    method public long getOutline();
+    method public long getOutlineVariant();
+    method public long getPrimary();
+    method public long getPrimaryContainer();
+    method public long getPrimaryDim();
+    method public long getSecondary();
+    method public long getSecondaryContainer();
+    method public long getSecondaryDim();
+    method public long getSurface();
+    method public long getSurfaceBright();
+    method public long getSurfaceDim();
+    method public long getTertiary();
+    method public long getTertiaryContainer();
+    method public long getTertiaryDim();
+    method public void setSecondaryDim(long);
+    property public final long background;
+    property public final long error;
+    property public final long onBackground;
+    property public final long onError;
+    property public final long onPrimary;
+    property public final long onPrimaryContainer;
+    property public final long onSecondary;
+    property public final long onSecondaryContainer;
+    property public final long onSurface;
+    property public final long onSurfaceVariant;
+    property public final long onTertiary;
+    property public final long onTertiaryContainer;
+    property public final long outline;
+    property public final long outlineVariant;
+    property public final long primary;
+    property public final long primaryContainer;
+    property public final long primaryDim;
+    property public final long secondary;
+    property public final long secondaryContainer;
+    property public final long secondaryDim;
+    property public final long surface;
+    property public final long surfaceBright;
+    property public final long surfaceDim;
+    property public final long tertiary;
+    property public final long tertiaryContainer;
+    property public final long tertiaryDim;
+  }
+
+  public final class ColorSchemeKt {
+    method public static long contentColorFor(androidx.wear.compose.material3.ColorScheme, long backgroundColor);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static long contentColorFor(long backgroundColor);
+  }
+
+  public final class ContentAlpha {
+    method @androidx.compose.runtime.Composable public float getDisabled();
+    method @androidx.compose.runtime.Composable public float getHigh();
+    method @androidx.compose.runtime.Composable public float getMedium();
+    property @androidx.compose.runtime.Composable public final float disabled;
+    property @androidx.compose.runtime.Composable public final float high;
+    property @androidx.compose.runtime.Composable public final float medium;
+    field public static final androidx.wear.compose.material3.ContentAlpha INSTANCE;
+  }
+
+  public final class ContentAlphaKt {
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Float> getLocalContentAlpha();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Float> LocalContentAlpha;
+  }
+
+  public final class ContentColorKt {
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> getLocalContentColor();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
+  }
+
+  public final class IconKt {
+    method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.vector.ImageVector imageVector, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.ImageBitmap bitmap, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.painter.Painter painter, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+  }
+
+  public final class MaterialTheme {
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.wear.compose.material3.ColorScheme getColorScheme();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.wear.compose.material3.Shapes getShapes();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.wear.compose.material3.Typography getTypography();
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.wear.compose.material3.ColorScheme colorScheme;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.wear.compose.material3.Shapes shapes;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.wear.compose.material3.Typography typography;
+    field public static final androidx.wear.compose.material3.MaterialTheme INSTANCE;
+  }
+
+  public final class MaterialThemeKt {
+    method @androidx.compose.runtime.Composable public static void MaterialTheme(optional androidx.wear.compose.material3.ColorScheme colorScheme, optional androidx.wear.compose.material3.Typography typography, optional androidx.wear.compose.material3.Shapes shapes, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class ShapeDefaults {
+    method public androidx.compose.foundation.shape.RoundedCornerShape getExtraLarge();
+    method public androidx.compose.foundation.shape.RoundedCornerShape getExtraSmall();
+    method public androidx.compose.foundation.shape.RoundedCornerShape getFull();
+    method public androidx.compose.foundation.shape.RoundedCornerShape getLarge();
+    method public androidx.compose.foundation.shape.RoundedCornerShape getMedium();
+    method public androidx.compose.ui.graphics.Shape getNone();
+    method public androidx.compose.foundation.shape.RoundedCornerShape getSmall();
+    property public final androidx.compose.foundation.shape.RoundedCornerShape ExtraLarge;
+    property public final androidx.compose.foundation.shape.RoundedCornerShape ExtraSmall;
+    property public final androidx.compose.foundation.shape.RoundedCornerShape Full;
+    property public final androidx.compose.foundation.shape.RoundedCornerShape Large;
+    property public final androidx.compose.foundation.shape.RoundedCornerShape Medium;
+    property public final androidx.compose.ui.graphics.Shape None;
+    property public final androidx.compose.foundation.shape.RoundedCornerShape Small;
+    field public static final androidx.wear.compose.material3.ShapeDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Immutable public final class Shapes {
+    ctor public Shapes(optional androidx.compose.ui.graphics.Shape none, optional androidx.compose.ui.graphics.Shape extraSmall, optional androidx.compose.ui.graphics.Shape small, optional androidx.compose.ui.graphics.Shape medium, optional androidx.compose.ui.graphics.Shape large, optional androidx.compose.ui.graphics.Shape extraLarge, optional androidx.compose.ui.graphics.Shape full);
+    method public androidx.wear.compose.material3.Shapes copy(optional androidx.compose.ui.graphics.Shape none, optional androidx.compose.ui.graphics.Shape extraSmall, optional androidx.compose.ui.graphics.Shape small, optional androidx.compose.ui.graphics.Shape medium, optional androidx.compose.ui.graphics.Shape large, optional androidx.compose.ui.graphics.Shape extraLarge, optional androidx.compose.ui.graphics.Shape full);
+    method public androidx.compose.ui.graphics.Shape getExtraLarge();
+    method public androidx.compose.ui.graphics.Shape getExtraSmall();
+    method public androidx.compose.ui.graphics.Shape getFull();
+    method public androidx.compose.ui.graphics.Shape getLarge();
+    method public androidx.compose.ui.graphics.Shape getMedium();
+    method public androidx.compose.ui.graphics.Shape getNone();
+    method public androidx.compose.ui.graphics.Shape getSmall();
+    property public final androidx.compose.ui.graphics.Shape extraLarge;
+    property public final androidx.compose.ui.graphics.Shape extraSmall;
+    property public final androidx.compose.ui.graphics.Shape full;
+    property public final androidx.compose.ui.graphics.Shape large;
+    property public final androidx.compose.ui.graphics.Shape medium;
+    property public final androidx.compose.ui.graphics.Shape none;
+    property public final androidx.compose.ui.graphics.Shape small;
+  }
+
+  public final class TextKt {
+    method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> getLocalTextStyle();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
+  }
+
+  @androidx.compose.runtime.Immutable public final class Typography {
+    ctor public Typography(optional androidx.compose.ui.text.font.FontFamily defaultFontFamily, optional androidx.compose.ui.text.TextStyle displayExtraLarge, optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle buttonMedium, optional androidx.compose.ui.text.TextStyle captionLarge, optional androidx.compose.ui.text.TextStyle captionMedium, optional androidx.compose.ui.text.TextStyle captionSmall);
+    method public androidx.wear.compose.material3.Typography copy(optional androidx.compose.ui.text.TextStyle displayExtraLarge, optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle buttonMedium, optional androidx.compose.ui.text.TextStyle captionLarge, optional androidx.compose.ui.text.TextStyle captionMedium, optional androidx.compose.ui.text.TextStyle captionSmall);
+    method public androidx.compose.ui.text.TextStyle getBodyLarge();
+    method public androidx.compose.ui.text.TextStyle getBodyMedium();
+    method public androidx.compose.ui.text.TextStyle getBodySmall();
+    method public androidx.compose.ui.text.TextStyle getButtonMedium();
+    method public androidx.compose.ui.text.TextStyle getCaptionLarge();
+    method public androidx.compose.ui.text.TextStyle getCaptionMedium();
+    method public androidx.compose.ui.text.TextStyle getCaptionSmall();
+    method public androidx.compose.ui.text.TextStyle getDisplayExtraLarge();
+    method public androidx.compose.ui.text.TextStyle getDisplayLarge();
+    method public androidx.compose.ui.text.TextStyle getDisplayMedium();
+    method public androidx.compose.ui.text.TextStyle getDisplaySmall();
+    method public androidx.compose.ui.text.TextStyle getTitleLarge();
+    method public androidx.compose.ui.text.TextStyle getTitleMedium();
+    method public androidx.compose.ui.text.TextStyle getTitleSmall();
+    property public final androidx.compose.ui.text.TextStyle bodyLarge;
+    property public final androidx.compose.ui.text.TextStyle bodyMedium;
+    property public final androidx.compose.ui.text.TextStyle bodySmall;
+    property public final androidx.compose.ui.text.TextStyle buttonMedium;
+    property public final androidx.compose.ui.text.TextStyle captionLarge;
+    property public final androidx.compose.ui.text.TextStyle captionMedium;
+    property public final androidx.compose.ui.text.TextStyle captionSmall;
+    property public final androidx.compose.ui.text.TextStyle displayExtraLarge;
+    property public final androidx.compose.ui.text.TextStyle displayLarge;
+    property public final androidx.compose.ui.text.TextStyle displayMedium;
+    property public final androidx.compose.ui.text.TextStyle displaySmall;
+    property public final androidx.compose.ui.text.TextStyle titleLarge;
+    property public final androidx.compose.ui.text.TextStyle titleMedium;
+    property public final androidx.compose.ui.text.TextStyle titleSmall;
+  }
+
+}
+
diff --git a/wear/compose/compose-material3/api/restricted_current.txt b/wear/compose/compose-material3/api/restricted_current.txt
index e6f50d0..2dbfac5 100644
--- a/wear/compose/compose-material3/api/restricted_current.txt
+++ b/wear/compose/compose-material3/api/restricted_current.txt
@@ -1 +1,186 @@
 // Signature format: 4.0
+package androidx.wear.compose.material3 {
+
+  @androidx.compose.runtime.Stable public final class 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 surfaceDim, optional long surface, optional long surfaceBright, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError);
+    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 surfaceDim, optional long surface, optional long surfaceBright, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError);
+    method public long getBackground();
+    method public long getError();
+    method public long getOnBackground();
+    method public long getOnError();
+    method public long getOnPrimary();
+    method public long getOnPrimaryContainer();
+    method public long getOnSecondary();
+    method public long getOnSecondaryContainer();
+    method public long getOnSurface();
+    method public long getOnSurfaceVariant();
+    method public long getOnTertiary();
+    method public long getOnTertiaryContainer();
+    method public long getOutline();
+    method public long getOutlineVariant();
+    method public long getPrimary();
+    method public long getPrimaryContainer();
+    method public long getPrimaryDim();
+    method public long getSecondary();
+    method public long getSecondaryContainer();
+    method public long getSecondaryDim();
+    method public long getSurface();
+    method public long getSurfaceBright();
+    method public long getSurfaceDim();
+    method public long getTertiary();
+    method public long getTertiaryContainer();
+    method public long getTertiaryDim();
+    method public void setSecondaryDim(long);
+    property public final long background;
+    property public final long error;
+    property public final long onBackground;
+    property public final long onError;
+    property public final long onPrimary;
+    property public final long onPrimaryContainer;
+    property public final long onSecondary;
+    property public final long onSecondaryContainer;
+    property public final long onSurface;
+    property public final long onSurfaceVariant;
+    property public final long onTertiary;
+    property public final long onTertiaryContainer;
+    property public final long outline;
+    property public final long outlineVariant;
+    property public final long primary;
+    property public final long primaryContainer;
+    property public final long primaryDim;
+    property public final long secondary;
+    property public final long secondaryContainer;
+    property public final long secondaryDim;
+    property public final long surface;
+    property public final long surfaceBright;
+    property public final long surfaceDim;
+    property public final long tertiary;
+    property public final long tertiaryContainer;
+    property public final long tertiaryDim;
+  }
+
+  public final class ColorSchemeKt {
+    method public static long contentColorFor(androidx.wear.compose.material3.ColorScheme, long backgroundColor);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static long contentColorFor(long backgroundColor);
+  }
+
+  public final class ContentAlpha {
+    method @androidx.compose.runtime.Composable public float getDisabled();
+    method @androidx.compose.runtime.Composable public float getHigh();
+    method @androidx.compose.runtime.Composable public float getMedium();
+    property @androidx.compose.runtime.Composable public final float disabled;
+    property @androidx.compose.runtime.Composable public final float high;
+    property @androidx.compose.runtime.Composable public final float medium;
+    field public static final androidx.wear.compose.material3.ContentAlpha INSTANCE;
+  }
+
+  public final class ContentAlphaKt {
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Float> getLocalContentAlpha();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Float> LocalContentAlpha;
+  }
+
+  public final class ContentColorKt {
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> getLocalContentColor();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
+  }
+
+  public final class IconKt {
+    method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.vector.ImageVector imageVector, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.ImageBitmap bitmap, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+    method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.painter.Painter painter, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
+  }
+
+  public final class MaterialTheme {
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.wear.compose.material3.ColorScheme getColorScheme();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.wear.compose.material3.Shapes getShapes();
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.wear.compose.material3.Typography getTypography();
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.wear.compose.material3.ColorScheme colorScheme;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.wear.compose.material3.Shapes shapes;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.wear.compose.material3.Typography typography;
+    field public static final androidx.wear.compose.material3.MaterialTheme INSTANCE;
+  }
+
+  public final class MaterialThemeKt {
+    method @androidx.compose.runtime.Composable public static void MaterialTheme(optional androidx.wear.compose.material3.ColorScheme colorScheme, optional androidx.wear.compose.material3.Typography typography, optional androidx.wear.compose.material3.Shapes shapes, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
+  public final class ShapeDefaults {
+    method public androidx.compose.foundation.shape.RoundedCornerShape getExtraLarge();
+    method public androidx.compose.foundation.shape.RoundedCornerShape getExtraSmall();
+    method public androidx.compose.foundation.shape.RoundedCornerShape getFull();
+    method public androidx.compose.foundation.shape.RoundedCornerShape getLarge();
+    method public androidx.compose.foundation.shape.RoundedCornerShape getMedium();
+    method public androidx.compose.ui.graphics.Shape getNone();
+    method public androidx.compose.foundation.shape.RoundedCornerShape getSmall();
+    property public final androidx.compose.foundation.shape.RoundedCornerShape ExtraLarge;
+    property public final androidx.compose.foundation.shape.RoundedCornerShape ExtraSmall;
+    property public final androidx.compose.foundation.shape.RoundedCornerShape Full;
+    property public final androidx.compose.foundation.shape.RoundedCornerShape Large;
+    property public final androidx.compose.foundation.shape.RoundedCornerShape Medium;
+    property public final androidx.compose.ui.graphics.Shape None;
+    property public final androidx.compose.foundation.shape.RoundedCornerShape Small;
+    field public static final androidx.wear.compose.material3.ShapeDefaults INSTANCE;
+  }
+
+  @androidx.compose.runtime.Immutable public final class Shapes {
+    ctor public Shapes(optional androidx.compose.ui.graphics.Shape none, optional androidx.compose.ui.graphics.Shape extraSmall, optional androidx.compose.ui.graphics.Shape small, optional androidx.compose.ui.graphics.Shape medium, optional androidx.compose.ui.graphics.Shape large, optional androidx.compose.ui.graphics.Shape extraLarge, optional androidx.compose.ui.graphics.Shape full);
+    method public androidx.wear.compose.material3.Shapes copy(optional androidx.compose.ui.graphics.Shape none, optional androidx.compose.ui.graphics.Shape extraSmall, optional androidx.compose.ui.graphics.Shape small, optional androidx.compose.ui.graphics.Shape medium, optional androidx.compose.ui.graphics.Shape large, optional androidx.compose.ui.graphics.Shape extraLarge, optional androidx.compose.ui.graphics.Shape full);
+    method public androidx.compose.ui.graphics.Shape getExtraLarge();
+    method public androidx.compose.ui.graphics.Shape getExtraSmall();
+    method public androidx.compose.ui.graphics.Shape getFull();
+    method public androidx.compose.ui.graphics.Shape getLarge();
+    method public androidx.compose.ui.graphics.Shape getMedium();
+    method public androidx.compose.ui.graphics.Shape getNone();
+    method public androidx.compose.ui.graphics.Shape getSmall();
+    property public final androidx.compose.ui.graphics.Shape extraLarge;
+    property public final androidx.compose.ui.graphics.Shape extraSmall;
+    property public final androidx.compose.ui.graphics.Shape full;
+    property public final androidx.compose.ui.graphics.Shape large;
+    property public final androidx.compose.ui.graphics.Shape medium;
+    property public final androidx.compose.ui.graphics.Shape none;
+    property public final androidx.compose.ui.graphics.Shape small;
+  }
+
+  public final class TextKt {
+    method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional int minLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> getLocalTextStyle();
+    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
+  }
+
+  @androidx.compose.runtime.Immutable public final class Typography {
+    ctor public Typography(optional androidx.compose.ui.text.font.FontFamily defaultFontFamily, optional androidx.compose.ui.text.TextStyle displayExtraLarge, optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle buttonMedium, optional androidx.compose.ui.text.TextStyle captionLarge, optional androidx.compose.ui.text.TextStyle captionMedium, optional androidx.compose.ui.text.TextStyle captionSmall);
+    method public androidx.wear.compose.material3.Typography copy(optional androidx.compose.ui.text.TextStyle displayExtraLarge, optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle buttonMedium, optional androidx.compose.ui.text.TextStyle captionLarge, optional androidx.compose.ui.text.TextStyle captionMedium, optional androidx.compose.ui.text.TextStyle captionSmall);
+    method public androidx.compose.ui.text.TextStyle getBodyLarge();
+    method public androidx.compose.ui.text.TextStyle getBodyMedium();
+    method public androidx.compose.ui.text.TextStyle getBodySmall();
+    method public androidx.compose.ui.text.TextStyle getButtonMedium();
+    method public androidx.compose.ui.text.TextStyle getCaptionLarge();
+    method public androidx.compose.ui.text.TextStyle getCaptionMedium();
+    method public androidx.compose.ui.text.TextStyle getCaptionSmall();
+    method public androidx.compose.ui.text.TextStyle getDisplayExtraLarge();
+    method public androidx.compose.ui.text.TextStyle getDisplayLarge();
+    method public androidx.compose.ui.text.TextStyle getDisplayMedium();
+    method public androidx.compose.ui.text.TextStyle getDisplaySmall();
+    method public androidx.compose.ui.text.TextStyle getTitleLarge();
+    method public androidx.compose.ui.text.TextStyle getTitleMedium();
+    method public androidx.compose.ui.text.TextStyle getTitleSmall();
+    property public final androidx.compose.ui.text.TextStyle bodyLarge;
+    property public final androidx.compose.ui.text.TextStyle bodyMedium;
+    property public final androidx.compose.ui.text.TextStyle bodySmall;
+    property public final androidx.compose.ui.text.TextStyle buttonMedium;
+    property public final androidx.compose.ui.text.TextStyle captionLarge;
+    property public final androidx.compose.ui.text.TextStyle captionMedium;
+    property public final androidx.compose.ui.text.TextStyle captionSmall;
+    property public final androidx.compose.ui.text.TextStyle displayExtraLarge;
+    property public final androidx.compose.ui.text.TextStyle displayLarge;
+    property public final androidx.compose.ui.text.TextStyle displayMedium;
+    property public final androidx.compose.ui.text.TextStyle displaySmall;
+    property public final androidx.compose.ui.text.TextStyle titleLarge;
+    property public final androidx.compose.ui.text.TextStyle titleMedium;
+    property public final androidx.compose.ui.text.TextStyle titleSmall;
+  }
+
+}
+
diff --git a/wear/compose/compose-material3/build.gradle b/wear/compose/compose-material3/build.gradle
index fd5a497..8e0a522 100644
--- a/wear/compose/compose-material3/build.gradle
+++ b/wear/compose/compose-material3/build.gradle
@@ -40,7 +40,7 @@
     implementation(project(":compose:ui:ui-util"))
     implementation(project(":wear:compose:compose-foundation"))
     implementation(project(":wear:compose:compose-material-core"))
-    implementation("androidx.profileinstaller:profileinstaller:1.2.0")
+    implementation("androidx.profileinstaller:profileinstaller:1.3.0")
 
     androidTestImplementation(project(":compose:ui:ui-test"))
     androidTestImplementation(project(":compose:ui:ui-test-junit4"))
diff --git a/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/IconTest.kt b/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/IconTest.kt
new file mode 100644
index 0000000..729aace
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/IconTest.kt
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3
+
+import android.os.Build
+import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Menu
+import androidx.compose.testutils.assertPixels
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Canvas
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ImageBitmap
+import androidx.compose.ui.graphics.drawscope.CanvasDrawScope
+import androidx.compose.ui.graphics.painter.BitmapPainter
+import androidx.compose.ui.graphics.painter.ColorPainter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertContentDescriptionEquals
+import androidx.compose.ui.test.assertHeightIsEqualTo
+import androidx.compose.ui.test.assertWidthIsEqualTo
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.SdkSuppress
+import org.junit.Rule
+import org.junit.Test
+
+class IconTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    private val testTag = "TestText"
+
+    @Test
+    fun vector_materialIconSize_dimensions() {
+        val width = 24.dp
+        val height = 24.dp
+        val vector = Icons.Filled.Menu
+        rule
+            .setContentWithThemeForSizeAssertions {
+                Icon(vector, null)
+            }
+            .assertWidthIsEqualTo(width)
+            .assertHeightIsEqualTo(height)
+    }
+
+    @Test
+    fun vector_customIconSize_dimensions() {
+        val width = 35.dp
+        val height = 83.dp
+        val vector = ImageVector.Builder(
+            defaultWidth = width, defaultHeight = height,
+            viewportWidth = width.value, viewportHeight = height.value
+        ).build()
+        rule
+            .setContentWithThemeForSizeAssertions {
+                Icon(vector, null)
+            }
+            .assertWidthIsEqualTo(width)
+            .assertHeightIsEqualTo(height)
+    }
+
+    @Test
+    fun image_noIntrinsicSize_dimensions() {
+        val width = 24.dp
+        val height = 24.dp
+        rule
+            .setContentWithThemeForSizeAssertions {
+                val image = with(LocalDensity.current) {
+                    ImageBitmap(width.roundToPx(), height.roundToPx())
+                }
+
+                Icon(image, null)
+            }
+            .assertWidthIsEqualTo(width)
+            .assertHeightIsEqualTo(height)
+    }
+
+    @Test
+    fun image_withIntrinsicSize_dimensions() {
+        val width = 35.dp
+        val height = 83.dp
+
+        rule
+            .setContentWithThemeForSizeAssertions {
+                val image = with(LocalDensity.current) {
+                    ImageBitmap(width.roundToPx(), height.roundToPx())
+                }
+
+                Icon(image, null)
+            }
+            .assertWidthIsEqualTo(width)
+            .assertHeightIsEqualTo(height)
+    }
+
+    @Test
+    fun painter_noIntrinsicSize_dimensions() {
+        val width = 24.dp
+        val height = 24.dp
+        val painter = ColorPainter(Color.Red)
+
+        rule
+            .setContentWithThemeForSizeAssertions {
+                Icon(painter, null)
+            }
+            .assertWidthIsEqualTo(width)
+            .assertHeightIsEqualTo(height)
+    }
+
+    @Test
+    fun painter_withIntrinsicSize_dimensions() {
+        val width = 35.dp
+        val height = 83.dp
+
+        rule
+            .setContentWithThemeForSizeAssertions {
+                val image = with(LocalDensity.current) {
+                    ImageBitmap(width.roundToPx(), height.roundToPx())
+                }
+
+                val bitmapPainter = BitmapPainter(image)
+                Icon(bitmapPainter, null)
+            }
+            .assertWidthIsEqualTo(width)
+            .assertHeightIsEqualTo(height)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun iconScalesToFitSize() {
+        // Image with intrinsic size of 24dp
+        val width = 24.dp
+        val height = 24.dp
+        var expectedIntSize: IntSize? = null
+
+        rule
+            .setContentWithTheme {
+                val image: ImageBitmap
+                with(LocalDensity.current) {
+                    image = createBitmapWithColor(
+                        this,
+                        width.roundToPx(),
+                        height.roundToPx(),
+                        Color.Red
+                    )
+                }
+                Icon(
+                    image,
+                    null,
+                    // Force Icon to be 50dp
+                    modifier = Modifier.requiredSize(50.dp).testTag(testTag),
+                    tint = Color.Unspecified
+                )
+                with(LocalDensity.current) {
+                    val dimension = 50.dp.roundToPx()
+                    expectedIntSize = IntSize(dimension, dimension)
+                }
+            }
+
+        rule.onNodeWithTag(testTag)
+            .captureToImage()
+            // The icon should be 50x50 and fill the whole size with red pixels
+            .assertPixels(expectedSize = expectedIntSize!!) {
+                Color.Red
+            }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun iconUnspecifiedTintColorIgnored() {
+        val width = 35.dp
+        val height = 83.dp
+
+        rule
+            .setContentWithTheme {
+                val image: ImageBitmap
+                with(LocalDensity.current) {
+                    image = createBitmapWithColor(
+                        this,
+                        width.roundToPx(),
+                        height.roundToPx(),
+                        Color.Red
+                    )
+                }
+                Icon(
+                    image,
+                    null,
+                    modifier = Modifier.testTag(testTag),
+                    tint = Color.Unspecified
+                )
+            }
+
+        // With no color provided for a tint, the icon should render the original pixels
+        rule.onNodeWithTag(testTag).captureToImage().assertPixels { Color.Red }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun iconSpecifiedTintColorApplied() {
+        val width = 35.dp
+        val height = 83.dp
+
+        rule
+            .setContentWithTheme {
+                val image: ImageBitmap
+                with(LocalDensity.current) {
+                    image = createBitmapWithColor(
+                        this,
+                        width.roundToPx(),
+                        height.roundToPx(),
+                        Color.Red
+                    )
+                }
+                Icon(
+                    image,
+                    null,
+                    modifier = Modifier.testTag(testTag),
+                    tint = Color.Blue
+                )
+            }
+
+        // With a tint color provided, all pixels should be blue
+        rule.onNodeWithTag(testTag).captureToImage().assertPixels { Color.Blue }
+    }
+
+    @Test
+    fun defaultSemanticsWhenContentDescriptionProvided() {
+        rule
+            .setContent {
+                Icon(
+                    bitmap = ImageBitmap(100, 100),
+                    contentDescription = "qwerty",
+                    modifier = Modifier.testTag(testTag)
+                )
+            }
+
+        rule.onNodeWithTag(testTag)
+            .assertContentDescriptionEquals("qwerty")
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Image))
+    }
+
+    private fun createBitmapWithColor(
+        density: Density,
+        width: Int,
+        height: Int,
+        color: Color
+    ): ImageBitmap {
+        val size = Size(width.toFloat(), height.toFloat())
+        val image = ImageBitmap(width, height)
+        CanvasDrawScope().draw(
+            density,
+            LayoutDirection.Ltr,
+            Canvas(image),
+            size
+        ) {
+            drawRect(color)
+        }
+        return image
+    }
+}
\ No newline at end of file
diff --git a/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/Material3Test.kt b/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/Material3Test.kt
new file mode 100644
index 0000000..f427c75
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/Material3Test.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.sizeIn
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+
+val BigTestMaxWidth = 5000.dp
+val BigTestMaxHeight = 5000.dp
+
+fun ComposeContentTestRule.setContentWithThemeForSizeAssertions(
+    parentMaxWidth: Dp = BigTestMaxWidth,
+    parentMaxHeight: Dp = BigTestMaxHeight,
+    useUnmergedTree: Boolean = false,
+    content: @Composable () -> Unit
+): SemanticsNodeInteraction {
+    setContent {
+        MaterialTheme {
+            Box {
+                Box(
+                    Modifier
+                        .sizeIn(
+                            maxWidth = parentMaxWidth,
+                            maxHeight = parentMaxHeight
+                        )
+                        .testTag("containerForSizeAssertion")
+                ) {
+                    content()
+                }
+            }
+        }
+    }
+
+    return onNodeWithTag("containerForSizeAssertion", useUnmergedTree)
+}
+
+fun ComposeContentTestRule.setContentWithTheme(
+    modifier: Modifier = Modifier,
+    composable: @Composable BoxScope.() -> Unit
+) {
+    setContent {
+        MaterialTheme {
+            Box(modifier = modifier, content = composable)
+        }
+    }
+}
\ No newline at end of file
diff --git a/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/TextTest.kt b/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/TextTest.kt
new file mode 100644
index 0000000..a61944f
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidAndroidTest/kotlin/androidx/wear/compose/material3/TextTest.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.wear.compose.material3
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.text.TextStyle
+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.style.TextAlign
+import androidx.compose.ui.text.style.TextDecoration
+import androidx.compose.ui.unit.TextUnit
+import androidx.compose.ui.unit.em
+import androidx.compose.ui.unit.sp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.lang.IllegalArgumentException
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TextTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    private val ExpectedTextStyle = TextStyle(
+        color = Color.Red,
+        fontSize = 32.sp,
+        fontStyle = FontStyle.Italic,
+        fontWeight = FontWeight.Normal,
+        fontFamily = FontFamily.Default,
+        letterSpacing = 1.sp,
+        textDecoration = TextDecoration.Underline,
+        textAlign = TextAlign.End,
+        lineHeight = 10.sp,
+    )
+
+    private val TestText = "TestText"
+
+    @Test
+    fun validateGreaterMinLinesResultsGreaterSize() {
+        var size1 = 0
+        var size2 = 0
+
+        rule.setContent {
+            CompositionLocalProvider(LocalContentColor provides Color.Blue) {
+                Column(Modifier.background(Color.White)) {
+                    Text(
+                        "Lorem ipsum",
+                        minLines = 1,
+                        maxLines = 3,
+                        onTextLayout = {
+                            size1 = it.size.height
+                        }
+                    )
+
+                    Text(
+                        "Lorem ipsum",
+                        minLines = 2,
+                        maxLines = 3,
+                        onTextLayout = {
+                            size2 = it.size.height
+                        }
+                    )
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(size2).isGreaterThan(size1)
+        }
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun validateMinLinesGreaterThanZero() {
+        rule.setContent {
+            Text(
+                TestText,
+                minLines = 0
+            )
+        }
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun validateMaxLinesGreaterThanMinLines() {
+        rule.setContent {
+            Text(
+                TestText,
+                minLines = 2,
+                maxLines = 1
+            )
+        }
+    }
+
+    @Test
+    fun colorParameterOverridesStyleColor() {
+        var textColor: Color? = null
+        val expectedColor = Color.Red
+
+        rule.setContent {
+            CompositionLocalProvider(LocalContentColor provides Color.Blue) {
+                ProvideTextStyle(ExpectedTextStyle) {
+                    Box(Modifier.background(Color.White)) {
+                        Text(
+                            TestText,
+                            color = expectedColor,
+                            onTextLayout = {
+                                textColor = it.layoutInput.style.color
+                            }
+                        )
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(textColor).isEqualTo(expectedColor)
+        }
+    }
+
+    @Test
+    fun styleColorOverridesLocalContentColor() {
+        var textColor: Color? = null
+
+        rule.setContent {
+            CompositionLocalProvider(LocalContentColor provides Color.Blue) {
+                ProvideTextStyle(ExpectedTextStyle) {
+                    Box(Modifier.background(Color.White)) {
+                        Text(
+                            TestText,
+                            onTextLayout = {
+                                textColor = it.layoutInput.style.color
+                            }
+                        )
+                    }
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(textColor).isEqualTo(ExpectedTextStyle.color)
+        }
+    }
+
+    @Test
+    fun usesLocalContentColorAsFallback() {
+        var textColor: Color? = null
+
+        rule.setContent {
+            CompositionLocalProvider(LocalContentColor provides Color.Blue) {
+                Box(Modifier.background(Color.White)) {
+                    Text(
+                        TestText,
+                        onTextLayout = {
+                            textColor = it.layoutInput.style.color
+                        }
+                    )
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(textColor).isEqualTo(Color.Blue)
+        }
+    }
+
+    @Test
+    fun inheritsTextStyle() {
+        var fontSize: TextUnit? = null
+        var fontStyle: FontStyle? = null
+        var fontWeight: FontWeight? = null
+        var fontFamily: FontFamily? = null
+        var letterSpacing: TextUnit? = null
+        var textDecoration: TextDecoration? = null
+        var textAlign: TextAlign? = null
+
+        rule.setContent {
+            ProvideTextStyle(ExpectedTextStyle) {
+                Box(Modifier.background(Color.White)) {
+                    Text(
+                        TestText,
+                        onTextLayout = {
+                            fontSize = it.layoutInput.style.fontSize
+                            fontStyle = it.layoutInput.style.fontStyle
+                            fontWeight = it.layoutInput.style.fontWeight
+                            fontFamily = it.layoutInput.style.fontFamily
+                            letterSpacing = it.layoutInput.style.letterSpacing
+                            textDecoration = it.layoutInput.style.textDecoration
+                            textAlign = it.layoutInput.style.textAlign
+                        }
+                    )
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(fontSize).isEqualTo(ExpectedTextStyle.fontSize)
+            Truth.assertThat(fontStyle).isEqualTo(ExpectedTextStyle.fontStyle)
+            Truth.assertThat(fontWeight).isEqualTo(ExpectedTextStyle.fontWeight)
+            Truth.assertThat(fontFamily).isEqualTo(ExpectedTextStyle.fontFamily)
+            Truth.assertThat(letterSpacing).isEqualTo(ExpectedTextStyle.letterSpacing)
+            Truth.assertThat(textDecoration).isEqualTo(ExpectedTextStyle.textDecoration)
+            Truth.assertThat(textAlign).isEqualTo(ExpectedTextStyle.textAlign)
+        }
+    }
+
+    @Test
+    fun setsParametersExplicitly() {
+        // Test to ensure that when parameter is set explicitly, then this parameter will be used
+        var textAlign: TextAlign? = null
+        var fontSize: TextUnit? = null
+        var fontStyle: FontStyle? = null
+        var letterSpacing: TextUnit? = null
+        val expectedColor = Color.Red
+        val expectedTextAlign = TextAlign.End
+        val expectedFontSize = 32.sp
+        val expectedFontStyle = FontStyle.Italic
+        val expectedLetterSpacing = 1.em
+
+        rule.setContent {
+            ProvideTextStyle(ExpectedTextStyle) {
+                Box(Modifier.background(Color.White)) {
+                    Text(
+                        TestText,
+                        color = expectedColor,
+                        textAlign = expectedTextAlign,
+                        fontSize = expectedFontSize,
+                        fontStyle = expectedFontStyle,
+                        letterSpacing = expectedLetterSpacing,
+                        onTextLayout = {
+                            textAlign = it.layoutInput.style.textAlign
+                            fontSize = it.layoutInput.style.fontSize
+                            fontStyle = it.layoutInput.style.fontStyle
+                            letterSpacing = it.layoutInput.style.letterSpacing
+                        }
+                    )
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(textAlign).isEqualTo(expectedTextAlign)
+            Truth.assertThat(fontSize).isEqualTo(expectedFontSize)
+            Truth.assertThat(fontStyle).isEqualTo(expectedFontStyle)
+            Truth.assertThat(letterSpacing).isEqualTo(expectedLetterSpacing)
+        }
+    }
+}
\ No newline at end of file
diff --git a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/ColorScheme.kt b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/ColorScheme.kt
new file mode 100644
index 0000000..1216db9
--- /dev/null
+++ b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/ColorScheme.kt
@@ -0,0 +1,456 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.wear.compose.material3
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.staticCompositionLocalOf
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.takeOrElse
+
+/**
+ * A [ColorScheme] holds all the named color parameters for a [MaterialTheme].
+ *
+ * Color schemes are designed to be harmonious, ensure accessible text, and distinguish UI
+ * elements and surfaces from one another.
+ *
+ * The Material color system and custom schemes provide default values for color as a starting point
+ * for customization.
+ *
+ * To learn more about color schemes,
+ * see [Material Design Color System](https://m3.material.io/styles/color/the-color-system/color-roles).
+ *
+ * @property primary The primary color is the color displayed most frequently across your app’s
+ * screens and components.
+ * @property primaryDim is less prominent than [primary] for component backgrounds
+ * @property primaryContainer is a standout container color for key components.
+ * @property onPrimary Color used for text and icons displayed on top of the primary color.
+ * @property onPrimaryContainer The color (and state variants) that should be used for content on
+ * top of [primaryContainer].
+ * @property secondary The secondary color provides more ways to accent and distinguish your
+ * product.
+ * @property secondaryDim is less prominent than [secondary] for component backgrounds.
+ * @property secondaryContainer A tonal color to be used in containers.
+ * @property onSecondary Color used for text and icons displayed on top of the secondary color.
+ * @property onSecondaryContainer The color (and state variants) that should be used for content on
+ * top of [secondaryContainer].
+ * @property tertiary The tertiary color that can be used to balance primary and secondary
+ * colors, or bring heightened attention to an element.
+ * @property tertiaryDim A less prominent tertiary color that can be used to balance
+ * primary and secondary colors, or bring heightened attention to an element.
+ * @property tertiaryContainer A tonal color to be used in containers.
+ * @property onTertiary Color used for text and icons displayed on top of the tertiary color.
+ * @property onTertiaryContainer The color (and state variants) that should be used for content on
+ * top of [tertiaryContainer].
+ * @property surfaceDim A surface color used for large containment components
+ * such as Card and Button with low prominence.
+ * @property surface The main surface color that affect surfaces of components with large
+ * containment areas, such as Card and Button.
+ * @property surfaceBright A surface color used for large containment components
+ * such Card and Button with high prominence.
+ * @property onSurface Color used for text and icons displayed on top of the surface color.
+ * @property onSurfaceVariant The color for secondary text and icons on top of
+ * [surface].
+ * @property outline The main color for primary outline components.
+ * The outline color role adds contrast for accessibility purposes.
+ * @property outlineVariant The secondary color for secondary outline components.
+ * @property background The background color that appears behind other content.
+ * @property onBackground Color used for text and icons displayed on top of the background color.
+ * @property error The error color is used to indicate errors.
+ * @property onError Color used for text and icons displayed on top of the error color.
+ */@Stable
+public class ColorScheme(
+    primary: Color = Color(0xFFD3E3FD),
+    primaryDim: Color = Color(0xFFA8C7FA),
+    primaryContainer: Color = Color(0xFF04409F),
+    onPrimary: Color = Color(0xFF001944),
+    onPrimaryContainer: Color = Color(0xFFD3E3FD),
+    secondary: Color = Color(0xFFC2E7FF),
+    secondaryDim: Color = Color(0xFF7FCFFF),
+    secondaryContainer: Color = Color(0xFF004A77),
+    onSecondary: Color = Color(0xFF001D35),
+    onSecondaryContainer: Color = Color(0xFFC2E7FF),
+    tertiary: Color = Color(0xFFC3EDCF),
+    tertiaryDim: Color = Color(0xFF73DC92),
+    tertiaryContainer: Color = Color(0xFF0F5223),
+    onTertiary: Color = Color(0xFF02210C),
+    onTertiaryContainer: Color = Color(0xFFC3EDCF),
+    surfaceDim: Color = Color(0xFF252626),
+    surface: Color = Color(0xFF303030),
+    surfaceBright: Color = Color(0xFF474747),
+    onSurface: Color = Color(0xFFF2F2F2),
+    onSurfaceVariant: Color = Color(0xFFC4C7C5),
+    outline: Color = Color(0xFF8E918F),
+    outlineVariant: Color = Color(0xFF5C5F5E),
+    background: Color = Color.Black,
+    onBackground: Color = Color(0xFFFFFFFF),
+    error: Color = Color(0xFFFD7267),
+    onError: Color = Color(0xFF410002),
+) {
+    /**
+     * [primary] is the main color used across screens and components
+     */
+    public var primary: Color by mutableStateOf(primary)
+        internal set
+
+    /**
+     * [primaryDim] is less prominent than [primary] for component backgrounds
+     */
+    public var primaryDim: Color by mutableStateOf(primaryDim)
+        internal set
+
+    /**
+     * [primaryContainer] is a standout container color for key components
+     */
+    public var primaryContainer: Color by mutableStateOf(primaryContainer)
+        internal set
+
+    /**
+     * [onPrimary] is for text and icons shown against the Primary and primaryDim colors
+     */
+    public var onPrimary: Color by mutableStateOf(onPrimary)
+        internal set
+
+    /**
+     * [onPrimaryContainer] is a contrast-passing color shown against the primaryContainer
+     */
+    public var onPrimaryContainer: Color by mutableStateOf(onPrimaryContainer)
+        internal set
+
+    /**
+     * [secondary] is an accent color used across screens and components
+     */
+    public var secondary: Color by mutableStateOf(secondary)
+        internal set
+
+    /**
+     * [secondaryDim] is less prominent than [secondary] for component backgrounds
+     */
+    public var secondaryDim: Color by mutableStateOf(secondaryDim)
+
+    /**
+     * [secondaryContainer] is a less prominent container color than [primaryContainer],
+     * for components like tonal buttons
+     */
+    public var secondaryContainer: Color by mutableStateOf(secondaryContainer)
+        internal set
+
+    /**
+     * [onSecondary] is for text and icons shown against the Secondary and SecondaryDim colors
+     */
+    public var onSecondary: Color by mutableStateOf(onSecondary)
+        internal set
+
+    /**
+     * [onSecondaryContainer] is a contrast-passing color shown against the secondaryContainer
+     */
+    public var onSecondaryContainer: Color by mutableStateOf(onSecondaryContainer)
+        internal set
+
+    /**
+     * [tertiary] is a complementary color to create contrast and draw attention to elements
+     */
+    public var tertiary: Color by mutableStateOf(tertiary)
+        internal set
+
+    /**
+     * [tertiaryDim] is less prominent than [tertiary] for component backgrounds
+     */
+    public var tertiaryDim: Color by mutableStateOf(tertiaryDim)
+        internal set
+
+    /**
+     * [tertiaryContainer] is a contrasting container color for components
+     */
+    public var tertiaryContainer: Color by mutableStateOf(tertiaryContainer)
+        internal set
+
+    /**
+     * [onTertiary] is for text and icons shown against the Tertiary and tertiaryDim colors
+     */
+    public var onTertiary: Color by mutableStateOf(onTertiary)
+        internal set
+
+    /**
+     * [onTertiaryContainer] is a contrast-passing color shown against the tertiaryContainer
+     */
+    public var onTertiaryContainer: Color by mutableStateOf(onTertiaryContainer)
+        internal set
+
+    /**
+     * [surfaceDim] is a surface color used for large containment components, with the
+     * lowest prominence behind Surface and surfaceBright
+     */
+    public var surfaceDim: Color by mutableStateOf(surfaceDim)
+        internal set
+
+    /**
+     * [surface] is the main color for large containment components like card and button backgrounds
+     */
+    public var surface: Color by mutableStateOf(surface)
+        internal set
+
+    /**
+     * [surfaceBright] is a surface color used for large containment components, with
+     * the highest prominence, ahead of Surface and surfaceDim
+     */
+    public var surfaceBright: Color by mutableStateOf(surfaceBright)
+        internal set
+
+    /**
+     * [onSurface] for primary text and icons shown against the
+     * [surface], [surfaceDim] and [surfaceBright]
+     */
+    public var onSurface: Color by mutableStateOf(onSurface)
+        internal set
+
+    /**
+     * [onSurfaceVariant] for secondary text and icons on
+     * [surface], [surfaceDim] and [surfaceBright]
+     */
+    public var onSurfaceVariant: Color by mutableStateOf(onSurfaceVariant)
+        internal set
+
+    /**
+     * [outline] is the main color for primary outline components
+     */
+    public var outline: Color by mutableStateOf(outline)
+        internal set
+
+    /**
+     * [outlineVariant] is the secondary color for secondary outline components
+     */
+    public var outlineVariant: Color by mutableStateOf(outlineVariant)
+        internal set
+
+    /**
+     * [background] is the static color used behind all texts and components
+     */
+    public var background: Color by mutableStateOf(background)
+        internal set
+
+    /**
+     * [onBackground] is used for text and icons shown against the background color
+     */
+    public var onBackground: Color by mutableStateOf(onBackground)
+        internal set
+
+    /**
+     * [error] indicates errors and emergency states
+     */
+    public var error: Color by mutableStateOf(error)
+        internal set
+
+    /**
+     * [onError] is used for text and icons on the error color
+     */
+    public var onError: Color by mutableStateOf(onError)
+        internal set
+
+    /**
+     * Returns a copy of this Colors, optionally overriding some of the values.
+     */
+    public fun copy(
+        primary: Color = this.primary,
+        primaryDim: Color = this.primaryDim,
+        primaryContainer: Color = this.primaryContainer,
+        onPrimary: Color = this.onPrimary,
+        onPrimaryContainer: Color = this.onPrimaryContainer,
+        secondary: Color = this.secondary,
+        secondaryDim: Color = this.secondaryDim,
+        secondaryContainer: Color = this.secondaryContainer,
+        onSecondary: Color = this.onSecondary,
+        onSecondaryContainer: Color = this.onSecondaryContainer,
+        tertiary: Color = this.tertiary,
+        tertiaryDim: Color = this.tertiaryDim,
+        tertiaryContainer: Color = this.tertiaryContainer,
+        onTertiary: Color = this.onTertiary,
+        onTertiaryContainer: Color = this.onTertiaryContainer,
+        surfaceDim: Color = this.surfaceDim,
+        surface: Color = this.surface,
+        surfaceBright: Color = this.surfaceBright,
+        onSurface: Color = this.onSurface,
+        onSurfaceVariant: Color = this.onSurfaceVariant,
+        outline: Color = this.outline,
+        outlineVariant: Color = this.outlineVariant,
+        background: Color = this.background,
+        onBackground: Color = this.onBackground,
+        error: Color = this.error,
+        onError: Color = this.onError
+    ): ColorScheme = ColorScheme(
+        primary = primary,
+        primaryDim = primaryDim,
+        primaryContainer = primaryContainer,
+        onPrimary = onPrimary,
+        onPrimaryContainer = onPrimaryContainer,
+        secondary = secondary,
+        secondaryDim = secondaryDim,
+        secondaryContainer = secondaryContainer,
+        onSecondary = onSecondary,
+        onSecondaryContainer = onSecondaryContainer,
+        tertiary = tertiary,
+        tertiaryDim = tertiaryDim,
+        tertiaryContainer = tertiaryContainer,
+        onTertiary = onTertiary,
+        onTertiaryContainer = onTertiaryContainer,
+        surfaceDim = surfaceDim,
+        surface = surface,
+        surfaceBright = surfaceBright,
+        onSurface = onSurface,
+        onSurfaceVariant = onSurfaceVariant,
+        outline = outline,
+        outlineVariant = outlineVariant,
+        background = background,
+        onBackground = onBackground,
+        error = error,
+        onError = onError
+    )
+
+    override fun toString(): String {
+        return "Colors(" +
+            "primary=$primary, " +
+            "primaryDim=$primaryDim, " +
+            "primaryContainer=$primaryContainer, " +
+            "onPrimary=$onPrimary, " +
+            "onPrimaryContainer=$onPrimaryContainer, " +
+            "secondary=$secondary, " +
+            "secondaryDim=$secondaryDim, " +
+            "secondaryContainer=$secondaryContainer, " +
+            "onSecondary=$onSecondary, " +
+            "onSecondaryContainer=$onSecondaryContainer, " +
+            "tertiary=$tertiary, " +
+            "tertiaryDim=$tertiaryDim, " +
+            "tertiaryContainer=$tertiaryContainer, " +
+            "onTertiary=$onTertiary, " +
+            "onTertiaryContainer=$onTertiaryContainer, " +
+            "surfaceDim=$surfaceDim, " +
+            "surface=$surface, " +
+            "surfaceBright=$surfaceBright, " +
+            "onSurface=$onSurface, " +
+            "onSurfaceVariant=$onSurfaceVariant, " +
+            "outline=$outline, " +
+            "outlineVariant=$outlineVariant, " +
+            "background=$background, " +
+            "onBackground=$onBackground, " +
+            "error=$error, " +
+            "onError=$onError" +
+            ")"
+    }
+}
+
+/**
+ * The Material color system contains pairs of colors that are typically used for the background
+ * and content color inside a component. For example, a Button typically uses `primary` for its
+ * background, and `onPrimary` for the color of its content (usually text or iconography).
+ *
+ * This function tries to match the provided [backgroundColor] to a 'background' color in this
+ * [ColorScheme], and then will return the corresponding color used for content. For example, when
+ * [backgroundColor] is [ColorScheme.primary], this will return [ColorScheme.onPrimary].
+ *
+ * If [backgroundColor] does not match a background color in the theme, this will return
+ * [Color.Unspecified].
+ *
+ * @return the matching content color for [backgroundColor]. If [backgroundColor] is not present in
+ * the theme's [ColorScheme], then returns [Color.Unspecified].
+ *
+ * @see contentColorFor
+ */
+public fun ColorScheme.contentColorFor(backgroundColor: Color): Color {
+    return when (backgroundColor) {
+        primary, primaryDim -> onPrimary
+        primaryContainer -> onPrimaryContainer
+        secondary, secondaryDim -> onSecondary
+        secondaryContainer -> onSecondaryContainer
+        tertiary, tertiaryDim -> onTertiary
+        tertiaryContainer -> onTertiaryContainer
+        surface, surfaceDim, surfaceBright -> onSurface
+        background -> onBackground
+        error -> onError
+        else -> Color.Unspecified
+    }
+}
+
+/**
+ * The Material color system contains pairs of colors that are typically used for the background
+ * and content color inside a component. For example, a Button typically uses `primary` for its
+ * background, and `onPrimary` for the color of its content (usually text or iconography).
+ *
+ * This function tries to match the provided [backgroundColor] to a 'background' color in this
+ * [ColorScheme], and then will return the corresponding color used for content. For example, when
+ * [backgroundColor] is [ColorScheme.primary], this will return [ColorScheme.onPrimary].
+ *
+ * If [backgroundColor] does not match a background color in the theme, this will return
+ * the current value of [LocalContentColor] as a best-effort color.
+ *
+ * @return the matching content color for [backgroundColor]. If [backgroundColor] is not present in
+ * the theme's [ColorScheme], then returns the current value of [LocalContentColor].
+ *
+ * @see ColorScheme.contentColorFor
+ */
+@Composable
+@ReadOnlyComposable
+public fun contentColorFor(backgroundColor: Color): Color =
+    MaterialTheme.colorScheme
+        .contentColorFor(backgroundColor)
+        .takeOrElse { LocalContentColor.current }
+
+/**
+ * Updates the internal values of the given [ColorScheme] with values from the [other] [ColorScheme]. This
+ * allows efficiently updating a subset of [ColorScheme], without recomposing every composable that
+ * consumes values from [LocalColors].
+ *
+ * Because [ColorScheme] is very wide-reaching, and used by many expensive composables in the
+ * hierarchy, providing a new value to [LocalColors] causes every composable consuming
+ * [LocalColors] to recompose, which is prohibitively expensive in cases such as animating one
+ * color in the theme. Instead, [ColorScheme] is internally backed by [mutableStateOf], and this
+ * function mutates the internal state of [this] to match values in [other]. This means that any
+ * changes will mutate the internal state of [this], and only cause composables that are reading
+ * the specific changed value to recompose.
+ */
+internal fun ColorScheme.updateColorSchemeFrom(other: ColorScheme) {
+    primary = other.primary
+    primaryDim = other.primaryDim
+    primaryContainer = other.primaryContainer
+    onPrimary = other.onPrimary
+    onPrimaryContainer = other.onPrimaryContainer
+    secondary = other.secondary
+    secondaryDim = other.secondaryDim
+    secondaryContainer = other.secondaryContainer
+    onSecondary = other.onSecondary
+    onSecondaryContainer = other.onSecondaryContainer
+    tertiary = other.tertiary
+    tertiaryDim = other.tertiaryDim
+    tertiaryContainer = other.tertiaryContainer
+    onTertiary = other.onTertiary
+    onTertiaryContainer = other.onTertiaryContainer
+    surfaceDim = other.surfaceDim
+    surface = other.surface
+    surfaceBright = other.surfaceBright
+    onSurface = other.onSurface
+    onSurfaceVariant = other.onSurfaceVariant
+    outline = other.outline
+    outlineVariant = other.outlineVariant
+    background = other.background
+    onBackground = other.onBackground
+    error = other.error
+    onError = other.onError
+}
+
+internal val LocalColors = staticCompositionLocalOf<ColorScheme> { ColorScheme() }
diff --git a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/ContentAlpha.kt b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/ContentAlpha.kt
new file mode 100644
index 0000000..91e559b
--- /dev/null
+++ b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/ContentAlpha.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.wear.compose.material3
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ProvidableCompositionLocal
+import androidx.compose.runtime.compositionLocalOf
+import androidx.compose.ui.graphics.luminance
+
+/**
+ * CompositionLocal containing the preferred content alpha for a given position in the hierarchy.
+ * This alpha is used for text and iconography ([Text] and Icon) to emphasize / de-emphasize
+ * different parts of a component. See the Material guide on
+ * [Text Legibility](https://material.io/design/color/text-legibility.html) for more information on
+ * alpha levels used by text and iconography.
+ *
+ * See [ContentAlpha] for the default levels used by most Material components.
+ *
+ * [MaterialTheme] sets this to [ContentAlpha.high] by default, as this is the default alpha for
+ * body text.
+ *
+ */
+public val LocalContentAlpha: ProvidableCompositionLocal<Float> = compositionLocalOf { 1f }
+
+/**
+ * Default alpha levels used by Material components.
+ *
+ * See [LocalContentAlpha].
+ */
+public object ContentAlpha {
+    /**
+     * A high level of content alpha, used to represent high emphasis text.
+     */
+    public val high: Float
+        @Composable
+        get() = contentAlpha(
+            highContrastAlpha = HighContrastContentAlpha.high,
+            lowContrastAlpha = LowContrastContentAlpha.high
+        )
+
+    /**
+     * A medium level of content alpha, used to represent medium emphasis text such as
+     * placeholder text.
+     */
+    public val medium: Float
+        @Composable
+        get() = contentAlpha(
+            highContrastAlpha = HighContrastContentAlpha.medium,
+            lowContrastAlpha = LowContrastContentAlpha.medium
+        )
+
+    /**
+     * A low level of content alpha used to represent disabled components, such as text in a
+     * disabled Button.
+     */
+    public val disabled: Float
+        @Composable
+        get() = contentAlpha(
+            highContrastAlpha = HighContrastContentAlpha.disabled,
+            lowContrastAlpha = LowContrastContentAlpha.disabled
+        )
+
+    /**
+     * This default implementation uses separate alpha levels depending on the luminance of the
+     * incoming color, and whether the theme is light or dark. This is to ensure correct contrast
+     * and accessibility on all surfaces.
+     *
+     * See [HighContrastContentAlpha] and [LowContrastContentAlpha] for what the levels are
+     * used for, and under what circumstances.
+     */
+    @Composable
+    private fun contentAlpha(
+        /*@FloatRange(from = 0.0, to = 1.0)*/
+        highContrastAlpha: Float,
+        /*@FloatRange(from = 0.0, to = 1.0)*/
+        lowContrastAlpha: Float
+    ): Float {
+        val contentColor = LocalContentColor.current
+        return if (contentColor.luminance() < 0.5) highContrastAlpha else lowContrastAlpha
+    }
+}
+
+/**
+ * Alpha levels for high luminance content in light theme, or low luminance content in dark theme.
+ *
+ * This content will typically be placed on colored surfaces, so it is important that the
+ * contrast here is higher to meet accessibility standards, and increase legibility.
+ *
+ * These levels are typically used for text / iconography in primary colored tabs /
+ * bottom navigation / etc.
+ */
+private object HighContrastContentAlpha {
+    const val high: Float = 1.00f
+    const val medium: Float = 0.74f
+    const val disabled: Float = 0.38f
+}
+
+/**
+ * Alpha levels for low luminance content in light theme, or high luminance content in dark theme.
+ *
+ * This content will typically be placed on grayscale surfaces, so the contrast here can be lower
+ * without sacrificing accessibility and legibility.
+ *
+ * These levels are typically used for body text on the main surface (white in light theme, grey
+ * in dark theme) and text / iconography in surface colored tabs / bottom navigation / etc.
+ */
+private object LowContrastContentAlpha {
+    const val high: Float = 0.87f
+    const val medium: Float = 0.60f
+    const val disabled: Float = 0.38f
+}
diff --git a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/ContentColor.kt b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/ContentColor.kt
new file mode 100644
index 0000000..f05bed6
--- /dev/null
+++ b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/ContentColor.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.wear.compose.material3
+
+import androidx.compose.runtime.ProvidableCompositionLocal
+import androidx.compose.runtime.compositionLocalOf
+import androidx.compose.ui.graphics.Color
+
+/**
+ * CompositionLocal containing the preferred content color for a given position in the hierarchy.
+ * This typically represents the `on` color for a color in [ColorScheme]. For example, if the background
+ * color is [ColorScheme.surface], this color is typically set to [ColorScheme.onSurface].
+ *
+ * This color should be used for any typography / iconography, to ensure that the color of these
+ * adjusts when the background color changes. For example, on a dark background, text should be
+ * light, and on a light background, text should be dark.
+ *
+ * Defaults to [Color.White] if no color has been explicitly set.
+ */
+public val LocalContentColor: ProvidableCompositionLocal<Color> = compositionLocalOf { Color.White }
diff --git a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Icon.kt b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Icon.kt
new file mode 100644
index 0000000..2113a27
--- /dev/null
+++ b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Icon.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ImageBitmap
+import androidx.compose.ui.graphics.painter.BitmapPainter
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+
+/**
+ * Icon component that draws [imageVector] using [tint], defaulting to [LocalContentColor]. For a
+ * clickable icon, see Button.
+ *
+ * @param imageVector [ImageVector] to draw inside this Icon
+ * @param contentDescription Text used by accessibility services to describe what this icon
+ * represents. This should always be provided unless this icon is used for decorative purposes,
+ * and does not represent a meaningful action that a user can take. This text should be
+ * localized, such as by using [androidx.compose.ui.res.stringResource] or similar
+ * @param modifier Optional [Modifier] for this Icon
+ * @param tint Tint to be applied to [imageVector]. If [Color.Unspecified] is provided, then no
+ *  tint is applied
+ */
+@Composable
+fun Icon(
+    imageVector: ImageVector,
+    contentDescription: String?,
+    modifier: Modifier = Modifier,
+    tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
+) {
+    Icon(
+        painter = rememberVectorPainter(imageVector),
+        contentDescription = contentDescription,
+        modifier = modifier,
+        tint = tint
+    )
+}
+
+/**
+ * Icon component that draws [bitmap] using [tint], defaulting to [LocalContentColor]. For a
+ * clickable icon, see Button.
+ *
+ * @param bitmap [ImageBitmap] to draw inside this Icon
+ * @param contentDescription Text used by accessibility services to describe what this icon
+ * represents. This should always be provided unless this icon is used for decorative purposes,
+ * and does not represent a meaningful action that a user can take. This text should be
+ * localized, such as by using [androidx.compose.ui.res.stringResource] or similar
+ * @param modifier Optional [Modifier] for this Icon
+ * @param tint Tint to be applied to [bitmap]. If [Color.Unspecified] is provided, then no
+ *  tint is applied
+ */
+@Composable
+fun Icon(
+    bitmap: ImageBitmap,
+    contentDescription: String?,
+    modifier: Modifier = Modifier,
+    tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
+) {
+    val painter = remember(bitmap) { BitmapPainter(bitmap) }
+    Icon(
+        painter = painter,
+        contentDescription = contentDescription,
+        modifier = modifier,
+        tint = tint
+    )
+}
+
+/**
+ * Icon component that draws a [painter] using [tint], defaulting to [LocalContentColor]. For a
+ * clickable icon, see Button.
+ *
+ * @param painter [Painter] to draw inside this Icon
+ * @param contentDescription Text used by accessibility services to describe what this icon
+ * represents. This should always be provided unless this icon is used for decorative purposes,
+ * and does not represent a meaningful action that a user can take. This text should be
+ * localized, such as by using [androidx.compose.ui.res.stringResource] or similar
+ * @param modifier Optional [Modifier] for this Icon
+ * @param tint Tint to be applied to [painter]. If [Color.Unspecified] is provided, then no
+ *  tint is applied
+ */
+@Composable
+fun Icon(
+    painter: Painter,
+    contentDescription: String?,
+    modifier: Modifier = Modifier,
+    tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
+) {
+    androidx.wear.compose.materialcore.Icon(
+        painter = painter,
+        contentDescription = contentDescription,
+        tint = tint,
+        modifier = modifier
+    )
+}
\ No newline at end of file
diff --git a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/MaterialTextSelectionColors.kt b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/MaterialTextSelectionColors.kt
new file mode 100644
index 0000000..8939ee4
--- /dev/null
+++ b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/MaterialTextSelectionColors.kt
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.wear.compose.material3
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.compositeOver
+import androidx.compose.ui.graphics.luminance
+import androidx.compose.ui.graphics.takeOrElse
+import androidx.compose.foundation.text.selection.TextSelectionColors
+import kotlin.math.max
+import kotlin.math.min
+
+/**
+ * Remembers a [TextSelectionColors] based on [colorScheme]. The handle color will be
+ * [ColorScheme.primary] and the background color will be [ColorScheme.primary] with alpha applied.
+ *
+ * See [calculateSelectionBackgroundColor].
+ */
+@Composable
+internal fun rememberTextSelectionColors(colorScheme: ColorScheme): TextSelectionColors {
+    val primaryColor = colorScheme.primary
+    val backgroundColor = colorScheme.background
+    // Test with ContentAlpha.medium to ensure that the selection background is accessible in the
+    // 'worst case' scenario. We explicitly don't test with ContentAlpha.disabled, as disabled
+    // text shouldn't be selectable / is noted as disabled for accessibility purposes.
+    val textColorWithLowestAlpha = colorScheme.contentColorFor(backgroundColor)
+        .takeOrElse {
+            LocalContentColor.current
+        }.copy(
+            alpha = ContentAlpha.medium
+        )
+    return remember(primaryColor, backgroundColor, textColorWithLowestAlpha) {
+        TextSelectionColors(
+            handleColor = colorScheme.primary,
+            backgroundColor = calculateSelectionBackgroundColor(
+                selectionColor = primaryColor,
+                textColor = textColorWithLowestAlpha,
+                backgroundColor = backgroundColor
+            )
+        )
+    }
+}
+
+/**
+ * Best-effort calculates a color (with alpha) for the selection background that (if possible)
+ * will have at least [DesiredContrastRatio] with [textColor], when the selection background
+ * is on top of [backgroundColor].
+ *
+ * Since this is a minimum contrast ratio, [textColor] should have the lowest alpha that
+ * may be applied to content so we can ensure that the selection background color is accessible
+ * in that worst-case scenario for contrast.
+ *
+ * @param selectionColor the 'raw' (without alpha) selection color that we should search alpha for
+ * @param textColor the color of text with minimal alpha applied to test for contrast with
+ * @param backgroundColor the color of the background that the selection color will typically be
+ * placed against
+ *
+ * @return a resulting [selectionColor] with alpha applied that results in acceptable contrast
+ * (if possible with the values for [selectionColor], [textColor] and [backgroundColor]).
+ */
+/*@VisibleForTesting*/
+internal fun calculateSelectionBackgroundColor(
+    selectionColor: Color,
+    textColor: Color,
+    backgroundColor: Color
+): Color {
+    val maximumContrastRatio = calculateContrastRatio(
+        selectionColor = selectionColor,
+        selectionColorAlpha = DefaultSelectionBackgroundAlpha,
+        textColor = textColor,
+        backgroundColor = backgroundColor
+    )
+
+    val minimumContrastRatio = calculateContrastRatio(
+        selectionColor = selectionColor,
+        selectionColorAlpha = MinimumSelectionBackgroundAlpha,
+        textColor = textColor,
+        backgroundColor = backgroundColor
+    )
+
+    val alpha = when {
+        // If the default alpha has enough contrast, use that
+        maximumContrastRatio >= DesiredContrastRatio -> DefaultSelectionBackgroundAlpha
+        // If the minimum alpha still does not have enough contrast, just use the minimum and return
+        minimumContrastRatio < DesiredContrastRatio -> MinimumSelectionBackgroundAlpha
+        else -> binarySearchForAccessibleSelectionColorAlpha(
+            selectionColor = selectionColor,
+            textColor = textColor,
+            backgroundColor = backgroundColor
+        )
+    }
+
+    return selectionColor.copy(alpha = alpha)
+}
+
+/**
+ * Binary searches for the highest alpha for selection color that results in a contrast ratio at
+ * least equal to and within 1% of [DesiredContrastRatio].
+ *
+ * The resulting alpha will be within the range of [MinimumSelectionBackgroundAlpha] and
+ * [DefaultSelectionBackgroundAlpha] - since not all values for [selectionColor], [textColor] and
+ * [backgroundColor] can be guaranteed to produce an accessible contrast ratio, this is a
+ * best-effort attempt and [MinimumSelectionBackgroundAlpha] might still not produce an
+ * accessible contrast ratio. In this case developers are encouraged to manually choose a
+ * different color for selection that _is_ accessible with their chosen content and background
+ * colors.
+ *
+ * Caps the number of attempts at 7 for performance and to avoid infinite searching when there is
+ * no value that results in an accessible contrast ratio. Because alpha is limited to [0,1], 7
+ * steps results in a precision of ~0.01, since log2(1/0.01) ≈ 7.
+ *
+ * Note: binary searching here is chosen since it is not possible to 'solve' for alpha, since the
+ * transformation from color -> contrast ratio is not linear (the gamma exponent for sRGB colors
+ * is 2.4). We can approximate this to 2, but this results in not that accurate solutions, and we
+ * need to guarantee that they are at least above [DesiredContrastRatio] - falling just below is
+ * not an acceptable result.
+ *
+ * @param selectionColor the 'raw' (without alpha) selection color that we should search alpha for
+ * @param textColor the color of text with minimal alpha applied to test for contrast with
+ * @param backgroundColor the color of the background that the selection color will typically be
+ * placed against
+ */
+private fun binarySearchForAccessibleSelectionColorAlpha(
+    selectionColor: Color,
+    textColor: Color,
+    backgroundColor: Color
+): Float {
+    var attempts = 0
+    val maxAttempts = 7
+
+    var lowAlpha = MinimumSelectionBackgroundAlpha
+    var alpha = DefaultSelectionBackgroundAlpha
+    var highAlpha = DefaultSelectionBackgroundAlpha
+
+    while (attempts < maxAttempts) {
+        val contrastRatio = calculateContrastRatio(
+            selectionColor = selectionColor,
+            selectionColorAlpha = alpha,
+            textColor = textColor,
+            backgroundColor = backgroundColor
+        )
+
+        // Percentage error of the calculated contrast compared to the actual contrast. Positive
+        // numbers here mean we have higher contrast than needed.
+        val percentageError = (contrastRatio / DesiredContrastRatio) - 1f
+        when {
+            // Contrast is at most 1% above the guideline, return
+            percentageError in 0f..0.01f -> break
+            // Contrast too low, decrease alpha
+            percentageError < 0f -> highAlpha = alpha
+            // Contrast higher than required, increase alpha
+            else -> lowAlpha = alpha
+        }
+        alpha = (highAlpha + lowAlpha) / 2f
+        attempts++
+    }
+
+    return alpha
+}
+
+/**
+ * Calculates the contrast ratio of [textColor] against [selectionColor] with
+ * [selectionColorAlpha], all on top of [backgroundColor].
+ *
+ * Both the [selectionColor] and [textColor] will be composited to handle transparency.
+ *
+ * @param selectionColor the 'raw' (without alpha) selection color that we should search alpha for
+ * @param selectionColorAlpha the alpha for [selectionColor] to test contrast with
+ * @param textColor the color of text with minimal alpha applied to test for contrast with
+ * @param backgroundColor the color of the background that the selection color will typically be
+ * placed against
+ *
+ * @return the contrast ratio as a value between 1 and 21. See [calculateContrastRatio]
+ */
+private fun calculateContrastRatio(
+    selectionColor: Color,
+    selectionColorAlpha: Float,
+    textColor: Color,
+    backgroundColor: Color
+): Float {
+    val compositeBackground = selectionColor.copy(alpha = selectionColorAlpha)
+        .compositeOver(backgroundColor)
+    val compositeTextColor = textColor.compositeOver(compositeBackground)
+    return calculateContrastRatio(compositeTextColor, compositeBackground)
+}
+
+/**
+ * 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]
+ */
+/*@VisibleForTesting*/
+internal 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)
+}
+
+/**
+ * Default selection background alpha - we will try and use this if it is accessible and produces
+ * the correct contrast ratio.
+ */
+private const val DefaultSelectionBackgroundAlpha = 0.4f
+
+/**
+ * Not all combinations of text color and selection color will have a reasonable alpha that
+ * produces a contrast ratio of at least [DesiredContrastRatio] - in this case just pick a low
+ * but still visible alpha so at least the contrast ratio is as good as it can be - this is
+ * preferable to crashing at runtime.
+ */
+private const val MinimumSelectionBackgroundAlpha = DefaultSelectionBackgroundAlpha / 2f
+
+/**
+ * Material and WCAG 2.0 sc 1.4.3 minimum contrast for AA text
+ */
+private const val DesiredContrastRatio = 4.5f
diff --git a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/MaterialTheme.kt b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/MaterialTheme.kt
new file mode 100644
index 0000000..9c15d6f2
--- /dev/null
+++ b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/MaterialTheme.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.wear.compose.material3
+
+import androidx.compose.foundation.LocalIndication
+import androidx.compose.foundation.text.selection.LocalTextSelectionColors
+import androidx.compose.material.ripple.LocalRippleTheme
+import androidx.compose.material.ripple.RippleTheme
+import androidx.compose.material.ripple.rememberRipple
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.remember
+
+/**
+ * MaterialTheme defines the styling principles from the Wear Material3 design specification
+ * which extends the Material design specification.
+ *
+ * Wear Material components from package/sub-packages in [androidx.wear.compose.material3]
+ * use values provided here when retrieving default values.
+ *
+ * TODO(b/273543423) Update references to Material3 design specs
+ *
+ * All values may be set by providing this component with the [colors][ColorScheme],
+ * [typography][Typography], and [shapes][Shapes] 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.
+ *
+ * For more information, see the
+ * [Theming](https://developer.android.com/training/wearables/components/theme)
+ * guide.
+ *
+ * @param colorScheme A complete definition of the Wear Material Color 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 shapes to be used by the components in this hierarchy
+ */
+@Composable
+public fun MaterialTheme(
+    colorScheme: ColorScheme = MaterialTheme.colorScheme,
+    typography: Typography = MaterialTheme.typography,
+    shapes: Shapes = MaterialTheme.shapes,
+    content: @Composable () -> Unit
+) {
+    val rememberedColors = remember {
+        // Explicitly creating a new object here so we don't mutate the initial [colors]
+        // provided, and overwrite the values set in it.
+        colorScheme.copy()
+    }.apply { updateColorSchemeFrom(colorScheme) }
+    val rippleIndication = rememberRipple()
+    val selectionColors = rememberTextSelectionColors(rememberedColors)
+    CompositionLocalProvider(
+        LocalColors provides rememberedColors,
+        LocalShapes provides shapes,
+        LocalTypography provides typography,
+        LocalContentAlpha provides ContentAlpha.high,
+        LocalIndication provides rippleIndication,
+        LocalRippleTheme provides MaterialRippleTheme,
+        LocalTextSelectionColors provides selectionColors,
+
+        ) {
+        ProvideTextStyle(value = typography.bodyLarge, content = content)
+    }
+}
+
+public object MaterialTheme {
+    public val colorScheme: ColorScheme
+        @ReadOnlyComposable
+        @Composable
+        get() = LocalColors.current
+
+    public val typography: Typography
+        @ReadOnlyComposable
+        @Composable
+        get() = LocalTypography.current
+
+    public val shapes: Shapes
+        @ReadOnlyComposable
+        @Composable
+        get() = LocalShapes.current
+}
+
+@Immutable
+private object MaterialRippleTheme : RippleTheme {
+    @Composable
+    override fun defaultColor() = RippleTheme.defaultRippleColor(
+        contentColor = LocalContentColor.current,
+        lightTheme = false
+    )
+
+    @Composable
+    override fun rippleAlpha() = RippleTheme.defaultRippleAlpha(
+        contentColor = LocalContentColor.current,
+        lightTheme = false
+    )
+}
diff --git a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Shapes.kt b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Shapes.kt
new file mode 100644
index 0000000..aa54053
--- /dev/null
+++ b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Shapes.kt
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.wear.compose.material3
+
+import androidx.compose.foundation.shape.CornerSize
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.staticCompositionLocalOf
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.unit.dp
+
+/**
+ * Material surfaces can be displayed in different shapes. Shapes direct attention, identify
+ * components, communicate state, and express brand.
+ *
+ * The shape scale defines the style of container, offering a range of
+ * curved shapes (mostly polygonal). The default [Shapes] theme for Material3 is rounded rectangles,
+ * with various degrees of corner roundness:
+ *
+ * - None
+ * - Extra Small
+ * - Small
+ * - Medium
+ * - Large
+ * - Full
+ *
+ * You can customize the shape system for all components in the [MaterialTheme] or you can do it
+ * on a per component basis by overriding the shape parameter for that
+ * component. For example, by default, buttons use the shape style “full.” If your product requires
+ * a smaller amount of roundness, you can override the shape parameter with a different shape
+ * value like [Shapes.small].
+ *
+ * TODO(b/273226734) Review documentation with references to components that use the shape themes.
+ *
+ * @param none By default, provides [ShapeDefaults.None], which is [RectangleShape]
+ * @param extraSmall By default, provides [ShapeDefaults.ExtraSmall], a [RoundedCornerShape]
+ * with 4dp [CornerSize] (used by bundled Cards).
+ * @param small By default, provides [ShapeDefaults.Small], a [RoundedCornerShape]
+ * with 8dp [CornerSize].
+ * @param medium By default, provides [ShapeDefaults.Medium], a [RoundedCornerShape] with
+ * 16dp [CornerSize] (used by shape-shifting Buttons and rounded rectangle buttons).
+ * @param large By default, provides [ShapeDefaults.Large], a [RoundedCornerShape]
+ * with 24dp [CornerSize] (used by Cards).
+ * @param extraLarge By default, provides [ShapeDefaults.ExtraLarge], a
+ * [RoundedCornerShape] with 32dp [CornerSize].
+ * @param full By default, provides [ShapeDefaults.Full], a Stadium shape with
+ * 50% rounded [CornerSize] (used by Button).
+ */
+@Immutable
+class Shapes(
+    val none: Shape = ShapeDefaults.None,
+    val extraSmall: Shape = ShapeDefaults.ExtraSmall,
+    val small: Shape = ShapeDefaults.Small,
+    val medium: Shape = ShapeDefaults.Medium,
+    val large: Shape = ShapeDefaults.Large,
+    val extraLarge: Shape = ShapeDefaults.ExtraLarge,
+    val full: Shape = ShapeDefaults.Full,
+) {
+    /** Returns a copy of this Shapes, optionally overriding some of the values. */
+    fun copy(
+        none: Shape = this.none,
+        extraSmall: Shape = this.extraSmall,
+        small: Shape = this.small,
+        medium: Shape = this.medium,
+        large: Shape = this.large,
+        extraLarge: Shape = this.extraLarge,
+        full: Shape = this.full
+    ): Shapes = Shapes(
+        none = none,
+        extraSmall = extraSmall,
+        small = small,
+        medium = medium,
+        large = large,
+        extraLarge = extraLarge,
+        full = full,
+    )
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is Shapes) return false
+        if (none != other.none) return false
+        if (extraSmall != other.extraSmall) return false
+        if (small != other.small) return false
+        if (medium != other.medium) return false
+        if (large != other.large) return false
+        if (extraLarge != other.extraLarge) return false
+        if (full != other.full) return false
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = none.hashCode()
+        result = 31 * result + extraSmall.hashCode()
+        result = 31 * result + small.hashCode()
+        result = 31 * result + medium.hashCode()
+        result = 31 * result + large.hashCode()
+        result = 31 * result + extraLarge.hashCode()
+        result = 31 * result + full.hashCode()
+        return result
+    }
+
+    override fun toString(): String {
+        return "Shapes(" +
+            "none=$none, " +
+            "extraSmall=$extraSmall, " +
+            "small=$small, " +
+            "medium=$medium, " +
+            "large=$large, " +
+            "extraLarge=$extraLarge, " +
+            "full=$full)"
+    }
+}
+
+/**
+ * Contains the default values used by [Shapes]
+ */
+object ShapeDefaults {
+    /** None provides a RectangleShape */
+    val None = RectangleShape
+
+    /** Extra small sized corner shape */
+    val ExtraSmall = RoundedCornerShape(corner = CornerSize(4.dp))
+
+    /** Small sized corner shape */
+    val Small = RoundedCornerShape(corner = CornerSize(8.dp))
+
+    /** Medium sized corner shape */
+    val Medium = RoundedCornerShape(corner = CornerSize(16.dp))
+
+    /** Large sized corner shape */
+    val Large = RoundedCornerShape(corner = CornerSize(24.dp))
+
+    /** Extra large sized corner shape */
+    val ExtraLarge = RoundedCornerShape(corner = CornerSize(32.dp))
+
+    /** Full provides a stadium-shape with 50% rounded corners */
+    val Full = RoundedCornerShape(corner = CornerSize(50))
+}
+
+/** CompositionLocal used to specify the default shapes for the surfaces. */
+internal val LocalShapes = staticCompositionLocalOf { Shapes() }
diff --git a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Text.kt b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Text.kt
new file mode 100644
index 0000000..9efaa5e
--- /dev/null
+++ b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Text.kt
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3
+
+import androidx.compose.foundation.text.InlineTextContent
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.ProvidableCompositionLocal
+import androidx.compose.runtime.compositionLocalOf
+import androidx.compose.runtime.structuralEqualityPolicy
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.takeOrElse
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.Paragraph
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextStyle
+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.style.TextAlign
+import androidx.compose.ui.text.style.TextDecoration
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.TextUnit
+
+/**
+ * High level element that displays text and provides semantics / accessibility information.
+ *
+ * The default [style] uses the [LocalTextStyle] provided by the [MaterialTheme] / components. If
+ * you are setting your own style, you may want to consider first retrieving [LocalTextStyle],
+ * and using [TextStyle.copy] to keep any theme defined attributes, only modifying the specific
+ * attributes you want to override.
+ *
+ * For ease of use, commonly used parameters from [TextStyle] are also present here. The order of
+ * precedence is as follows:
+ * - If a parameter is explicitly set here (i.e, it is _not_ `null` or [TextUnit.Unspecified]),
+ * then this parameter will always be used.
+ * - If a parameter is _not_ set, (`null` or [TextUnit.Unspecified]), then the corresponding value
+ * from [style] will be used instead.
+ *
+ * Additionally, for [color], if [color] is not set, and [style] does not have a color, then
+ * [LocalContentColor] will be used with an alpha of [LocalContentAlpha]- this allows this
+ * [Text] or element containing this [Text] to adapt to different background colors and still
+ * maintain contrast and accessibility.
+ *
+ * @param text The text to be displayed.
+ * @param modifier [Modifier] to apply to this layout node.
+ * @param color [Color] to apply to the text. If [Color.Unspecified], and [style] has no color set,
+ * this will be [LocalContentColor].
+ * @param fontSize The size of glyphs to use when painting the text. See [TextStyle.fontSize].
+ * @param fontStyle The typeface variant to use when drawing the letters (e.g., italic).
+ * See [TextStyle.fontStyle].
+ * @param fontWeight The typeface thickness to use when painting the text (e.g., [FontWeight.Bold]).
+ * @param fontFamily The font family to be used when rendering the text. See [TextStyle.fontFamily].
+ * @param letterSpacing The amount of space to add between each letter.
+ * See [TextStyle.letterSpacing].
+ * @param textDecoration The decorations to paint on the text (e.g., an underline).
+ * See [TextStyle.textDecoration].
+ * @param textAlign The alignment of the text within the lines of the paragraph.
+ * See [TextStyle.textAlign].
+ * @param lineHeight Line height for the [Paragraph] in [TextUnit] unit, e.g. SP or EM.
+ * See [TextStyle.lineHeight].
+ * @param overflow How visual overflow should be handled.
+ * @param softWrap Whether the text should break at soft line breaks. If false, the glyphs in the
+ * text will be positioned as if there was unlimited horizontal space. If [softWrap] is false,
+ * [overflow] and TextAlign may have unexpected effects.
+ * @param maxLines An optional maximum number of lines for the text to span, wrapping if
+ * necessary. If the text exceeds the given number of lines, it will be truncated according to
+ * [overflow] and [softWrap]. If it is not null, then it must be greater than zero.
+ * @param minLines The minimum height in terms of minimum number of visible lines. It is required
+ * that 1 <= [minLines] <= [maxLines].
+ * @param onTextLayout Callback that is executed when a new text layout is calculated. A
+ * [TextLayoutResult] object that callback provides contains paragraph information, size of the
+ * text, baselines and other details. The callback can be used to add additional decoration or
+ * functionality to the text. For example, to draw selection around the text.
+ * @param style Style configuration for the text such as color, font, line height etc.
+ */
+@Composable
+public fun Text(
+    text: String,
+    modifier: Modifier = Modifier,
+    color: Color = Color.Unspecified,
+    fontSize: TextUnit = TextUnit.Unspecified,
+    fontStyle: FontStyle? = null,
+    fontWeight: FontWeight? = null,
+    fontFamily: FontFamily? = null,
+    letterSpacing: TextUnit = TextUnit.Unspecified,
+    textDecoration: TextDecoration? = null,
+    textAlign: TextAlign? = null,
+    lineHeight: TextUnit = TextUnit.Unspecified,
+    overflow: TextOverflow = TextOverflow.Clip,
+    softWrap: Boolean = true,
+    maxLines: Int = Int.MAX_VALUE,
+    minLines: Int = 1,
+    onTextLayout: (TextLayoutResult) -> Unit = {},
+    style: TextStyle = LocalTextStyle.current
+) {
+    Text(
+        AnnotatedString(text),
+        modifier,
+        color,
+        fontSize,
+        fontStyle,
+        fontWeight,
+        fontFamily,
+        letterSpacing,
+        textDecoration,
+        textAlign,
+        lineHeight,
+        overflow,
+        softWrap,
+        maxLines,
+        minLines,
+        emptyMap(),
+        onTextLayout,
+        style
+    )
+}
+
+/**
+ * High level element that displays text and provides semantics / accessibility information.
+ *
+ * The default [style] uses the [LocalTextStyle] provided by the [MaterialTheme] / components. If
+ * you are setting your own style, you may want to consider first retrieving [LocalTextStyle],
+ * and using [TextStyle.copy] to keep any theme defined attributes, only modifying the specific
+ * attributes you want to override.
+ *
+ * For ease of use, commonly used parameters from [TextStyle] are also present here. The order of
+ * precedence is as follows:
+ * - If a parameter is explicitly set here (i.e, it is _not_ `null` or [TextUnit.Unspecified]),
+ * then this parameter will always be used.
+ * - If a parameter is _not_ set, (`null` or [TextUnit.Unspecified]), then the corresponding value
+ * from [style] will be used instead.
+ *
+ * Additionally, for [color], if [color] is not set, and [style] does not have a color, then
+ * [LocalContentColor] will be used with an alpha of [LocalContentAlpha]- this allows this
+ * [Text] or element containing this [Text] to adapt to different background colors and still
+ * maintain contrast and accessibility.
+ *
+ * @param text The text to be displayed, where [AnnotatedString] allows multiple styles to be used.
+ * @param modifier [Modifier] to apply to this layout node.
+ * @param color [Color] to apply to the text. If [Color.Unspecified], and [style] has no color set,
+ * this will be [LocalContentColor].
+ * @param fontSize The size of glyphs to use when painting the text. See [TextStyle.fontSize].
+ * @param fontStyle The typeface variant to use when drawing the letters (e.g., italic).
+ * See [TextStyle.fontStyle].
+ * @param fontWeight The typeface thickness to use when painting the text (e.g., [FontWeight.Bold]).
+ * @param fontFamily The font family to be used when rendering the text. See [TextStyle.fontFamily].
+ * @param letterSpacing The amount of space to add between each letter.
+ * See [TextStyle.letterSpacing].
+ * @param textDecoration The decorations to paint on the text (e.g., an underline).
+ * See [TextStyle.textDecoration].
+ * @param textAlign The alignment of the text within the lines of the paragraph.
+ * See [TextStyle.textAlign].
+ * @param lineHeight Line height for the [Paragraph] in [TextUnit] unit, e.g. SP or EM.
+ * See [TextStyle.lineHeight].
+ * @param overflow How visual overflow should be handled.
+ * @param softWrap Whether the text should break at soft line breaks. If false, the glyphs in the
+ * text will be positioned as if there was unlimited horizontal space. If [softWrap] is false,
+ * [overflow] and TextAlign may have unexpected effects.
+ * @param maxLines An optional maximum number of lines for the text to span, wrapping if
+ * necessary. If the text exceeds the given number of lines, it will be truncated according to
+ * [overflow] and [softWrap]. If it is not null, then it must be greater than zero.
+ * @param minLines The minimum height in terms of minimum number of visible lines. It is required
+ * that 1 <= [minLines] <= [maxLines].
+ * @param inlineContent A map store composables that replaces certain ranges of the text. It's
+ * used to insert composables into text layout. Check [InlineTextContent] for more information.
+ * @param onTextLayout Callback that is executed when a new text layout is calculated. A
+ * [TextLayoutResult] object that callback provides contains paragraph information, size of the
+ * text, baselines and other details. The callback can be used to add additional decoration or
+ * functionality to the text. For example, to draw selection around the text.
+ * @param style Style configuration for the text such as color, font, line height etc.
+ */
+@Composable
+public fun Text(
+    text: AnnotatedString,
+    modifier: Modifier = Modifier,
+    color: Color = Color.Unspecified,
+    fontSize: TextUnit = TextUnit.Unspecified,
+    fontStyle: FontStyle? = null,
+    fontWeight: FontWeight? = null,
+    fontFamily: FontFamily? = null,
+    letterSpacing: TextUnit = TextUnit.Unspecified,
+    textDecoration: TextDecoration? = null,
+    textAlign: TextAlign? = null,
+    lineHeight: TextUnit = TextUnit.Unspecified,
+    overflow: TextOverflow = TextOverflow.Clip,
+    softWrap: Boolean = true,
+    maxLines: Int = Int.MAX_VALUE,
+    minLines: Int = 1,
+    inlineContent: Map<String, InlineTextContent> = mapOf(),
+    onTextLayout: (TextLayoutResult) -> Unit = {},
+    style: TextStyle = LocalTextStyle.current
+) {
+    val textColor = color.takeOrElse {
+        style.color.takeOrElse {
+            LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
+        }
+    }
+
+    androidx.wear.compose.materialcore.Text(
+        text = text,
+        modifier = modifier,
+        color = textColor,
+        fontSize = fontSize,
+        fontStyle = fontStyle,
+        fontWeight = fontWeight,
+        fontFamily = fontFamily,
+        letterSpacing = letterSpacing,
+        textDecoration = textDecoration,
+        textAlign = textAlign,
+        lineHeight = lineHeight,
+        overflow = overflow,
+        softWrap = softWrap,
+        maxLines = maxLines,
+        minLines = minLines,
+        inlineContent = inlineContent,
+        onTextLayout = onTextLayout,
+        style = style
+    )
+}
+
+/**
+ * CompositionLocal containing the preferred [TextStyle] that will be used by [Text] components by
+ * default. To set the value for this CompositionLocal, see [ProvideTextStyle] which will merge any
+ * missing [TextStyle] properties with the existing [TextStyle] set in this CompositionLocal.
+ *
+ * @see ProvideTextStyle
+ */
+public val LocalTextStyle: ProvidableCompositionLocal<TextStyle> =
+    compositionLocalOf(structuralEqualityPolicy()) { TextStyle.Default }
+
+/**
+ * This function is used to set the current value of [LocalTextStyle], merging the given style
+ * with the current style values for any missing attributes. Any [Text] components included in
+ * this component's [content] will be styled with this style unless styled explicitly.
+ *
+ * @see LocalTextStyle
+ */
+@Composable
+public fun ProvideTextStyle(value: TextStyle, content: @Composable () -> Unit) {
+    val mergedStyle = LocalTextStyle.current.merge(value)
+    CompositionLocalProvider(LocalTextStyle provides mergedStyle, content = content)
+}
diff --git a/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Typography.kt b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Typography.kt
new file mode 100644
index 0000000..34d16b6
--- /dev/null
+++ b/wear/compose/compose-material3/src/commonMain/kotlin/androidx/wear/compose/material3/Typography.kt
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.wear.compose.material3
+
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.staticCompositionLocalOf
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+/**
+ * Class holding typography definitions as defined by the Wear Material typography specification.
+ *
+ * The text styles in this typography are scaled according to the user's preferred font size in
+ * the system settings. Larger font sizes can be fixed if necessary in order to avoid pressure on
+ * screen space, because they are already sufficiently accessible.
+ * Here is an example of fixing the font size for Display1:
+ * @sample androidx.wear.compose.material.samples.FixedFontSize
+ *
+ * TODO(b/273526150) Review documentation for typography, add examples for each size.
+ * @property displayExtraLarge DisplayExtraLarge is the largest headline. Displays are the
+ * largest text on the screen, reserved for short, important text or numerals.
+ *
+ * @property displayLarge DisplayLarge is the second largest headline. Displays are the largest text
+ * on the screen, reserved for short, important text or numerals.
+ *
+ * @property displayMedium DisplayMedium is the third largest headline. Displays are the
+ * largest text on the screen, reserved for short, important text or numerals.
+ *
+ * @property displaySmall DisplaySmall is the fourth largest headline. Displays are the largest
+ * text on the screen, reserved for short, important text or numerals.
+ *
+ * @property titleLarge TitleLarge is the largest title. Titles are smaller than Displays. They are
+ * typically reserved for medium-emphasis text that is shorter in length.
+ *
+ * @property titleMedium TitleMedium is the medium title. Titles are smaller than Displays. They are
+ * typically reserved for medium-emphasis text that is shorter in length.
+ *
+ * @property titleSmall TitleSmall is the smallest title. Titles are smaller than Displays. They are
+ * typically reserved for medium-emphasis text that is shorter in length.
+ *
+ * @property bodyLarge BodyLarge is the largest body. Body texts are typically used for long-form
+ * writing as it works well for small text sizes. For longer sections of text, a serif or
+ * sans serif typeface is recommended.
+ *
+ * @property bodyMedium BodyMedium is the medium body. Body texts are typically used for long-form
+ * writing as it works well for small text sizes. For longer sections of text, a serif or sans serif
+ * typeface is recommended.
+ *
+ * @property bodySmall BodySmall is the smallest body. Body texts are typically used for long-form
+ * writing as it works well for small text sizes. For longer sections of text, a serif or sans serif
+ * typeface is recommended.
+ *
+ * @property buttonMedium ButtonMedium text is a call to action used in different types of buttons
+ * (such as text, outlined and contained buttons) and in tabs, dialogs, and cards. Button text is
+ * typically sans serif, using all caps text.
+ *
+ * @property captionLarge CaptionLarge is the largest caption. Caption texts are the smallest
+ * font sizes. They are used on secondary content.
+ *
+ * @property captionMedium CaptionMedium is the second largest caption. Caption texts are the
+ * smallest font sizes. They are used on secondary content.
+ *
+ * @property captionSmall CaptionSmall is an exceptional small font size which is used for the extra
+ * long-form writing like legal texts.
+ */
+@Immutable
+public class Typography internal constructor(
+    public val displayExtraLarge: TextStyle,
+    public val displayLarge: TextStyle,
+    public val displayMedium: TextStyle,
+    public val displaySmall: TextStyle,
+    public val titleLarge: TextStyle,
+    public val titleMedium: TextStyle,
+    public val titleSmall: TextStyle,
+    public val bodyLarge: TextStyle,
+    public val bodyMedium: TextStyle,
+    public val bodySmall: TextStyle,
+    public val buttonMedium: TextStyle,
+    public val captionLarge: TextStyle,
+    public val captionMedium: TextStyle,
+    public val captionSmall: TextStyle,
+) {
+    public constructor (
+        defaultFontFamily: FontFamily = FontFamily.Default,
+        displayExtraLarge: TextStyle = TextStyle(
+            fontWeight = FontWeight.Medium,
+            fontSize = 50.sp,
+            lineHeight = 56.sp,
+            letterSpacing = 0.5.sp
+        ),
+        displayLarge: TextStyle = TextStyle(
+            fontWeight = FontWeight.Medium,
+            fontSize = 40.sp,
+            lineHeight = 46.sp,
+            letterSpacing = 0.5.sp
+        ),
+        displayMedium: TextStyle = TextStyle(
+            fontWeight = FontWeight.Medium,
+            fontSize = 34.sp,
+            lineHeight = 40.sp,
+            letterSpacing = 1.sp
+        ),
+        displaySmall: TextStyle = TextStyle(
+            fontWeight = FontWeight.Medium,
+            fontSize = 30.sp,
+            lineHeight = 36.sp,
+            letterSpacing = 0.8.sp,
+        ),
+        titleLarge: TextStyle = TextStyle(
+            fontWeight = FontWeight.Medium,
+            fontSize = 24.sp,
+            lineHeight = 28.sp,
+            letterSpacing = 0.2.sp
+        ),
+        titleMedium: TextStyle = TextStyle(
+            fontWeight = FontWeight.Medium,
+            fontSize = 20.sp,
+            lineHeight = 24.sp,
+            letterSpacing = 0.2.sp
+        ),
+        titleSmall: TextStyle = TextStyle(
+            fontWeight = FontWeight.Medium,
+            fontSize = 16.sp,
+            lineHeight = 20.sp,
+            letterSpacing = 0.2.sp
+        ),
+        bodyLarge: TextStyle = TextStyle(
+            fontWeight = FontWeight.Normal,
+            fontSize = 16.sp,
+            lineHeight = 20.sp,
+            letterSpacing = 0.18.sp
+        ),
+        bodyMedium: TextStyle = TextStyle(
+            fontWeight = FontWeight.Medium,
+            fontSize = 16.sp,
+            lineHeight = 20.sp,
+            letterSpacing = 0.2.sp
+        ),
+        bodySmall: TextStyle = TextStyle(
+            fontWeight = FontWeight.Normal,
+            fontSize = 14.sp,
+            lineHeight = 18.sp,
+            letterSpacing = 0.2.sp
+        ),
+        buttonMedium: TextStyle = TextStyle(
+            fontWeight = FontWeight.Medium,
+            fontSize = 15.sp,
+            lineHeight = 19.sp,
+            letterSpacing = 0.2.sp
+        ),
+        captionLarge: TextStyle = TextStyle(
+            fontWeight = FontWeight.Medium,
+            fontSize = 14.sp,
+            lineHeight = 18.sp,
+            letterSpacing = 0.3.sp
+        ),
+        captionMedium: TextStyle = TextStyle(
+            fontWeight = FontWeight.Medium,
+            fontSize = 12.sp,
+            lineHeight = 16.sp,
+            letterSpacing = 0.4.sp
+        ),
+        captionSmall: TextStyle = TextStyle(
+            fontWeight = FontWeight.Medium,
+            fontSize = 10.sp,
+            lineHeight = 14.sp,
+            letterSpacing = 0.4.sp
+        )
+
+    ) : this(
+        displayExtraLarge = displayExtraLarge.withDefaultFontFamily(defaultFontFamily),
+        displayLarge = displayLarge.withDefaultFontFamily(defaultFontFamily),
+        displayMedium = displayMedium.withDefaultFontFamily(defaultFontFamily),
+        displaySmall = displaySmall.withDefaultFontFamily(defaultFontFamily),
+        titleLarge = titleLarge.withDefaultFontFamily(defaultFontFamily),
+        titleMedium = titleMedium.withDefaultFontFamily(defaultFontFamily),
+        titleSmall = titleSmall.withDefaultFontFamily(defaultFontFamily),
+        bodyLarge = bodyLarge.withDefaultFontFamily(defaultFontFamily),
+        bodyMedium = bodyMedium.withDefaultFontFamily(defaultFontFamily),
+        bodySmall = bodySmall.withDefaultFontFamily(defaultFontFamily),
+        buttonMedium = buttonMedium.withDefaultFontFamily(defaultFontFamily),
+        captionLarge = captionLarge.withDefaultFontFamily(defaultFontFamily),
+        captionMedium = captionMedium.withDefaultFontFamily(defaultFontFamily),
+        captionSmall = captionSmall.withDefaultFontFamily(defaultFontFamily),
+    )
+
+    /**
+     * Returns a copy of this Typography, optionally overriding some of the values.
+     */
+    public fun copy(
+        displayExtraLarge: TextStyle = this.displayExtraLarge,
+        displayLarge: TextStyle = this.displayLarge,
+        displayMedium: TextStyle = this.displayMedium,
+        displaySmall: TextStyle = this.displaySmall,
+        titleLarge: TextStyle = this.titleLarge,
+        titleMedium: TextStyle = this.titleMedium,
+        titleSmall: TextStyle = this.titleSmall,
+        bodyLarge: TextStyle = this.bodyLarge,
+        bodyMedium: TextStyle = this.bodyMedium,
+        bodySmall: TextStyle = this.bodySmall,
+        buttonMedium: TextStyle = this.buttonMedium,
+        captionLarge: TextStyle = this.captionLarge,
+        captionMedium: TextStyle = this.captionMedium,
+        captionSmall: TextStyle = this.captionSmall,
+    ): Typography = Typography(
+        displayExtraLarge,
+        displayLarge,
+        displayMedium,
+        displaySmall,
+        titleLarge,
+        titleMedium,
+        titleSmall,
+        bodyLarge,
+        bodyMedium,
+        bodySmall,
+        buttonMedium,
+        captionLarge,
+        captionMedium,
+        captionSmall,
+    )
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is Typography) return false
+
+        if (displayExtraLarge != other.displayExtraLarge) return false
+        if (displayLarge != other.displayLarge) return false
+        if (displayMedium != other.displayMedium) return false
+        if (displaySmall != other.displaySmall) return false
+        if (titleLarge != other.titleLarge) return false
+        if (titleMedium != other.titleMedium) return false
+        if (titleSmall != other.titleSmall) return false
+        if (bodyLarge != other.bodyLarge) return false
+        if (bodyMedium != other.bodyMedium) return false
+        if (bodySmall != other.bodySmall) return false
+        if (buttonMedium != other.buttonMedium) return false
+        if (captionLarge != other.captionLarge) return false
+        if (captionMedium != other.captionMedium) return false
+        if (captionSmall != other.captionSmall) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = displayExtraLarge.hashCode()
+        result = 31 * result + displayLarge.hashCode()
+        result = 31 * result + displayMedium.hashCode()
+        result = 31 * result + displaySmall.hashCode()
+        result = 31 * result + titleLarge.hashCode()
+        result = 31 * result + titleMedium.hashCode()
+        result = 31 * result + titleSmall.hashCode()
+        result = 31 * result + bodyLarge.hashCode()
+        result = 31 * result + bodyMedium.hashCode()
+        result = 31 * result + bodySmall.hashCode()
+        result = 31 * result + buttonMedium.hashCode()
+        result = 31 * result + captionLarge.hashCode()
+        result = 31 * result + captionMedium.hashCode()
+        result = 31 * result + captionSmall.hashCode()
+        return result
+    }
+
+    override fun toString(): String {
+        return "Typography(displayExtraLarge=$displayExtraLarge, displayLarge=$displayLarge, " +
+            "displayMedium=$displayMedium, displaySmall=$displaySmall, " +
+            "titleLarge=$titleLarge, titleMedium=$titleMedium, titleSmall=$titleSmall, " +
+            "bodyLarge=$bodyLarge, bodyMedium=$bodyMedium, bodySmall=$bodySmall, " +
+            "buttonMedium=$buttonMedium, captionLarge=$captionLarge, " +
+            "captionMedium=$captionMedium, captionSmall=$captionSmall)"
+    }
+}
+
+/**
+ * @return [this] if there is a [FontFamily] defined, otherwise copies [this] with [default] as
+ * the [FontFamily].
+ */
+private fun TextStyle.withDefaultFontFamily(default: FontFamily): TextStyle {
+    return if (fontFamily != null) this else copy(fontFamily = default)
+}
+
+/**
+ * This Ambient holds on to the current definition of typography for this application as described
+ * by the Wear Material spec. You can read the values in it when creating custom components that
+ * want to use Wear Material types, as well as override the values when you want to re-style a part
+ * of your hierarchy. Material components related to text such as Button will use this Ambient
+ * to set values with which to style children text components.
+ *
+ * To access values within this ambient, use [MaterialTheme.typography].
+ */
+internal val LocalTypography = staticCompositionLocalOf { Typography() }
diff --git a/wear/compose/compose-navigation/build.gradle b/wear/compose/compose-navigation/build.gradle
index cee0747..7bfe4ee 100644
--- a/wear/compose/compose-navigation/build.gradle
+++ b/wear/compose/compose-navigation/build.gradle
@@ -29,13 +29,13 @@
     api(project(":compose:runtime:runtime"))
     api("androidx.navigation:navigation-runtime:2.5.3")
     api(project(":wear:compose:compose-material"))
-    api(project(":activity:activity-compose"))
+    api("androidx.activity:activity-compose:1.7.0")
     api("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.0")
 
     implementation(libs.kotlinStdlib)
     implementation(project(":navigation:navigation-common"))
     implementation("androidx.navigation:navigation-compose:2.5.3")
-    implementation("androidx.profileinstaller:profileinstaller:1.2.0")
+    implementation("androidx.profileinstaller:profileinstaller:1.3.0")
 
     androidTestImplementation(project(":compose:test-utils"))
     androidTestImplementation(project(":compose:ui:ui-test-junit4"))
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt
index e0b1320..108b9ad 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt
@@ -106,12 +106,15 @@
         is ActivityDemo<*> -> {
             /* should never get here as activity demos are not added to the backstack*/
         }
+
         is ComposableDemo -> {
             demo.content(DemoParameters(onNavigateBack, state))
         }
+
         is DemoCategory -> {
             DisplayDemoList(demo, onNavigateTo)
         }
+
         else -> {
         }
     }
@@ -190,7 +193,7 @@
 fun Modifier.rsbScroll(
     scrollableState: ScrollableState,
     flingBehavior: FlingBehavior,
-    focusRequester: FocusRequester
+    focusRequester: FocusRequester? = null
 ): Modifier {
     val channel = remember {
         Channel<TimestampedDelta>(
@@ -247,9 +250,12 @@
             channel.trySend(TimestampedDelta(it.uptimeMillis, it.verticalScrollPixels))
             rsbScrollInProgress = true
             true
+        }.let {
+            if (focusRequester != null) {
+                it.focusRequester(focusRequester)
+                    .focusable()
+            } else it
         }
-            .focusRequester(focusRequester)
-            .focusable()
     }
 }
 
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ExpandableDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ExpandableDemo.kt
index 39f1e3c..c629daa 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ExpandableDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ExpandableDemo.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
@@ -29,22 +30,26 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.ExpandableItemsDefaults
+import androidx.wear.compose.foundation.expandableItem
+import androidx.wear.compose.foundation.expandableItems
 import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
 import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
+import androidx.wear.compose.foundation.rememberExpandableItemsState
 import androidx.wear.compose.material.Chip
 import androidx.wear.compose.material.ChipDefaults
-import androidx.wear.compose.material.ExpandableItemsDefaults
+import androidx.wear.compose.material.CompactChip
 import androidx.wear.compose.material.MaterialTheme
-import androidx.wear.compose.material.OutlinedChip
-import androidx.wear.compose.material.ExpandableItemsState
+import androidx.wear.compose.material.OutlinedCompactChip
 import androidx.wear.compose.material.Text
-import androidx.wear.compose.material.rememberExpandableItemsState
-import androidx.wear.compose.material.expandableItem
-import androidx.wear.compose.material.expandableItems
 
 @Composable
 fun ExpandableListItems() {
-    val state = rememberExpandableItemsState()
+    // We have two expandable items, one for the actual items, that is initially in the collapsed
+    // state, and for the "Show More" button (initially expanded).
+    // When the button is pressed, we show the items and hide the button.
+    val itemsState = rememberExpandableItemsState()
+    val buttonState = rememberExpandableItemsState(initiallyExpanded = true)
 
     val items = List(10) { "Item $it" }
     val top = items.take(3)
@@ -54,10 +59,21 @@
         items(top.size) {
             DemoItem(top[it], color = color)
         }
-        expandableItems(state, rest.size) {
+        expandableItems(itemsState, rest.size) {
             DemoItem(rest[it], color = color)
         }
-        item { ExpandButton(state) }
+        // Use another expandable to animate hiding the "Show More" button itself when it's pressed
+        expandableItem(buttonState) { expanded ->
+            if (expanded) {
+                CompactChip(
+                    label = { ExpandButtonContent(false, 0f, Color.Black) },
+                    onClick = {
+                        itemsState.expanded = true
+                        buttonState.expanded = false
+                    }
+                )
+            }
+        }
     }
 }
 
@@ -78,8 +94,13 @@
                 modifier = Modifier.padding(horizontal = 10.dp)
             )
         }
-        item { ExpandButton(state) }
-   }
+        item {
+            OutlinedCompactChip(
+                label = { ExpandButtonContent(state.expanded, state.expandProgress) },
+                onClick = { state.toggle() }
+            )
+        }
+    }
 }
 
 @Composable
@@ -105,20 +126,21 @@
 }
 
 @Composable
-private fun ExpandButton(expandableItemsState: ExpandableItemsState) = OutlinedChip(
-    label = {
-        Text(if (expandableItemsState.expanded) "Show Less" else "Show More")
-        Spacer(Modifier.size(10.dp))
-        ExpandableItemsDefaults.Chevron(
-            expandableItemsState.expandProgress,
-            color = MaterialTheme.colors.primary,
-            modifier = Modifier
-                .size(15.dp, 11.dp)
-                .align(CenterVertically)
-        )
-    },
-    onClick = { expandableItemsState.toggle() }
-)
+private fun RowScope.ExpandButtonContent(
+    expanded: Boolean,
+    expandProgress: Float = 0f,
+    chevronColor: Color = MaterialTheme.colors.primary
+) {
+    Text(if (expanded) "Show Less" else "Show More")
+    Spacer(Modifier.size(6.dp))
+    ExpandableItemsDefaults.Chevron(
+        expandProgress,
+        color = chevronColor,
+        modifier = Modifier
+            .size(15.dp, 11.dp)
+            .align(CenterVertically)
+    )
+}
 
 private fun ScalingLazyListScope.demoSeparator() = item {
     Box(
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/FoundationDemos.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/FoundationDemos.kt
index 49fb373..c0f9761 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/FoundationDemos.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/FoundationDemos.kt
@@ -24,6 +24,8 @@
 import androidx.wear.compose.foundation.samples.CurvedFonts
 import androidx.wear.compose.foundation.samples.CurvedRowAndColumn
 import androidx.wear.compose.foundation.samples.CurvedWeight
+import androidx.wear.compose.foundation.samples.ExpandableTextSample
+import androidx.wear.compose.foundation.samples.ExpandableWithItemsSample
 import androidx.wear.compose.foundation.samples.HierarchicalFocusCoordinatorSample
 import androidx.wear.compose.foundation.samples.OversizeComposable
 import androidx.wear.compose.foundation.samples.ScalingLazyColumnEdgeAnchoredAndAnimatedScrollTo
@@ -35,6 +37,15 @@
 val WearFoundationDemos = DemoCategory(
     "Foundation",
     listOf(
+        DemoCategory(
+            "Expandables",
+            listOf(
+                ComposableDemo("Items in SLC") { ExpandableListItems() },
+                ComposableDemo("Expandable Text") { ExpandableText() },
+                ComposableDemo("Items Sample") { ExpandableWithItemsSample() },
+                ComposableDemo("Text Sample") { ExpandableTextSample() },
+            )
+        ),
         DemoCategory("CurvedLayout", listOf(
             ComposableDemo("Curved Row") { CurvedWorldDemo() },
             ComposableDemo("Curved Row and Column") { CurvedRowAndColumn() },
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt
index baf4417..45ecbf2 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt
@@ -50,8 +50,6 @@
 import androidx.wear.compose.material.samples.CurvedTextDemo
 import androidx.wear.compose.material.samples.CurvedTextProviderDemo
 import androidx.wear.compose.material.samples.EdgeSwipeForSwipeToDismiss
-import androidx.wear.compose.material.samples.ExpandableTextSample
-import androidx.wear.compose.material.samples.ExpandableWithItemsSample
 import androidx.wear.compose.material.samples.FixedFontSize
 import androidx.wear.compose.material.samples.HorizontalPageIndicatorSample
 import androidx.wear.compose.material.samples.IndeterminateCircularProgressIndicator
@@ -130,15 +128,6 @@
     "Material",
     listOf(
         DemoCategory(
-            "Expandables",
-            listOf(
-                ComposableDemo("Items in SLC") { ExpandableListItems() },
-                ComposableDemo("Expandable Text") { ExpandableText() },
-                ComposableDemo("Items Sample") { ExpandableWithItemsSample() },
-                ComposableDemo("Text Sample") { ExpandableTextSample() },
-            )
-        ),
-        DemoCategory(
             "ScrollAway",
             listOf(
                 ComposableDemo("Column") { ScrollAwayColumnDemo() },
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PickerDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PickerDemo.kt
index def1922..510f4b4 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PickerDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/PickerDemo.kt
@@ -61,6 +61,7 @@
 import androidx.compose.ui.input.pointer.pointerInteropFilter
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalFocusManager
 import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.compose.ui.semantics.clearAndSetSemantics
 import androidx.compose.ui.semantics.focused
@@ -165,6 +166,7 @@
                 pickerGroupState.selectedIndex = curr.index
             } else if (next == FocusableElementsTimePicker.CONFIRM_BUTTON) {
                 focusRequesterConfirmButton.requestFocus()
+                pickerGroupState.selectedIndex = next.index
             } else {
                 pickerGroupState.selectedIndex = next.index
             }
@@ -201,8 +203,11 @@
                     PickerGroup(
                         PickerGroupItem(
                             pickerState = hourState,
-                            modifier = Modifier.size(40.dp, 100.dp),
-                            focusRequester = remember { FocusRequester() },
+                            modifier = Modifier
+                                .size(40.dp, 100.dp).rsbScroll(
+                                    scrollableState = hourState,
+                                    flingBehavior = PickerDefaults.flingBehavior(hourState),
+                                ),
                             onSelected = {
                                 onPickerSelected(
                                     FocusableElementsTimePicker.HOURS,
@@ -214,8 +219,11 @@
                         ),
                         PickerGroupItem(
                             pickerState = minuteState,
-                            modifier = Modifier.size(40.dp, 100.dp),
-                            focusRequester = remember { FocusRequester() },
+                            modifier = Modifier
+                                .size(40.dp, 100.dp).rsbScroll(
+                                    scrollableState = minuteState,
+                                    flingBehavior = PickerDefaults.flingBehavior(minuteState),
+                                ),
                             onSelected = {
                                 onPickerSelected(
                                     FocusableElementsTimePicker.MINUTES,
@@ -227,8 +235,11 @@
                         ),
                         PickerGroupItem(
                             pickerState = secondState,
-                            modifier = Modifier.size(40.dp, 100.dp),
-                            focusRequester = remember { FocusRequester() },
+                            modifier = Modifier
+                                .size(40.dp, 100.dp).rsbScroll(
+                                    scrollableState = secondState,
+                                    flingBehavior = PickerDefaults.flingBehavior(secondState),
+                                ),
                             onSelected = {
                                 onPickerSelected(
                                     FocusableElementsTimePicker.SECONDS,
@@ -376,24 +387,27 @@
                 Row(
                     modifier = Modifier.fillMaxWidth(),
                     verticalAlignment = Alignment.CenterVertically,
-                    horizontalArrangement = Arrangement.Center,
-
+                    horizontalArrangement = Arrangement.End,
                     ) {
                     val doubleTapToNext =
                         { current: FocusableElement12Hour, next: FocusableElement12Hour ->
                             if (pickerGroupState.selectedIndex != current.index) {
-                                focusRequesterConfirmButton.requestFocus()
-                            } else if (next == FocusableElement12Hour.CONFIRM_BUTTON) {
                                 pickerGroupState.selectedIndex = current.index
+                            } else if (next == FocusableElement12Hour.CONFIRM_BUTTON) {
+                                focusRequesterConfirmButton.requestFocus()
+                                pickerGroupState.selectedIndex = next.index
                             } else {
                                 pickerGroupState.selectedIndex = next.index
                             }
                         }
+                    Spacer(Modifier.width(16.dp))
                     PickerGroup(
                         PickerGroupItem(
                             pickerState = hourState,
-                            modifier = Modifier.size(40.dp, 100.dp),
-                            focusRequester = remember { FocusRequester() },
+                            modifier = Modifier.size(50.dp, 100.dp).rsbScroll(
+                                scrollableState = hourState,
+                                flingBehavior = PickerDefaults.flingBehavior(hourState),
+                            ),
                             onSelected = {
                                 doubleTapToNext(
                                     FocusableElement12Hour.HOURS,
@@ -401,12 +415,14 @@
                                 )
                             },
                             contentDescription = hoursContentDescription,
-                            option = pickerOption
+                            option = pickerTextOption(textStyle) { "%02d".format(it + 1) }
                         ),
                         PickerGroupItem(
                             pickerState = minuteState,
-                            modifier = Modifier.size(48.dp, 100.dp),
-                            focusRequester = remember { FocusRequester() },
+                            modifier = Modifier.size(50.dp, 100.dp).rsbScroll(
+                                scrollableState = minuteState,
+                                flingBehavior = PickerDefaults.flingBehavior(minuteState),
+                            ),
 
                             onSelected = {
                                 doubleTapToNext(
@@ -419,8 +435,10 @@
                         ),
                         PickerGroupItem(
                             pickerState = periodState,
-                            modifier = Modifier.size(64.dp, 100.dp),
-                            focusRequester = remember { FocusRequester() },
+                            modifier = Modifier.size(64.dp, 100.dp).rsbScroll(
+                                scrollableState = periodState,
+                                flingBehavior = PickerDefaults.flingBehavior(periodState),
+                            ),
                             contentDescription = periodContentDescription,
                             onSelected = {
                                 doubleTapToNext(
@@ -446,7 +464,11 @@
                         ),
                         autoCenter = false,
                         pickerGroupState = pickerGroupState,
-                        separator = { Separator(6.dp, textStyle) },
+                        separator = {
+                            if (it == 0) {
+                                Separator(0.dp, textStyle)
+                            }
+                        },
                     )
                 }
                 Spacer(
@@ -583,6 +605,17 @@
                 )
             } }
 
+        val focusManager = LocalFocusManager.current
+
+        LaunchedEffect(focusedElement) {
+            when (focusedElement) {
+                FocusableElementDatePicker.DAY -> focusRequesterDay.requestFocus()
+                FocusableElementDatePicker.MONTH -> focusRequesterMonth.requestFocus()
+                FocusableElementDatePicker.YEAR -> focusRequesterYear.requestFocus()
+                else -> focusManager.clearFocus()
+            }
+        }
+
         BoxWithConstraints(
             modifier = modifier
                 .fillMaxSize()
diff --git a/wear/protolayout/protolayout-expression-pipeline/api/current.txt b/wear/protolayout/protolayout-expression-pipeline/api/current.txt
index e36b8b2..50ef0aa 100644
--- a/wear/protolayout/protolayout-expression-pipeline/api/current.txt
+++ b/wear/protolayout/protolayout-expression-pipeline/api/current.txt
@@ -7,8 +7,8 @@
   }
 
   public class DynamicTypeEvaluator implements java.lang.AutoCloseable {
-    ctor public DynamicTypeEvaluator(boolean, androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway?, androidx.wear.protolayout.expression.pipeline.ObservableStateStore);
-    ctor public DynamicTypeEvaluator(boolean, androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway?, androidx.wear.protolayout.expression.pipeline.ObservableStateStore, androidx.wear.protolayout.expression.pipeline.QuotaManager);
+    ctor public DynamicTypeEvaluator(boolean, androidx.wear.protolayout.expression.pipeline.ObservableStateStore, androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway?);
+    ctor public DynamicTypeEvaluator(boolean, androidx.wear.protolayout.expression.pipeline.ObservableStateStore, androidx.wear.protolayout.expression.pipeline.QuotaManager, androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway?);
     method public androidx.wear.protolayout.expression.pipeline.BoundDynamicType bind(androidx.wear.protolayout.expression.DynamicBuilders.DynamicString, android.icu.util.ULocale, java.util.concurrent.Executor, androidx.wear.protolayout.expression.pipeline.DynamicTypeValueReceiver<java.lang.String!>);
     method public androidx.wear.protolayout.expression.pipeline.BoundDynamicType bind(androidx.wear.protolayout.expression.DynamicBuilders.DynamicInt32, java.util.concurrent.Executor, androidx.wear.protolayout.expression.pipeline.DynamicTypeValueReceiver<java.lang.Integer!>);
     method public androidx.wear.protolayout.expression.pipeline.BoundDynamicType bind(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat, java.util.concurrent.Executor, androidx.wear.protolayout.expression.pipeline.DynamicTypeValueReceiver<java.lang.Float!>);
diff --git a/wear/protolayout/protolayout-expression-pipeline/api/public_plus_experimental_current.txt b/wear/protolayout/protolayout-expression-pipeline/api/public_plus_experimental_current.txt
index e36b8b2..50ef0aa 100644
--- a/wear/protolayout/protolayout-expression-pipeline/api/public_plus_experimental_current.txt
+++ b/wear/protolayout/protolayout-expression-pipeline/api/public_plus_experimental_current.txt
@@ -7,8 +7,8 @@
   }
 
   public class DynamicTypeEvaluator implements java.lang.AutoCloseable {
-    ctor public DynamicTypeEvaluator(boolean, androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway?, androidx.wear.protolayout.expression.pipeline.ObservableStateStore);
-    ctor public DynamicTypeEvaluator(boolean, androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway?, androidx.wear.protolayout.expression.pipeline.ObservableStateStore, androidx.wear.protolayout.expression.pipeline.QuotaManager);
+    ctor public DynamicTypeEvaluator(boolean, androidx.wear.protolayout.expression.pipeline.ObservableStateStore, androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway?);
+    ctor public DynamicTypeEvaluator(boolean, androidx.wear.protolayout.expression.pipeline.ObservableStateStore, androidx.wear.protolayout.expression.pipeline.QuotaManager, androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway?);
     method public androidx.wear.protolayout.expression.pipeline.BoundDynamicType bind(androidx.wear.protolayout.expression.DynamicBuilders.DynamicString, android.icu.util.ULocale, java.util.concurrent.Executor, androidx.wear.protolayout.expression.pipeline.DynamicTypeValueReceiver<java.lang.String!>);
     method public androidx.wear.protolayout.expression.pipeline.BoundDynamicType bind(androidx.wear.protolayout.expression.DynamicBuilders.DynamicInt32, java.util.concurrent.Executor, androidx.wear.protolayout.expression.pipeline.DynamicTypeValueReceiver<java.lang.Integer!>);
     method public androidx.wear.protolayout.expression.pipeline.BoundDynamicType bind(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat, java.util.concurrent.Executor, androidx.wear.protolayout.expression.pipeline.DynamicTypeValueReceiver<java.lang.Float!>);
diff --git a/wear/protolayout/protolayout-expression-pipeline/api/restricted_current.txt b/wear/protolayout/protolayout-expression-pipeline/api/restricted_current.txt
index e32ed48..0b89ad0 100644
--- a/wear/protolayout/protolayout-expression-pipeline/api/restricted_current.txt
+++ b/wear/protolayout/protolayout-expression-pipeline/api/restricted_current.txt
@@ -7,8 +7,8 @@
   }
 
   public class DynamicTypeEvaluator implements java.lang.AutoCloseable {
-    ctor public DynamicTypeEvaluator(boolean, androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway?, androidx.wear.protolayout.expression.pipeline.ObservableStateStore);
-    ctor public DynamicTypeEvaluator(boolean, androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway?, androidx.wear.protolayout.expression.pipeline.ObservableStateStore, androidx.wear.protolayout.expression.pipeline.QuotaManager);
+    ctor public DynamicTypeEvaluator(boolean, androidx.wear.protolayout.expression.pipeline.ObservableStateStore, androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway?);
+    ctor public DynamicTypeEvaluator(boolean, androidx.wear.protolayout.expression.pipeline.ObservableStateStore, androidx.wear.protolayout.expression.pipeline.QuotaManager, androidx.wear.protolayout.expression.pipeline.sensor.SensorGateway?);
     method public androidx.wear.protolayout.expression.pipeline.BoundDynamicType bind(androidx.wear.protolayout.expression.DynamicBuilders.DynamicString, android.icu.util.ULocale, java.util.concurrent.Executor, androidx.wear.protolayout.expression.pipeline.DynamicTypeValueReceiver<java.lang.String!>);
     method public androidx.wear.protolayout.expression.pipeline.BoundDynamicType bind(androidx.wear.protolayout.expression.DynamicBuilders.DynamicInt32, java.util.concurrent.Executor, androidx.wear.protolayout.expression.pipeline.DynamicTypeValueReceiver<java.lang.Integer!>);
     method public androidx.wear.protolayout.expression.pipeline.BoundDynamicType bind(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat, java.util.concurrent.Executor, androidx.wear.protolayout.expression.pipeline.DynamicTypeValueReceiver<java.lang.Float!>);
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 cfe4d48..fd2b9f9 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
@@ -141,15 +141,15 @@
      */
     public DynamicTypeEvaluator(
             boolean platformDataSourcesInitiallyEnabled,
-            @Nullable SensorGateway sensorGateway,
-            @NonNull ObservableStateStore stateStore) {
+            @NonNull ObservableStateStore stateStore,
+            @Nullable SensorGateway sensorGateway) {
         // Build pipeline with quota that doesn't allow any animations.
         this(
                 platformDataSourcesInitiallyEnabled,
-                sensorGateway,
                 stateStore,
                 /* enableAnimations= */ false,
-                DISABLED_ANIMATIONS_QUOTA_MANAGER);
+                DISABLED_ANIMATIONS_QUOTA_MANAGER,
+                sensorGateway);
     }
 
     /**
@@ -170,15 +170,15 @@
      */
     public DynamicTypeEvaluator(
             boolean platformDataSourcesInitiallyEnabled,
-            @Nullable SensorGateway sensorGateway,
             @NonNull ObservableStateStore stateStore,
-            @NonNull QuotaManager animationQuotaManager) {
+            @NonNull QuotaManager animationQuotaManager,
+            @Nullable SensorGateway sensorGateway) {
         this(
                 platformDataSourcesInitiallyEnabled,
-                sensorGateway,
                 stateStore,
                 /* enableAnimations= */ true,
-                animationQuotaManager);
+                animationQuotaManager,
+                sensorGateway);
     }
 
     /**
@@ -196,10 +196,10 @@
      */
     private DynamicTypeEvaluator(
             boolean platformDataSourcesInitiallyEnabled,
-            @Nullable SensorGateway sensorGateway,
             @NonNull ObservableStateStore stateStore,
             boolean enableAnimations,
-            @NonNull QuotaManager animationQuotaManager) {
+            @NonNull QuotaManager animationQuotaManager,
+            @Nullable SensorGateway sensorGateway) {
         this.mSensorGateway = sensorGateway;
         Handler uiHandler = new Handler(Looper.getMainLooper());
         MainThreadExecutor uiExecutor = new MainThreadExecutor(uiHandler);
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/MainThreadExecutor.java b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/MainThreadExecutor.java
index 600bb618..fbe8d56 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/MainThreadExecutor.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/main/java/androidx/wear/protolayout/expression/pipeline/MainThreadExecutor.java
@@ -35,6 +35,10 @@
 
     @Override
     public void execute(Runnable r) {
-        mHandler.post(r);
+        if (mHandler.getLooper().isCurrentThread()) {
+            r.run();
+        } else {
+            mHandler.post(r);
+        }
     }
 }
diff --git a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluatorTest.java b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluatorTest.java
index 0bcba4e..cb10184 100644
--- a/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluatorTest.java
+++ b/wear/protolayout/protolayout-expression-pipeline/src/test/java/androidx/wear/protolayout/expression/pipeline/DynamicTypeEvaluatorTest.java
@@ -20,12 +20,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.robolectric.Shadows.shadowOf;
-
 import static java.lang.Integer.MAX_VALUE;
 
 import android.icu.util.ULocale;
-import android.os.Looper;
 
 import androidx.annotation.NonNull;
 import androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool;
@@ -273,9 +270,9 @@
         DynamicTypeEvaluator evaluator =
                 new DynamicTypeEvaluator(
                         /* platformDataSourcesInitiallyEnabled= */ true,
-                        /* sensorGateway= */ null,
                         stateStore,
-                        new FixedQuotaManagerImpl(MAX_VALUE));
+                        new FixedQuotaManagerImpl(MAX_VALUE),
+                        /* sensorGateway= */ null);
 
         mTestCase.runTest(evaluator);
     }
@@ -362,7 +359,6 @@
                     };
 
             this.mExpressionEvaluator.accept(evaluator, callback);
-            shadowOf(Looper.getMainLooper()).idle();
 
             assertThat(results).hasSize(1);
             assertThat(results).containsExactly(mExpectedValue);
diff --git a/wear/protolayout/protolayout-material/api/current.txt b/wear/protolayout/protolayout-material/api/current.txt
index e6f50d0..862495c3 100644
--- a/wear/protolayout/protolayout-material/api/current.txt
+++ b/wear/protolayout/protolayout-material/api/current.txt
@@ -1 +1,298 @@
 // Signature format: 4.0
+package androidx.wear.protolayout.material {
+
+  public class Button implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.Button? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.ButtonColors getButtonColors();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable getClickable();
+    method public CharSequence? getContentDescription();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getCustomContent();
+    method public String? getIconContent();
+    method public String? getImageContent();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension getSize();
+    method public String? getTextContent();
+  }
+
+  public static final class Button.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public Button.Builder(android.content.Context, androidx.wear.protolayout.ModifiersBuilders.Clickable);
+    method public androidx.wear.protolayout.material.Button build();
+    method public androidx.wear.protolayout.material.Button.Builder setButtonColors(androidx.wear.protolayout.material.ButtonColors);
+    method public androidx.wear.protolayout.material.Button.Builder setContentDescription(CharSequence);
+    method public androidx.wear.protolayout.material.Button.Builder setCustomContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.Button.Builder setIconContent(String, androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.material.Button.Builder setIconContent(String);
+    method public androidx.wear.protolayout.material.Button.Builder setImageContent(String);
+    method public androidx.wear.protolayout.material.Button.Builder setSize(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.material.Button.Builder setSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    method public androidx.wear.protolayout.material.Button.Builder setTextContent(String);
+    method public androidx.wear.protolayout.material.Button.Builder setTextContent(String, int);
+  }
+
+  public class ButtonColors {
+    ctor public ButtonColors(@ColorInt int, @ColorInt int);
+    ctor public ButtonColors(androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getBackgroundColor();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getContentColor();
+    method public static androidx.wear.protolayout.material.ButtonColors primaryButtonColors(androidx.wear.protolayout.material.Colors);
+    method public static androidx.wear.protolayout.material.ButtonColors secondaryButtonColors(androidx.wear.protolayout.material.Colors);
+  }
+
+  public class ButtonDefaults {
+    method public static androidx.wear.protolayout.DimensionBuilders.DpProp recommendedIconSize(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public static androidx.wear.protolayout.DimensionBuilders.DpProp recommendedIconSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    field public static final androidx.wear.protolayout.DimensionBuilders.DpProp DEFAULT_SIZE;
+    field public static final androidx.wear.protolayout.DimensionBuilders.DpProp EXTRA_LARGE_SIZE;
+    field public static final androidx.wear.protolayout.DimensionBuilders.DpProp LARGE_SIZE;
+    field public static final androidx.wear.protolayout.material.ButtonColors PRIMARY_COLORS;
+    field public static final androidx.wear.protolayout.material.ButtonColors SECONDARY_COLORS;
+  }
+
+  public class Chip implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.Chip? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.ChipColors getChipColors();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable getClickable();
+    method public CharSequence? getContentDescription();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getCustomContent();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension getHeight();
+    method public int getHorizontalAlignment();
+    method public String? getIconContent();
+    method public String? getPrimaryLabelContent();
+    method public String? getSecondaryLabelContent();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension getWidth();
+  }
+
+  public static final class Chip.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public Chip.Builder(android.content.Context, androidx.wear.protolayout.ModifiersBuilders.Clickable, androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.protolayout.material.Chip build();
+    method public androidx.wear.protolayout.material.Chip.Builder setChipColors(androidx.wear.protolayout.material.ChipColors);
+    method public androidx.wear.protolayout.material.Chip.Builder setContentDescription(CharSequence);
+    method public androidx.wear.protolayout.material.Chip.Builder setCustomContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.Chip.Builder setHorizontalAlignment(int);
+    method public androidx.wear.protolayout.material.Chip.Builder setIconContent(String);
+    method public androidx.wear.protolayout.material.Chip.Builder setPrimaryLabelContent(String);
+    method public androidx.wear.protolayout.material.Chip.Builder setSecondaryLabelContent(String);
+    method public androidx.wear.protolayout.material.Chip.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+    method public androidx.wear.protolayout.material.Chip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+  public class ChipColors {
+    ctor public ChipColors(@ColorInt int, @ColorInt int, @ColorInt int, @ColorInt int);
+    ctor public ChipColors(@ColorInt int, @ColorInt int);
+    ctor public ChipColors(androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.ColorBuilders.ColorProp);
+    ctor public ChipColors(androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getBackgroundColor();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getContentColor();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getIconColor();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getSecondaryContentColor();
+    method public static androidx.wear.protolayout.material.ChipColors primaryChipColors(androidx.wear.protolayout.material.Colors);
+    method public static androidx.wear.protolayout.material.ChipColors secondaryChipColors(androidx.wear.protolayout.material.Colors);
+  }
+
+  public class ChipDefaults {
+    field public static final androidx.wear.protolayout.material.ChipColors COMPACT_PRIMARY_COLORS;
+    field public static final androidx.wear.protolayout.material.ChipColors COMPACT_SECONDARY_COLORS;
+    field public static final androidx.wear.protolayout.material.ChipColors PRIMARY_COLORS;
+    field public static final androidx.wear.protolayout.material.ChipColors SECONDARY_COLORS;
+    field public static final androidx.wear.protolayout.material.ChipColors TITLE_PRIMARY_COLORS;
+    field public static final androidx.wear.protolayout.material.ChipColors TITLE_SECONDARY_COLORS;
+  }
+
+  public class CircularProgressIndicator implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.CircularProgressIndicator? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.ProgressIndicatorColors getCircularProgressIndicatorColors();
+    method public CharSequence? getContentDescription();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp getEndAngle();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp getProgress();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp getStartAngle();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp getStrokeWidth();
+  }
+
+  public static final class CircularProgressIndicator.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public CircularProgressIndicator.Builder();
+    method public androidx.wear.protolayout.material.CircularProgressIndicator build();
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setCircularProgressIndicatorColors(androidx.wear.protolayout.material.ProgressIndicatorColors);
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setContentDescription(CharSequence);
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setEndAngle(float);
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setProgress(@FloatRange(from=0, to=1) float);
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setStartAngle(float);
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setStrokeWidth(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setStrokeWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+  public class Colors {
+    ctor public Colors(@ColorInt int, @ColorInt int, @ColorInt int, @ColorInt int);
+    method @ColorInt public int getOnPrimary();
+    method @ColorInt public int getOnSurface();
+    method @ColorInt public int getPrimary();
+    method @ColorInt public int getSurface();
+    field public static final androidx.wear.protolayout.material.Colors DEFAULT;
+  }
+
+  public class CompactChip implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.CompactChip? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.ChipColors getChipColors();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable getClickable();
+    method public String getText();
+  }
+
+  public static final class CompactChip.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public CompactChip.Builder(android.content.Context, String, androidx.wear.protolayout.ModifiersBuilders.Clickable, androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.protolayout.material.CompactChip build();
+    method public androidx.wear.protolayout.material.CompactChip.Builder setChipColors(androidx.wear.protolayout.material.ChipColors);
+  }
+
+  public class ProgressIndicatorColors {
+    ctor public ProgressIndicatorColors(androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.ColorBuilders.ColorProp);
+    ctor public ProgressIndicatorColors(@ColorInt int, @ColorInt int);
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getIndicatorColor();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getTrackColor();
+    method public static androidx.wear.protolayout.material.ProgressIndicatorColors progressIndicatorColors(androidx.wear.protolayout.material.Colors);
+  }
+
+  public class ProgressIndicatorDefaults {
+    field public static final androidx.wear.protolayout.material.ProgressIndicatorColors DEFAULT_COLORS;
+    field public static final androidx.wear.protolayout.DimensionBuilders.DpProp DEFAULT_STROKE_WIDTH;
+    field public static final float GAP_END_ANGLE = 156.1f;
+    field public static final float GAP_START_ANGLE = -156.1f;
+  }
+
+  public class Text implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.Text? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getColor();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle getFontStyle();
+    method public float getLineHeight();
+    method public int getMaxLines();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers getModifiers();
+    method public int getMultilineAlignment();
+    method public int getOverflow();
+    method public String getText();
+    method public int getWeight();
+    method public boolean isItalic();
+    method public boolean isUnderline();
+  }
+
+  public static final class Text.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public Text.Builder(android.content.Context, String);
+    method public androidx.wear.protolayout.material.Text build();
+    method public androidx.wear.protolayout.material.Text.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.material.Text.Builder setItalic(boolean);
+    method public androidx.wear.protolayout.material.Text.Builder setMaxLines(@IntRange(from=1) int);
+    method public androidx.wear.protolayout.material.Text.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.material.Text.Builder setMultilineAlignment(int);
+    method public androidx.wear.protolayout.material.Text.Builder setOverflow(int);
+    method public androidx.wear.protolayout.material.Text.Builder setTypography(int);
+    method public androidx.wear.protolayout.material.Text.Builder setUnderline(boolean);
+    method public androidx.wear.protolayout.material.Text.Builder setWeight(int);
+  }
+
+  public class TitleChip implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.TitleChip? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.ChipColors getChipColors();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable getClickable();
+    method public int getHorizontalAlignment();
+    method public String getText();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension getWidth();
+  }
+
+  public static final class TitleChip.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public TitleChip.Builder(android.content.Context, String, androidx.wear.protolayout.ModifiersBuilders.Clickable, androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.protolayout.material.TitleChip build();
+    method public androidx.wear.protolayout.material.TitleChip.Builder setChipColors(androidx.wear.protolayout.material.ChipColors);
+    method public androidx.wear.protolayout.material.TitleChip.Builder setHorizontalAlignment(int);
+    method public androidx.wear.protolayout.material.TitleChip.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+    method public androidx.wear.protolayout.material.TitleChip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+  public class Typography {
+    field public static final int TYPOGRAPHY_BODY1 = 7; // 0x7
+    field public static final int TYPOGRAPHY_BODY2 = 8; // 0x8
+    field public static final int TYPOGRAPHY_BUTTON = 9; // 0x9
+    field public static final int TYPOGRAPHY_CAPTION1 = 10; // 0xa
+    field public static final int TYPOGRAPHY_CAPTION2 = 11; // 0xb
+    field public static final int TYPOGRAPHY_CAPTION3 = 12; // 0xc
+    field public static final int TYPOGRAPHY_DISPLAY1 = 1; // 0x1
+    field public static final int TYPOGRAPHY_DISPLAY2 = 2; // 0x2
+    field public static final int TYPOGRAPHY_DISPLAY3 = 3; // 0x3
+    field public static final int TYPOGRAPHY_TITLE1 = 4; // 0x4
+    field public static final int TYPOGRAPHY_TITLE2 = 5; // 0x5
+    field public static final int TYPOGRAPHY_TITLE3 = 6; // 0x6
+  }
+
+}
+
+package androidx.wear.protolayout.material.layouts {
+
+  public class EdgeContentLayout implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.layouts.EdgeContentLayout? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getContent();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getEdgeContent();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getPrimaryLabelTextContent();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getSecondaryLabelTextContent();
+  }
+
+  public static final class EdgeContentLayout.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public EdgeContentLayout.Builder(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.protolayout.material.layouts.EdgeContentLayout build();
+    method public androidx.wear.protolayout.material.layouts.EdgeContentLayout.Builder setContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.EdgeContentLayout.Builder setEdgeContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.EdgeContentLayout.Builder setPrimaryLabelTextContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.EdgeContentLayout.Builder setSecondaryLabelTextContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+  }
+
+  public class LayoutDefaults {
+    field public static final androidx.wear.protolayout.DimensionBuilders.DpProp DEFAULT_VERTICAL_SPACER_HEIGHT;
+    field public static final float EDGE_CONTENT_LAYOUT_PADDING_ABOVE_MAIN_CONTENT_DP = 6.0f;
+    field public static final float EDGE_CONTENT_LAYOUT_PADDING_BELOW_MAIN_CONTENT_DP = 8.0f;
+    field public static final int MULTI_BUTTON_MAX_NUMBER = 7; // 0x7
+    field public static final androidx.wear.protolayout.DimensionBuilders.DpProp MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH;
+  }
+
+  public class MultiButtonLayout implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.layouts.MultiButtonLayout? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.LayoutElement!> getButtonContents();
+    method public int getFiveButtonDistribution();
+    field public static final int FIVE_BUTTON_DISTRIBUTION_BOTTOM_HEAVY = 2; // 0x2
+    field public static final int FIVE_BUTTON_DISTRIBUTION_TOP_HEAVY = 1; // 0x1
+  }
+
+  public static final class MultiButtonLayout.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public MultiButtonLayout.Builder();
+    method public androidx.wear.protolayout.material.layouts.MultiButtonLayout.Builder addButtonContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.MultiButtonLayout build();
+    method public androidx.wear.protolayout.material.layouts.MultiButtonLayout.Builder setFiveButtonDistribution(int);
+  }
+
+  public class MultiSlotLayout implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.layouts.MultiSlotLayout? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getHorizontalSpacerWidth();
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.LayoutElement!> getSlotContents();
+  }
+
+  public static final class MultiSlotLayout.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public MultiSlotLayout.Builder();
+    method public androidx.wear.protolayout.material.layouts.MultiSlotLayout.Builder addSlotContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.MultiSlotLayout build();
+    method public androidx.wear.protolayout.material.layouts.MultiSlotLayout.Builder setHorizontalSpacerWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+  public class PrimaryLayout implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.layouts.PrimaryLayout? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getContent();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getPrimaryChipContent();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getPrimaryLabelTextContent();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getSecondaryLabelTextContent();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getVerticalSpacerHeight();
+  }
+
+  public static final class PrimaryLayout.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public PrimaryLayout.Builder(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.protolayout.material.layouts.PrimaryLayout build();
+    method public androidx.wear.protolayout.material.layouts.PrimaryLayout.Builder setContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.PrimaryLayout.Builder setPrimaryChipContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.PrimaryLayout.Builder setPrimaryLabelTextContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.PrimaryLayout.Builder setSecondaryLabelTextContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.PrimaryLayout.Builder setVerticalSpacerHeight(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+}
+
diff --git a/wear/protolayout/protolayout-material/api/public_plus_experimental_current.txt b/wear/protolayout/protolayout-material/api/public_plus_experimental_current.txt
index e6f50d0..862495c3 100644
--- a/wear/protolayout/protolayout-material/api/public_plus_experimental_current.txt
+++ b/wear/protolayout/protolayout-material/api/public_plus_experimental_current.txt
@@ -1 +1,298 @@
 // Signature format: 4.0
+package androidx.wear.protolayout.material {
+
+  public class Button implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.Button? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.ButtonColors getButtonColors();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable getClickable();
+    method public CharSequence? getContentDescription();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getCustomContent();
+    method public String? getIconContent();
+    method public String? getImageContent();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension getSize();
+    method public String? getTextContent();
+  }
+
+  public static final class Button.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public Button.Builder(android.content.Context, androidx.wear.protolayout.ModifiersBuilders.Clickable);
+    method public androidx.wear.protolayout.material.Button build();
+    method public androidx.wear.protolayout.material.Button.Builder setButtonColors(androidx.wear.protolayout.material.ButtonColors);
+    method public androidx.wear.protolayout.material.Button.Builder setContentDescription(CharSequence);
+    method public androidx.wear.protolayout.material.Button.Builder setCustomContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.Button.Builder setIconContent(String, androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.material.Button.Builder setIconContent(String);
+    method public androidx.wear.protolayout.material.Button.Builder setImageContent(String);
+    method public androidx.wear.protolayout.material.Button.Builder setSize(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.material.Button.Builder setSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    method public androidx.wear.protolayout.material.Button.Builder setTextContent(String);
+    method public androidx.wear.protolayout.material.Button.Builder setTextContent(String, int);
+  }
+
+  public class ButtonColors {
+    ctor public ButtonColors(@ColorInt int, @ColorInt int);
+    ctor public ButtonColors(androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getBackgroundColor();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getContentColor();
+    method public static androidx.wear.protolayout.material.ButtonColors primaryButtonColors(androidx.wear.protolayout.material.Colors);
+    method public static androidx.wear.protolayout.material.ButtonColors secondaryButtonColors(androidx.wear.protolayout.material.Colors);
+  }
+
+  public class ButtonDefaults {
+    method public static androidx.wear.protolayout.DimensionBuilders.DpProp recommendedIconSize(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public static androidx.wear.protolayout.DimensionBuilders.DpProp recommendedIconSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    field public static final androidx.wear.protolayout.DimensionBuilders.DpProp DEFAULT_SIZE;
+    field public static final androidx.wear.protolayout.DimensionBuilders.DpProp EXTRA_LARGE_SIZE;
+    field public static final androidx.wear.protolayout.DimensionBuilders.DpProp LARGE_SIZE;
+    field public static final androidx.wear.protolayout.material.ButtonColors PRIMARY_COLORS;
+    field public static final androidx.wear.protolayout.material.ButtonColors SECONDARY_COLORS;
+  }
+
+  public class Chip implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.Chip? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.ChipColors getChipColors();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable getClickable();
+    method public CharSequence? getContentDescription();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getCustomContent();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension getHeight();
+    method public int getHorizontalAlignment();
+    method public String? getIconContent();
+    method public String? getPrimaryLabelContent();
+    method public String? getSecondaryLabelContent();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension getWidth();
+  }
+
+  public static final class Chip.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public Chip.Builder(android.content.Context, androidx.wear.protolayout.ModifiersBuilders.Clickable, androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.protolayout.material.Chip build();
+    method public androidx.wear.protolayout.material.Chip.Builder setChipColors(androidx.wear.protolayout.material.ChipColors);
+    method public androidx.wear.protolayout.material.Chip.Builder setContentDescription(CharSequence);
+    method public androidx.wear.protolayout.material.Chip.Builder setCustomContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.Chip.Builder setHorizontalAlignment(int);
+    method public androidx.wear.protolayout.material.Chip.Builder setIconContent(String);
+    method public androidx.wear.protolayout.material.Chip.Builder setPrimaryLabelContent(String);
+    method public androidx.wear.protolayout.material.Chip.Builder setSecondaryLabelContent(String);
+    method public androidx.wear.protolayout.material.Chip.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+    method public androidx.wear.protolayout.material.Chip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+  public class ChipColors {
+    ctor public ChipColors(@ColorInt int, @ColorInt int, @ColorInt int, @ColorInt int);
+    ctor public ChipColors(@ColorInt int, @ColorInt int);
+    ctor public ChipColors(androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.ColorBuilders.ColorProp);
+    ctor public ChipColors(androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getBackgroundColor();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getContentColor();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getIconColor();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getSecondaryContentColor();
+    method public static androidx.wear.protolayout.material.ChipColors primaryChipColors(androidx.wear.protolayout.material.Colors);
+    method public static androidx.wear.protolayout.material.ChipColors secondaryChipColors(androidx.wear.protolayout.material.Colors);
+  }
+
+  public class ChipDefaults {
+    field public static final androidx.wear.protolayout.material.ChipColors COMPACT_PRIMARY_COLORS;
+    field public static final androidx.wear.protolayout.material.ChipColors COMPACT_SECONDARY_COLORS;
+    field public static final androidx.wear.protolayout.material.ChipColors PRIMARY_COLORS;
+    field public static final androidx.wear.protolayout.material.ChipColors SECONDARY_COLORS;
+    field public static final androidx.wear.protolayout.material.ChipColors TITLE_PRIMARY_COLORS;
+    field public static final androidx.wear.protolayout.material.ChipColors TITLE_SECONDARY_COLORS;
+  }
+
+  public class CircularProgressIndicator implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.CircularProgressIndicator? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.ProgressIndicatorColors getCircularProgressIndicatorColors();
+    method public CharSequence? getContentDescription();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp getEndAngle();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp getProgress();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp getStartAngle();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp getStrokeWidth();
+  }
+
+  public static final class CircularProgressIndicator.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public CircularProgressIndicator.Builder();
+    method public androidx.wear.protolayout.material.CircularProgressIndicator build();
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setCircularProgressIndicatorColors(androidx.wear.protolayout.material.ProgressIndicatorColors);
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setContentDescription(CharSequence);
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setEndAngle(float);
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setProgress(@FloatRange(from=0, to=1) float);
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setStartAngle(float);
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setStrokeWidth(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setStrokeWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+  public class Colors {
+    ctor public Colors(@ColorInt int, @ColorInt int, @ColorInt int, @ColorInt int);
+    method @ColorInt public int getOnPrimary();
+    method @ColorInt public int getOnSurface();
+    method @ColorInt public int getPrimary();
+    method @ColorInt public int getSurface();
+    field public static final androidx.wear.protolayout.material.Colors DEFAULT;
+  }
+
+  public class CompactChip implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.CompactChip? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.ChipColors getChipColors();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable getClickable();
+    method public String getText();
+  }
+
+  public static final class CompactChip.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public CompactChip.Builder(android.content.Context, String, androidx.wear.protolayout.ModifiersBuilders.Clickable, androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.protolayout.material.CompactChip build();
+    method public androidx.wear.protolayout.material.CompactChip.Builder setChipColors(androidx.wear.protolayout.material.ChipColors);
+  }
+
+  public class ProgressIndicatorColors {
+    ctor public ProgressIndicatorColors(androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.ColorBuilders.ColorProp);
+    ctor public ProgressIndicatorColors(@ColorInt int, @ColorInt int);
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getIndicatorColor();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getTrackColor();
+    method public static androidx.wear.protolayout.material.ProgressIndicatorColors progressIndicatorColors(androidx.wear.protolayout.material.Colors);
+  }
+
+  public class ProgressIndicatorDefaults {
+    field public static final androidx.wear.protolayout.material.ProgressIndicatorColors DEFAULT_COLORS;
+    field public static final androidx.wear.protolayout.DimensionBuilders.DpProp DEFAULT_STROKE_WIDTH;
+    field public static final float GAP_END_ANGLE = 156.1f;
+    field public static final float GAP_START_ANGLE = -156.1f;
+  }
+
+  public class Text implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.Text? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getColor();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle getFontStyle();
+    method public float getLineHeight();
+    method public int getMaxLines();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers getModifiers();
+    method public int getMultilineAlignment();
+    method public int getOverflow();
+    method public String getText();
+    method public int getWeight();
+    method public boolean isItalic();
+    method public boolean isUnderline();
+  }
+
+  public static final class Text.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public Text.Builder(android.content.Context, String);
+    method public androidx.wear.protolayout.material.Text build();
+    method public androidx.wear.protolayout.material.Text.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.material.Text.Builder setItalic(boolean);
+    method public androidx.wear.protolayout.material.Text.Builder setMaxLines(@IntRange(from=1) int);
+    method public androidx.wear.protolayout.material.Text.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.material.Text.Builder setMultilineAlignment(int);
+    method public androidx.wear.protolayout.material.Text.Builder setOverflow(int);
+    method public androidx.wear.protolayout.material.Text.Builder setTypography(int);
+    method public androidx.wear.protolayout.material.Text.Builder setUnderline(boolean);
+    method public androidx.wear.protolayout.material.Text.Builder setWeight(int);
+  }
+
+  public class TitleChip implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.TitleChip? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.ChipColors getChipColors();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable getClickable();
+    method public int getHorizontalAlignment();
+    method public String getText();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension getWidth();
+  }
+
+  public static final class TitleChip.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public TitleChip.Builder(android.content.Context, String, androidx.wear.protolayout.ModifiersBuilders.Clickable, androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.protolayout.material.TitleChip build();
+    method public androidx.wear.protolayout.material.TitleChip.Builder setChipColors(androidx.wear.protolayout.material.ChipColors);
+    method public androidx.wear.protolayout.material.TitleChip.Builder setHorizontalAlignment(int);
+    method public androidx.wear.protolayout.material.TitleChip.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+    method public androidx.wear.protolayout.material.TitleChip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+  public class Typography {
+    field public static final int TYPOGRAPHY_BODY1 = 7; // 0x7
+    field public static final int TYPOGRAPHY_BODY2 = 8; // 0x8
+    field public static final int TYPOGRAPHY_BUTTON = 9; // 0x9
+    field public static final int TYPOGRAPHY_CAPTION1 = 10; // 0xa
+    field public static final int TYPOGRAPHY_CAPTION2 = 11; // 0xb
+    field public static final int TYPOGRAPHY_CAPTION3 = 12; // 0xc
+    field public static final int TYPOGRAPHY_DISPLAY1 = 1; // 0x1
+    field public static final int TYPOGRAPHY_DISPLAY2 = 2; // 0x2
+    field public static final int TYPOGRAPHY_DISPLAY3 = 3; // 0x3
+    field public static final int TYPOGRAPHY_TITLE1 = 4; // 0x4
+    field public static final int TYPOGRAPHY_TITLE2 = 5; // 0x5
+    field public static final int TYPOGRAPHY_TITLE3 = 6; // 0x6
+  }
+
+}
+
+package androidx.wear.protolayout.material.layouts {
+
+  public class EdgeContentLayout implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.layouts.EdgeContentLayout? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getContent();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getEdgeContent();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getPrimaryLabelTextContent();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getSecondaryLabelTextContent();
+  }
+
+  public static final class EdgeContentLayout.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public EdgeContentLayout.Builder(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.protolayout.material.layouts.EdgeContentLayout build();
+    method public androidx.wear.protolayout.material.layouts.EdgeContentLayout.Builder setContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.EdgeContentLayout.Builder setEdgeContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.EdgeContentLayout.Builder setPrimaryLabelTextContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.EdgeContentLayout.Builder setSecondaryLabelTextContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+  }
+
+  public class LayoutDefaults {
+    field public static final androidx.wear.protolayout.DimensionBuilders.DpProp DEFAULT_VERTICAL_SPACER_HEIGHT;
+    field public static final float EDGE_CONTENT_LAYOUT_PADDING_ABOVE_MAIN_CONTENT_DP = 6.0f;
+    field public static final float EDGE_CONTENT_LAYOUT_PADDING_BELOW_MAIN_CONTENT_DP = 8.0f;
+    field public static final int MULTI_BUTTON_MAX_NUMBER = 7; // 0x7
+    field public static final androidx.wear.protolayout.DimensionBuilders.DpProp MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH;
+  }
+
+  public class MultiButtonLayout implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.layouts.MultiButtonLayout? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.LayoutElement!> getButtonContents();
+    method public int getFiveButtonDistribution();
+    field public static final int FIVE_BUTTON_DISTRIBUTION_BOTTOM_HEAVY = 2; // 0x2
+    field public static final int FIVE_BUTTON_DISTRIBUTION_TOP_HEAVY = 1; // 0x1
+  }
+
+  public static final class MultiButtonLayout.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public MultiButtonLayout.Builder();
+    method public androidx.wear.protolayout.material.layouts.MultiButtonLayout.Builder addButtonContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.MultiButtonLayout build();
+    method public androidx.wear.protolayout.material.layouts.MultiButtonLayout.Builder setFiveButtonDistribution(int);
+  }
+
+  public class MultiSlotLayout implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.layouts.MultiSlotLayout? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getHorizontalSpacerWidth();
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.LayoutElement!> getSlotContents();
+  }
+
+  public static final class MultiSlotLayout.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public MultiSlotLayout.Builder();
+    method public androidx.wear.protolayout.material.layouts.MultiSlotLayout.Builder addSlotContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.MultiSlotLayout build();
+    method public androidx.wear.protolayout.material.layouts.MultiSlotLayout.Builder setHorizontalSpacerWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+  public class PrimaryLayout implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.layouts.PrimaryLayout? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getContent();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getPrimaryChipContent();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getPrimaryLabelTextContent();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getSecondaryLabelTextContent();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getVerticalSpacerHeight();
+  }
+
+  public static final class PrimaryLayout.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public PrimaryLayout.Builder(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.protolayout.material.layouts.PrimaryLayout build();
+    method public androidx.wear.protolayout.material.layouts.PrimaryLayout.Builder setContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.PrimaryLayout.Builder setPrimaryChipContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.PrimaryLayout.Builder setPrimaryLabelTextContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.PrimaryLayout.Builder setSecondaryLabelTextContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.PrimaryLayout.Builder setVerticalSpacerHeight(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+}
+
diff --git a/wear/protolayout/protolayout-material/api/restricted_current.txt b/wear/protolayout/protolayout-material/api/restricted_current.txt
index e6f50d0..862495c3 100644
--- a/wear/protolayout/protolayout-material/api/restricted_current.txt
+++ b/wear/protolayout/protolayout-material/api/restricted_current.txt
@@ -1 +1,298 @@
 // Signature format: 4.0
+package androidx.wear.protolayout.material {
+
+  public class Button implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.Button? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.ButtonColors getButtonColors();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable getClickable();
+    method public CharSequence? getContentDescription();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getCustomContent();
+    method public String? getIconContent();
+    method public String? getImageContent();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension getSize();
+    method public String? getTextContent();
+  }
+
+  public static final class Button.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public Button.Builder(android.content.Context, androidx.wear.protolayout.ModifiersBuilders.Clickable);
+    method public androidx.wear.protolayout.material.Button build();
+    method public androidx.wear.protolayout.material.Button.Builder setButtonColors(androidx.wear.protolayout.material.ButtonColors);
+    method public androidx.wear.protolayout.material.Button.Builder setContentDescription(CharSequence);
+    method public androidx.wear.protolayout.material.Button.Builder setCustomContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.Button.Builder setIconContent(String, androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.material.Button.Builder setIconContent(String);
+    method public androidx.wear.protolayout.material.Button.Builder setImageContent(String);
+    method public androidx.wear.protolayout.material.Button.Builder setSize(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.material.Button.Builder setSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    method public androidx.wear.protolayout.material.Button.Builder setTextContent(String);
+    method public androidx.wear.protolayout.material.Button.Builder setTextContent(String, int);
+  }
+
+  public class ButtonColors {
+    ctor public ButtonColors(@ColorInt int, @ColorInt int);
+    ctor public ButtonColors(androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getBackgroundColor();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getContentColor();
+    method public static androidx.wear.protolayout.material.ButtonColors primaryButtonColors(androidx.wear.protolayout.material.Colors);
+    method public static androidx.wear.protolayout.material.ButtonColors secondaryButtonColors(androidx.wear.protolayout.material.Colors);
+  }
+
+  public class ButtonDefaults {
+    method public static androidx.wear.protolayout.DimensionBuilders.DpProp recommendedIconSize(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public static androidx.wear.protolayout.DimensionBuilders.DpProp recommendedIconSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    field public static final androidx.wear.protolayout.DimensionBuilders.DpProp DEFAULT_SIZE;
+    field public static final androidx.wear.protolayout.DimensionBuilders.DpProp EXTRA_LARGE_SIZE;
+    field public static final androidx.wear.protolayout.DimensionBuilders.DpProp LARGE_SIZE;
+    field public static final androidx.wear.protolayout.material.ButtonColors PRIMARY_COLORS;
+    field public static final androidx.wear.protolayout.material.ButtonColors SECONDARY_COLORS;
+  }
+
+  public class Chip implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.Chip? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.ChipColors getChipColors();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable getClickable();
+    method public CharSequence? getContentDescription();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getCustomContent();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension getHeight();
+    method public int getHorizontalAlignment();
+    method public String? getIconContent();
+    method public String? getPrimaryLabelContent();
+    method public String? getSecondaryLabelContent();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension getWidth();
+  }
+
+  public static final class Chip.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public Chip.Builder(android.content.Context, androidx.wear.protolayout.ModifiersBuilders.Clickable, androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.protolayout.material.Chip build();
+    method public androidx.wear.protolayout.material.Chip.Builder setChipColors(androidx.wear.protolayout.material.ChipColors);
+    method public androidx.wear.protolayout.material.Chip.Builder setContentDescription(CharSequence);
+    method public androidx.wear.protolayout.material.Chip.Builder setCustomContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.Chip.Builder setHorizontalAlignment(int);
+    method public androidx.wear.protolayout.material.Chip.Builder setIconContent(String);
+    method public androidx.wear.protolayout.material.Chip.Builder setPrimaryLabelContent(String);
+    method public androidx.wear.protolayout.material.Chip.Builder setSecondaryLabelContent(String);
+    method public androidx.wear.protolayout.material.Chip.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+    method public androidx.wear.protolayout.material.Chip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+  public class ChipColors {
+    ctor public ChipColors(@ColorInt int, @ColorInt int, @ColorInt int, @ColorInt int);
+    ctor public ChipColors(@ColorInt int, @ColorInt int);
+    ctor public ChipColors(androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.ColorBuilders.ColorProp);
+    ctor public ChipColors(androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getBackgroundColor();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getContentColor();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getIconColor();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getSecondaryContentColor();
+    method public static androidx.wear.protolayout.material.ChipColors primaryChipColors(androidx.wear.protolayout.material.Colors);
+    method public static androidx.wear.protolayout.material.ChipColors secondaryChipColors(androidx.wear.protolayout.material.Colors);
+  }
+
+  public class ChipDefaults {
+    field public static final androidx.wear.protolayout.material.ChipColors COMPACT_PRIMARY_COLORS;
+    field public static final androidx.wear.protolayout.material.ChipColors COMPACT_SECONDARY_COLORS;
+    field public static final androidx.wear.protolayout.material.ChipColors PRIMARY_COLORS;
+    field public static final androidx.wear.protolayout.material.ChipColors SECONDARY_COLORS;
+    field public static final androidx.wear.protolayout.material.ChipColors TITLE_PRIMARY_COLORS;
+    field public static final androidx.wear.protolayout.material.ChipColors TITLE_SECONDARY_COLORS;
+  }
+
+  public class CircularProgressIndicator implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.CircularProgressIndicator? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.ProgressIndicatorColors getCircularProgressIndicatorColors();
+    method public CharSequence? getContentDescription();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp getEndAngle();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp getProgress();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp getStartAngle();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp getStrokeWidth();
+  }
+
+  public static final class CircularProgressIndicator.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public CircularProgressIndicator.Builder();
+    method public androidx.wear.protolayout.material.CircularProgressIndicator build();
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setCircularProgressIndicatorColors(androidx.wear.protolayout.material.ProgressIndicatorColors);
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setContentDescription(CharSequence);
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setEndAngle(float);
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setProgress(@FloatRange(from=0, to=1) float);
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setStartAngle(float);
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setStrokeWidth(androidx.wear.protolayout.DimensionBuilders.DpProp);
+    method public androidx.wear.protolayout.material.CircularProgressIndicator.Builder setStrokeWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+  public class Colors {
+    ctor public Colors(@ColorInt int, @ColorInt int, @ColorInt int, @ColorInt int);
+    method @ColorInt public int getOnPrimary();
+    method @ColorInt public int getOnSurface();
+    method @ColorInt public int getPrimary();
+    method @ColorInt public int getSurface();
+    field public static final androidx.wear.protolayout.material.Colors DEFAULT;
+  }
+
+  public class CompactChip implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.CompactChip? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.ChipColors getChipColors();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable getClickable();
+    method public String getText();
+  }
+
+  public static final class CompactChip.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public CompactChip.Builder(android.content.Context, String, androidx.wear.protolayout.ModifiersBuilders.Clickable, androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.protolayout.material.CompactChip build();
+    method public androidx.wear.protolayout.material.CompactChip.Builder setChipColors(androidx.wear.protolayout.material.ChipColors);
+  }
+
+  public class ProgressIndicatorColors {
+    ctor public ProgressIndicatorColors(androidx.wear.protolayout.ColorBuilders.ColorProp, androidx.wear.protolayout.ColorBuilders.ColorProp);
+    ctor public ProgressIndicatorColors(@ColorInt int, @ColorInt int);
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getIndicatorColor();
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getTrackColor();
+    method public static androidx.wear.protolayout.material.ProgressIndicatorColors progressIndicatorColors(androidx.wear.protolayout.material.Colors);
+  }
+
+  public class ProgressIndicatorDefaults {
+    field public static final androidx.wear.protolayout.material.ProgressIndicatorColors DEFAULT_COLORS;
+    field public static final androidx.wear.protolayout.DimensionBuilders.DpProp DEFAULT_STROKE_WIDTH;
+    field public static final float GAP_END_ANGLE = 156.1f;
+    field public static final float GAP_START_ANGLE = -156.1f;
+  }
+
+  public class Text implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.Text? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.ColorBuilders.ColorProp getColor();
+    method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle getFontStyle();
+    method public float getLineHeight();
+    method public int getMaxLines();
+    method public androidx.wear.protolayout.ModifiersBuilders.Modifiers getModifiers();
+    method public int getMultilineAlignment();
+    method public int getOverflow();
+    method public String getText();
+    method public int getWeight();
+    method public boolean isItalic();
+    method public boolean isUnderline();
+  }
+
+  public static final class Text.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public Text.Builder(android.content.Context, String);
+    method public androidx.wear.protolayout.material.Text build();
+    method public androidx.wear.protolayout.material.Text.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.material.Text.Builder setItalic(boolean);
+    method public androidx.wear.protolayout.material.Text.Builder setMaxLines(@IntRange(from=1) int);
+    method public androidx.wear.protolayout.material.Text.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
+    method public androidx.wear.protolayout.material.Text.Builder setMultilineAlignment(int);
+    method public androidx.wear.protolayout.material.Text.Builder setOverflow(int);
+    method public androidx.wear.protolayout.material.Text.Builder setTypography(int);
+    method public androidx.wear.protolayout.material.Text.Builder setUnderline(boolean);
+    method public androidx.wear.protolayout.material.Text.Builder setWeight(int);
+  }
+
+  public class TitleChip implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.TitleChip? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.ChipColors getChipColors();
+    method public androidx.wear.protolayout.ModifiersBuilders.Clickable getClickable();
+    method public int getHorizontalAlignment();
+    method public String getText();
+    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension getWidth();
+  }
+
+  public static final class TitleChip.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public TitleChip.Builder(android.content.Context, String, androidx.wear.protolayout.ModifiersBuilders.Clickable, androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.protolayout.material.TitleChip build();
+    method public androidx.wear.protolayout.material.TitleChip.Builder setChipColors(androidx.wear.protolayout.material.ChipColors);
+    method public androidx.wear.protolayout.material.TitleChip.Builder setHorizontalAlignment(int);
+    method public androidx.wear.protolayout.material.TitleChip.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.ContainerDimension);
+    method public androidx.wear.protolayout.material.TitleChip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+  public class Typography {
+    field public static final int TYPOGRAPHY_BODY1 = 7; // 0x7
+    field public static final int TYPOGRAPHY_BODY2 = 8; // 0x8
+    field public static final int TYPOGRAPHY_BUTTON = 9; // 0x9
+    field public static final int TYPOGRAPHY_CAPTION1 = 10; // 0xa
+    field public static final int TYPOGRAPHY_CAPTION2 = 11; // 0xb
+    field public static final int TYPOGRAPHY_CAPTION3 = 12; // 0xc
+    field public static final int TYPOGRAPHY_DISPLAY1 = 1; // 0x1
+    field public static final int TYPOGRAPHY_DISPLAY2 = 2; // 0x2
+    field public static final int TYPOGRAPHY_DISPLAY3 = 3; // 0x3
+    field public static final int TYPOGRAPHY_TITLE1 = 4; // 0x4
+    field public static final int TYPOGRAPHY_TITLE2 = 5; // 0x5
+    field public static final int TYPOGRAPHY_TITLE3 = 6; // 0x6
+  }
+
+}
+
+package androidx.wear.protolayout.material.layouts {
+
+  public class EdgeContentLayout implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.layouts.EdgeContentLayout? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getContent();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getEdgeContent();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getPrimaryLabelTextContent();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getSecondaryLabelTextContent();
+  }
+
+  public static final class EdgeContentLayout.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public EdgeContentLayout.Builder(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.protolayout.material.layouts.EdgeContentLayout build();
+    method public androidx.wear.protolayout.material.layouts.EdgeContentLayout.Builder setContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.EdgeContentLayout.Builder setEdgeContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.EdgeContentLayout.Builder setPrimaryLabelTextContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.EdgeContentLayout.Builder setSecondaryLabelTextContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+  }
+
+  public class LayoutDefaults {
+    field public static final androidx.wear.protolayout.DimensionBuilders.DpProp DEFAULT_VERTICAL_SPACER_HEIGHT;
+    field public static final float EDGE_CONTENT_LAYOUT_PADDING_ABOVE_MAIN_CONTENT_DP = 6.0f;
+    field public static final float EDGE_CONTENT_LAYOUT_PADDING_BELOW_MAIN_CONTENT_DP = 8.0f;
+    field public static final int MULTI_BUTTON_MAX_NUMBER = 7; // 0x7
+    field public static final androidx.wear.protolayout.DimensionBuilders.DpProp MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH;
+  }
+
+  public class MultiButtonLayout implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.layouts.MultiButtonLayout? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.LayoutElement!> getButtonContents();
+    method public int getFiveButtonDistribution();
+    field public static final int FIVE_BUTTON_DISTRIBUTION_BOTTOM_HEAVY = 2; // 0x2
+    field public static final int FIVE_BUTTON_DISTRIBUTION_TOP_HEAVY = 1; // 0x1
+  }
+
+  public static final class MultiButtonLayout.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public MultiButtonLayout.Builder();
+    method public androidx.wear.protolayout.material.layouts.MultiButtonLayout.Builder addButtonContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.MultiButtonLayout build();
+    method public androidx.wear.protolayout.material.layouts.MultiButtonLayout.Builder setFiveButtonDistribution(int);
+  }
+
+  public class MultiSlotLayout implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.layouts.MultiSlotLayout? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getHorizontalSpacerWidth();
+    method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.LayoutElement!> getSlotContents();
+  }
+
+  public static final class MultiSlotLayout.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public MultiSlotLayout.Builder();
+    method public androidx.wear.protolayout.material.layouts.MultiSlotLayout.Builder addSlotContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.MultiSlotLayout build();
+    method public androidx.wear.protolayout.material.layouts.MultiSlotLayout.Builder setHorizontalSpacerWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+  public class PrimaryLayout implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
+    method public static androidx.wear.protolayout.material.layouts.PrimaryLayout? fromLayoutElement(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getContent();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getPrimaryChipContent();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getPrimaryLabelTextContent();
+    method public androidx.wear.protolayout.LayoutElementBuilders.LayoutElement? getSecondaryLabelTextContent();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getVerticalSpacerHeight();
+  }
+
+  public static final class PrimaryLayout.Builder implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement.Builder {
+    ctor public PrimaryLayout.Builder(androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters);
+    method public androidx.wear.protolayout.material.layouts.PrimaryLayout build();
+    method public androidx.wear.protolayout.material.layouts.PrimaryLayout.Builder setContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.PrimaryLayout.Builder setPrimaryChipContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.PrimaryLayout.Builder setPrimaryLabelTextContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.PrimaryLayout.Builder setSecondaryLabelTextContent(androidx.wear.protolayout.LayoutElementBuilders.LayoutElement);
+    method public androidx.wear.protolayout.material.layouts.PrimaryLayout.Builder setVerticalSpacerHeight(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  }
+
+}
+
diff --git a/wear/protolayout/protolayout-material/build.gradle b/wear/protolayout/protolayout-material/build.gradle
index 7241009..536215b 100644
--- a/wear/protolayout/protolayout-material/build.gradle
+++ b/wear/protolayout/protolayout-material/build.gradle
@@ -18,15 +18,51 @@
 
 plugins {
     id("AndroidXPlugin")
+    id("kotlin-android")
     id("com.android.library")
 }
 
 dependencies {
     annotationProcessor(libs.nullaway)
-    // Add dependencies here
+    api("androidx.annotation:annotation:1.2.0")
+    implementation(project(":wear:protolayout:protolayout"))
+    implementation(project(":wear:protolayout:protolayout-proto"))
+    implementation(project(":wear:protolayout:protolayout-expression"))
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation("androidx.core:core:1.7.0")
+    androidTestImplementation(project(":test:screenshot:screenshot"))
+    androidTestImplementation(project(":wear:protolayout:protolayout-renderer"))
+    androidTestRuntimeOnly(project(path: ":wear:protolayout:protolayout-proto", configuration: "shadow"))
+    androidTestImplementation(libs.protobuf)
+
+    testImplementation(libs.junit)
+    testImplementation(libs.mockitoCore4)
+    testImplementation(libs.robolectric)
+    testImplementation(libs.testExtJunit)
+    testImplementation(libs.testExtTruth)
+    testImplementation(libs.testCore)
+    testImplementation(libs.testRunner)
+    testImplementation(libs.testRules)
+    testImplementation(libs.truth)
 }
 
 android {
+    defaultConfig {
+        minSdkVersion 26
+    }
+
+    sourceSets {
+        androidTest.assets.srcDirs += project.rootDir.absolutePath + "/../../golden/wear/wear-protolayout-material"
+    }
+
+    defaultConfig {
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
     namespace "androidx.wear.protolayout.material"
 }
 
diff --git a/wear/protolayout/protolayout-material/src/androidTest/AndroidManifest.xml b/wear/protolayout/protolayout-material/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..fc13b63
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+    <application
+        android:label="Golden Tests"
+        android:supportsRtl="true"
+        android:theme="@android:style/Theme.DeviceDefault"
+        android:taskAffinity="">
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="androidx.wear.protolayout.material.testapp.GoldenTestActivity"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/MaterialGoldenTest.java b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/MaterialGoldenTest.java
new file mode 100644
index 0000000..13d9252
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/MaterialGoldenTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.wear.protolayout.material.RunnerUtils.SCREEN_HEIGHT;
+import static androidx.wear.protolayout.material.RunnerUtils.SCREEN_WIDTH;
+import static androidx.wear.protolayout.material.RunnerUtils.runSingleScreenshotTest;
+import static androidx.wear.protolayout.material.TestCasesGenerator.generateTestCases;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+
+import androidx.annotation.Dimension;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.screenshot.AndroidXScreenshotTestRule;
+import androidx.wear.protolayout.DeviceParametersBuilders;
+import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class MaterialGoldenTest {
+    private final LayoutElement mLayoutElement;
+    private final String mExpected;
+
+    @Rule
+    public AndroidXScreenshotTestRule mScreenshotRule =
+            new AndroidXScreenshotTestRule("wear/wear-protolayout-material");
+
+    public MaterialGoldenTest(String expected, LayoutElement layoutElement) {
+        mLayoutElement = layoutElement;
+        mExpected = expected;
+    }
+
+    @Dimension(unit = Dimension.DP)
+    static int pxToDp(int px, float scale) {
+        return (int) ((px - 0.5f) / scale);
+    }
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+        float scale = displayMetrics.density;
+
+        InstrumentationRegistry.getInstrumentation()
+                .getContext()
+                .getResources()
+                .getDisplayMetrics()
+                .setTo(displayMetrics);
+        InstrumentationRegistry.getInstrumentation()
+                .getTargetContext()
+                .getResources()
+                .getDisplayMetrics()
+                .setTo(displayMetrics);
+
+        DeviceParameters deviceParameters =
+                new DeviceParameters.Builder()
+                        .setScreenWidthDp(pxToDp(SCREEN_WIDTH, scale))
+                        .setScreenHeightDp(pxToDp(SCREEN_HEIGHT, scale))
+                        .setScreenDensity(displayMetrics.density)
+                        // Not important for components.
+                        .setScreenShape(DeviceParametersBuilders.SCREEN_SHAPE_RECT)
+                        .build();
+
+        Map<String, LayoutElement> testCases = generateTestCases(context, deviceParameters, "");
+
+        return testCases.entrySet().stream()
+                .map(test -> new Object[] {test.getKey(), test.getValue()})
+                .collect(Collectors.toList());
+    }
+
+    @SdkSuppress(maxSdkVersion = 32) // b/271486183
+    @Test
+    public void test() {
+        runSingleScreenshotTest(mScreenshotRule, mLayoutElement, mExpected);
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/MaterialGoldenXLTest.java b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/MaterialGoldenXLTest.java
new file mode 100644
index 0000000..7e03861
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/MaterialGoldenXLTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.wear.protolayout.material.RunnerUtils.SCREEN_HEIGHT;
+import static androidx.wear.protolayout.material.RunnerUtils.SCREEN_WIDTH;
+import static androidx.wear.protolayout.material.RunnerUtils.runSingleScreenshotTest;
+import static androidx.wear.protolayout.material.TestCasesGenerator.XXXL_SCALE_SUFFIX;
+import static androidx.wear.protolayout.material.TestCasesGenerator.generateTestCases;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+
+import androidx.annotation.Dimension;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.screenshot.AndroidXScreenshotTestRule;
+import androidx.wear.protolayout.DeviceParametersBuilders;
+import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class MaterialGoldenXLTest {
+    /* We set DisplayMetrics in the data() method for creating test cases. However, when running all
+    tests together, first all parametrization (data()) methods are called, and then individual
+    tests, causing that actual DisplayMetrics will be different. So we need to restore it before
+    each test. */
+    private static final DisplayMetrics DISPLAY_METRICS_FOR_TEST = new DisplayMetrics();
+    private static final DisplayMetrics OLD_DISPLAY_METRICS = new DisplayMetrics();
+
+    private static final float FONT_SCALE_XXXL = 1.24f;
+
+    private final LayoutElement mLayoutElement;
+    private final String mExpected;
+
+    @Rule
+    public AndroidXScreenshotTestRule mScreenshotRule =
+            new AndroidXScreenshotTestRule("wear/wear-protolayout-material");
+
+    public MaterialGoldenXLTest(String expected, LayoutElement layoutElement) {
+        mLayoutElement = layoutElement;
+        mExpected = expected;
+    }
+
+    @Dimension(unit = Dimension.DP)
+    static int pxToDp(int px, float scale) {
+        return (int) ((px - 0.5f) / scale);
+    }
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        DisplayMetrics currentDisplayMetrics = new DisplayMetrics();
+        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+        currentDisplayMetrics.setTo(displayMetrics);
+        displayMetrics.scaledDensity *= FONT_SCALE_XXXL;
+
+        InstrumentationRegistry.getInstrumentation()
+                .getContext()
+                .getResources()
+                .getDisplayMetrics()
+                .setTo(displayMetrics);
+        InstrumentationRegistry.getInstrumentation()
+                .getTargetContext()
+                .getResources()
+                .getDisplayMetrics()
+                .setTo(displayMetrics);
+
+        DISPLAY_METRICS_FOR_TEST.setTo(displayMetrics);
+
+        float scale = displayMetrics.density;
+        DeviceParameters deviceParameters =
+                new DeviceParameters.Builder()
+                        .setScreenWidthDp(pxToDp(SCREEN_WIDTH, scale))
+                        .setScreenHeightDp(pxToDp(SCREEN_HEIGHT, scale))
+                        .setScreenDensity(displayMetrics.density)
+                        // Not important for components.
+                        .setScreenShape(DeviceParametersBuilders.SCREEN_SHAPE_RECT)
+                        .build();
+
+        Map<String, LayoutElement> testCases =
+                generateTestCases(context, deviceParameters, XXXL_SCALE_SUFFIX);
+
+        // Restore state before this method, so other test have correct context.
+        InstrumentationRegistry.getInstrumentation()
+                .getContext()
+                .getResources()
+                .getDisplayMetrics()
+                .setTo(currentDisplayMetrics);
+        InstrumentationRegistry.getInstrumentation()
+                .getTargetContext()
+                .getResources()
+                .getDisplayMetrics()
+                .setTo(currentDisplayMetrics);
+
+        return testCases.entrySet().stream()
+                .map(test -> new Object[] {test.getKey(), test.getValue()})
+                .collect(Collectors.toList());
+    }
+
+    @Parameterized.BeforeParam
+    public static void restoreBefore() {
+        OLD_DISPLAY_METRICS.setTo(getApplicationContext().getResources().getDisplayMetrics());
+        getApplicationContext().getResources().getDisplayMetrics().setTo(DISPLAY_METRICS_FOR_TEST);
+    }
+
+    @Parameterized.AfterParam
+    public static void restoreAfter() {
+        getApplicationContext().getResources().getDisplayMetrics().setTo(OLD_DISPLAY_METRICS);
+    }
+
+    @SdkSuppress(maxSdkVersion = 32) // b/271486183
+    @Test
+    public void test() {
+        runSingleScreenshotTest(mScreenshotRule, mLayoutElement, mExpected);
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/RunnerUtils.java b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/RunnerUtils.java
new file mode 100644
index 0000000..1033504
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/RunnerUtils.java
@@ -0,0 +1,94 @@
+/*
+ * 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.wear.protolayout.material;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.screenshot.AndroidXScreenshotTestRule;
+import androidx.test.screenshot.matchers.MSSIMMatcher;
+import androidx.wear.protolayout.LayoutElementBuilders;
+import androidx.wear.protolayout.material.testapp.GoldenTestActivity;
+
+public class RunnerUtils {
+    // This isn't totally ideal right now. The screenshot tests run on a phone, so emulate some
+    // watch dimensions here.
+    public static final int SCREEN_WIDTH = 390;
+    public static final int SCREEN_HEIGHT = 390;
+
+    private RunnerUtils() {}
+
+    public static void runSingleScreenshotTest(
+            @NonNull AndroidXScreenshotTestRule rule,
+            @NonNull LayoutElementBuilders.LayoutElement layoutElement,
+            @NonNull String expected) {
+        LayoutElementBuilders.Layout layout =
+                LayoutElementBuilders.Layout.fromLayoutElement(layoutElement);
+        byte[] layoutPayload = layout.toByteArray();
+
+        Intent startIntent =
+                new Intent(
+                        InstrumentationRegistry.getInstrumentation().getTargetContext(),
+                        GoldenTestActivity.class);
+        startIntent.putExtra("layout", layoutPayload);
+
+        ActivityScenario<GoldenTestActivity> scenario = ActivityScenario.launch(startIntent);
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        try {
+            // Wait 1s after launching the activity. This allows for the old white layout in the
+            // bootstrap activity to fully go away before proceeding.
+            Thread.sleep(1000);
+        } catch (Exception ex) {
+            if (ex instanceof InterruptedException) {
+                Thread.currentThread().interrupt();
+            }
+            Log.e("MaterialGoldenTest", "Error sleeping", ex);
+        }
+
+        Bitmap bitmap =
+                Bitmap.createBitmap(
+                        InstrumentationRegistry.getInstrumentation()
+                                .getUiAutomation()
+                                .takeScreenshot(),
+                        0,
+                        0,
+                        SCREEN_WIDTH,
+                        SCREEN_HEIGHT);
+        rule.assertBitmapAgainstGolden(bitmap, expected, new MSSIMMatcher());
+
+        // There's a weird bug (related to b/159805732) where, when calling .close() on
+        // ActivityScenario or calling finish() and immediately exiting the test, the test can hang
+        // on a white screen for 45s. Closing the activity here and waiting for 1s seems to fix
+        // this.
+        scenario.onActivity(Activity::finish);
+
+        try {
+            Thread.sleep(1000);
+        } catch (Exception ex) {
+            if (ex instanceof InterruptedException) {
+                Thread.currentThread().interrupt();
+            }
+            Log.e("MaterialGoldenTest", "Error sleeping", ex);
+        }
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/TestCasesGenerator.java b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/TestCasesGenerator.java
new file mode 100644
index 0000000..cb43b9b
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/TestCasesGenerator.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.wear.protolayout.material;
+
+import static androidx.wear.protolayout.ColorBuilders.argb;
+import static androidx.wear.protolayout.DimensionBuilders.dp;
+import static androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER;
+import static androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_END;
+import static androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_START;
+
+import android.content.Context;
+import android.graphics.Color;
+
+import androidx.annotation.NonNull;
+import androidx.wear.protolayout.ActionBuilders.LaunchAction;
+import androidx.wear.protolayout.DeviceParametersBuilders;
+import androidx.wear.protolayout.LayoutElementBuilders;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+import androidx.wear.protolayout.LayoutElementBuilders.Row;
+import androidx.wear.protolayout.ModifiersBuilders.Clickable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class TestCasesGenerator {
+    private TestCasesGenerator() {}
+
+    public static final String NORMAL_SCALE_SUFFIX = "";
+    public static final String XXXL_SCALE_SUFFIX = "_xxxl";
+    private static final String ICON_ID = "tile_icon";
+    private static final String AVATAR = "avatar_image";
+
+    /**
+     * This function will append goldenSuffix on the name of the golden images that should be
+     * different for different user font sizes. Note that some of the golden will have the same name
+     * as it should point on the same size independent image.
+     */
+    @NonNull
+    static Map<String, LayoutElement> generateTestCases(
+            @NonNull Context context,
+            @NonNull DeviceParametersBuilders.DeviceParameters deviceParameters,
+            @NonNull String goldenSuffix) {
+        Clickable clickable =
+                new Clickable.Builder()
+                        .setOnClick(new LaunchAction.Builder().build())
+                        .setId("action_id")
+                        .build();
+        String mainText = "Primary label";
+        String labelText = "Secondary label";
+        String largeChipText = "Action";
+        HashMap<String, LayoutElement> testCases = new HashMap<>();
+
+        testCases.put(
+                "default_icon_button_golden" + NORMAL_SCALE_SUFFIX,
+                new Button.Builder(context, clickable).setIconContent(ICON_ID).build());
+        testCases.put(
+                "extralarge_secondary_icon_after_button_golden" + NORMAL_SCALE_SUFFIX,
+                new Button.Builder(context, clickable)
+                        .setButtonColors(ButtonDefaults.SECONDARY_COLORS)
+                        .setIconContent(ICON_ID)
+                        .setSize(ButtonDefaults.EXTRA_LARGE_SIZE)
+                        .build());
+        testCases.put(
+                "large_secondary_icon_40size_button_golden" + NORMAL_SCALE_SUFFIX,
+                new Button.Builder(context, clickable)
+                        .setSize(ButtonDefaults.LARGE_SIZE)
+                        .setButtonColors(ButtonDefaults.SECONDARY_COLORS)
+                        .setIconContent(ICON_ID, dp(40))
+                        .build());
+        testCases.put(
+                "extralarge_custom_text_custom_sizefont_button_golden" + goldenSuffix,
+                new Button.Builder(context, clickable)
+                        .setButtonColors(new ButtonColors(Color.YELLOW, Color.GREEN))
+                        .setSize(ButtonDefaults.EXTRA_LARGE_SIZE)
+                        .setCustomContent(
+                                new Text.Builder(context, "ABC")
+                                        .setTypography(Typography.TYPOGRAPHY_DISPLAY1)
+                                        .setItalic(true)
+                                        .setColor(argb(Color.GREEN))
+                                        .build())
+                        .build());
+        testCases.put(
+                "default_text_button_golden" + goldenSuffix,
+                new Button.Builder(context, clickable).setTextContent("ABC").build());
+        testCases.put(
+                "default_image_button_golden" + NORMAL_SCALE_SUFFIX,
+                new Button.Builder(context, clickable).setImageContent(AVATAR).build());
+
+        testCases.put(
+                "default_chip_maintext_golden" + goldenSuffix,
+                new Chip.Builder(context, clickable, deviceParameters)
+                        .setPrimaryLabelContent(mainText)
+                        .build());
+        testCases.put(
+                "default_chip_maintextlabeltext_golden" + goldenSuffix,
+                new Chip.Builder(context, clickable, deviceParameters)
+                        .setPrimaryLabelContent(mainText)
+                        .setSecondaryLabelContent(labelText)
+                        .setHorizontalAlignment(HORIZONTAL_ALIGN_START)
+                        .build());
+        testCases.put(
+                "default_chip_maintexticon_golden" + goldenSuffix,
+                new Chip.Builder(context, clickable, deviceParameters)
+                        .setPrimaryLabelContent(mainText)
+                        .setIconContent(ICON_ID)
+                        .build());
+        testCases.put(
+                "secondary_chip_maintext_centered_golden" + goldenSuffix,
+                new Chip.Builder(context, clickable, deviceParameters)
+                        .setHorizontalAlignment(HORIZONTAL_ALIGN_CENTER)
+                        .setChipColors(ChipDefaults.SECONDARY_COLORS)
+                        .setPrimaryLabelContent(mainText)
+                        .build());
+        testCases.put(
+                "custom_chip_all_overflows_golden" + goldenSuffix,
+                new Chip.Builder(context, clickable, deviceParameters)
+                        .setWidth(130)
+                        .setPrimaryLabelContent(mainText)
+                        .setSecondaryLabelContent(labelText)
+                        .setIconContent(ICON_ID)
+                        .setChipColors(
+                                new ChipColors(Color.YELLOW, Color.GREEN, Color.BLACK, Color.GRAY))
+                        .build());
+        testCases.put(
+                "default_chip_all_centered_golden" + goldenSuffix,
+                new Chip.Builder(context, clickable, deviceParameters)
+                        .setHorizontalAlignment(HORIZONTAL_ALIGN_CENTER)
+                        .setPrimaryLabelContent(mainText)
+                        .setSecondaryLabelContent(labelText)
+                        .setIconContent(ICON_ID)
+                        .build());
+        testCases.put(
+                "default_chip_all_rigthalign_golden" + goldenSuffix,
+                new Chip.Builder(context, clickable, deviceParameters)
+                        .setHorizontalAlignment(HORIZONTAL_ALIGN_END)
+                        .setPrimaryLabelContent(mainText)
+                        .setSecondaryLabelContent(labelText)
+                        .setIconContent(ICON_ID)
+                        .build());
+        testCases.put(
+                "custom_chip_icon_primary_overflows_golden" + goldenSuffix,
+                new Chip.Builder(context, clickable, deviceParameters)
+                        .setPrimaryLabelContent(mainText)
+                        .setIconContent(ICON_ID)
+                        .setWidth(150)
+                        .setHorizontalAlignment(HORIZONTAL_ALIGN_START)
+                        .setChipColors(
+                                new ChipColors(Color.YELLOW, Color.GREEN, Color.BLACK, Color.GRAY))
+                        .build());
+        testCases.put(
+                "chip_custom_content_centered_golden" + goldenSuffix,
+                new Chip.Builder(context, clickable, deviceParameters)
+                        .setHorizontalAlignment(HORIZONTAL_ALIGN_CENTER)
+                        .setCustomContent(
+                                new Box.Builder()
+                                        .addContent(
+                                                new Text.Builder(context, "random text")
+                                                        .setTypography(Typography.TYPOGRAPHY_TITLE3)
+                                                        .setItalic(true)
+                                                        .build())
+                                        .build())
+                        .build());
+        testCases.put(
+                "chip_custom_content_leftaligned_golden" + goldenSuffix,
+                new Chip.Builder(context, clickable, deviceParameters)
+                        .setChipColors(ChipDefaults.SECONDARY_COLORS)
+                        .setHorizontalAlignment(HORIZONTAL_ALIGN_START)
+                        .setCustomContent(
+                                new Row.Builder()
+                                        .addContent(
+                                                new Text.Builder(context, "text1")
+                                                        .setTypography(Typography.TYPOGRAPHY_TITLE3)
+                                                        .setItalic(true)
+                                                        .setColor(argb(Color.WHITE))
+                                                        .build())
+                                        .addContent(
+                                                new Text.Builder(context, "text2")
+                                                        .setTypography(Typography.TYPOGRAPHY_TITLE2)
+                                                        .setColor(argb(Color.YELLOW))
+                                                        .build())
+                                        .build())
+                        .setWidth(150)
+                        .build());
+        testCases.put(
+                "chip_2lines_primary_overflows_golden" + goldenSuffix,
+                new Chip.Builder(context, clickable, deviceParameters)
+                        .setPrimaryLabelContent("abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde")
+                        .build());
+
+        // Different text lengths to test expanding the width based on the size of text. If it's
+        // more than 9, the rest will be deleted.
+        testCases.put(
+                "compactchip_default_len2_golden" + goldenSuffix,
+                new CompactChip.Builder(context, "Ab", clickable, deviceParameters).build());
+        testCases.put(
+                "compactchip_default_len5_golden" + goldenSuffix,
+                new CompactChip.Builder(context, "Abcde", clickable, deviceParameters).build());
+        testCases.put(
+                "compactchip_default_len9_golden" + goldenSuffix,
+                new CompactChip.Builder(context, "Abcdefghi", clickable, deviceParameters).build());
+        testCases.put(
+                "compactchip_default_toolong_golden" + goldenSuffix,
+                new CompactChip.Builder(
+                                context, "AbcdefghiEXTRAEXTRAEXTRA", clickable, deviceParameters)
+                        .build());
+        testCases.put(
+                "compactchip_custom_default_golden" + goldenSuffix,
+                new CompactChip.Builder(context, "Action", clickable, deviceParameters)
+                        .setChipColors(new ChipColors(Color.YELLOW, Color.BLACK))
+                        .build());
+
+        testCases.put(
+                "titlechip_default_golden" + goldenSuffix,
+                new TitleChip.Builder(context, largeChipText, clickable, deviceParameters).build());
+        testCases.put(
+                "titlechip_default_texttoolong_golden" + goldenSuffix,
+                new TitleChip.Builder(context, "abcdeabcdeabcdeEXTRA", clickable, deviceParameters)
+                        .build());
+        testCases.put(
+                "titlechip_leftalign_secondary_default_golden" + goldenSuffix,
+                new TitleChip.Builder(context, largeChipText, clickable, deviceParameters)
+                        .setHorizontalAlignment(HORIZONTAL_ALIGN_START)
+                        .setChipColors(ChipDefaults.TITLE_SECONDARY_COLORS)
+                        .build());
+        testCases.put(
+                "titlechip_centered_custom_150_secondary_default_golden" + goldenSuffix,
+                new TitleChip.Builder(context, largeChipText, clickable, deviceParameters)
+                        .setHorizontalAlignment(HORIZONTAL_ALIGN_CENTER)
+                        .setChipColors(new ChipColors(Color.YELLOW, Color.BLUE))
+                        .setWidth(150)
+                        .build());
+
+        testCases.put(
+                "default_full_circularprogressindicator",
+                new CircularProgressIndicator.Builder().build());
+        testCases.put(
+                "default_gap_circularprogressindicator",
+                new CircularProgressIndicator.Builder()
+                        .setStartAngle(ProgressIndicatorDefaults.GAP_START_ANGLE)
+                        .setEndAngle(ProgressIndicatorDefaults.GAP_END_ANGLE)
+                        .build());
+        testCases.put(
+                "default_full_90_circularprogressindicator",
+                new CircularProgressIndicator.Builder().setProgress(0.25f).build());
+        testCases.put(
+                "default_gap_90_circularprogressindicator",
+                new CircularProgressIndicator.Builder()
+                        .setProgress(0.25f)
+                        .setStartAngle(ProgressIndicatorDefaults.GAP_START_ANGLE)
+                        .setEndAngle(ProgressIndicatorDefaults.GAP_END_ANGLE)
+                        .build());
+        testCases.put(
+                "custom_gap_45_circularprogressindicator",
+                new CircularProgressIndicator.Builder()
+                        .setStartAngle(45)
+                        .setEndAngle(270)
+                        .setProgress(0.2f)
+                        .setStrokeWidth(12)
+                        .setCircularProgressIndicatorColors(
+                                new ProgressIndicatorColors(Color.BLUE, Color.YELLOW))
+                        .build());
+
+        testCases.put(
+                "default_text_golden" + goldenSuffix, new Text.Builder(context, "Testing").build());
+        testCases.put(
+                "custom_text_golden" + goldenSuffix,
+                new Text.Builder(context, "Testing text.")
+                        .setItalic(true)
+                        .setColor(argb(Color.YELLOW))
+                        .setWeight(LayoutElementBuilders.FONT_WEIGHT_BOLD)
+                        .setTypography(Typography.TYPOGRAPHY_BODY2)
+                        .build());
+        testCases.put(
+                "overflow_text_golden" + goldenSuffix,
+                new Text.Builder(context, "abcdeabcdeabcde").build());
+
+        return testCases;
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/layouts/LayoutsGoldenTest.java b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/layouts/LayoutsGoldenTest.java
new file mode 100644
index 0000000..e39d6fa
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/layouts/LayoutsGoldenTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.wear.protolayout.material.layouts;
+
+import static androidx.wear.protolayout.material.RunnerUtils.SCREEN_HEIGHT;
+import static androidx.wear.protolayout.material.RunnerUtils.SCREEN_WIDTH;
+import static androidx.wear.protolayout.material.RunnerUtils.runSingleScreenshotTest;
+import static androidx.wear.protolayout.material.layouts.TestCasesGenerator.generateTestCases;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+
+import androidx.annotation.Dimension;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.screenshot.AndroidXScreenshotTestRule;
+import androidx.wear.protolayout.DeviceParametersBuilders;
+import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class LayoutsGoldenTest {
+    private final LayoutElement mLayoutElement;
+    private final String mExpected;
+
+    @Rule
+    public AndroidXScreenshotTestRule mScreenshotRule =
+            new AndroidXScreenshotTestRule("wear/wear-protolayout-material");
+
+    public LayoutsGoldenTest(String expected, LayoutElement layoutElement) {
+        mLayoutElement = layoutElement;
+        mExpected = expected;
+    }
+
+    @Dimension(unit = Dimension.DP)
+    static int pxToDp(int px, float scale) {
+        return (int) ((px - 0.5f) / scale);
+    }
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+        float scale = displayMetrics.density;
+
+        InstrumentationRegistry.getInstrumentation()
+                .getContext()
+                .getResources()
+                .getDisplayMetrics()
+                .setTo(displayMetrics);
+        InstrumentationRegistry.getInstrumentation()
+                .getTargetContext()
+                .getResources()
+                .getDisplayMetrics()
+                .setTo(displayMetrics);
+
+        DeviceParameters deviceParameters =
+                new DeviceParameters.Builder()
+                        .setScreenWidthDp(pxToDp(SCREEN_WIDTH, scale))
+                        .setScreenHeightDp(pxToDp(SCREEN_HEIGHT, scale))
+                        .setScreenDensity(displayMetrics.density)
+                        // TODO(b/231543947): Add test cases for round screen.
+                        .setScreenShape(DeviceParametersBuilders.SCREEN_SHAPE_RECT)
+                        .build();
+
+        Map<String, LayoutElement> testCases = generateTestCases(context, deviceParameters, "");
+
+        return testCases.entrySet().stream()
+                .map(test -> new Object[] {test.getKey(), test.getValue()})
+                .collect(Collectors.toList());
+    }
+
+    @SdkSuppress(maxSdkVersion = 32) // b/271486183
+    @Test
+    public void test() {
+        runSingleScreenshotTest(mScreenshotRule, mLayoutElement, mExpected);
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/layouts/LayoutsGoldenXLTest.java b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/layouts/LayoutsGoldenXLTest.java
new file mode 100644
index 0000000..0089ecd
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/layouts/LayoutsGoldenXLTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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.wear.protolayout.material.layouts;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.wear.protolayout.material.RunnerUtils.SCREEN_HEIGHT;
+import static androidx.wear.protolayout.material.RunnerUtils.SCREEN_WIDTH;
+import static androidx.wear.protolayout.material.RunnerUtils.runSingleScreenshotTest;
+import static androidx.wear.protolayout.material.layouts.TestCasesGenerator.XXXL_SCALE_SUFFIX;
+import static androidx.wear.protolayout.material.layouts.TestCasesGenerator.generateTestCases;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+
+import androidx.annotation.Dimension;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.screenshot.AndroidXScreenshotTestRule;
+import androidx.wear.protolayout.DeviceParametersBuilders;
+import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class LayoutsGoldenXLTest {
+    /* We set DisplayMetrics in the data() method for creating test cases. However, when running all
+    tests together, first all parametrization (data()) methods are called, and then individual
+    tests, causing that actual DisplayMetrics will be different. So we need to restore it before
+    each test. */
+    private static final DisplayMetrics DISPLAY_METRICS_FOR_TEST = new DisplayMetrics();
+    private static final DisplayMetrics OLD_DISPLAY_METRICS = new DisplayMetrics();
+
+    private static final float FONT_SCALE_XXXL = 1.24f;
+
+    private final LayoutElement mLayoutElement;
+    private final String mExpected;
+
+    @Rule
+    public AndroidXScreenshotTestRule mScreenshotRule =
+            new AndroidXScreenshotTestRule("wear/wear-protolayout-material");
+
+    public LayoutsGoldenXLTest(String expected, LayoutElement layoutElement) {
+        mLayoutElement = layoutElement;
+        mExpected = expected;
+    }
+
+    @Dimension(unit = Dimension.DP)
+    static int pxToDp(int px, float scale) {
+        return (int) ((px - 0.5f) / scale);
+    }
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        DisplayMetrics currentDisplayMetrics = new DisplayMetrics();
+        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+        currentDisplayMetrics.setTo(displayMetrics);
+        displayMetrics.scaledDensity *= FONT_SCALE_XXXL;
+
+        InstrumentationRegistry.getInstrumentation()
+                .getContext()
+                .getResources()
+                .getDisplayMetrics()
+                .setTo(displayMetrics);
+        InstrumentationRegistry.getInstrumentation()
+                .getTargetContext()
+                .getResources()
+                .getDisplayMetrics()
+                .setTo(displayMetrics);
+
+        DISPLAY_METRICS_FOR_TEST.setTo(displayMetrics);
+
+        float scale = displayMetrics.density;
+        DeviceParameters deviceParameters =
+                new DeviceParameters.Builder()
+                        .setScreenWidthDp(pxToDp(SCREEN_WIDTH, scale))
+                        .setScreenHeightDp(pxToDp(SCREEN_HEIGHT, scale))
+                        .setScreenDensity(displayMetrics.density)
+                        // TODO(b/231543947): Add test cases for round screen.
+                        .setScreenShape(DeviceParametersBuilders.SCREEN_SHAPE_RECT)
+                        .build();
+
+        Map<String, LayoutElement> testCases =
+                generateTestCases(context, deviceParameters, XXXL_SCALE_SUFFIX);
+
+        // Restore state before this method, so other test have correct context. This is needed here
+        // too, besides in restoreBefore and restoreAfter as the test cases builder uses the context
+        // to apply font scaling, so we need that display metrics passed in. However, after
+        // generating cases we need to restore the state as other data() methods in this package can
+        // work correctly with the default state, as when the tests are run, first all data() static
+        // methods are called, and then parameterized test cases.
+        InstrumentationRegistry.getInstrumentation()
+                .getContext()
+                .getResources()
+                .getDisplayMetrics()
+                .setTo(currentDisplayMetrics);
+        InstrumentationRegistry.getInstrumentation()
+                .getTargetContext()
+                .getResources()
+                .getDisplayMetrics()
+                .setTo(currentDisplayMetrics);
+
+        return testCases.entrySet().stream()
+                .map(test -> new Object[] {test.getKey(), test.getValue()})
+                .collect(Collectors.toList());
+    }
+
+    @Parameterized.BeforeParam
+    public static void restoreBefore() {
+        // Set the state as it was in data() method when we generated test cases. This was
+        // overridden by other static data() methods, so we need to restore it.
+        OLD_DISPLAY_METRICS.setTo(getApplicationContext().getResources().getDisplayMetrics());
+        getApplicationContext().getResources().getDisplayMetrics().setTo(DISPLAY_METRICS_FOR_TEST);
+    }
+
+    @Parameterized.AfterParam
+    public static void restoreAfter() {
+        // Restore the state to default, so the other tests and emulator have the correct starter
+        // state.
+        getApplicationContext().getResources().getDisplayMetrics().setTo(OLD_DISPLAY_METRICS);
+    }
+
+    @SdkSuppress(maxSdkVersion = 32) // b/271486183
+    @Test
+    public void test() {
+        runSingleScreenshotTest(mScreenshotRule, mLayoutElement, mExpected);
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/layouts/TestCasesGenerator.java b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/layouts/TestCasesGenerator.java
new file mode 100644
index 0000000..e95c9da
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/layouts/TestCasesGenerator.java
@@ -0,0 +1,644 @@
+/*
+ * 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.wear.protolayout.material.layouts;
+
+import static androidx.wear.protolayout.ColorBuilders.argb;
+import static androidx.wear.protolayout.DimensionBuilders.dp;
+import static androidx.wear.protolayout.DimensionBuilders.expand;
+import static androidx.wear.protolayout.DimensionBuilders.wrap;
+
+import android.content.Context;
+import android.graphics.Color;
+
+import androidx.annotation.Dimension;
+import androidx.annotation.NonNull;
+import androidx.wear.protolayout.ActionBuilders.LaunchAction;
+import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.Column;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+import androidx.wear.protolayout.LayoutElementBuilders.Spacer;
+import androidx.wear.protolayout.ModifiersBuilders.Background;
+import androidx.wear.protolayout.ModifiersBuilders.Clickable;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+import androidx.wear.protolayout.material.Button;
+import androidx.wear.protolayout.material.ButtonDefaults;
+import androidx.wear.protolayout.material.Chip;
+import androidx.wear.protolayout.material.ChipColors;
+import androidx.wear.protolayout.material.CircularProgressIndicator;
+import androidx.wear.protolayout.material.Colors;
+import androidx.wear.protolayout.material.CompactChip;
+import androidx.wear.protolayout.material.ProgressIndicatorColors;
+import androidx.wear.protolayout.material.ProgressIndicatorDefaults;
+import androidx.wear.protolayout.material.Text;
+import androidx.wear.protolayout.material.TitleChip;
+import androidx.wear.protolayout.material.Typography;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class TestCasesGenerator {
+    private TestCasesGenerator() {}
+
+    public static final String NORMAL_SCALE_SUFFIX = "";
+    public static final String XXXL_SCALE_SUFFIX = "_xxxl";
+
+    /**
+     * This function will append goldenSuffix on the name of the golden images that should be
+     * different for different user font sizes. Note that some of the golden will have the same name
+     * as it should point on the same size independent image.
+     */
+    @NonNull
+    static Map<String, LayoutElement> generateTestCases(
+            @NonNull Context context,
+            @NonNull DeviceParameters deviceParameters,
+            @NonNull String goldenSuffix) {
+        Clickable clickable =
+                new Clickable.Builder()
+                        .setOnClick(new LaunchAction.Builder().build())
+                        .setId("action_id")
+                        .build();
+        HashMap<String, LayoutElement> testCases = new HashMap<>();
+
+        TitleChip content =
+                new TitleChip.Builder(context, "Action", clickable, deviceParameters).build();
+        CompactChip.Builder primaryChipBuilder =
+                new CompactChip.Builder(context, "Action", clickable, deviceParameters);
+
+        testCases.put(
+                "default_empty_primarychiplayout_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .build());
+        testCases.put(
+                "default_longtext_primarychiplayout_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setPrimaryChipContent(
+                                new CompactChip.Builder(
+                                                context,
+                                                "Too_long_textToo_long_textToo_long_text",
+                                                clickable,
+                                                deviceParameters)
+                                        .build())
+                        .build());
+        testCases.put(
+                "coloredbox_primarylabel_primarychiplayout_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .setContent(buildColoredBoxPLL(Color.YELLOW))
+                        .setPrimaryLabelTextContent(buildTextLabel(context, "Primary label"))
+                        .build());
+        testCases.put(
+                "coloredbox_primarylabel_secondarylabel_primarychiplayout_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .setContent(buildColoredBoxPLL(Color.YELLOW))
+                        .setPrimaryLabelTextContent(buildTextLabel(context, "Primary label"))
+                        .setSecondaryLabelTextContent(buildTextLabel(context, "Secondary label"))
+                        .build());
+        testCases.put(
+                "coloredbox_secondarylabel_primarychiplayout_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .setContent(buildColoredBoxPLL(Color.YELLOW))
+                        .setSecondaryLabelTextContent(buildTextLabel(context, "Secondary label"))
+                        .build());
+
+        testCases.put(
+                "custom_primarychiplayout_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(content)
+                        .setPrimaryChipContent(
+                                primaryChipBuilder
+                                        .setChipColors(new ChipColors(Color.YELLOW, Color.GREEN))
+                                        .build())
+                        .build());
+        testCases.put(
+                "coloredbox_primarychiplayout_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .setContent(buildColoredBoxPLL(Color.YELLOW))
+                        .build());
+        testCases.put(
+                "two_chips_content_primarychiplayout_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .setContent(
+                                new Column.Builder()
+                                        .setWidth(expand())
+                                        .setHeight(wrap())
+                                        .addContent(
+                                                new Chip.Builder(
+                                                                context,
+                                                                clickable,
+                                                                deviceParameters)
+                                                        .setPrimaryLabelContent("First chip")
+                                                        .setWidth(expand())
+                                                        .build())
+                                        .addContent(new Spacer.Builder().setHeight(dp(4)).build())
+                                        .addContent(
+                                                new Chip.Builder(
+                                                                context,
+                                                                clickable,
+                                                                deviceParameters)
+                                                        .setPrimaryLabelContent("Second chip")
+                                                        .setWidth(expand())
+                                                        .build())
+                                        .build())
+                        .build());
+
+        primaryChipBuilder =
+                new CompactChip.Builder(context, "Action", clickable, deviceParameters);
+        testCases.put(
+                "coloredbox_1_chip_columnslayout_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .setContent(
+                                new MultiSlotLayout.Builder()
+                                        .addSlotContent(buildColoredBoxMSL(Color.YELLOW))
+                                        .build())
+                        .build());
+        testCases.put(
+                "coloredbox_2_chip_columnslayout_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .setContent(
+                                new MultiSlotLayout.Builder()
+                                        .addSlotContent(buildColoredBoxMSL(Color.YELLOW))
+                                        .addSlotContent(buildColoredBoxMSL(Color.BLUE))
+                                        .build())
+                        .build());
+        testCases.put(
+                "coloredbox_3_chip_columnslayout_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .setContent(
+                                new MultiSlotLayout.Builder()
+                                        .addSlotContent(buildColoredBoxMSL(Color.YELLOW))
+                                        .addSlotContent(buildColoredBoxMSL(Color.BLUE))
+                                        .addSlotContent(buildColoredBoxMSL(Color.MAGENTA))
+                                        .build())
+                        .build());
+        testCases.put(
+                "coloredbox_2_chip_primary_columnslayout_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .setContent(
+                                new MultiSlotLayout.Builder()
+                                        .addSlotContent(buildColoredBoxMSL(Color.YELLOW))
+                                        .addSlotContent(buildColoredBoxMSL(Color.BLUE))
+                                        .build())
+                        .setPrimaryLabelTextContent(buildTextLabel(context, "Primary label"))
+                        .build());
+        testCases.put(
+                "coloredbox_2_chip_primary_secondary_columnslayout_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .setContent(
+                                new MultiSlotLayout.Builder()
+                                        .addSlotContent(buildColoredBoxMSL(Color.YELLOW))
+                                        .addSlotContent(buildColoredBoxMSL(Color.BLUE))
+                                        .build())
+                        .setPrimaryLabelTextContent(buildTextLabel(context, "Primary label"))
+                        .setSecondaryLabelTextContent(buildTextLabel(context, "Secondary label"))
+                        .build());
+        testCases.put(
+                "coloredbox_2_columnslayout_golden" + NORMAL_SCALE_SUFFIX,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiSlotLayout.Builder()
+                                        .addSlotContent(buildColoredBoxMSL(Color.YELLOW))
+                                        .addSlotContent(buildColoredBoxMSL(Color.BLUE))
+                                        .build())
+                        .build());
+        testCases.put(
+                "coloredbox_2_primary_columnslayout_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiSlotLayout.Builder()
+                                        .addSlotContent(buildColoredBoxMSL(Color.YELLOW))
+                                        .addSlotContent(buildColoredBoxMSL(Color.BLUE))
+                                        .build())
+                        .setPrimaryLabelTextContent(buildTextLabel(context, "Primary label"))
+                        .build());
+        testCases.put(
+                "coloredbox_2_primary_secondary_columnslayout_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiSlotLayout.Builder()
+                                        .addSlotContent(buildColoredBoxMSL(Color.YELLOW))
+                                        .addSlotContent(buildColoredBoxMSL(Color.BLUE))
+                                        .build())
+                        .setPrimaryLabelTextContent(buildTextLabel(context, "Primary label"))
+                        .setSecondaryLabelTextContent(buildTextLabel(context, "Secondary label"))
+                        .build());
+        testCases.put(
+                "coloredbox_3_chip_primary_columnslayout_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .setContent(
+                                new MultiSlotLayout.Builder()
+                                        .addSlotContent(buildColoredBoxMSL(Color.YELLOW))
+                                        .addSlotContent(buildColoredBoxMSL(Color.BLUE))
+                                        .addSlotContent(buildColoredBoxMSL(Color.MAGENTA))
+                                        .build())
+                        .setPrimaryLabelTextContent(buildTextLabel(context, "Primary label"))
+                        .build());
+        testCases.put(
+                "coloredbox_3_chip_primary_secondary_columnslayout_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .setContent(
+                                new MultiSlotLayout.Builder()
+                                        .addSlotContent(buildColoredBoxMSL(Color.YELLOW))
+                                        .addSlotContent(buildColoredBoxMSL(Color.BLUE))
+                                        .addSlotContent(buildColoredBoxMSL(Color.MAGENTA))
+                                        .build())
+                        .setPrimaryLabelTextContent(buildTextLabel(context, "Primary label"))
+                        .setSecondaryLabelTextContent(buildTextLabel(context, "Secondary label"))
+                        .build());
+        testCases.put(
+                "coloredbox_3_columnslayout_golden" + NORMAL_SCALE_SUFFIX,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiSlotLayout.Builder()
+                                        .addSlotContent(buildColoredBoxMSL(Color.YELLOW))
+                                        .addSlotContent(buildColoredBoxMSL(Color.BLUE))
+                                        .addSlotContent(buildColoredBoxMSL(Color.MAGENTA))
+                                        .build())
+                        .build());
+        testCases.put(
+                "coloredbox_3_primary_columnslayout_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiSlotLayout.Builder()
+                                        .addSlotContent(buildColoredBoxMSL(Color.YELLOW))
+                                        .addSlotContent(buildColoredBoxMSL(Color.BLUE))
+                                        .addSlotContent(buildColoredBoxMSL(Color.MAGENTA))
+                                        .build())
+                        .setPrimaryLabelTextContent(buildTextLabel(context, "Primary label"))
+                        .build());
+        testCases.put(
+                "coloredbox_3_primary_secondary_columnslayout_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiSlotLayout.Builder()
+                                        .addSlotContent(buildColoredBoxMSL(Color.YELLOW))
+                                        .addSlotContent(buildColoredBoxMSL(Color.BLUE))
+                                        .addSlotContent(buildColoredBoxMSL(Color.MAGENTA))
+                                        .build())
+                        .setPrimaryLabelTextContent(buildTextLabel(context, "Primary label"))
+                        .setSecondaryLabelTextContent(buildTextLabel(context, "Secondary label"))
+                        .build());
+        testCases.put(
+                "custom_spacer_coloredbox_3_chip_primary_secondary_columnslayout_golden"
+                        + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .setContent(
+                                new MultiSlotLayout.Builder()
+                                        .addSlotContent(buildColoredBoxMSL(Color.YELLOW))
+                                        .addSlotContent(buildColoredBoxMSL(Color.BLUE))
+                                        .addSlotContent(buildColoredBoxMSL(Color.MAGENTA))
+                                        .setHorizontalSpacerWidth(2)
+                                        .build())
+                        .setPrimaryLabelTextContent(buildTextLabel(context, "Primary label"))
+                        .setSecondaryLabelTextContent(buildTextLabel(context, "Secondary label"))
+                        .setVerticalSpacerHeight(1)
+                        .build());
+
+        CircularProgressIndicator.Builder progressIndicatorBuilder =
+                new CircularProgressIndicator.Builder().setProgress(0.3f);
+        Text textContent =
+                new Text.Builder(context, "Text")
+                        .setColor(argb(Color.WHITE))
+                        .setTypography(Typography.TYPOGRAPHY_DISPLAY1)
+                        .build();
+        testCases.put(
+                "default_text_progressindicatorlayout_golden" + goldenSuffix,
+                new EdgeContentLayout.Builder(deviceParameters)
+                        .setEdgeContent(progressIndicatorBuilder.build())
+                        .setPrimaryLabelTextContent(
+                                new Text.Builder(context, "Primary label")
+                                        .setTypography(Typography.TYPOGRAPHY_CAPTION1)
+                                        .setColor(argb(Colors.PRIMARY))
+                                        .build())
+                        .setContent(textContent)
+                        .setSecondaryLabelTextContent(
+                                new Text.Builder(context, "Secondary label")
+                                        .setTypography(Typography.TYPOGRAPHY_CAPTION1)
+                                        .setColor(argb(Colors.ON_SURFACE))
+                                        .build())
+                        .build());
+        testCases.put(
+                "default_empty_progressindicatorlayout_golden" + NORMAL_SCALE_SUFFIX,
+                new EdgeContentLayout.Builder(deviceParameters)
+                        .setEdgeContent(progressIndicatorBuilder.build())
+                        .build());
+        testCases.put(
+                "custom_progressindicatorlayout_golden" + goldenSuffix,
+                new EdgeContentLayout.Builder(deviceParameters)
+                        .setContent(textContent)
+                        .setEdgeContent(
+                                progressIndicatorBuilder
+                                        .setCircularProgressIndicatorColors(
+                                                new ProgressIndicatorColors(
+                                                        Color.YELLOW, Color.GREEN))
+                                        .build())
+                        .build());
+        testCases.put(
+                "coloredbox_progressindicatorlayout_golden" + NORMAL_SCALE_SUFFIX,
+                new EdgeContentLayout.Builder(deviceParameters)
+                        .setEdgeContent(
+                                progressIndicatorBuilder
+                                        .setCircularProgressIndicatorColors(
+                                                ProgressIndicatorDefaults.DEFAULT_COLORS)
+                                        .build())
+                        .setContent(
+                                new Box.Builder()
+                                        .setWidth(dp(500))
+                                        .setHeight(dp(500))
+                                        .setModifiers(
+                                                new Modifiers.Builder()
+                                                        .setBackground(
+                                                                new Background.Builder()
+                                                                        .setColor(
+                                                                                argb(Color.YELLOW))
+                                                                        .build())
+                                                        .build())
+                                        .build())
+                        .build());
+
+        Button button1 = new Button.Builder(context, clickable).setTextContent("1").build();
+        Button button2 = new Button.Builder(context, clickable).setTextContent("2").build();
+        Button button3 = new Button.Builder(context, clickable).setTextContent("3").build();
+        Button button4 = new Button.Builder(context, clickable).setTextContent("4").build();
+        Button button5 = new Button.Builder(context, clickable).setTextContent("5").build();
+        Button button6 = new Button.Builder(context, clickable).setTextContent("6").build();
+        Button button7 = new Button.Builder(context, clickable).setTextContent("7").build();
+        Button largeButton1 =
+                new Button.Builder(context, clickable)
+                        .setTextContent("1")
+                        .setSize(ButtonDefaults.LARGE_SIZE)
+                        .build();
+        Button largeButton2 =
+                new Button.Builder(context, clickable)
+                        .setTextContent("2")
+                        .setSize(ButtonDefaults.LARGE_SIZE)
+                        .build();
+        Button extraLargeButton =
+                new Button.Builder(context, clickable)
+                        .setTextContent("1")
+                        .setSize(ButtonDefaults.EXTRA_LARGE_SIZE)
+                        .build();
+        testCases.put(
+                "multibutton_layout_1button_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiButtonLayout.Builder()
+                                        .addButtonContent(extraLargeButton)
+                                        .build())
+                        .build());
+        testCases.put(
+                "multibutton_layout_1button_chip_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiButtonLayout.Builder()
+                                        .addButtonContent(extraLargeButton)
+                                        .build())
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .build());
+        testCases.put(
+                "multibutton_layout_2button_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiButtonLayout.Builder()
+                                        .addButtonContent(largeButton1)
+                                        .addButtonContent(largeButton2)
+                                        .build())
+                        .build());
+        testCases.put(
+                "multibutton_layout_2button_chip_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiButtonLayout.Builder()
+                                        .addButtonContent(largeButton1)
+                                        .addButtonContent(largeButton2)
+                                        .build())
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .build());
+        testCases.put(
+                "multibutton_layout_2button_primarylabel_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiButtonLayout.Builder()
+                                        .addButtonContent(largeButton1)
+                                        .addButtonContent(largeButton2)
+                                        .build())
+                        .setPrimaryLabelTextContent(buildTextLabel(context, "Primary label"))
+                        .build());
+        testCases.put(
+                "multibutton_layout_2button_chip_primarylabel_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiButtonLayout.Builder()
+                                        .addButtonContent(largeButton1)
+                                        .addButtonContent(largeButton2)
+                                        .build())
+                        .setPrimaryLabelTextContent(buildTextLabel(context, "Primary label"))
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .build());
+        testCases.put(
+                "multibutton_layout_3button_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiButtonLayout.Builder()
+                                        .addButtonContent(button1)
+                                        .addButtonContent(button2)
+                                        .addButtonContent(button3)
+                                        .build())
+                        .build());
+        testCases.put(
+                "multibutton_layout_3button_chip_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiButtonLayout.Builder()
+                                        .addButtonContent(button1)
+                                        .addButtonContent(button2)
+                                        .addButtonContent(button3)
+                                        .build())
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .build());
+        testCases.put(
+                "multibutton_layout_3button_primarylabel_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiButtonLayout.Builder()
+                                        .addButtonContent(button1)
+                                        .addButtonContent(button2)
+                                        .addButtonContent(button3)
+                                        .build())
+                        .setPrimaryLabelTextContent(buildTextLabel(context, "Primary label"))
+                        .build());
+        testCases.put(
+                "multibutton_layout_3button_chip_primarylabel_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiButtonLayout.Builder()
+                                        .addButtonContent(button1)
+                                        .addButtonContent(button2)
+                                        .addButtonContent(button3)
+                                        .build())
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .setPrimaryLabelTextContent(buildTextLabel(context, "Primary label"))
+                        .build());
+        testCases.put(
+                "multibutton_layout_4button_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiButtonLayout.Builder()
+                                        .addButtonContent(button1)
+                                        .addButtonContent(button2)
+                                        .addButtonContent(button3)
+                                        .addButtonContent(button4)
+                                        .build())
+                        .build());
+        testCases.put(
+                "multibutton_layout_4button_chip_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiButtonLayout.Builder()
+                                        .addButtonContent(button1)
+                                        .addButtonContent(button2)
+                                        .addButtonContent(button3)
+                                        .addButtonContent(button4)
+                                        .build())
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .build());
+        testCases.put(
+                "multibutton_layout_5button_top_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiButtonLayout.Builder()
+                                        .addButtonContent(button1)
+                                        .addButtonContent(button2)
+                                        .addButtonContent(button3)
+                                        .addButtonContent(button4)
+                                        .addButtonContent(button5)
+                                        .setFiveButtonDistribution(
+                                                MultiButtonLayout
+                                                        .FIVE_BUTTON_DISTRIBUTION_TOP_HEAVY)
+                                        .build())
+                        .build());
+        testCases.put(
+                "multibutton_layout_5button_bottom_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiButtonLayout.Builder()
+                                        .addButtonContent(button1)
+                                        .addButtonContent(button2)
+                                        .addButtonContent(button3)
+                                        .addButtonContent(button4)
+                                        .addButtonContent(button5)
+                                        .setFiveButtonDistribution(
+                                                MultiButtonLayout
+                                                        .FIVE_BUTTON_DISTRIBUTION_BOTTOM_HEAVY)
+                                        .build())
+                        .build());
+        testCases.put(
+                "multibutton_layout_5button_bottom_chip_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiButtonLayout.Builder()
+                                        .addButtonContent(button1)
+                                        .addButtonContent(button2)
+                                        .addButtonContent(button3)
+                                        .addButtonContent(button4)
+                                        .addButtonContent(button5)
+                                        .setFiveButtonDistribution(
+                                                MultiButtonLayout
+                                                        .FIVE_BUTTON_DISTRIBUTION_BOTTOM_HEAVY)
+                                        .build())
+                        .setPrimaryChipContent(primaryChipBuilder.build())
+                        .build());
+        testCases.put(
+                "multibutton_layout_6button_golden" + goldenSuffix,
+                new PrimaryLayout.Builder(deviceParameters)
+                        .setContent(
+                                new MultiButtonLayout.Builder()
+                                        .addButtonContent(button1)
+                                        .addButtonContent(button2)
+                                        .addButtonContent(button3)
+                                        .addButtonContent(button4)
+                                        .addButtonContent(button5)
+                                        .addButtonContent(button6)
+                                        .build())
+                        .build());
+        testCases.put(
+                "multibutton_layout_7button_golden" + goldenSuffix,
+                new MultiButtonLayout.Builder()
+                        .addButtonContent(button1)
+                        .addButtonContent(button2)
+                        .addButtonContent(button3)
+                        .addButtonContent(button4)
+                        .addButtonContent(button5)
+                        .addButtonContent(button6)
+                        .addButtonContent(button7)
+                        .build());
+
+        return testCases;
+    }
+
+    @NonNull
+    private static Text buildTextLabel(@NonNull Context context, @NonNull String text) {
+        return new Text.Builder(context, text)
+                .setTypography(Typography.TYPOGRAPHY_CAPTION1)
+                .setColor(argb(Color.WHITE))
+                .build();
+    }
+
+    @NonNull
+    private static Box buildColoredBoxMSL(int color) {
+        return new Box.Builder()
+                .setWidth(dp(60))
+                .setHeight(dp(60))
+                .setModifiers(
+                        new Modifiers.Builder()
+                                .setBackground(
+                                        new Background.Builder().setColor(argb(color)).build())
+                                .build())
+                .build();
+    }
+
+    @NonNull
+    private static Box buildColoredBoxPLL(int color) {
+        return new Box.Builder()
+                .setWidth(expand())
+                .setHeight(dp(60))
+                .setModifiers(
+                        new Modifiers.Builder()
+                                .setBackground(
+                                        new Background.Builder().setColor(argb(color)).build())
+                                .build())
+                .build();
+    }
+
+    @Dimension(unit = Dimension.DP)
+    static int pxToDp(int px, float scale) {
+        return (int) ((px - 0.5f) / scale);
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/testapp/GoldenTestActivity.java b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/testapp/GoldenTestActivity.java
new file mode 100644
index 0000000..53fc7d0
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/testapp/GoldenTestActivity.java
@@ -0,0 +1,113 @@
+/*
+ * 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.wear.protolayout.material.testapp;
+
+import static androidx.wear.protolayout.material.Helper.checkNotNull;
+import static androidx.wear.protolayout.material.RunnerUtils.SCREEN_HEIGHT;
+import static androidx.wear.protolayout.material.RunnerUtils.SCREEN_WIDTH;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.FrameLayout.LayoutParams;
+
+import androidx.annotation.Nullable;
+import androidx.wear.protolayout.LayoutElementBuilders.Layout;
+import androidx.wear.protolayout.ResourceBuilders.AndroidImageResourceByResId;
+import androidx.wear.protolayout.ResourceBuilders.ImageResource;
+import androidx.wear.protolayout.ResourceBuilders.Resources;
+import androidx.wear.protolayout.material.R;
+import androidx.wear.protolayout.renderer.impl.ProtoLayoutViewInstance;
+
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+@SuppressWarnings("deprecation")
+public class GoldenTestActivity extends Activity {
+    private static final String ICON_ID = "icon";
+    private static final String AVATAR = "avatar_image";
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        byte[] layoutPayload = getIntent().getExtras().getByteArray("layout");
+        Layout layout = Layout.fromByteArray(layoutPayload);
+
+        Context appContext = getApplicationContext();
+        FrameLayout root = new FrameLayout(appContext);
+        root.setBackgroundColor(Color.BLACK);
+        root.setLayoutParams(new LayoutParams(SCREEN_WIDTH, SCREEN_HEIGHT));
+
+        ListeningExecutorService mainExecutor = MoreExecutors.newDirectExecutorService();
+        Resources resources = generateResources();
+
+        ProtoLayoutViewInstance instance =
+                new ProtoLayoutViewInstance(
+                        new ProtoLayoutViewInstance.Config.Builder(
+                                appContext,
+                                mainExecutor,
+                                mainExecutor,
+                                "androidx.wear.tiles.extra.CLICKABLE_ID")
+                                .setIsViewFullyVisible(true)
+                        .build());
+
+        instance.renderAndAttach(checkNotNull(layout).toProto(), resources.toProto(), root);
+
+        View firstChild = root.getChildAt(0);
+
+        // Simulate what the thing outside the renderer should do. Center the contents.
+        LayoutParams layoutParams = (LayoutParams) firstChild.getLayoutParams();
+        layoutParams.gravity = Gravity.CENTER;
+
+        // Set the activity to be full screen so when we crop the Bitmap we don't get time bar etc.
+        requestWindowFeature(Window.FEATURE_NO_TITLE);
+        getWindow()
+                .setFlags(
+                        WindowManager.LayoutParams.FLAG_FULLSCREEN,
+                        WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+        setContentView(root, new ViewGroup.LayoutParams(SCREEN_WIDTH, SCREEN_HEIGHT));
+        super.onCreate(savedInstanceState);
+    }
+
+    private static Resources generateResources() {
+        return new Resources.Builder()
+                .addIdToImageMapping(
+                        ICON_ID,
+                        new ImageResource.Builder()
+                                .setAndroidResourceByResId(
+                                        new AndroidImageResourceByResId.Builder()
+                                                .setResourceId(R.drawable.icon)
+                                                .build())
+                                .build())
+                .addIdToImageMapping(
+                        AVATAR,
+                        new ImageResource.Builder()
+                                .setAndroidResourceByResId(
+                                        new AndroidImageResourceByResId.Builder()
+                                                .setResourceId(R.drawable.avatar)
+                                                .build())
+                                .build())
+                .build();
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Button.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Button.java
new file mode 100644
index 0000000..996cae8
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Button.java
@@ -0,0 +1,565 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.annotation.Dimension.DP;
+import static androidx.wear.protolayout.DimensionBuilders.dp;
+import static androidx.wear.protolayout.LayoutElementBuilders.CONTENT_SCALE_MODE_FILL_BOUNDS;
+import static androidx.wear.protolayout.material.ButtonDefaults.DEFAULT_SIZE;
+import static androidx.wear.protolayout.material.ButtonDefaults.EXTRA_LARGE_SIZE;
+import static androidx.wear.protolayout.material.ButtonDefaults.LARGE_SIZE;
+import static androidx.wear.protolayout.material.ButtonDefaults.PRIMARY_COLORS;
+import static androidx.wear.protolayout.material.Helper.checkNotNull;
+import static androidx.wear.protolayout.material.Helper.checkTag;
+import static androidx.wear.protolayout.material.Helper.getMetadataTagName;
+import static androidx.wear.protolayout.material.Helper.getTagBytes;
+import static androidx.wear.protolayout.material.Helper.radiusOf;
+
+import android.content.Context;
+
+import androidx.annotation.Dimension;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.ColorBuilders.ColorProp;
+import androidx.wear.protolayout.DimensionBuilders.ContainerDimension;
+import androidx.wear.protolayout.DimensionBuilders.DpProp;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.ColorFilter;
+import androidx.wear.protolayout.LayoutElementBuilders.Image;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+import androidx.wear.protolayout.ModifiersBuilders;
+import androidx.wear.protolayout.ModifiersBuilders.Background;
+import androidx.wear.protolayout.ModifiersBuilders.Clickable;
+import androidx.wear.protolayout.ModifiersBuilders.Corner;
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+import androidx.wear.protolayout.ModifiersBuilders.Semantics;
+import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.material.Typography.TypographyName;
+import androidx.wear.protolayout.proto.LayoutElementProto;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Tiles component {@link Button} that represents clickable button with the given content.
+ *
+ * <p>The Button is circular in shape. The recommended sizes are defined in {@link ButtonDefaults}.
+ *
+ * <p>The recommended set of {@link ButtonColors} styles can be obtained from {@link
+ * ButtonDefaults}., e.g. {@link ButtonDefaults#PRIMARY_COLORS} to get a color scheme for a primary
+ * {@link Button}.
+ *
+ * <p>When accessing the contents of a container for testing, note that this element can't be simply
+ * casted back to the original type, i.e.:
+ *
+ * <pre>{@code
+ * Button button = new Button...
+ * Box box = new Box.Builder().addContent(button).build();
+ *
+ * Button myButton = (Button) box.getContents().get(0);
+ * }</pre>
+ *
+ * will fail.
+ *
+ * <p>To be able to get {@link Button} object from any layout element, {@link #fromLayoutElement}
+ * method should be used, i.e.:
+ *
+ * <pre>{@code
+ * Button myButton = Button.fromLayoutElement(box.getContents().get(0));
+ * }</pre>
+ */
+public class Button implements LayoutElement {
+    /** Tool tag for Metadata in Modifiers, so we know that Box is actually a Button with text. */
+    static final String METADATA_TAG_TEXT = "TXTBTN";
+    /** Tool tag for Metadata in Modifiers, so we know that Box is actually a Button with icon. */
+    static final String METADATA_TAG_ICON = "ICNBTN";
+    /** Tool tag for Metadata in Modifiers, so we know that Box is actually a Button with image. */
+    static final String METADATA_TAG_IMAGE = "IMGBTN";
+    /**
+     * Tool tag for Metadata in Modifiers, so we know that Box is actually a Button with custom
+     * content.
+     */
+    static final String METADATA_TAG_CUSTOM_CONTENT = "CSTBTN";
+
+    @NonNull private final Box mElement;
+
+    Button(@NonNull Box element) {
+        mElement = element;
+    }
+
+    /** Builder class for {@link Button}. */
+    public static final class Builder implements LayoutElement.Builder {
+        private static final int NOT_SET = -1;
+        private static final int ICON = 0;
+        private static final int TEXT = 1;
+        private static final int IMAGE = 2;
+        private static final int CUSTOM_CONTENT = 3;
+
+        @NonNull static final Map<Integer, String> TYPE_TO_TAG = new HashMap<>();
+
+        @RestrictTo(Scope.LIBRARY)
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef({NOT_SET, ICON, TEXT, IMAGE, CUSTOM_CONTENT})
+        @interface ButtonType {}
+
+        @NonNull private final Context mContext;
+        @Nullable private LayoutElement mCustomContent;
+        @NonNull private final Clickable mClickable;
+        @NonNull private CharSequence mContentDescription = "";
+        @NonNull private DpProp mSize = DEFAULT_SIZE;
+        @Nullable private String mText = null;
+        @Nullable private Integer mTypographyName = null;
+        @Nullable private String mIcon = null;
+        @Nullable private DpProp mIconSize = null;
+        @Nullable private String mImage = null;
+        @NonNull private ButtonColors mButtonColors = PRIMARY_COLORS;
+        @ButtonType private int mType = NOT_SET;
+
+        static {
+            TYPE_TO_TAG.put(ICON, METADATA_TAG_ICON);
+            TYPE_TO_TAG.put(TEXT, METADATA_TAG_TEXT);
+            TYPE_TO_TAG.put(IMAGE, METADATA_TAG_IMAGE);
+            TYPE_TO_TAG.put(CUSTOM_CONTENT, METADATA_TAG_CUSTOM_CONTENT);
+        }
+
+        /**
+         * Creates a builder for the {@link Button} from the given content. Custom content should be
+         * later set with one of the following ({@link #setIconContent}, {@link #setTextContent},
+         * {@link #setImageContent}.
+         *
+         * @param context The application's context.
+         * @param clickable Associated {@link Clickable} for click events. When the Button is
+         *     clicked it will fire the associated action.
+         */
+        public Builder(@NonNull Context context, @NonNull Clickable clickable) {
+            mClickable = clickable;
+            mContext = context;
+        }
+
+        /**
+         * Sets the content description for the {@link Button}. It is highly recommended to provide
+         * this for button containing icon or image.
+         */
+        @NonNull
+        public Builder setContentDescription(@NonNull CharSequence contentDescription) {
+            this.mContentDescription = contentDescription;
+            return this;
+        }
+
+        /**
+         * Sets the size for the {@link Button}. Strongly recommended values are {@link
+         * ButtonDefaults#DEFAULT_SIZE}, {@link ButtonDefaults#LARGE_SIZE} and {@link
+         * ButtonDefaults#EXTRA_LARGE_SIZE}. If not set, {@link ButtonDefaults#DEFAULT_SIZE} will be
+         * used.
+         */
+        @NonNull
+        public Builder setSize(@NonNull DpProp size) {
+            mSize = size;
+            return this;
+        }
+
+        /**
+         * Sets the size for the {@link Button}. Strongly recommended values are {@link
+         * ButtonDefaults#DEFAULT_SIZE}, {@link ButtonDefaults#LARGE_SIZE} and {@link
+         * ButtonDefaults#EXTRA_LARGE_SIZE}. If not set, {@link ButtonDefaults#DEFAULT_SIZE} will be
+         * used.
+         */
+        @NonNull
+        public Builder setSize(@Dimension(unit = DP) float size) {
+            mSize = dp(size);
+            return this;
+        }
+
+        /**
+         * Sets the colors for the {@link Button}. If not set, {@link ButtonDefaults#PRIMARY_COLORS}
+         * will be used.
+         */
+        @NonNull
+        public Builder setButtonColors(@NonNull ButtonColors buttonColors) {
+            mButtonColors = buttonColors;
+            return this;
+        }
+
+        /**
+         * Sets the custom content for this Button. Any previously added content will be overridden.
+         */
+        @NonNull
+        public Builder setCustomContent(@NonNull LayoutElement content) {
+            resetContent();
+            this.mCustomContent = content;
+            this.mType = CUSTOM_CONTENT;
+            return this;
+        }
+
+        /**
+         * Sets the content of this Button to be the given icon with the given size. Any previously
+         * added content will be overridden. Provided icon will be tinted to the given content color
+         * from {@link ButtonColors} and with the given size. This icon should be image with chosen
+         * alpha channel and not an actual image.
+         */
+        @NonNull
+        public Builder setIconContent(@NonNull String imageResourceId, @NonNull DpProp size) {
+            resetContent();
+            this.mIcon = imageResourceId;
+            this.mType = ICON;
+            this.mIconSize = size;
+            return this;
+        }
+
+        /**
+         * Sets the content of this Button to be the given icon with the default size that is half
+         * of the set size of the button. Any previously added content will be overridden. Provided
+         * icon will be tinted to the given content color from {@link ButtonColors}. This icon
+         * should be image with chosen alpha channel and not an actual image.
+         */
+        @NonNull
+        public Builder setIconContent(@NonNull String imageResourceId) {
+            resetContent();
+            this.mIcon = imageResourceId;
+            this.mType = ICON;
+            return this;
+        }
+
+        /**
+         * Sets the content of this Button to be the given text with the default font for the set
+         * size (for the {@link ButtonDefaults#DEFAULT_SIZE}, {@link ButtonDefaults#LARGE_SIZE} and
+         * {@link ButtonDefaults#EXTRA_LARGE_SIZE} is {@link Typography#TYPOGRAPHY_TITLE2}, {@link
+         * Typography#TYPOGRAPHY_TITLE1} and {@link Typography#TYPOGRAPHY_DISPLAY3} respectively).
+         * Any previously added content will be overridden. Text should contain no more than 3
+         * characters, otherwise it will overflow from the edges.
+         */
+        @NonNull
+        public Builder setTextContent(@NonNull String text) {
+            resetContent();
+            this.mText = text;
+            this.mType = TEXT;
+            return this;
+        }
+
+        /**
+         * Sets the content of this Button to be the given text with the given font. If you need
+         * more font related customization, consider using {@link #setCustomContent} with {@link
+         * Text} component. Any previously added content will be overridden. Text should contain no
+         * more than 3 characters, otherwise it will overflow from the edges.
+         */
+        @NonNull
+        public Builder setTextContent(@NonNull String text, @TypographyName int typographyName) {
+            resetContent();
+            this.mText = text;
+            this.mTypographyName = typographyName;
+            this.mType = TEXT;
+            return this;
+        }
+
+        /**
+         * Sets the content of this Button to be the given image, i.e. contacts photo. Any
+         * previously added content will be overridden.
+         */
+        @NonNull
+        public Builder setImageContent(@NonNull String imageResourceId) {
+            resetContent();
+            this.mImage = imageResourceId;
+            this.mType = IMAGE;
+            return this;
+        }
+
+        private void resetContent() {
+            this.mText = null;
+            this.mTypographyName = null;
+            this.mIcon = null;
+            this.mImage = null;
+            this.mCustomContent = null;
+            this.mIconSize = null;
+        }
+
+        /** Constructs and returns {@link Button} with the provided field and look. */
+        @NonNull
+        @Override
+        public Button build() {
+            Modifiers.Builder modifiers =
+                    new Modifiers.Builder()
+                            .setClickable(mClickable)
+                            .setBackground(
+                                    new Background.Builder()
+                                            .setColor(mButtonColors.getBackgroundColor())
+                                            .setCorner(
+                                                    new Corner.Builder()
+                                                            .setRadius(radiusOf(mSize))
+                                                            .build())
+                                            .build())
+                            .setMetadata(
+                                    new ElementMetadata.Builder()
+                                            .setTagData(
+                                                    getTagBytes(
+                                                            checkNotNull(TYPE_TO_TAG.get(mType))))
+                                            .build());
+            if (mContentDescription.length() > 0) {
+                modifiers.setSemantics(
+                        new ModifiersBuilders.Semantics.Builder()
+                                .setContentDescription(mContentDescription.toString())
+                                .build());
+            }
+
+            Box.Builder element =
+                    new Box.Builder()
+                            .setHeight(mSize)
+                            .setWidth(mSize)
+                            .setModifiers(modifiers.build());
+
+            element.addContent(getCorrectContent());
+
+            return new Button(element.build());
+        }
+
+        @NonNull
+        private LayoutElement getCorrectContent() {
+            LayoutElement.Builder content;
+            switch (mType) {
+                case ICON:
+                {
+                    DpProp iconSize =
+                            mIconSize != null
+                                    ? mIconSize
+                                    : ButtonDefaults.recommendedIconSize(mSize);
+                    content =
+                            new Image.Builder()
+                                    .setResourceId(checkNotNull(mIcon))
+                                    .setHeight(checkNotNull(iconSize))
+                                    .setWidth(iconSize)
+                                    .setContentScaleMode(CONTENT_SCALE_MODE_FILL_BOUNDS)
+                                    .setColorFilter(
+                                            new ColorFilter.Builder()
+                                                    .setTint(mButtonColors.getContentColor())
+                                                    .build());
+
+                    return content.build();
+                }
+                case TEXT:
+                {
+                    @TypographyName
+                    int typographyName =
+                            mTypographyName != null
+                                    ? mTypographyName
+                                    : getDefaultTypographyForSize(mSize);
+                    content =
+                            new Text.Builder(mContext, checkNotNull(mText))
+                                    .setMaxLines(1)
+                                    .setTypography(typographyName)
+                                    .setColor(mButtonColors.getContentColor());
+
+                    return content.build();
+                }
+                case IMAGE:
+                {
+                    content =
+                            new Image.Builder()
+                                    .setResourceId(checkNotNull(mImage))
+                                    .setHeight(mSize)
+                                    .setWidth(mSize)
+                                    .setContentScaleMode(CONTENT_SCALE_MODE_FILL_BOUNDS);
+                    return content.build();
+                }
+                case CUSTOM_CONTENT:
+                    return checkNotNull(mCustomContent);
+                case NOT_SET:
+                    // Shouldn't happen.
+                default:
+                    // Shouldn't happen.
+                    throw new IllegalArgumentException("Wrong Button type");
+            }
+        }
+
+        private static @TypographyName int getDefaultTypographyForSize(@NonNull DpProp size) {
+            if (size.getValue() == LARGE_SIZE.getValue()) {
+                return Typography.TYPOGRAPHY_TITLE1;
+            } else if (size.getValue() == EXTRA_LARGE_SIZE.getValue()) {
+                return Typography.TYPOGRAPHY_DISPLAY3;
+            } else {
+                return Typography.TYPOGRAPHY_TITLE2;
+            }
+        }
+    }
+
+    /**
+     * Returns the custom content of this Button if it has been added. Otherwise, it returns null.
+     */
+    @Nullable
+    public LayoutElement getCustomContent() {
+        if (!getMetadataTag().equals(METADATA_TAG_CUSTOM_CONTENT)) {
+            return null;
+        }
+        return getAnyContent();
+    }
+
+    /** Returns the icon content of this Button if it has been added. Otherwise, it returns null. */
+    @Nullable
+    public String getIconContent() {
+        Image icon = getIconContentObject();
+        return icon != null ? checkNotNull(icon.getResourceId()).getValue() : null;
+    }
+
+    /**
+     * Returns the image content of this Button if it has been added. Otherwise, it returns null.
+     */
+    @Nullable
+    public String getImageContent() {
+        Image image = getImageContentObject();
+        return image != null ? checkNotNull(image.getResourceId()).getValue() : null;
+    }
+
+    /** Returns the text content of this Button if it has been added. Otherwise, it returns null. */
+    @Nullable
+    public String getTextContent() {
+        Text text = getTextContentObject();
+        return text != null ? text.getText() : null;
+    }
+
+    @NonNull
+    private LayoutElement getAnyContent() {
+        return checkNotNull(mElement.getContents().get(0));
+    }
+
+    @Nullable
+    private Image getIconContentObject() {
+        if (!getMetadataTag().equals(METADATA_TAG_ICON)) {
+            return null;
+        }
+        return (Image) getAnyContent();
+    }
+
+    @Nullable
+    private Text getTextContentObject() {
+        if (!getMetadataTag().equals(METADATA_TAG_TEXT)) {
+            return null;
+        }
+        return Text.fromLayoutElement(getAnyContent());
+    }
+
+    @Nullable
+    private Image getImageContentObject() {
+        if (!getMetadataTag().equals(METADATA_TAG_IMAGE)) {
+            return null;
+        }
+        return (Image) getAnyContent();
+    }
+
+    /** Returns click event action associated with this Button. */
+    @NonNull
+    public Clickable getClickable() {
+        return checkNotNull(checkNotNull(mElement.getModifiers()).getClickable());
+    }
+
+    /** Returns content description for this Button. */
+    @Nullable
+    public CharSequence getContentDescription() {
+        Semantics semantics = checkNotNull(mElement.getModifiers()).getSemantics();
+        if (semantics == null) {
+            return null;
+        }
+        return semantics.getContentDescription();
+    }
+
+    /** Returns size for this Button. */
+    @NonNull
+    public ContainerDimension getSize() {
+        return checkNotNull(mElement.getWidth());
+    }
+
+    private ColorProp getBackgroundColor() {
+        return checkNotNull(
+                checkNotNull(checkNotNull(mElement.getModifiers()).getBackground()).getColor());
+    }
+
+    /** Returns button color of this Button. */
+    @NonNull
+    public ButtonColors getButtonColors() {
+        ColorProp backgroundColor = getBackgroundColor();
+        ColorProp contentColor = null;
+
+        switch (getMetadataTag()) {
+            case METADATA_TAG_TEXT:
+                contentColor = checkNotNull(getTextContentObject()).getColor();
+                break;
+            case METADATA_TAG_ICON:
+                contentColor =
+                        checkNotNull(checkNotNull(getIconContentObject()).getColorFilter())
+                                .getTint();
+                break;
+            case METADATA_TAG_IMAGE:
+                contentColor =
+                        checkNotNull(checkNotNull(getImageContentObject()).getColorFilter())
+                                .getTint();
+                break;
+            case METADATA_TAG_CUSTOM_CONTENT:
+                break;
+        }
+
+        if (contentColor == null) {
+            contentColor = new ColorProp.Builder(0).build();
+        }
+
+        return new ButtonColors(backgroundColor, contentColor);
+    }
+
+    /** Returns metadata tag set to this Button. */
+    @NonNull
+    String getMetadataTag() {
+        return getMetadataTagName(
+                checkNotNull(checkNotNull(mElement.getModifiers()).getMetadata()));
+    }
+
+    /**
+     * Returns Button object from the given LayoutElement (e.g. one retrieved from a container's
+     * content with {@code container.getContents().get(index)}) if that element can be converted to
+     * Button. Otherwise, it will return null.
+     */
+    @Nullable
+    public static Button fromLayoutElement(@NonNull LayoutElement element) {
+        if (element instanceof Button) {
+            return (Button) element;
+        }
+        if (!(element instanceof Box)) {
+            return null;
+        }
+        Box boxElement = (Box) element;
+        if (!checkTag(boxElement.getModifiers(), Builder.TYPE_TO_TAG.values())) {
+            return null;
+        }
+        // Now we are sure that this element is a Button.
+        return new Button(boxElement);
+    }
+
+    @NonNull
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public LayoutElementProto.LayoutElement toLayoutElementProto() {
+        return checkNotNull(mElement.toLayoutElementProto());
+    }
+
+    @Nullable
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public Fingerprint getFingerprint() {
+        return mElement.getFingerprint();
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/ButtonColors.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/ButtonColors.java
new file mode 100644
index 0000000..3ebfc62
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/ButtonColors.java
@@ -0,0 +1,89 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.wear.protolayout.ColorBuilders.argb;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+import androidx.wear.protolayout.ColorBuilders.ColorProp;
+
+/**
+ * Represents the background and content colors used in a button Tiles component.
+ *
+ * <p>See {@link ButtonDefaults#PRIMARY_COLORS} for the default colors used in a primary styled
+ * {@link Button}. See {@link ButtonDefaults#SECONDARY_COLORS} for the default colors used in a
+ * secondary styled {@link Button}.
+ */
+public class ButtonColors {
+    @NonNull private final ColorProp mBackgroundColor;
+    @NonNull private final ColorProp mContentColor;
+
+    /**
+     * Constructor for {@link ButtonColors} object.
+     *
+     * @param backgroundColor The background color to be used for a button Tiles component. Should
+     *     be in ARGB format.
+     * @param contentColor The content color or tint color to be used for a button Tiles component.
+     *     Should be in ARGB format.
+     */
+    public ButtonColors(@ColorInt int backgroundColor, @ColorInt int contentColor) {
+        mBackgroundColor = argb(backgroundColor);
+        mContentColor = argb(contentColor);
+    }
+
+    /**
+     * Constructor for {@link ButtonColors} object.
+     *
+     * @param backgroundColor The background color to be used for a button.
+     * @param contentColor The content color or tint color to be used for a button.
+     */
+    public ButtonColors(@NonNull ColorProp backgroundColor, @NonNull ColorProp contentColor) {
+        mBackgroundColor = backgroundColor;
+        mContentColor = contentColor;
+    }
+
+    /**
+     * Returns a {@link ButtonColors} object, using the current Primary colors from the given {@link
+     * Colors}.
+     */
+    @NonNull
+    public static ButtonColors primaryButtonColors(@NonNull Colors colors) {
+        return new ButtonColors(colors.getPrimary(), colors.getOnPrimary());
+    }
+
+    /**
+     * Returns a {@link ButtonColors} object, using the current Surface colors from the given {@link
+     * Colors}.
+     */
+    @NonNull
+    public static ButtonColors secondaryButtonColors(@NonNull Colors colors) {
+        return new ButtonColors(colors.getSurface(), colors.getOnSurface());
+    }
+
+    /** The background color to be used on a button Tiles components. */
+    @NonNull
+    public ColorProp getBackgroundColor() {
+        return mBackgroundColor;
+    }
+
+    /** The content or tint color to be used on a button Tiles components. */
+    @NonNull
+    public ColorProp getContentColor() {
+        return mContentColor;
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/ButtonDefaults.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/ButtonDefaults.java
new file mode 100644
index 0000000..e98e212
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/ButtonDefaults.java
@@ -0,0 +1,60 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.annotation.Dimension.DP;
+import static androidx.wear.protolayout.DimensionBuilders.dp;
+
+import androidx.annotation.Dimension;
+import androidx.annotation.NonNull;
+import androidx.wear.protolayout.DimensionBuilders.DpProp;
+
+/** Contains the default values used by button Tiles components. */
+public class ButtonDefaults {
+    private ButtonDefaults() {}
+
+    /** The default size for standard {@link Button}. */
+    @NonNull public static final DpProp DEFAULT_SIZE = dp(52);
+
+    /** The recommended size for large {@link Button}. */
+    @NonNull public static final DpProp LARGE_SIZE = dp(60);
+
+    /** The recommended size for extra large {@link Button}. */
+    @NonNull public static final DpProp EXTRA_LARGE_SIZE = dp(88);
+
+    /** Returns the recommended icon size for the given size of a {@link Button}. */
+    @NonNull
+    public static DpProp recommendedIconSize(@NonNull DpProp buttonSize) {
+        return recommendedIconSize(buttonSize.getValue());
+    }
+
+    /** Returns the recommended icon size for the given size of a {@link Button}. */
+    @NonNull
+    public static DpProp recommendedIconSize(@Dimension(unit = DP) float buttonSize) {
+        return dp(buttonSize / 2);
+    }
+
+    /** The recommended colors for a primary {@link Button}. */
+    @NonNull
+    public static final ButtonColors PRIMARY_COLORS =
+            ButtonColors.primaryButtonColors(Colors.DEFAULT);
+
+    /** The recommended colors for a secondary {@link Button}. */
+    @NonNull
+    public static final ButtonColors SECONDARY_COLORS =
+            ButtonColors.secondaryButtonColors(Colors.DEFAULT);
+}
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
new file mode 100644
index 0000000..74732d5
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Chip.java
@@ -0,0 +1,682 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.annotation.Dimension.DP;
+import static androidx.wear.protolayout.DimensionBuilders.dp;
+import static androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER;
+import static androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_START;
+import static androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_UNDEFINED;
+import static androidx.wear.protolayout.material.ChipDefaults.DEFAULT_HEIGHT;
+import static androidx.wear.protolayout.material.ChipDefaults.DEFAULT_MARGIN_PERCENT;
+import static androidx.wear.protolayout.material.ChipDefaults.HORIZONTAL_PADDING;
+import static androidx.wear.protolayout.material.ChipDefaults.ICON_SIZE;
+import static androidx.wear.protolayout.material.ChipDefaults.ICON_SPACER_WIDTH;
+import static androidx.wear.protolayout.material.ChipDefaults.PRIMARY_COLORS;
+import static androidx.wear.protolayout.material.Helper.checkNotNull;
+import static androidx.wear.protolayout.material.Helper.checkTag;
+import static androidx.wear.protolayout.material.Helper.getMetadataTagName;
+import static androidx.wear.protolayout.material.Helper.getTagBytes;
+import static androidx.wear.protolayout.material.Helper.radiusOf;
+
+import android.content.Context;
+
+import androidx.annotation.Dimension;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.ColorBuilders.ColorProp;
+import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.protolayout.DimensionBuilders.ContainerDimension;
+import androidx.wear.protolayout.DimensionBuilders.DpProp;
+import androidx.wear.protolayout.LayoutElementBuilders;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.ColorFilter;
+import androidx.wear.protolayout.LayoutElementBuilders.Column;
+import androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignment;
+import androidx.wear.protolayout.LayoutElementBuilders.Image;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+import androidx.wear.protolayout.LayoutElementBuilders.Row;
+import androidx.wear.protolayout.LayoutElementBuilders.Spacer;
+import androidx.wear.protolayout.ModifiersBuilders.Background;
+import androidx.wear.protolayout.ModifiersBuilders.Clickable;
+import androidx.wear.protolayout.ModifiersBuilders.Corner;
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+import androidx.wear.protolayout.ModifiersBuilders.Padding;
+import androidx.wear.protolayout.ModifiersBuilders.Semantics;
+import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.material.Typography.TypographyName;
+import androidx.wear.protolayout.proto.LayoutElementProto;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Tiles component {@link Chip} that represents clickable object with the text, optional label and
+ * optional icon or with custom content.
+ *
+ * <p>The Chip is Stadium shape and has a max height designed to take no more than two lines of text
+ * of {@link Typography#TYPOGRAPHY_BUTTON} style. The {@link Chip} can have an icon horizontally
+ * parallel to the two lines of text. Width of chip can very, and the recommended size is screen
+ * dependent with the recommended margin being applied.
+ *
+ * <p>The recommended set of {@link ChipColors} styles can be obtained from {@link ChipDefaults}.,
+ * e.g. {@link ChipDefaults#PRIMARY_COLORS} to get a color scheme for a primary {@link Chip}.
+ *
+ * <p>When accessing the contents of a container for testing, note that this element can't be simply
+ * casted back to the original type, i.e.:
+ *
+ * <pre>{@code
+ * Chip chip = new Chip...
+ * Box box = new Box.Builder().addContent(chip).build();
+ *
+ * Chip myChip = (Chip) box.getContents().get(0);
+ * }</pre>
+ *
+ * will fail.
+ *
+ * <p>To be able to get {@link Chip} object from any layout element, {@link #fromLayoutElement}
+ * method should be used, i.e.:
+ *
+ * <pre>{@code
+ * Chip myChip = Chip.fromLayoutElement(box.getContents().get(0));
+ * }</pre>
+ *
+ * @see  androidx.wear.protolayout.material.layouts.PrimaryLayout.Builder#setContent if this Chip is
+ * used inside of {@link androidx.wear.protolayout.material.layouts.PrimaryLayout}.
+ */
+public class Chip implements LayoutElement {
+    /**
+     * Tool tag for Metadata in Modifiers, so we know that Box is actually a Chip with only text.
+     */
+    static final String METADATA_TAG_TEXT = "TXTCHP";
+    /** Tool tag for Metadata in Modifiers, so we know that Box is actually a Chip with icon. */
+    static final String METADATA_TAG_ICON = "ICNCHP";
+    /**
+     * Tool tag for Metadata in Modifiers, so we know that Box is actually a Chip with custom
+     * content.
+     */
+    static final String METADATA_TAG_CUSTOM_CONTENT = "CSTCHP";
+
+    @NonNull private final Box mElement;
+
+    Chip(@NonNull Box element) {
+        mElement = element;
+    }
+    /** Builder class for {@link androidx.wear.protolayout.material.Chip}. */
+    public static final class Builder implements LayoutElement.Builder {
+        private static final int NOT_SET = 0;
+        private static final int TEXT = 1;
+        private static final int ICON = 2;
+        private static final int CUSTOM_CONTENT = 3;
+
+        @RestrictTo(Scope.LIBRARY)
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef({NOT_SET, TEXT, ICON, CUSTOM_CONTENT})
+        @interface ChipType {}
+
+        @NonNull private final Context mContext;
+        @Nullable private LayoutElement mCustomContent;
+        @Nullable private String mImageResourceId = null;
+        @Nullable private String mPrimaryLabel = null;
+        @Nullable private String mSecondaryLabel = null;
+        @NonNull private final Clickable mClickable;
+        @NonNull private CharSequence mContentDescription = "";
+        @NonNull private ContainerDimension mWidth;
+        @NonNull private DpProp mHeight = DEFAULT_HEIGHT;
+        @NonNull private ChipColors mChipColors = PRIMARY_COLORS;
+        @HorizontalAlignment private int mHorizontalAlign = HORIZONTAL_ALIGN_UNDEFINED;
+        @TypographyName private int mPrimaryLabelTypography;
+        @NonNull private DpProp mHorizontalPadding = HORIZONTAL_PADDING;
+        private boolean mIsScalable = true;
+        private int mMaxLines = 0; // 0 indicates that is not set.
+        @NonNull private String mMetadataTag = "";
+
+        @NonNull static final Map<Integer, String> TYPE_TO_TAG = new HashMap<>();
+
+        static {
+            TYPE_TO_TAG.put(ICON, METADATA_TAG_ICON);
+            TYPE_TO_TAG.put(TEXT, METADATA_TAG_TEXT);
+            TYPE_TO_TAG.put(CUSTOM_CONTENT, METADATA_TAG_CUSTOM_CONTENT);
+        }
+
+        /**
+         * Creates a builder for the {@link Chip} with associated action. It is required to add
+         * content later with setters.
+         *
+         * @param context The application's context.
+         * @param clickable Associated {@link Clickable} for click events. When the Chip is clicked
+         *     it will fire the associated action.
+         * @param deviceParameters The device parameters used to derive defaults for this Chip.
+         */
+        public Builder(
+                @NonNull Context context,
+                @NonNull Clickable clickable,
+                @NonNull DeviceParameters deviceParameters) {
+            mContext = context;
+            mClickable = clickable;
+            mWidth =
+                    dp(
+                            (100 - 2 * DEFAULT_MARGIN_PERCENT)
+                                    * deviceParameters.getScreenWidthDp()
+                                    / 100);
+            mPrimaryLabelTypography = Typography.TYPOGRAPHY_BUTTON;
+        }
+
+        /**
+         * Sets the width of {@link Chip}. If not set, default value will be set to fill the screen.
+         */
+        @NonNull
+        public Builder setWidth(@NonNull ContainerDimension width) {
+            mWidth = width;
+            return this;
+        }
+
+        /**
+         * Sets the width of {@link TitleChip}. If not set, default value will be set to fill the
+         * screen.
+         */
+        @NonNull
+        public Builder setWidth(@Dimension(unit = DP) float width) {
+            mWidth = dp(width);
+            return this;
+        }
+
+        /**
+         * Sets the custom content for the {@link Chip}. Any previously added content will be
+         * overridden.
+         */
+        @NonNull
+        public Builder setCustomContent(@NonNull LayoutElement content) {
+            this.mCustomContent = content;
+            this.mPrimaryLabel = "";
+            this.mSecondaryLabel = "";
+            this.mImageResourceId = "";
+            return this;
+        }
+
+        /**
+         * Sets the content description for the {@link Chip}. It is highly recommended to provide
+         * this for chip containing icon.
+         */
+        @NonNull
+        public Builder setContentDescription(@NonNull CharSequence contentDescription) {
+            this.mContentDescription = contentDescription;
+            return this;
+        }
+
+        /**
+         * Sets the primary label for the {@link Chip}. Any previously added custom content will be
+         * overridden. Primary label can be on 1 or 2 lines, depending on the length and existence
+         * of secondary label.
+         */
+        @NonNull
+        public Builder setPrimaryLabelContent(@NonNull String primaryLabel) {
+            this.mPrimaryLabel = primaryLabel;
+            this.mCustomContent = null;
+            return this;
+        }
+
+        /**
+         * Used for creating CompactChip and TitleChip.
+         *
+         * <p>Sets the font for the primary label and should only be used internally.
+         */
+        @NonNull
+        Builder setPrimaryLabelTypography(@TypographyName int typography) {
+            this.mPrimaryLabelTypography = typography;
+            return this;
+        }
+
+        /**
+         * Used for creating CompactChip and TitleChip.
+         *
+         * <p>Sets whether the font for the primary label is scalable.
+         */
+        @NonNull
+        Builder setIsPrimaryLabelScalable(boolean isScalable) {
+            this.mIsScalable = isScalable;
+            return this;
+        }
+
+        /**
+         * Sets the secondary label for the {@link Chip}. Any previously added custom content will
+         * be overridden. If secondary label is set, primary label must be set too with {@link
+         * #setPrimaryLabelContent}.
+         */
+        @NonNull
+        public Builder setSecondaryLabelContent(@NonNull String secondaryLabel) {
+            this.mSecondaryLabel = secondaryLabel;
+            this.mCustomContent = null;
+            return this;
+        }
+
+        /**
+         * Sets the icon for the {@link Chip}. 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.
+         * If icon is set, primary label must be set too with {@link #setPrimaryLabelContent}.
+         */
+        @NonNull
+        public Builder setIconContent(@NonNull String imageResourceId) {
+            this.mImageResourceId = imageResourceId;
+            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
+         * ChipColors#getIconColor()} will be used as color for the icon itself. If not set, {@link
+         * ChipDefaults#PRIMARY_COLORS} will be used.
+         */
+        @NonNull
+        public Builder setChipColors(@NonNull ChipColors chipColors) {
+            mChipColors = chipColors;
+            return this;
+        }
+
+        /**
+         * Sets the horizontal alignment in the chip. It is strongly recommended that the content of
+         * the chip is start-aligned if there is more than primary text in it. By default, {@link
+         * HorizontalAlignment#HORIZONTAL_ALIGN_CENTER} will be used when only a primary label is
+         * present. Otherwise {@link HorizontalAlignment#HORIZONTAL_ALIGN_START} will be used.
+         */
+        @NonNull
+        public Builder setHorizontalAlignment(@HorizontalAlignment int horizontalAlignment) {
+            mHorizontalAlign = horizontalAlignment;
+            return this;
+        }
+
+        /** Used for creating CompactChip and TitleChip. */
+        @NonNull
+        Builder setHorizontalPadding(@NonNull DpProp horizontalPadding) {
+            this.mHorizontalPadding = horizontalPadding;
+            return this;
+        }
+
+        /** Used for creating CompactChip and TitleChip. */
+        @NonNull
+        Builder setHeight(@NonNull DpProp height) {
+            this.mHeight = height;
+            return this;
+        }
+
+        /** Used for creating CompactChip and TitleChip. */
+        @NonNull
+        Builder setMaxLines(int maxLines) {
+            this.mMaxLines = maxLines;
+            return this;
+        }
+
+        /** Used for setting the correct tag in CompactChip and TitleChip. */
+        @NonNull
+        Builder setMetadataTag(@NonNull String metadataTag) {
+            this.mMetadataTag = metadataTag;
+            return this;
+        }
+
+        /** Constructs and returns {@link Chip} with the provided content and look. */
+        @NonNull
+        @Override
+        public Chip build() {
+            Modifiers.Builder modifiers =
+                    new Modifiers.Builder()
+                            .setClickable(mClickable)
+                            .setPadding(
+                                    new Padding.Builder()
+                                            .setStart(mHorizontalPadding)
+                                            .setEnd(mHorizontalPadding)
+                                            .build())
+                            .setBackground(
+                                    new Background.Builder()
+                                            .setColor(mChipColors.getBackgroundColor())
+                                            .setCorner(
+                                                    new Corner.Builder()
+                                                            .setRadius(radiusOf(mHeight))
+                                                            .build())
+                                            .build())
+                            .setMetadata(
+                                    new ElementMetadata.Builder()
+                                            .setTagData(getTagBytes(getCorrectMetadataTag()))
+                                            .build())
+                            .setSemantics(
+                                    new Semantics.Builder()
+                                            .setContentDescription(getCorrectContentDescription())
+                                            .build());
+
+            Box.Builder element =
+                    new Box.Builder()
+                            .setWidth(mWidth)
+                            .setHeight(mHeight)
+                            .setHorizontalAlignment(getCorrectHorizontalAlignment())
+                            .addContent(getCorrectContent())
+                            .setModifiers(modifiers.build());
+
+            return new Chip(element.build());
+        }
+
+        @NonNull
+        private String getCorrectContentDescription() {
+            if (mContentDescription.length() == 0) {
+                mContentDescription = "";
+                if (mPrimaryLabel != null) {
+                    mContentDescription += mPrimaryLabel;
+                }
+                if (mSecondaryLabel != null) {
+                    mContentDescription += "\n" + mSecondaryLabel;
+                }
+            }
+            return mContentDescription.toString();
+        }
+
+        @HorizontalAlignment
+        private int getCorrectHorizontalAlignment() {
+            if (mHorizontalAlign != HORIZONTAL_ALIGN_UNDEFINED) {
+                return mHorizontalAlign;
+            }
+            if (mPrimaryLabel != null && mSecondaryLabel == null && mImageResourceId == null) {
+                return HORIZONTAL_ALIGN_CENTER;
+            } else {
+                return HORIZONTAL_ALIGN_START;
+            }
+        }
+
+        private String getCorrectMetadataTag() {
+            if (!mMetadataTag.isEmpty()) {
+                return mMetadataTag;
+            }
+            if (mCustomContent != null) {
+                return METADATA_TAG_CUSTOM_CONTENT;
+            }
+            if (mImageResourceId != null) {
+                return METADATA_TAG_ICON;
+            }
+            return METADATA_TAG_TEXT;
+        }
+
+        @NonNull
+        private LayoutElement getCorrectContent() {
+            if (mCustomContent != null) {
+                return mCustomContent;
+            }
+
+            Text mainTextElement =
+                    new Text.Builder(mContext, checkNotNull(mPrimaryLabel))
+                            .setTypography(mPrimaryLabelTypography)
+                            .setColor(mChipColors.getContentColor())
+                            .setMaxLines(getCorrectMaxLines())
+                            .setOverflow(LayoutElementBuilders.TEXT_OVERFLOW_ELLIPSIZE_END)
+                            .setMultilineAlignment(LayoutElementBuilders.TEXT_ALIGN_START)
+                            .setIsScalable(mIsScalable)
+                            .build();
+
+            // Placeholder for text.
+            Column.Builder column =
+                    new Column.Builder()
+                            .setHorizontalAlignment(HORIZONTAL_ALIGN_START)
+                            .addContent(putLayoutInBox(mainTextElement).build());
+
+            if (mSecondaryLabel != null) {
+                Text labelTextElement =
+                        new Text.Builder(mContext, mSecondaryLabel)
+                                .setTypography(Typography.TYPOGRAPHY_CAPTION2)
+                                .setColor(mChipColors.getSecondaryContentColor())
+                                .setMaxLines(1)
+                                .setOverflow(LayoutElementBuilders.TEXT_OVERFLOW_ELLIPSIZE_END)
+                                .setMultilineAlignment(LayoutElementBuilders.TEXT_ALIGN_START)
+                                .build();
+                column.addContent(putLayoutInBox(labelTextElement).build());
+            }
+
+            Box texts = putLayoutInBox(column.build()).build();
+            if (mImageResourceId == null) {
+                return texts;
+            } else {
+                return new Row.Builder()
+                        .addContent(
+                                new Image.Builder()
+                                        .setResourceId(mImageResourceId)
+                                        .setWidth(ICON_SIZE)
+                                        .setHeight(ICON_SIZE)
+                                        .setColorFilter(
+                                                new ColorFilter.Builder()
+                                                        .setTint(mChipColors.getIconColor())
+                                                        .build())
+                                        .build())
+                        .addContent(
+                                new Spacer.Builder()
+                                        .setHeight(mHeight)
+                                        .setWidth(ICON_SPACER_WIDTH)
+                                        .build())
+                        .addContent(texts)
+                        .setVerticalAlignment(LayoutElementBuilders.VERTICAL_ALIGN_CENTER)
+                        .build();
+            }
+        }
+
+        private int getCorrectMaxLines() {
+            if (mMaxLines > 0) {
+                return mMaxLines;
+            }
+            return mSecondaryLabel != null ? 1 : 2;
+        }
+
+        private Box.Builder putLayoutInBox(@NonNull LayoutElement element) {
+            // Wrapped and centered content are default.
+            return new Box.Builder().addContent(element);
+        }
+    }
+
+    /** Returns height of this Chip. */
+    @NonNull
+    public ContainerDimension getHeight() {
+        return checkNotNull(mElement.getHeight());
+    }
+
+    /** Returns width of this Chip. */
+    @NonNull
+    public ContainerDimension getWidth() {
+        return checkNotNull(mElement.getWidth());
+    }
+
+    /** Returns click event action associated with this Chip. */
+    @NonNull
+    public Clickable getClickable() {
+        return checkNotNull(checkNotNull(mElement.getModifiers()).getClickable());
+    }
+
+    /** Returns background color of this Chip. */
+    @NonNull
+    private ColorProp getBackgroundColor() {
+        return checkNotNull(
+                checkNotNull(checkNotNull(mElement.getModifiers()).getBackground()).getColor());
+    }
+
+    /** Returns chip colors of this Chip. */
+    @NonNull
+    public ChipColors getChipColors() {
+        ColorProp backgroundColor = getBackgroundColor();
+        ColorProp contentColor = null;
+        ColorProp secondaryContentColor = null;
+        ColorProp iconTintColor = null;
+
+        if (!getMetadataTag().equals(METADATA_TAG_CUSTOM_CONTENT)) {
+            if (getMetadataTag().equals(METADATA_TAG_ICON)) {
+                Image icon = checkNotNull(getIconContentObject());
+                iconTintColor = checkNotNull(checkNotNull(icon.getColorFilter()).getTint());
+            }
+
+            contentColor = checkNotNull(getPrimaryLabelContentObject()).getColor();
+            Text label = getSecondaryLabelContentObject();
+            if (label != null) {
+                secondaryContentColor = label.getColor();
+            }
+        }
+
+        // Populate other colors if they are not found.
+        if (contentColor == null) {
+            contentColor = new ColorProp.Builder(0).build();
+        }
+        if (secondaryContentColor == null) {
+            secondaryContentColor = contentColor;
+        }
+        if (iconTintColor == null) {
+            iconTintColor = contentColor;
+        }
+
+        return new ChipColors(backgroundColor, iconTintColor, contentColor, secondaryContentColor);
+    }
+
+    /** Returns content description of this Chip. */
+    @Nullable
+    public CharSequence getContentDescription() {
+        Semantics semantics = checkNotNull(mElement.getModifiers()).getSemantics();
+        if (semantics == null) {
+            return null;
+        }
+        return semantics.getContentDescription();
+    }
+
+    /** Returns custom content from this Chip if it has been added. Otherwise, it returns null. */
+    @Nullable
+    public LayoutElement getCustomContent() {
+        if (getMetadataTag().equals(METADATA_TAG_CUSTOM_CONTENT)) {
+            return checkNotNull(checkNotNull(mElement.getContents()).get(0));
+        }
+        return null;
+    }
+
+    /** Returns primary label from this Chip if it has been added. Otherwise, it returns null. */
+    @Nullable
+    public String getPrimaryLabelContent() {
+        Text primaryLabel = getPrimaryLabelContentObject();
+        return primaryLabel != null ? primaryLabel.getText() : null;
+    }
+
+    /** Returns secondary label from this Chip if it has been added. Otherwise, it returns null. */
+    @Nullable
+    public String getSecondaryLabelContent() {
+        Text label = getSecondaryLabelContentObject();
+        return label != null ? label.getText() : null;
+    }
+
+    /** Returns icon id from this Chip if it has been added. Otherwise, it returns null. */
+    @Nullable
+    public String getIconContent() {
+        Image icon = getIconContentObject();
+        return icon != null ? checkNotNull(icon.getResourceId()).getValue() : null;
+    }
+
+    @Nullable
+    private Text getPrimaryLabelContentObject() {
+        return getPrimaryOrSecondaryLabelContent(0);
+    }
+
+    @Nullable
+    private Text getSecondaryLabelContentObject() {
+        return getPrimaryOrSecondaryLabelContent(1);
+    }
+
+    @Nullable
+    private Image getIconContentObject() {
+        if (!getMetadataTag().equals(METADATA_TAG_ICON)) {
+            return null;
+        }
+        return ((Image) ((Row) mElement.getContents().get(0)).getContents().get(0));
+    }
+
+    @Nullable
+    private Text getPrimaryOrSecondaryLabelContent(int index) {
+        String metadataTag = getMetadataTag();
+        if (metadataTag.equals(METADATA_TAG_CUSTOM_CONTENT)) {
+            return null;
+        }
+        // In any other case, text (either primary or primary + label) must be present.
+        Column content;
+        if (metadataTag.equals(METADATA_TAG_ICON)) {
+            content =
+                    (Column)
+                            ((Box) ((Row) mElement.getContents().get(0)).getContents().get(2))
+                                    .getContents()
+                                    .get(0);
+        } else {
+            content = (Column) ((Box) mElement.getContents().get(0)).getContents().get(0);
+        }
+
+        // We need to check this as this can be the case when we called for label, which doesn't
+        // exist.
+        return index < content.getContents().size()
+                ? Text.fromLayoutElement(
+                        ((Box) content.getContents().get(index)).getContents().get(0))
+                : null;
+    }
+
+    /** Returns the horizontal alignment of the content in this Chip. */
+    @HorizontalAlignment
+    public int getHorizontalAlignment() {
+        return checkNotNull(mElement.getHorizontalAlignment()).getValue();
+    }
+
+    /** Returns metadata tag set to this Chip. */
+    @NonNull
+    String getMetadataTag() {
+        return getMetadataTagName(
+                checkNotNull(checkNotNull(mElement.getModifiers()).getMetadata()));
+    }
+
+    /**
+     * Returns Chip object from the given LayoutElement (e.g. one retrieved from a container's
+     * content with {@code container.getContents().get(index)}) if that element can be converted to
+     * Chip. Otherwise, it will return null.
+     */
+    @Nullable
+    public static Chip fromLayoutElement(@NonNull LayoutElement element) {
+        if (element instanceof Chip) {
+            return (Chip) element;
+        }
+        if (!(element instanceof Box)) {
+            return null;
+        }
+        Box boxElement = (Box) element;
+        if (!checkTag(boxElement.getModifiers(), Builder.TYPE_TO_TAG.values())) {
+            return null;
+        }
+        // Now we are sure that this element is a Chip.
+        return new Chip(boxElement);
+    }
+
+    @NonNull
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public LayoutElementProto.LayoutElement toLayoutElementProto() {
+        return mElement.toLayoutElementProto();
+    }
+
+    @Nullable
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public Fingerprint getFingerprint() {
+        return mElement.getFingerprint();
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/ChipColors.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/ChipColors.java
new file mode 100644
index 0000000..a24e6c4
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/ChipColors.java
@@ -0,0 +1,150 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.wear.protolayout.ColorBuilders.argb;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+import androidx.wear.protolayout.ColorBuilders.ColorProp;
+
+/**
+ * Represents the background and content colors used in a chip Tiles component.
+ *
+ * <p>See {@link ChipDefaults#PRIMARY_COLORS} for the default colors used in a primary styled {@link
+ * Chip}. See {@link ChipDefaults#SECONDARY_COLORS} for the default colors used in a secondary
+ * styled {@link Chip}.
+ */
+public class ChipColors {
+    @NonNull private final ColorProp mBackgroundColor;
+    @NonNull private final ColorProp mIconColor;
+    @NonNull private final ColorProp mContentColor;
+    @NonNull private final ColorProp mSecondaryContentColor;
+
+    /**
+     * Constructor for the {@link ChipColors} object.
+     *
+     * @param backgroundColor The background color to be used for a chip Tiles component. Should be
+     *     in ARGB format.
+     * @param iconColor The color to be used for an icon in a chip Tiles component. Should be in
+     *     ARGB format.
+     * @param contentColor The text color to be used for a main text in a chip Tiles component.
+     *     Should be in ARGB format.
+     * @param secondaryContentColor The text color to be used for a label text in a chip Tiles
+     *     component. Should be in ARGB format.
+     */
+    public ChipColors(
+            @ColorInt int backgroundColor,
+            @ColorInt int iconColor,
+            @ColorInt int contentColor,
+            @ColorInt int secondaryContentColor) {
+        mBackgroundColor = argb(backgroundColor);
+        mIconColor = argb(iconColor);
+        mContentColor = argb(contentColor);
+        mSecondaryContentColor = argb(secondaryContentColor);
+    }
+
+    /**
+     * Constructor for the {@link ChipColors} object.
+     *
+     * @param backgroundColor The background color to be used for a chip Tiles component. Should be
+     *     in ARGB format.
+     * @param contentColor The content color to be used for all items inside a chip Tiles component.
+     *     Should be in ARGB format.
+     */
+    public ChipColors(@ColorInt int backgroundColor, @ColorInt int contentColor) {
+        mBackgroundColor = argb(backgroundColor);
+        mIconColor = argb(contentColor);
+        mContentColor = argb(contentColor);
+        mSecondaryContentColor = argb(contentColor);
+    }
+
+    /**
+     * Constructor for the {@link ChipColors} object.
+     *
+     * @param backgroundColor The background color to be used for a chip Tiles component.
+     * @param iconColor The color to be used for an icon in a chip Tiles component.
+     * @param contentColor The text color to be used for a main text in a chip Tiles component.
+     * @param secondaryContentColor The text color to be used for a label text in a chip Tiles
+     *     component.
+     */
+    public ChipColors(
+            @NonNull ColorProp backgroundColor,
+            @NonNull ColorProp iconColor,
+            @NonNull ColorProp contentColor,
+            @NonNull ColorProp secondaryContentColor) {
+        mBackgroundColor = backgroundColor;
+        mIconColor = iconColor;
+        mContentColor = contentColor;
+        mSecondaryContentColor = secondaryContentColor;
+    }
+
+    /**
+     * Constructor for the {@link ChipColors} object.
+     *
+     * @param backgroundColor The background color to be used for a chip Tiles component.
+     * @param contentColor The content color to be used for all items inside a chip Tiles component.
+     */
+    public ChipColors(@NonNull ColorProp backgroundColor, @NonNull ColorProp contentColor) {
+        mBackgroundColor = backgroundColor;
+        mIconColor = contentColor;
+        mContentColor = contentColor;
+        mSecondaryContentColor = contentColor;
+    }
+
+    /**
+     * Returns a {@link ChipColors} object, using the current Primary colors from the given {@link
+     * Colors}.
+     */
+    @NonNull
+    public static ChipColors primaryChipColors(@NonNull Colors colors) {
+        return new ChipColors(colors.getPrimary(), colors.getOnPrimary());
+    }
+
+    /**
+     * Returns a {@link ChipColors} object, using the current Surface colors from the given {@link
+     * Colors}.
+     */
+    @NonNull
+    public static ChipColors secondaryChipColors(@NonNull Colors colors) {
+        return new ChipColors(colors.getSurface(), colors.getOnSurface());
+    }
+
+    /** The background color to be used on a chip Tiles components. */
+    @NonNull
+    public ColorProp getBackgroundColor() {
+        return mBackgroundColor;
+    }
+
+    /** The icon color to be used on a chip Tiles components. */
+    @NonNull
+    public ColorProp getIconColor() {
+        return mIconColor;
+    }
+
+    /** The main text color to be used on a chip Tiles components. */
+    @NonNull
+    public ColorProp getContentColor() {
+        return mContentColor;
+    }
+
+    /** The label text color to be used on a chip Tiles components. */
+    @NonNull
+    public ColorProp getSecondaryContentColor() {
+        return mSecondaryContentColor;
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/ChipDefaults.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/ChipDefaults.java
new file mode 100644
index 0000000..8059369
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/ChipDefaults.java
@@ -0,0 +1,137 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.wear.protolayout.DimensionBuilders.dp;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.DimensionBuilders.DpProp;
+
+/** Contains the default values used by chip Tiles components. */
+public class ChipDefaults {
+    private ChipDefaults() {}
+
+    /**
+     * The default height for standard {@link Chip}
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public static final DpProp DEFAULT_HEIGHT = dp(52);
+
+    /**
+     * The default height for standard {@link CompactChip}
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public static final DpProp COMPACT_HEIGHT = dp(32);
+
+    /**
+     * The default height of tappable area for standard {@link CompactChip}
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public static final DpProp COMPACT_HEIGHT_TAPPABLE = dp(48);
+
+    /**
+     * The default height for standard {@link TitleChip}
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public static final DpProp TITLE_HEIGHT = dp(60);
+
+    /**
+     * The recommended horizontal margin used for width for standard {@link Chip}
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public static final float DEFAULT_MARGIN_PERCENT = 5.2f;
+
+    /**
+     * The recommended horizontal padding for standard {@link Chip}
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public static final DpProp HORIZONTAL_PADDING = dp(14);
+
+    /**
+     * The recommended horizontal padding for standard {@link CompactChip}
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public static final DpProp COMPACT_HORIZONTAL_PADDING = dp(12);
+
+    /**
+     * The recommended horizontal padding for standard {@link TitleChip}
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public static final DpProp TITLE_HORIZONTAL_PADDING = dp(16);
+
+    /**
+     * The recommended vertical space between icon and text in standard {@link Chip}
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public static final DpProp ICON_SPACER_WIDTH = dp(6);
+
+    /**
+     * The icon size used in standard {@link Chip}
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public static final DpProp ICON_SIZE = dp(24);
+
+    /** The recommended colors for a primary {@link Chip}. */
+    @NonNull
+    public static final ChipColors PRIMARY_COLORS = ChipColors.primaryChipColors(Colors.DEFAULT);
+
+    /** The recommended colors for a secondary {@link Chip}. */
+    @NonNull
+    public static final ChipColors SECONDARY_COLORS =
+            ChipColors.secondaryChipColors(Colors.DEFAULT);
+
+    /** The recommended colors for a primary {@link CompactChip}. */
+    @NonNull
+    public static final ChipColors COMPACT_PRIMARY_COLORS =
+            ChipColors.primaryChipColors(Colors.DEFAULT);
+
+    /** The recommended colors for a secondary {@link CompactChip}. */
+    @NonNull
+    public static final ChipColors COMPACT_SECONDARY_COLORS =
+            ChipColors.secondaryChipColors(Colors.DEFAULT);
+
+    /** The recommended colors for a primary {@link TitleChip}. */
+    @NonNull
+    public static final ChipColors TITLE_PRIMARY_COLORS =
+            ChipColors.primaryChipColors(Colors.DEFAULT);
+
+    /** The recommended colors for a secondary {@link TitleChip}. */
+    @NonNull
+    public static final ChipColors TITLE_SECONDARY_COLORS =
+            ChipColors.secondaryChipColors(Colors.DEFAULT);
+}
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/CircularProgressIndicator.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/CircularProgressIndicator.java
new file mode 100644
index 0000000..820be12
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/CircularProgressIndicator.java
@@ -0,0 +1,354 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.annotation.Dimension.DP;
+import static androidx.wear.protolayout.DimensionBuilders.degrees;
+import static androidx.wear.protolayout.DimensionBuilders.dp;
+import static androidx.wear.protolayout.material.Helper.checkNotNull;
+import static androidx.wear.protolayout.material.Helper.checkTag;
+import static androidx.wear.protolayout.material.Helper.getMetadataTagName;
+import static androidx.wear.protolayout.material.Helper.getTagBytes;
+import static androidx.wear.protolayout.material.ProgressIndicatorDefaults.DEFAULT_COLORS;
+import static androidx.wear.protolayout.material.ProgressIndicatorDefaults.DEFAULT_END_ANGLE;
+import static androidx.wear.protolayout.material.ProgressIndicatorDefaults.DEFAULT_PADDING;
+import static androidx.wear.protolayout.material.ProgressIndicatorDefaults.DEFAULT_START_ANGLE;
+import static androidx.wear.protolayout.material.ProgressIndicatorDefaults.DEFAULT_STROKE_WIDTH;
+
+import androidx.annotation.Dimension;
+import androidx.annotation.FloatRange;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.DimensionBuilders.DegreesProp;
+import androidx.wear.protolayout.DimensionBuilders.DpProp;
+import androidx.wear.protolayout.LayoutElementBuilders;
+import androidx.wear.protolayout.LayoutElementBuilders.Arc;
+import androidx.wear.protolayout.LayoutElementBuilders.ArcLine;
+import androidx.wear.protolayout.LayoutElementBuilders.ArcSpacer;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+import androidx.wear.protolayout.ModifiersBuilders;
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+import androidx.wear.protolayout.ModifiersBuilders.Padding;
+import androidx.wear.protolayout.ModifiersBuilders.Semantics;
+import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.proto.LayoutElementProto;
+
+/**
+ * Tiles component {@link CircularProgressIndicator} that represents circular progress indicator
+ * which supports a gap in the circular track between startAngle and endAngle. [Progress Indicator
+ * doc] (https://developer.android.com/training/wearables/components/progress-indicator)
+ *
+ * <p>The CircularProgressIndicator is a colored arc around the edge of the screen with the given
+ * start and end angles, which can describe a full or partial circle. Behind it is an arc with
+ * optional gap representing full progress. The recommended sizes are defined in {@link
+ * ProgressIndicatorDefaults}. Unless specified, the CircularProgressIndicator will have the full
+ * length.
+ *
+ * <p>The recommended set of {@link ProgressIndicatorColors} can be obtained from {@link
+ * ProgressIndicatorDefaults}, e.g. {@link ProgressIndicatorDefaults#DEFAULT_COLORS} to get a
+ * default color scheme for a {@link CircularProgressIndicator}.
+ *
+ * <p>When accessing the contents of a container for testing, note that this element can't be simply
+ * casted back to the original type, i.e.:
+ *
+ * <pre>{@code
+ * CircularProgressIndicator cpi = new CircularProgressIndicator...
+ * Box box = new Box.Builder().addContent(cpi).build();
+ *
+ * CircularProgressIndicator myCpi = (CircularProgressIndicator) box.getContents().get(0);
+ * }</pre>
+ *
+ * will fail.
+ *
+ * <p>To be able to get {@link CircularProgressIndicator} object from any layout element, {@link
+ * #fromLayoutElement} method should be used, i.e.:
+ *
+ * <pre>{@code
+ * CircularProgressIndicator myCpi =
+ *   CircularProgressIndicator.fromLayoutElement(box.getContents().get(0));
+ * }</pre>
+ */
+public class CircularProgressIndicator implements LayoutElement {
+    /**
+     * Tool tag for Metadata in Modifiers, so we know that Arc is actually a
+     * CircularProgressIndicator.
+     */
+    static final String METADATA_TAG = "CPI";
+
+    @NonNull private final Arc mElement;
+    @NonNull private final ArcLine mProgress;
+    @NonNull private final ArcLine mBackground;
+
+    CircularProgressIndicator(@NonNull Arc element) {
+        this.mElement = element;
+        this.mBackground = (ArcLine) element.getContents().get(0);
+        this.mProgress = (ArcLine) element.getContents().get(2);
+    }
+
+    /** Builder class for {@link CircularProgressIndicator} */
+    public static final class Builder implements LayoutElement.Builder {
+        @NonNull private ProgressIndicatorColors mCircularProgressIndicatorColors = DEFAULT_COLORS;
+        @NonNull private DpProp mStrokeWidth = DEFAULT_STROKE_WIDTH;
+        @NonNull private CharSequence mContentDescription = "";
+        @NonNull private DegreesProp mStartAngle = degrees(DEFAULT_START_ANGLE);
+        @NonNull private DegreesProp mEndAngle = degrees(DEFAULT_END_ANGLE);
+
+        @FloatRange(from = 0, to = 1)
+        private float mProgress = 0;
+
+        /** Creates a builder for the {@link CircularProgressIndicator}. */
+        public Builder() {}
+
+        /**
+         * Sets the progress of the {@link CircularProgressIndicator}. Progress should be percentage
+         * from 0 to 1. Progress will be colored in {@link ProgressIndicatorColors#getTrackColor}.
+         * If not set, 0 will be used.
+         */
+        @NonNull
+        public Builder setProgress(@FloatRange(from = 0, to = 1) float progressPercentage) {
+            this.mProgress = progressPercentage;
+            return this;
+        }
+
+        /**
+         * Sets the start angle of the {@link CircularProgressIndicator}'s background arc, where
+         * angle 0 is 12 o'clock. Start angle doesn't need to be within 0-360 range. I.e. -90 is to
+         * start arc from the 9 o'clock. If not set 0 will be used and the indicator will have full
+         * length.
+         */
+        @NonNull
+        public Builder setStartAngle(float startAngle) {
+            this.mStartAngle = degrees(startAngle);
+            return this;
+        }
+
+        /**
+         * Sets the end angle of the {@link CircularProgressIndicator}'s background arc, where angle
+         * 0 is 12 o'clock. End angle doesn't need to be within 0-360 range, but it must be larger
+         * than start angle. If not set 360 will be used and the indicator will have full length.
+         */
+        @NonNull
+        public Builder setEndAngle(float endAngle) {
+            this.mEndAngle = degrees(endAngle);
+            return this;
+        }
+
+        /**
+         * Sets the content description of the {@link CircularProgressIndicator} to be used for
+         * accessibility support.
+         */
+        @NonNull
+        public Builder setContentDescription(@NonNull CharSequence contentDescription) {
+            this.mContentDescription = contentDescription;
+            return this;
+        }
+
+        /**
+         * Sets the colors for the {@link CircularProgressIndicator}. If set, {@link
+         * ProgressIndicatorColors#getIndicatorColor()} will be used for a progress that has been
+         * made, while {@link ProgressIndicatorColors#getTrackColor()} will be used for a background
+         * full size arc. If not set, {@link ProgressIndicatorDefaults#DEFAULT_COLORS} will be used.
+         */
+        @NonNull
+        public Builder setCircularProgressIndicatorColors(
+                @NonNull ProgressIndicatorColors circularProgressIndicatorColors) {
+            this.mCircularProgressIndicatorColors = circularProgressIndicatorColors;
+            return this;
+        }
+
+        /**
+         * Sets the stroke width of the {@link CircularProgressIndicator}. Strongly recommended
+         * value is {@link ProgressIndicatorDefaults#DEFAULT_STROKE_WIDTH}.
+         */
+        @NonNull
+        public Builder setStrokeWidth(@NonNull DpProp strokeWidth) {
+            this.mStrokeWidth = strokeWidth;
+            return this;
+        }
+
+        /**
+         * Sets the stroke width of the {@link CircularProgressIndicator}. Strongly recommended
+         * value is {@link ProgressIndicatorDefaults#DEFAULT_STROKE_WIDTH}.
+         */
+        @NonNull
+        public Builder setStrokeWidth(@Dimension(unit = DP) float strokeWidth) {
+            this.mStrokeWidth = dp(strokeWidth);
+            return this;
+        }
+
+        /**
+         * Constructs and returns {@link CircularProgressIndicator} with the provided field and
+         * look.
+         */
+        @NonNull
+        @Override
+        public CircularProgressIndicator build() {
+            checkAngles();
+
+            DegreesProp length = getLength();
+            Modifiers.Builder modifiers =
+                    new Modifiers.Builder()
+                            .setPadding(new Padding.Builder().setAll(DEFAULT_PADDING).build())
+                            .setMetadata(
+                                    new ElementMetadata.Builder()
+                                            .setTagData(getTagBytes(METADATA_TAG))
+                                            .build());
+
+            if (mContentDescription.length() > 0) {
+                modifiers.setSemantics(
+                        new ModifiersBuilders.Semantics.Builder()
+                                .setContentDescription(mContentDescription.toString())
+                                .build());
+            }
+
+            Arc.Builder element =
+                    new Arc.Builder()
+                            .setAnchorType(LayoutElementBuilders.ARC_ANCHOR_START)
+                            .setAnchorAngle(mStartAngle)
+                            .setModifiers(modifiers.build())
+                            .addContent(
+                                    new ArcLine.Builder()
+                                            .setColor(
+                                                    mCircularProgressIndicatorColors
+                                                            .getTrackColor())
+                                            .setThickness(mStrokeWidth)
+                                            .setLength(length)
+                                            .build())
+                            .addContent(
+                                    // Fill in the space to make a full circle, so that progress is
+                                    // correctly aligned.
+                                    new ArcSpacer.Builder()
+                                            .setLength(degrees(360 - length.getValue()))
+                                            .build())
+                            .addContent(
+                                    new ArcLine.Builder()
+                                            .setColor(
+                                                    mCircularProgressIndicatorColors
+                                                            .getIndicatorColor())
+                                            .setThickness(mStrokeWidth)
+                                            .setLength(degrees(mProgress * length.getValue()))
+                                            .build());
+            return new CircularProgressIndicator(element.build());
+        }
+
+        private void checkAngles() {
+            if (mEndAngle.getValue() < mStartAngle.getValue()) {
+                throw new IllegalArgumentException("End angle must be bigger than start angle.");
+            }
+        }
+
+        @NonNull
+        private DegreesProp getLength() {
+            float startAngle = mStartAngle.getValue();
+            float endAngle = mEndAngle.getValue();
+            if (endAngle <= startAngle) {
+                endAngle += 360;
+            }
+            return degrees(endAngle - startAngle);
+        }
+    }
+
+    /** Returns angle representing progressed part of this CircularProgressIndicator. */
+    @NonNull
+    public DegreesProp getProgress() {
+        return checkNotNull(mProgress.getLength());
+    }
+
+    /** Returns stroke width of this CircularProgressIndicator. */
+    @NonNull
+    public DpProp getStrokeWidth() {
+        return checkNotNull(mProgress.getThickness());
+    }
+
+    /** Returns start angle of this CircularProgressIndicator. */
+    @NonNull
+    public DegreesProp getStartAngle() {
+        return checkNotNull(mElement.getAnchorAngle());
+    }
+
+    /** Returns start angle of this CircularProgressIndicator. */
+    @NonNull
+    public DegreesProp getEndAngle() {
+        float backArcLength = checkNotNull(mBackground.getLength()).getValue();
+        return degrees(getStartAngle().getValue() + backArcLength);
+    }
+
+    /** Returns main arc color of this CircularProgressIndicator. */
+    @NonNull
+    public ProgressIndicatorColors getCircularProgressIndicatorColors() {
+        return new ProgressIndicatorColors(
+                checkNotNull(mProgress.getColor()), checkNotNull(mBackground.getColor()));
+    }
+
+    /** Returns content description of this CircularProgressIndicator. */
+    @Nullable
+    public CharSequence getContentDescription() {
+        Semantics semantics = checkNotNull(mElement.getModifiers()).getSemantics();
+        if (semantics == null) {
+            return null;
+        }
+        return semantics.getContentDescription();
+    }
+
+    /**
+     * Returns metadata tag set to this CircularProgressIndicator, which should be {@link
+     * #METADATA_TAG}.
+     */
+    @NonNull
+    String getMetadataTag() {
+        return getMetadataTagName(
+                checkNotNull(checkNotNull(mElement.getModifiers()).getMetadata()));
+    }
+
+    /**
+     * Returns CircularProgressIndicator object from the given LayoutElement (e.g. one retrieved
+     * from a container's content with {@code container.getContents().get(index)}) if that element
+     * can be converted to CircularProgressIndicator. Otherwise, it will return null.
+     */
+    @Nullable
+    public static CircularProgressIndicator fromLayoutElement(@NonNull LayoutElement element) {
+        if (element instanceof CircularProgressIndicator) {
+            return (CircularProgressIndicator) element;
+        }
+        if (!(element instanceof Arc)) {
+            return null;
+        }
+        Arc arcElement = (Arc) element;
+        if (!checkTag(arcElement.getModifiers(), METADATA_TAG)) {
+            return null;
+        }
+        // Now we are sure that this element is a CircularProgressIndicator.
+        return new CircularProgressIndicator(arcElement);
+    }
+
+    @NonNull
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public LayoutElementProto.LayoutElement toLayoutElementProto() {
+        return mElement.toLayoutElementProto();
+    }
+
+    @Nullable
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public Fingerprint getFingerprint() {
+        return mElement.getFingerprint();
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Colors.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Colors.java
new file mode 100644
index 0000000..84026bd
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Colors.java
@@ -0,0 +1,119 @@
+/*
+ * 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.wear.protolayout.material;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+
+/**
+ * Represent the container for default color scheme in your Tile, that can be used to create color
+ * objects for all Material components.
+ *
+ * <p>See {@link #DEFAULT} for default color scheme.
+ */
+public class Colors {
+
+    /**
+     * The default color used for primary elements (i.e. background color).
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @ColorInt
+    public static final int PRIMARY = 0xFFAECBFA;
+
+    /**
+     * The default color used on primary elements (i.e. content color).
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @ColorInt
+    public static final int ON_PRIMARY = 0xFF303133;
+
+    /**
+     * The default color used for secondary elements (i.e. background color).
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @ColorInt
+    public static final int SURFACE = 0xFF303133;
+
+    /**
+     * The default color used on secondary elements (i.e. content color).
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @ColorInt
+    public static final int ON_SURFACE = 0xFFFFFFFF;
+
+    /** The default color scheme to be used in Tiles Material components. */
+    @NonNull
+    public static final Colors DEFAULT = new Colors(PRIMARY, ON_PRIMARY, SURFACE, ON_SURFACE);
+
+    private @ColorInt final int mPrimary;
+    private @ColorInt final int mOnPrimary;
+    private @ColorInt final int mSurface;
+    private @ColorInt final int mOnSurface;
+
+    /**
+     * Constructor for {@link Colors} object.
+     *
+     * @param primary The background color to be used for primary components. Should be in ARGB
+     *     format.
+     * @param onPrimary The content color or tint color to be used for primary components. Should be
+     *     in ARGB format.
+     * @param surface The background color to be used for secondary components. Should be in ARGB
+     *     format.
+     * @param onSurface The content color or tint color to be used for secondary components. Should
+     *     be in ARGB format.
+     */
+    public Colors(
+            @ColorInt int primary,
+            @ColorInt int onPrimary,
+            @ColorInt int surface,
+            @ColorInt int onSurface) {
+        this.mPrimary = primary;
+        this.mOnPrimary = onPrimary;
+        this.mSurface = surface;
+        this.mOnSurface = onSurface;
+    }
+
+    /** The primary color to be used on components. */
+    @ColorInt
+    public int getPrimary() {
+        return mPrimary;
+    }
+
+    /** The onPrimary color to be used on components. */
+    @ColorInt
+    public int getOnPrimary() {
+        return mOnPrimary;
+    }
+
+    /** The surface color to be used on components. */
+    @ColorInt
+    public int getSurface() {
+        return mSurface;
+    }
+
+    /** The onSurface color to be used on components. */
+    @ColorInt
+    public int getOnSurface() {
+        return mOnSurface;
+    }
+}
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
new file mode 100644
index 0000000..58bf3b8
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/CompactChip.java
@@ -0,0 +1,234 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.wear.protolayout.DimensionBuilders.wrap;
+import static androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER;
+import static androidx.wear.protolayout.material.ChipDefaults.COMPACT_HEIGHT;
+import static androidx.wear.protolayout.material.ChipDefaults.COMPACT_HEIGHT_TAPPABLE;
+import static androidx.wear.protolayout.material.ChipDefaults.COMPACT_HORIZONTAL_PADDING;
+import static androidx.wear.protolayout.material.ChipDefaults.COMPACT_PRIMARY_COLORS;
+import static androidx.wear.protolayout.material.Helper.checkNotNull;
+import static androidx.wear.protolayout.material.Helper.checkTag;
+import static androidx.wear.protolayout.material.Helper.getTagBytes;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.protolayout.LayoutElementBuilders;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+import androidx.wear.protolayout.ModifiersBuilders.Clickable;
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.proto.LayoutElementProto;
+
+/**
+ * Tiles component {@link CompactChip} that represents clickable object with the text.
+ *
+ * <p>The Chip is Stadium shape and has a max height designed to take no more than one line of text
+ * of {@link Typography#TYPOGRAPHY_CAPTION1} style. Width of the chip is adjustable to the text
+ * size.
+ *
+ * <p>The recommended set of {@link ChipColors} styles can be obtained from {@link ChipDefaults}.,
+ * e.g. {@link ChipDefaults#COMPACT_PRIMARY_COLORS} to get a color scheme for a primary {@link
+ * CompactChip}.
+ *
+ * <p>When accessing the contents of a container for testing, note that this element can't be simply
+ * casted back to the original type, i.e.:
+ *
+ * <pre>{@code
+ * CompactChip chip = new CompactChip...
+ * Box box = new Box.Builder().addContent(chip).build();
+ *
+ * CompactChip myChip = (CompactChip) box.getContents().get(0);
+ * }</pre>
+ *
+ * will fail.
+ *
+ * <p>To be able to get {@link CompactChip} object from any layout element, {@link
+ * #fromLayoutElement} method should be used, i.e.:
+ *
+ * <pre>{@code
+ * CompactChip myChip = CompactChip.fromLayoutElement(box.getContents().get(0));
+ * }</pre>
+ */
+public class CompactChip implements LayoutElement {
+    /** Tool tag for Metadata in Modifiers, so we know that Box is actually a CompactChip. */
+    static final String METADATA_TAG = "CMPCHP";
+
+    @NonNull private final Box mImpl;
+    @NonNull private final Chip mElement;
+
+    CompactChip(@NonNull Box element) {
+        this.mImpl = element;
+        // We know for sure that content of the Box is Chip.
+        this.mElement = new Chip((Box) element.getContents().get(0));
+    }
+
+    /** Builder class for {@link androidx.wear.protolayout.material.CompactChip}. */
+    public static final class Builder implements LayoutElement.Builder {
+        @NonNull private final Context mContext;
+        @NonNull private final String mText;
+        @NonNull private final Clickable mClickable;
+        @NonNull private final DeviceParameters mDeviceParameters;
+        @NonNull private ChipColors mChipColors = COMPACT_PRIMARY_COLORS;
+
+        /**
+         * Creates a builder for the {@link CompactChip} with associated action and the given text
+         *
+         * @param context The application's context.
+         * @param text The text to be displayed in this compact chip.
+         * @param clickable Associated {@link Clickable} for click events. When the CompactChip is
+         *     clicked it will fire the associated action.
+         * @param deviceParameters The device parameters used for styling text.
+         */
+        public Builder(
+                @NonNull Context context,
+                @NonNull String text,
+                @NonNull Clickable clickable,
+                @NonNull DeviceParameters deviceParameters) {
+            this.mContext = context;
+            this.mText = text;
+            this.mClickable = clickable;
+            this.mDeviceParameters = deviceParameters;
+        }
+
+        /**
+         * Sets the colors for the {@link CompactChip}. If set, {@link
+         * ChipColors#getBackgroundColor()} will be used for the background of the button and {@link
+         * ChipColors#getContentColor()} for the text. If not set, {@link
+         * ChipDefaults#COMPACT_PRIMARY_COLORS} will be used.
+         */
+        @NonNull
+        public Builder setChipColors(@NonNull ChipColors chipColors) {
+            mChipColors = chipColors;
+            return this;
+        }
+
+        /** Constructs and returns {@link CompactChip} with the provided content and look. */
+        @NonNull
+        @Override
+        public CompactChip build() {
+            Chip.Builder chipBuilder =
+                    new Chip.Builder(mContext, mClickable, mDeviceParameters)
+                            .setMetadataTag(METADATA_TAG)
+                            .setChipColors(mChipColors)
+                            .setContentDescription(mText)
+                            .setHorizontalAlignment(HORIZONTAL_ALIGN_CENTER)
+                            .setWidth(wrap())
+                            .setHeight(COMPACT_HEIGHT)
+                            .setMaxLines(1)
+                            .setHorizontalPadding(COMPACT_HORIZONTAL_PADDING)
+                            .setPrimaryLabelContent(mText)
+                            .setPrimaryLabelTypography(Typography.TYPOGRAPHY_CAPTION1)
+                            .setIsPrimaryLabelScalable(false);
+
+            Box tappableChip =
+                    new Box.Builder()
+                            .setModifiers(
+                                    new Modifiers.Builder()
+                                            .setClickable(mClickable)
+                                            .setMetadata(
+                                                    new ElementMetadata.Builder()
+                                                            .setTagData(getTagBytes(METADATA_TAG))
+                                                            .build())
+                                            .build())
+                            .setWidth(wrap())
+                            .setHeight(COMPACT_HEIGHT_TAPPABLE)
+                            .setVerticalAlignment(LayoutElementBuilders.VERTICAL_ALIGN_CENTER)
+                            .addContent(chipBuilder.build())
+                            .build();
+
+            return new CompactChip(tappableChip);
+        }
+    }
+
+    /** Returns click event action associated with this Chip. */
+    @NonNull
+    public Clickable getClickable() {
+        return mElement.getClickable();
+    }
+
+    /** Returns chip color of this Chip. */
+    @NonNull
+    public ChipColors getChipColors() {
+        return mElement.getChipColors();
+    }
+
+    /** Returns text content of this Chip. */
+    @NonNull
+    public String getText() {
+        return checkNotNull(mElement.getPrimaryLabelContent());
+    }
+
+    /** Returns metadata tag set to this CompactChip, which should be {@link #METADATA_TAG}. */
+    @NonNull
+    String getMetadataTag() {
+        return mElement.getMetadataTag();
+    }
+
+    /**
+     * Returns CompactChip object from the given LayoutElement (e.g. one retrieved from a
+     * container's content with {@code container.getContents().get(index)}) if that element can be
+     * converted to CompactChip. Otherwise, it will return null.
+     */
+    @Nullable
+    public static CompactChip fromLayoutElement(@NonNull LayoutElement element) {
+        if (element instanceof CompactChip) {
+            return (CompactChip) element;
+        }
+        if (!(element instanceof Box)) {
+            return null;
+        }
+        Box boxElement = (Box) element;
+        if (!checkTag(boxElement.getModifiers(), METADATA_TAG)) {
+            return null;
+        }
+        // Now to check that inner content of the Box is CompactChip's Chip.
+        LayoutElement innerElement = boxElement.getContents().get(0);
+        if (!(innerElement instanceof Box)) {
+            return null;
+        }
+        Box innerBoxElement = (Box) innerElement;
+        if (!checkTag(innerBoxElement.getModifiers(), METADATA_TAG)) {
+            return null;
+        }
+
+        // Now we are sure that this element is a CompactChip.
+        return new CompactChip(boxElement);
+    }
+
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    @Override
+    public LayoutElementProto.LayoutElement toLayoutElementProto() {
+        return mImpl.toLayoutElementProto();
+    }
+
+    @Nullable
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public Fingerprint getFingerprint() {
+        return mImpl.getFingerprint();
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/DeleteMe.kt b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/DeleteMe.kt
new file mode 100644
index 0000000..8f2cad2
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/DeleteMe.kt
@@ -0,0 +1,17 @@
+package androidx.wear.protolayout.material/*
+ * 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.
+ */
+
+// This file exists to trick AGP/lint to work around b/234865137
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Helper.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Helper.java
new file mode 100644
index 0000000..630bf29
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Helper.java
@@ -0,0 +1,134 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.wear.protolayout.DimensionBuilders.dp;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.DeviceParametersBuilders;
+import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.protolayout.DimensionBuilders.DpProp;
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collection;
+
+/**
+ * Helper class used for Tiles Material.
+ *
+ */
+@RestrictTo(Scope.LIBRARY_GROUP)
+public class Helper {
+    private Helper() {}
+
+    /**
+     * Returns given value if not null or throws {@code NullPointerException} otherwise.
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public static <T> T checkNotNull(@Nullable T value) {
+        if (value == null) {
+            throw new NullPointerException();
+        }
+        return value;
+    }
+
+    /** Returns radius in {@link DpProp} of the given diameter. */
+    @NonNull
+    static DpProp radiusOf(DpProp diameter) {
+        return dp(diameter.getValue() / 2);
+    }
+
+    /**
+     * Returns true if the given DeviceParameters belong to the round screen device.
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public static boolean isRoundDevice(@NonNull DeviceParameters deviceParameters) {
+        return deviceParameters.getScreenShape() == DeviceParametersBuilders.SCREEN_SHAPE_ROUND;
+    }
+
+    /**
+     * Returns String representation of tag from byte array.
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public static String getTagName(@NonNull byte[] tagData) {
+        return new String(tagData, StandardCharsets.UTF_8);
+    }
+
+    /**
+     * Returns byte array representation of tag from String.
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public static byte[] getTagBytes(@NonNull String tagName) {
+        return tagName.getBytes(StandardCharsets.UTF_8);
+    }
+
+    /** Returns the String representation of metadata tag from the given ElementMetadata. */
+    @NonNull
+    public static String getMetadataTagName(@NonNull ElementMetadata metadata) {
+        return getTagName(getMetadataTagBytes(metadata));
+    }
+
+    /** Returns the metadata tag from the given ElementMetadata. */
+    @NonNull
+    public static byte[] getMetadataTagBytes(@NonNull ElementMetadata metadata) {
+        return checkNotNull(metadata).getTagData();
+    }
+
+    /** Returns true if the given Modifiers have Metadata tag set to the given String value. */
+    public static boolean checkTag(@Nullable Modifiers modifiers, @NonNull String validTag) {
+        return modifiers != null
+                && modifiers.getMetadata() != null
+                && validTag.equals(getMetadataTagName(modifiers.getMetadata()));
+    }
+
+    /**
+     * Returns true if the given Modifiers have Metadata tag set to any of the value in the given
+     * String collection.
+     */
+    public static boolean checkTag(
+            @Nullable Modifiers modifiers, @NonNull Collection<String> validTags) {
+        return modifiers != null
+                && modifiers.getMetadata() != null
+                && validTags.contains(getMetadataTagName(modifiers.getMetadata()));
+    }
+
+    /**
+     * Returns true if the given Modifiers have Metadata tag set with prefix that is equal to the
+     * given String and its length is of the given base array.
+     */
+    public static boolean checkTag(
+            @Nullable Modifiers modifiers, @NonNull String validPrefix, @NonNull byte[] validBase) {
+        if (modifiers == null || modifiers.getMetadata() == null) {
+            return false;
+        }
+        byte[] metadataTag = getMetadataTagBytes(modifiers.getMetadata());
+        byte[] tag = Arrays.copyOf(metadataTag, validPrefix.length());
+        return metadataTag.length == validBase.length && validPrefix.equals(getTagName(tag));
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/ProgressIndicatorColors.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/ProgressIndicatorColors.java
new file mode 100644
index 0000000..668fe3e
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/ProgressIndicatorColors.java
@@ -0,0 +1,83 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.wear.protolayout.ColorBuilders.argb;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+import androidx.wear.protolayout.ColorBuilders.ColorProp;
+
+/**
+ * Represents the indicator and track colors used in a progress indicator Tiles component.
+ *
+ * <p>See {@link ProgressIndicatorDefaults#DEFAULT_COLORS} for the default colors used in a {@link
+ * CircularProgressIndicator}.
+ */
+public class ProgressIndicatorColors {
+    @NonNull private final ColorProp mIndicatorColor;
+    @NonNull private final ColorProp mTrackColor;
+
+    /**
+     * Constructor for {@link ProgressIndicatorColors} object.
+     *
+     * @param indicatorColor The indicator color to be used for a progress indicator Tiles
+     *     component.
+     * @param trackColor The background track color to be used for a progress indicator Tiles
+     *     component.
+     */
+    public ProgressIndicatorColors(
+            @NonNull ColorProp indicatorColor, @NonNull ColorProp trackColor) {
+        this.mIndicatorColor = indicatorColor;
+        this.mTrackColor = trackColor;
+    }
+
+    /**
+     * Constructor for {@link ProgressIndicatorColors} object.
+     *
+     * @param indicatorColor The indicator color to be used for a progress indicator Tiles
+     *     component. Should be in ARGB format.
+     * @param trackColor The background track color to be used for a progress indicator Tiles
+     *     component. Should be in ARGB format.
+     */
+    public ProgressIndicatorColors(@ColorInt int indicatorColor, @ColorInt int trackColor) {
+        this.mIndicatorColor = argb(indicatorColor);
+        this.mTrackColor = argb(trackColor);
+    }
+
+    /**
+     * Returns a {@link ProgressIndicatorColors} object, using the current Primary color for
+     * indicator color and the current Surface color for the track color from the given {@link
+     * Colors}.
+     */
+    @NonNull
+    public static ProgressIndicatorColors progressIndicatorColors(@NonNull Colors colors) {
+        return new ProgressIndicatorColors(colors.getPrimary(), colors.getSurface());
+    }
+
+    /** The indicator color to be used for a progress indicator Tiles component. */
+    @NonNull
+    public ColorProp getIndicatorColor() {
+        return mIndicatorColor;
+    }
+
+    /** The background track color to be used for a progress indicator Tiles component. */
+    @NonNull
+    public ColorProp getTrackColor() {
+        return mTrackColor;
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/ProgressIndicatorDefaults.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/ProgressIndicatorDefaults.java
new file mode 100644
index 0000000..dd2f9cf5
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/ProgressIndicatorDefaults.java
@@ -0,0 +1,59 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.wear.protolayout.DimensionBuilders.dp;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.DimensionBuilders.DpProp;
+
+/** Contains the default values used by {@link CircularProgressIndicator} Tiles components. */
+public class ProgressIndicatorDefaults {
+    private ProgressIndicatorDefaults() {}
+
+    /** The default stroke width for {@link CircularProgressIndicator} */
+    @NonNull public static final DpProp DEFAULT_STROKE_WIDTH = dp(8);
+
+    /**
+     * The default padding for {@link CircularProgressIndicator}
+     *
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    public static final DpProp DEFAULT_PADDING = dp(6);
+
+    /** The recommended colors for {@link CircularProgressIndicator}. */
+    @NonNull
+    public static final ProgressIndicatorColors DEFAULT_COLORS =
+            ProgressIndicatorColors.progressIndicatorColors(Colors.DEFAULT);
+
+    static final float DEFAULT_GAP_LENGTH = 47.8f;
+
+    /** The recommended start angle for {@link CircularProgressIndicator} if there's a gap. */
+    public static final float GAP_START_ANGLE = 180 + DEFAULT_GAP_LENGTH / 2 - 360;
+
+    /** The recommended end angle for {@link CircularProgressIndicator} if there's a gap. */
+    public static final float GAP_END_ANGLE = 180 - DEFAULT_GAP_LENGTH / 2;
+
+    /** Start angle for full length {@link CircularProgressIndicator}. */
+    static final float DEFAULT_START_ANGLE = 0;
+
+    /** End angle for full length {@link CircularProgressIndicator}. */
+    static final float DEFAULT_END_ANGLE = 360;
+}
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Text.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Text.java
new file mode 100644
index 0000000..ed5e309
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Text.java
@@ -0,0 +1,318 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.wear.protolayout.ColorBuilders.argb;
+import static androidx.wear.protolayout.LayoutElementBuilders.TEXT_ALIGN_CENTER;
+import static androidx.wear.protolayout.LayoutElementBuilders.TEXT_OVERFLOW_ELLIPSIZE_END;
+import static androidx.wear.protolayout.material.Helper.checkNotNull;
+import static androidx.wear.protolayout.material.Typography.TYPOGRAPHY_DISPLAY1;
+import static androidx.wear.protolayout.material.Typography.getFontStyleBuilder;
+import static androidx.wear.protolayout.material.Typography.getLineHeightForTypography;
+
+import android.content.Context;
+
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.ColorBuilders.ColorProp;
+import androidx.wear.protolayout.LayoutElementBuilders;
+import androidx.wear.protolayout.LayoutElementBuilders.FontStyle;
+import androidx.wear.protolayout.LayoutElementBuilders.FontWeight;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+import androidx.wear.protolayout.LayoutElementBuilders.TextAlignment;
+import androidx.wear.protolayout.LayoutElementBuilders.TextOverflow;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.material.Typography.TypographyName;
+import androidx.wear.protolayout.proto.LayoutElementProto;
+
+/**
+ * Tiles component {@link Text} that represents text object holding any information.
+ *
+ * <p>There are pre-built typography styles that can be obtained from constants in {@link
+ * FontStyle}.
+ *
+ * <p>When accessing the contents of a container for testing, note that this element can't be simply
+ * casted back to the original type, i.e.:
+ *
+ * <pre>{@code
+ * Text text = new Text...
+ * Box box = new Box.Builder().addContent(text).build();
+ *
+ * Text myText = (Text) box.getContents().get(0);
+ * }</pre>
+ *
+ * will fail.
+ *
+ * <p>To be able to get {@link Text} object from any layout element, {@link #fromLayoutElement}
+ * method should be used, i.e.:
+ *
+ * <pre>{@code
+ * Text myText = Text.fromLayoutElement(box.getContents().get(0));
+ * }</pre>
+ */
+public class Text implements LayoutElement {
+
+    @NonNull private final LayoutElementBuilders.Text mText;
+
+    Text(@NonNull LayoutElementBuilders.Text mText) {
+        this.mText = mText;
+    }
+
+    /** Builder class for {@link Text}. */
+    public static final class Builder implements LayoutElement.Builder {
+        @NonNull private final Context mContext;
+        @NonNull private String mTextContent = "";
+        @NonNull private ColorProp mColor = argb(Colors.DEFAULT.getOnPrimary());
+        private @TypographyName int mTypographyName = TYPOGRAPHY_DISPLAY1;
+        private boolean mItalic = false;
+        private int mMaxLines = 1;
+        private boolean mUnderline = false;
+        @TextAlignment private int mMultilineAlignment = TEXT_ALIGN_CENTER;
+        @NonNull private Modifiers mModifiers = new Modifiers.Builder().build();
+        private @TextOverflow int mOverflow = TEXT_OVERFLOW_ELLIPSIZE_END;
+        private boolean mIsScalable = true;
+        @Nullable private Integer mCustomWeight = null;
+
+        /**
+         * Creates a builder for {@link Text}.
+         *
+         * @param context The application's context.
+         * @param text The text content for this component.
+         */
+        public Builder(@NonNull Context context, @NonNull String text) {
+            mContext = context;
+            mTextContent = text;
+        }
+
+        /**
+         * Sets the typography for the {@link Text}. If not set, {@link
+         * Typography#TYPOGRAPHY_DISPLAY1} will be used.
+         */
+        @NonNull
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        // There is getFontStyle matching getter for this setter as the serialized format of the
+        // Tiles do not allow for a direct reconstruction of the all arguments, but it has FontStyle
+        // object of that text.
+        public Builder setTypography(@TypographyName int typography) {
+            this.mTypographyName = typography;
+            return this;
+        }
+
+        /**
+         * Sets whether the text size will change if user has changed the default font size. If not
+         * set, true will be used.
+         */
+        Builder setIsScalable(boolean isScalable) {
+            this.mIsScalable = isScalable;
+            return this;
+        }
+
+        /**
+         * Sets the color for the {@link Text}. If not set, onPrimary color from the {@link
+         * Colors#DEFAULT} will be used.
+         */
+        @NonNull
+        public Builder setColor(@NonNull ColorProp color) {
+            this.mColor = color;
+            return this;
+        }
+
+        /** Sets the text to be italic. If not set, false will be used. */
+        @NonNull
+        public Builder setItalic(boolean italic) {
+            this.mItalic = italic;
+            return this;
+        }
+
+        /** Sets the text to be underlined. If not set, false will be used. */
+        @NonNull
+        public Builder setUnderline(boolean underline) {
+            this.mUnderline = underline;
+            return this;
+        }
+
+        /** Sets the maximum lines of text. If not set, 1 will be used. */
+        @NonNull
+        public Builder setMaxLines(@IntRange(from = 1) int maxLines) {
+            this.mMaxLines = maxLines;
+            return this;
+        }
+
+        /**
+         * Sets the multiline alignment for text within bounds of the Text element. Note that this
+         * option has no effect for single line of text, and for that, alignment on the outer
+         * container should be used. If not set, {@link TextAlignment#TEXT_ALIGN_CENTER} will be
+         * used.
+         */
+        @NonNull
+        public Builder setMultilineAlignment(@TextAlignment int multilineAlignment) {
+            this.mMultilineAlignment = multilineAlignment;
+            return this;
+        }
+
+        /** Sets the modifiers of text. */
+        @NonNull
+        public Builder setModifiers(@NonNull Modifiers modifiers) {
+            this.mModifiers = modifiers;
+            return this;
+        }
+
+        /**
+         * Sets the overflow for text. If not set, {@link TextAlignment#TEXT_OVERFLOW_ELLIPSIZE_END}
+         * will be used.
+         */
+        @NonNull
+        public Builder setOverflow(@TextOverflow int overflow) {
+            this.mOverflow = overflow;
+            return this;
+        }
+
+        /**
+         * Sets the weight of the font. If not set, default weight for the chosen Typography will be
+         * used.
+         */
+        @NonNull
+        public Builder setWeight(@FontWeight int weight) {
+            this.mCustomWeight = weight;
+            return this;
+        }
+
+        /** Constructs and returns {@link Text} with the provided content and look. */
+        @NonNull
+        @Override
+        public Text build() {
+            FontStyle.Builder fontStyleBuilder =
+                    getFontStyleBuilder(mTypographyName, mContext, mIsScalable)
+                            .setColor(mColor)
+                            .setItalic(mItalic)
+                            .setUnderline(mUnderline);
+            if (mCustomWeight != null) {
+                fontStyleBuilder.setWeight(mCustomWeight);
+            }
+
+            LayoutElementBuilders.Text.Builder text =
+                    new LayoutElementBuilders.Text.Builder()
+                            .setText(mTextContent)
+                            .setFontStyle(fontStyleBuilder.build())
+                            .setLineHeight(getLineHeightForTypography(mTypographyName))
+                            .setMaxLines(mMaxLines)
+                            .setMultilineAlignment(mMultilineAlignment)
+                            .setModifiers(mModifiers)
+                            .setOverflow(mOverflow);
+            return new Text(text.build());
+        }
+    }
+
+    /** Returns the text of this Text element. */
+    @NonNull
+    public String getText() {
+        return checkNotNull(checkNotNull(mText.getText()).getValue());
+    }
+
+    /** Returns the color of this Text element. */
+    @NonNull
+    public ColorProp getColor() {
+        return checkNotNull(checkNotNull(mText.getFontStyle()).getColor());
+    }
+
+    /** Returns the font style of this Text element. */
+    @NonNull
+    public FontStyle getFontStyle() {
+        return checkNotNull(mText.getFontStyle());
+    }
+
+    /** Returns the line height of this Text element. */
+    public float getLineHeight() {
+        return checkNotNull(mText.getLineHeight()).getValue();
+    }
+
+    /** Returns the max lines of text of this Text element. */
+    public int getMaxLines() {
+        return checkNotNull(mText.getMaxLines()).getValue();
+    }
+
+    /** Returns the multiline alignment of this Text element. */
+    @TextAlignment
+    public int getMultilineAlignment() {
+        return checkNotNull(mText.getMultilineAlignment()).getValue();
+    }
+
+    /** Returns the modifiers of this Text element. */
+    @NonNull
+    public Modifiers getModifiers() {
+        return checkNotNull(mText.getModifiers());
+    }
+
+    /** Returns the overflow of this Text element. */
+    @TextOverflow
+    public int getOverflow() {
+        return checkNotNull(mText.getOverflow()).getValue();
+    }
+
+    /** Returns the overflow of this Text element. */
+    @FontWeight
+    public int getWeight() {
+        return checkNotNull(checkNotNull(mText.getFontStyle()).getWeight()).getValue();
+    }
+
+    /** Returns whether the Text is in italic. */
+    public boolean isItalic() {
+        return checkNotNull(checkNotNull(mText.getFontStyle()).getItalic()).getValue();
+    }
+
+    /** Returns whether the Text is underlined. */
+    public boolean isUnderline() {
+        return checkNotNull(checkNotNull(mText.getFontStyle()).getUnderline()).getValue();
+    }
+
+    /**
+     * Returns Material Text object from the given LayoutElement (e.g. one retrieved from a
+     * container's content with {@code container.getContents().get(index)}) if that element can be
+     * converted to Material Text. Otherwise, it will return null.
+     */
+    @Nullable
+    public static Text fromLayoutElement(@NonNull LayoutElement element) {
+        if (element instanceof Text) {
+            return (Text) element;
+        }
+        if (!(element instanceof LayoutElementBuilders.Text)) {
+            return null;
+        }
+        LayoutElementBuilders.Text textElement = (LayoutElementBuilders.Text) element;
+        // We don't need to check the tag as LayoutElement.Text will have the same fields as our
+        // getters even if it's not Material.
+        return new Text(textElement);
+    }
+
+    @NonNull
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public LayoutElementProto.LayoutElement toLayoutElementProto() {
+        return mText.toLayoutElementProto();
+    }
+
+    @Nullable
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public Fingerprint getFingerprint() {
+        return mText.getFingerprint();
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/TitleChip.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/TitleChip.java
new file mode 100644
index 0000000..009dd8c
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/TitleChip.java
@@ -0,0 +1,254 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.annotation.Dimension.DP;
+import static androidx.wear.protolayout.DimensionBuilders.dp;
+import static androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER;
+import static androidx.wear.protolayout.material.ChipDefaults.TITLE_HEIGHT;
+import static androidx.wear.protolayout.material.ChipDefaults.TITLE_HORIZONTAL_PADDING;
+import static androidx.wear.protolayout.material.ChipDefaults.TITLE_PRIMARY_COLORS;
+import static androidx.wear.protolayout.material.Helper.checkNotNull;
+import static androidx.wear.protolayout.material.Helper.checkTag;
+
+import android.content.Context;
+
+import androidx.annotation.Dimension;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.protolayout.DimensionBuilders.ContainerDimension;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignment;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+import androidx.wear.protolayout.ModifiersBuilders.Clickable;
+import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.proto.LayoutElementProto;
+
+/**
+ * Tiles component {@link TitleChip} that represents clickable object with the text.
+ *
+ * <p>The Title Chip is Stadium shaped object with a larger height then standard Chip and it will
+ * take one line of text of {@link Typography#TYPOGRAPHY_TITLE2} style.
+ *
+ * <p>The recommended set of {@link ChipColors} styles can be obtained from {@link ChipDefaults},
+ * e.g. {@link ChipDefaults#TITLE_PRIMARY_COLORS} to get a color scheme for a primary {@link
+ * TitleChip}.
+ *
+ * <p>When accessing the contents of a container for testing, note that this element can't be simply
+ * casted back to the original type, i.e.:
+ *
+ * <pre>{@code
+ * TitleChip chip = new TitleChip...
+ * Box box = new Box.Builder().addContent(chip).build();
+ *
+ * TitleChip myChip = (TitleChip) box.getContents().get(0);
+ * }</pre>
+ *
+ * will fail.
+ *
+ * <p>To be able to get {@link TitleChip} object from any layout element, {@link #fromLayoutElement}
+ * method should be used, i.e.:
+ *
+ * <pre>{@code
+ * TitleChip myChip = TitleChip.fromLayoutElement(box.getContents().get(0));
+ * }</pre>
+ *
+ * @see  androidx.wear.protolayout.material.layouts.PrimaryLayout.Builder#setContent if this
+ * TitleChip is used inside of {@link androidx.wear.protolayout.material.layouts.PrimaryLayout}.
+ */
+public class TitleChip implements LayoutElement {
+    /** Tool tag for Metadata in Modifiers, so we know that Box is actually a TitleChip. */
+    static final String METADATA_TAG = "TTLCHP";
+
+    @NonNull private final Chip mElement;
+
+    TitleChip(@NonNull Chip element) {
+        this.mElement = element;
+    }
+
+    /** Builder class for {@link TitleChip}. */
+    public static final class Builder implements LayoutElement.Builder {
+        @NonNull private final Context mContext;
+        @NonNull private final String mText;
+        @NonNull private final Clickable mClickable;
+        @NonNull private final DeviceParameters mDeviceParameters;
+        @NonNull private ChipColors mChipColors = TITLE_PRIMARY_COLORS;
+        @HorizontalAlignment private int mHorizontalAlign = HORIZONTAL_ALIGN_CENTER;
+
+        // Indicates that the width isn't set, so it will be automatically set by Chip.Builder
+        // constructor.
+        @Nullable private ContainerDimension mWidth = null;
+
+        /**
+         * Creates a builder for the {@link TitleChip} with associated action and the given text
+         *
+         * @param context The application's context.
+         * @param text The text to be displayed in this title chip. Text will be displayed in 1 line
+         *     and truncated if it doesn't fit.
+         * @param clickable Associated {@link Clickable} for click events. When the TitleChip is
+         *     clicked it will fire the associated action.
+         * @param deviceParameters The device parameters used for styling text.
+         */
+        public Builder(
+                @NonNull Context context,
+                @NonNull String text,
+                @NonNull Clickable clickable,
+                @NonNull DeviceParameters deviceParameters) {
+            this.mContext = context;
+            this.mText = text;
+            this.mClickable = clickable;
+            this.mDeviceParameters = deviceParameters;
+        }
+
+        /**
+         * Sets the colors for the {@link TitleChip}. If set, {@link
+         * ChipColors#getBackgroundColor()} will be used for the background of the button and {@link
+         * ChipColors#getContentColor()} for the text. If not set, {@link
+         * ChipDefaults#TITLE_PRIMARY_COLORS} will be used.
+         */
+        @NonNull
+        public Builder setChipColors(@NonNull ChipColors chipColors) {
+            mChipColors = chipColors;
+            return this;
+        }
+
+        /** Sets the horizontal alignment in the chip. If not set, content will be centered. */
+        @NonNull
+        public Builder setHorizontalAlignment(@HorizontalAlignment int horizontalAlignment) {
+            mHorizontalAlign = horizontalAlignment;
+            return this;
+        }
+
+        /**
+         * Sets the width of {@link TitleChip}. If not set, default value will be set to fill the
+         * screen.
+         */
+        @NonNull
+        public Builder setWidth(@NonNull ContainerDimension width) {
+            mWidth = width;
+            return this;
+        }
+
+        /**
+         * Sets the width of {@link TitleChip}. If not set, default value will be set to fill the
+         * screen.
+         */
+        @NonNull
+        public Builder setWidth(@Dimension(unit = DP) float width) {
+            mWidth = dp(width);
+            return this;
+        }
+
+        /** Constructs and returns {@link TitleChip} with the provided content and look. */
+        @NonNull
+        @Override
+        public TitleChip build() {
+            Chip.Builder chipBuilder =
+                    new Chip.Builder(mContext, mClickable, mDeviceParameters)
+                            .setMetadataTag(METADATA_TAG)
+                            .setChipColors(mChipColors)
+                            .setContentDescription(mText)
+                            .setHorizontalAlignment(mHorizontalAlign)
+                            .setHeight(TITLE_HEIGHT)
+                            .setMaxLines(1)
+                            .setHorizontalPadding(TITLE_HORIZONTAL_PADDING)
+                            .setPrimaryLabelContent(mText)
+                            .setPrimaryLabelTypography(Typography.TYPOGRAPHY_TITLE2)
+                            .setIsPrimaryLabelScalable(false);
+
+            if (mWidth != null) {
+                chipBuilder.setWidth(mWidth);
+            }
+
+            return new TitleChip(chipBuilder.build());
+        }
+    }
+
+    /** Returns width of this Chip. */
+    @NonNull
+    public ContainerDimension getWidth() {
+        return mElement.getWidth();
+    }
+
+    /** Returns click event action associated with this Chip. */
+    @NonNull
+    public Clickable getClickable() {
+        return mElement.getClickable();
+    }
+
+    /** Returns chip color of this Chip. */
+    @NonNull
+    public ChipColors getChipColors() {
+        return mElement.getChipColors();
+    }
+
+    /** Returns text content of this Chip. */
+    @NonNull
+    public String getText() {
+        return checkNotNull(mElement.getPrimaryLabelContent());
+    }
+
+    /** Returns the horizontal alignment of the content in this Chip. */
+    @HorizontalAlignment
+    public int getHorizontalAlignment() {
+        return mElement.getHorizontalAlignment();
+    }
+
+    /** Returns metadata tag set to this TitleChip, which should be {@link #METADATA_TAG}. */
+    @NonNull
+    String getMetadataTag() {
+        return mElement.getMetadataTag();
+    }
+
+    /**
+     * Returns TitleChip object from the given LayoutElement (e.g. one retrieved from a container's
+     * content with {@code container.getContents().get(index)}) if that element can be converted to
+     * TitleChip. Otherwise, it will return null.
+     */
+    @Nullable
+    public static TitleChip fromLayoutElement(@NonNull LayoutElement element) {
+        if (element instanceof TitleChip) {
+            return (TitleChip) element;
+        }
+        if (!(element instanceof Box)) {
+            return null;
+        }
+        Box boxElement = (Box) element;
+        if (!checkTag(boxElement.getModifiers(), METADATA_TAG)) {
+            return null;
+        }
+        // Now we are sure that this element is a TitleChip.
+        return new TitleChip(new Chip(boxElement));
+    }
+
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    @Override
+    public LayoutElementProto.LayoutElement toLayoutElementProto() {
+        return mElement.toLayoutElementProto();
+    }
+
+    @Nullable
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public Fingerprint getFingerprint() {
+        return mElement.getFingerprint();
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Typography.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Typography.java
new file mode 100644
index 0000000..d5b5b9f
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Typography.java
@@ -0,0 +1,298 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.annotation.Dimension.DP;
+import static androidx.annotation.Dimension.SP;
+import static androidx.wear.protolayout.DimensionBuilders.sp;
+import static androidx.wear.protolayout.LayoutElementBuilders.FONT_VARIANT_BODY;
+import static androidx.wear.protolayout.LayoutElementBuilders.FONT_VARIANT_TITLE;
+import static androidx.wear.protolayout.LayoutElementBuilders.FONT_WEIGHT_BOLD;
+import static androidx.wear.protolayout.LayoutElementBuilders.FONT_WEIGHT_MEDIUM;
+import static androidx.wear.protolayout.LayoutElementBuilders.FONT_WEIGHT_NORMAL;
+import static androidx.wear.protolayout.material.Helper.checkNotNull;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.util.DisplayMetrics;
+
+import androidx.annotation.Dimension;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.wear.protolayout.DimensionBuilders;
+import androidx.wear.protolayout.DimensionBuilders.SpProp;
+import androidx.wear.protolayout.LayoutElementBuilders.FontStyle;
+import androidx.wear.protolayout.LayoutElementBuilders.FontVariant;
+import androidx.wear.protolayout.LayoutElementBuilders.FontWeight;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+
+/** Typography styles, currently set up to match Wear's styling. */
+public class Typography {
+    /** Typography for large display text. */
+    public static final int TYPOGRAPHY_DISPLAY1 = 1;
+
+    /** Typography for medium display text. */
+    public static final int TYPOGRAPHY_DISPLAY2 = 2;
+
+    /** Typography for small display text. */
+    public static final int TYPOGRAPHY_DISPLAY3 = 3;
+
+    /** Typography for large title text. */
+    public static final int TYPOGRAPHY_TITLE1 = 4;
+
+    /** Typography for medium title text. */
+    public static final int TYPOGRAPHY_TITLE2 = 5;
+
+    /** Typography for small title text. */
+    public static final int TYPOGRAPHY_TITLE3 = 6;
+
+    /** Typography for large body text. */
+    public static final int TYPOGRAPHY_BODY1 = 7;
+
+    /** Typography for medium body text. */
+    public static final int TYPOGRAPHY_BODY2 = 8;
+
+    /** Typography for bold button text. */
+    public static final int TYPOGRAPHY_BUTTON = 9;
+
+    /** Typography for large caption text. */
+    public static final int TYPOGRAPHY_CAPTION1 = 10;
+
+    /** Typography for medium caption text. */
+    public static final int TYPOGRAPHY_CAPTION2 = 11;
+
+    /** Typography for small caption text. */
+    public static final int TYPOGRAPHY_CAPTION3 = 12;
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+        TYPOGRAPHY_DISPLAY1,
+        TYPOGRAPHY_DISPLAY2,
+        TYPOGRAPHY_DISPLAY3,
+        TYPOGRAPHY_TITLE1,
+        TYPOGRAPHY_TITLE2,
+        TYPOGRAPHY_TITLE3,
+        TYPOGRAPHY_BODY1,
+        TYPOGRAPHY_BODY2,
+        TYPOGRAPHY_BUTTON,
+        TYPOGRAPHY_CAPTION1,
+        TYPOGRAPHY_CAPTION2,
+        TYPOGRAPHY_CAPTION3
+    })
+    @interface TypographyName {}
+
+    /** Mapping for line height for different typography. */
+    @NonNull
+    private static final Map<Integer, Float> TYPOGRAPHY_TO_LINE_HEIGHT_SP = new HashMap<>();
+
+    static {
+        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_DISPLAY1, 46f);
+        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_DISPLAY2, 40f);
+        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_DISPLAY3, 36f);
+        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_TITLE1, 28f);
+        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_TITLE2, 24f);
+        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_TITLE3, 20f);
+        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_BODY1, 20f);
+        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_BODY2, 18f);
+        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_BUTTON, 19f);
+        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_CAPTION1, 18f);
+        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_CAPTION2, 16f);
+        TYPOGRAPHY_TO_LINE_HEIGHT_SP.put(TYPOGRAPHY_CAPTION3, 14f);
+    }
+    /**
+     * Returns the {@link FontStyle.Builder} for the given FontStyle code with the recommended size,
+     * weight and letter spacing. Font will be scalable.
+     */
+    @NonNull
+    static FontStyle.Builder getFontStyleBuilder(
+            @TypographyName int fontStyleCode, @NonNull Context context) {
+        return getFontStyleBuilder(fontStyleCode, context, true);
+    }
+
+    private Typography() {}
+
+    /**
+     * Returns the {@link FontStyle.Builder} for the given Typography code with the recommended
+     * size, weight and letter spacing, with the option to make this font not scalable.
+     */
+    @NonNull
+    static FontStyle.Builder getFontStyleBuilder(
+            @TypographyName int typographyCode, @NonNull Context context, boolean isScalable) {
+        switch (typographyCode) {
+            case TYPOGRAPHY_BODY1:
+                return body1(isScalable, context);
+            case TYPOGRAPHY_BODY2:
+                return body2(isScalable, context);
+            case TYPOGRAPHY_BUTTON:
+                return button(isScalable, context);
+            case TYPOGRAPHY_CAPTION1:
+                return caption1(isScalable, context);
+            case TYPOGRAPHY_CAPTION2:
+                return caption2(isScalable, context);
+            case TYPOGRAPHY_CAPTION3:
+                return caption3(isScalable, context);
+            case TYPOGRAPHY_DISPLAY1:
+                return display1(isScalable, context);
+            case TYPOGRAPHY_DISPLAY2:
+                return display2(isScalable, context);
+            case TYPOGRAPHY_DISPLAY3:
+                return display3(isScalable, context);
+            case TYPOGRAPHY_TITLE1:
+                return title1(isScalable, context);
+            case TYPOGRAPHY_TITLE2:
+                return title2(isScalable, context);
+            case TYPOGRAPHY_TITLE3:
+                return title3(isScalable, context);
+            default:
+                // Shouldn't happen.
+                throw new IllegalArgumentException(
+                        "Typography " + typographyCode + " doesn't exist.");
+        }
+    }
+
+    /**
+     * Returns the recommended line height for the given Typography to be added to the Text
+     * component.
+     */
+    @NonNull
+    static SpProp getLineHeightForTypography(@TypographyName int typography) {
+        if (!TYPOGRAPHY_TO_LINE_HEIGHT_SP.containsKey(typography)) {
+            throw new IllegalArgumentException("Typography " + typography + " doesn't exist.");
+        }
+        return sp(checkNotNull(TYPOGRAPHY_TO_LINE_HEIGHT_SP.get(typography)).intValue());
+    }
+
+    @NonNull
+    @SuppressLint("ResourceType")
+    @SuppressWarnings("deprecation")
+    // This is a helper function to make the font not scalable. It should interpret in value as DP
+    // and convert it to SP which is needed to be passed in as a font size. However, we will pass an
+    // SP object to it, because the default style is defined in it, but for the case when the font
+    // size on device in 1, so the DP is equal to SP.
+    private static SpProp dpToSp(@NonNull Context context, @Dimension(unit = DP) float valueDp) {
+        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+        float scaledSp = (valueDp / metrics.scaledDensity) * metrics.density;
+        return sp(scaledSp);
+    }
+
+    // The @Dimension(unit = SP) on sp() is seemingly being ignored, so lint complains that we're
+    // passing SP to something expecting PX. Just suppress the warning for now.
+    @SuppressLint("ResourceType")
+    private static FontStyle.Builder createFontStyleBuilder(
+            @Dimension(unit = SP) int size,
+            @FontWeight int weight,
+            @FontVariant int variant,
+            float letterSpacing,
+            boolean isScalable,
+            @NonNull Context context) {
+        return new FontStyle.Builder()
+                .setSize(isScalable ? DimensionBuilders.sp(size) : dpToSp(context, size))
+                .setLetterSpacing(DimensionBuilders.em(letterSpacing))
+                .setVariant(variant)
+                .setWeight(weight);
+    }
+
+    /** Font style for large display text. */
+    @NonNull
+    private static FontStyle.Builder display1(boolean isScalable, @NonNull Context context) {
+        return createFontStyleBuilder(
+                40, FONT_WEIGHT_MEDIUM, FONT_VARIANT_TITLE, 0.01f, isScalable, context);
+    }
+
+    /** Font style for medium display text. */
+    @NonNull
+    private static FontStyle.Builder display2(boolean isScalable, @NonNull Context context) {
+        return createFontStyleBuilder(
+                34, FONT_WEIGHT_MEDIUM, FONT_VARIANT_TITLE, 0.03f, isScalable, context);
+    }
+
+    /** Font style for small display text. */
+    @NonNull
+    private static FontStyle.Builder display3(boolean isScalable, @NonNull Context context) {
+        return createFontStyleBuilder(
+                30, FONT_WEIGHT_MEDIUM, FONT_VARIANT_TITLE, 0.03f, isScalable, context);
+    }
+
+    /** Font style for large title text. */
+    @NonNull
+    private static FontStyle.Builder title1(boolean isScalable, @NonNull Context context) {
+        return createFontStyleBuilder(
+                24, FONT_WEIGHT_MEDIUM, FONT_VARIANT_TITLE, 0.008f, isScalable, context);
+    }
+
+    /** Font style for medium title text. */
+    @NonNull
+    private static FontStyle.Builder title2(boolean isScalable, @NonNull Context context) {
+        return createFontStyleBuilder(
+                20, FONT_WEIGHT_MEDIUM, FONT_VARIANT_TITLE, 0.01f, isScalable, context);
+    }
+
+    /** Font style for small title text. */
+    @NonNull
+    private static FontStyle.Builder title3(boolean isScalable, @NonNull Context context) {
+        return createFontStyleBuilder(
+                16, FONT_WEIGHT_MEDIUM, FONT_VARIANT_TITLE, 0.01f, isScalable, context);
+    }
+
+    /** Font style for normal body text. */
+    @NonNull
+    private static FontStyle.Builder body1(boolean isScalable, @NonNull Context context) {
+        return createFontStyleBuilder(
+                16, FONT_WEIGHT_NORMAL, FONT_VARIANT_BODY, 0.01f, isScalable, context);
+    }
+
+    /** Font style for small body text. */
+    @NonNull
+    private static FontStyle.Builder body2(boolean isScalable, @NonNull Context context) {
+        return createFontStyleBuilder(
+                14, FONT_WEIGHT_NORMAL, FONT_VARIANT_BODY, 0.014f, isScalable, context);
+    }
+
+    /** Font style for bold button text. */
+    @NonNull
+    private static FontStyle.Builder button(boolean isScalable, @NonNull Context context) {
+        return createFontStyleBuilder(
+                15, FONT_WEIGHT_BOLD, FONT_VARIANT_BODY, 0.03f, isScalable, context);
+    }
+
+    /** Font style for large caption text. */
+    @NonNull
+    private static FontStyle.Builder caption1(boolean isScalable, @NonNull Context context) {
+        return createFontStyleBuilder(
+                14, FONT_WEIGHT_MEDIUM, FONT_VARIANT_BODY, 0.01f, isScalable, context);
+    }
+
+    /** Font style for medium caption text. */
+    @NonNull
+    private static FontStyle.Builder caption2(boolean isScalable, @NonNull Context context) {
+        return createFontStyleBuilder(
+                12, FONT_WEIGHT_MEDIUM, FONT_VARIANT_BODY, 0.01f, isScalable, context);
+    }
+
+    /** Font style for small caption text. */
+    @NonNull
+    private static FontStyle.Builder caption3(boolean isScalable, @NonNull Context context) {
+        return createFontStyleBuilder(
+                10, FONT_WEIGHT_MEDIUM, FONT_VARIANT_BODY, 0.01f, isScalable, context);
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/androidx-wear-protolayout-protolayout-material-documentation.md b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/androidx-wear-protolayout-protolayout-material-documentation.md
new file mode 100644
index 0000000..0b7dc77
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/androidx-wear-protolayout-protolayout-material-documentation.md
@@ -0,0 +1,8 @@
+# Module root
+
+Wear ProtoLayout Material
+
+# Package androidx.wear.protolayout.material
+
+This package provides helpers for Wear ProtoLayout, to enable you to use Wear Material components
+such as Buttons and Chips within your ProtoLayout.
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/layouts/EdgeContentLayout.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/layouts/EdgeContentLayout.java
new file mode 100644
index 0000000..e3e78d2
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/layouts/EdgeContentLayout.java
@@ -0,0 +1,381 @@
+/*
+ * 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.wear.protolayout.material.layouts;
+
+import static androidx.wear.protolayout.DimensionBuilders.dp;
+import static androidx.wear.protolayout.DimensionBuilders.expand;
+import static androidx.wear.protolayout.material.Helper.checkNotNull;
+import static androidx.wear.protolayout.material.Helper.checkTag;
+import static androidx.wear.protolayout.material.Helper.getMetadataTagBytes;
+import static androidx.wear.protolayout.material.Helper.getTagBytes;
+import static androidx.wear.protolayout.material.Helper.isRoundDevice;
+import static androidx.wear.protolayout.material.ProgressIndicatorDefaults.DEFAULT_PADDING;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.EDGE_CONTENT_LAYOUT_MARGIN_HORIZONTAL_ROUND_DP;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.EDGE_CONTENT_LAYOUT_MARGIN_HORIZONTAL_SQUARE_DP;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.EDGE_CONTENT_LAYOUT_PADDING_ABOVE_MAIN_CONTENT_DP;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.EDGE_CONTENT_LAYOUT_PADDING_BELOW_MAIN_CONTENT_DP;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.protolayout.DimensionBuilders.DpProp;
+import androidx.wear.protolayout.LayoutElementBuilders;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.Column;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+import androidx.wear.protolayout.LayoutElementBuilders.Spacer;
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+import androidx.wear.protolayout.ModifiersBuilders.Padding;
+import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.material.CircularProgressIndicator;
+import androidx.wear.protolayout.proto.LayoutElementProto;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tiles layout that represents the suggested layout style for Material Tiles, which has content
+ * around the edge of the screen (e.g. a ProgressIndicator) and the given content inside of it with
+ * the recommended margin and padding applied. Optional primary or secondary label can be added
+ * above and below the main content, respectively.
+ *
+ * <p>For additional examples and suggested layouts see <a
+ * href="/training/wearables/design/tiles-design-system">Tiles Design System</a>.
+ *
+ * <p>When accessing the contents of a container for testing, note that this element can't be simply
+ * casted back to the original type, i.e.:
+ *
+ * <pre>{@code
+ * EdgeContentLayout ecl = new EdgeContentLayout...
+ * Box box = new Box.Builder().addContent(ecl).build();
+ *
+ * EdgeContentLayout myEcl = (EdgeContentLayout) box.getContents().get(0);
+ * }</pre>
+ *
+ * will fail.
+ *
+ * <p>To be able to get {@link EdgeContentLayout} object from any layout element, {@link
+ * #fromLayoutElement} method should be used, i.e.:
+ *
+ * <pre>{@code
+ * EdgeContentLayout myEcl =
+ *   EdgeContentLayout.fromLayoutElement(box.getContents().get(0));
+ * }</pre>
+ */
+public class EdgeContentLayout implements LayoutElement {
+    /**
+     * Prefix tool tag for Metadata in Modifiers, so we know that Box is actually a
+     * EdgeContentLayout.
+     */
+    static final String METADATA_TAG_PREFIX = "ECL_";
+
+    /**
+     * Index for byte array that contains bits to check whether the content and indicator are
+     * present or not.
+     */
+    static final int FLAG_INDEX = METADATA_TAG_PREFIX.length();
+
+    /**
+     * Base tool tag for Metadata in Modifiers, so we know that Box is actually a EdgeContentLayout
+     * and what optional content is added.
+     */
+    static final byte[] METADATA_TAG_BASE =
+            Arrays.copyOf(getTagBytes(METADATA_TAG_PREFIX), FLAG_INDEX + 1);
+
+    /**
+     * Bit position in a byte on {@link #FLAG_INDEX} index in metadata byte array to check whether
+     * the edge content is present or not.
+     */
+    static final int EDGE_CONTENT_PRESENT = 0x1;
+    /**
+     * Bit position in a byte on {@link #FLAG_INDEX} index in metadata byte array to check whether
+     * the primary label is present or not.
+     */
+    static final int PRIMARY_LABEL_PRESENT = 0x2;
+    /**
+     * Bit position in a byte on {@link #FLAG_INDEX} index in metadata byte array to check whether
+     * the secondary label is present or not.
+     */
+    static final int SECONDARY_LABEL_PRESENT = 0x4;
+    /**
+     * Bit position in a byte on {@link #FLAG_INDEX} index in metadata byte array to check whether
+     * the main content is present or not.
+     */
+    static final int CONTENT_PRESENT = 0x8;
+
+    @RestrictTo(Scope.LIBRARY)
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            flag = true,
+            value = {
+                EDGE_CONTENT_PRESENT,
+                PRIMARY_LABEL_PRESENT,
+                CONTENT_PRESENT,
+                SECONDARY_LABEL_PRESENT
+            })
+    @interface ContentBits {}
+
+    @NonNull private final Box mImpl;
+
+    // This contains inner columns and edge content.
+    @NonNull private final List<LayoutElement> mContents;
+
+    // This contains optional labels, spacers and main content.
+    @NonNull private final List<LayoutElement> mInnerColumn;
+
+    EdgeContentLayout(@NonNull Box layoutElement) {
+        this.mImpl = layoutElement;
+        this.mContents = mImpl.getContents();
+        this.mInnerColumn = ((Column) ((Box) mContents.get(0)).getContents().get(0)).getContents();
+    }
+
+    /** Builder class for {@link EdgeContentLayout}. */
+    public static final class Builder implements LayoutElement.Builder {
+        @NonNull private final DeviceParameters mDeviceParameters;
+        @Nullable private LayoutElement mEdgeContent = null;
+        @Nullable private LayoutElement mPrimaryLabelText = null;
+        @Nullable private LayoutElement mSecondaryLabelText = null;
+        @Nullable private LayoutElement mContent = null;
+        private byte mMetadataContentByte = 0;
+
+        /**
+         * Creates a builder for the {@link EdgeContentLayout}t. Custom content inside of it can
+         * later be set with ({@link #setContent}.
+         */
+        public Builder(@NonNull DeviceParameters deviceParameters) {
+            this.mDeviceParameters = deviceParameters;
+        }
+
+        /**
+         * Sets the content to be around the edges. This can be {@link CircularProgressIndicator}.
+         */
+        @NonNull
+        public Builder setEdgeContent(@NonNull LayoutElement edgeContent) {
+            this.mEdgeContent = edgeContent;
+            mMetadataContentByte = (byte) (mMetadataContentByte | EDGE_CONTENT_PRESENT);
+            return this;
+        }
+
+        /** Sets the content in the primary label slot which will be above the main content. */
+        @NonNull
+        public Builder setPrimaryLabelTextContent(@NonNull LayoutElement primaryLabelText) {
+            this.mPrimaryLabelText = primaryLabelText;
+            mMetadataContentByte = (byte) (mMetadataContentByte | PRIMARY_LABEL_PRESENT);
+            return this;
+        }
+
+        /**
+         * Sets the content in the secondary label slot which will be below the main content. It is
+         * highly recommended to have primary label set when having secondary label.
+         */
+        @NonNull
+        public Builder setSecondaryLabelTextContent(@NonNull LayoutElement secondaryLabelText) {
+            this.mSecondaryLabelText = secondaryLabelText;
+            mMetadataContentByte = (byte) (mMetadataContentByte | SECONDARY_LABEL_PRESENT);
+            return this;
+        }
+
+        /** Sets the additional content to this layout, inside of the screen. */
+        @NonNull
+        public Builder setContent(@NonNull LayoutElement content) {
+            this.mContent = content;
+            mMetadataContentByte = (byte) (mMetadataContentByte | CONTENT_PRESENT);
+            return this;
+        }
+
+        /** Constructs and returns {@link EdgeContentLayout} with the provided content and look. */
+        @NonNull
+        @Override
+        public EdgeContentLayout build() {
+            float thicknessDp =
+                    mEdgeContent instanceof CircularProgressIndicator
+                            ? ((CircularProgressIndicator) mEdgeContent).getStrokeWidth().getValue()
+                            : 0;
+            float horizontalPaddingDp =
+                    isRoundDevice(mDeviceParameters)
+                            ? EDGE_CONTENT_LAYOUT_MARGIN_HORIZONTAL_ROUND_DP
+                            : EDGE_CONTENT_LAYOUT_MARGIN_HORIZONTAL_SQUARE_DP;
+            float indicatorWidth = 2 * (thicknessDp + DEFAULT_PADDING.getValue());
+            float mainContentHeightDp = mDeviceParameters.getScreenHeightDp() - indicatorWidth;
+            float mainContentWidthDp = mDeviceParameters.getScreenWidthDp() - indicatorWidth;
+
+            DpProp mainContentHeight = dp(Math.min(mainContentHeightDp, mainContentWidthDp));
+            DpProp mainContentWidth = dp(Math.min(mainContentHeightDp, mainContentWidthDp));
+
+            Modifiers modifiers =
+                    new Modifiers.Builder()
+                            .setPadding(
+                                    new Padding.Builder()
+                                            .setStart(dp(horizontalPaddingDp))
+                                            .setEnd(dp(horizontalPaddingDp))
+                                            .build())
+                            .build();
+
+            byte[] metadata = METADATA_TAG_BASE.clone();
+            metadata[FLAG_INDEX] = mMetadataContentByte;
+            Box.Builder mainBoxBuilder =
+                    new Box.Builder()
+                            .setWidth(expand())
+                            .setHeight(expand())
+                            .setModifiers(
+                                    new Modifiers.Builder()
+                                            .setMetadata(
+                                                    new ElementMetadata.Builder()
+                                                            .setTagData(metadata)
+                                                            .build())
+                                            .build())
+                            .setHorizontalAlignment(LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER)
+                            .setVerticalAlignment(LayoutElementBuilders.VERTICAL_ALIGN_CENTER);
+
+            Column.Builder innerContentBuilder =
+                    new Column.Builder()
+                            .setHorizontalAlignment(LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER);
+
+            if (mPrimaryLabelText != null) {
+                innerContentBuilder.addContent(mPrimaryLabelText);
+                innerContentBuilder.addContent(
+                        new Spacer.Builder()
+                                .setHeight(dp(EDGE_CONTENT_LAYOUT_PADDING_ABOVE_MAIN_CONTENT_DP))
+                                .build());
+            }
+
+            if (mContent != null) {
+                innerContentBuilder.addContent(
+                        new Box.Builder()
+                                .setVerticalAlignment(LayoutElementBuilders.VERTICAL_ALIGN_CENTER)
+                                .addContent(mContent)
+                                .build());
+            }
+
+            if (mSecondaryLabelText != null) {
+                innerContentBuilder.addContent(
+                        new Spacer.Builder()
+                                .setHeight(dp(EDGE_CONTENT_LAYOUT_PADDING_BELOW_MAIN_CONTENT_DP))
+                                .build());
+                innerContentBuilder.addContent(mSecondaryLabelText);
+            }
+
+            mainBoxBuilder.addContent(
+                    new Box.Builder()
+                            .setModifiers(modifiers)
+                            .setVerticalAlignment(LayoutElementBuilders.VERTICAL_ALIGN_CENTER)
+                            .setHorizontalAlignment(LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER)
+                            .setHeight(mainContentHeight)
+                            .setWidth(mainContentWidth)
+                            .addContent(innerContentBuilder.build())
+                            .build());
+
+            if (mEdgeContent != null) {
+                mainBoxBuilder.addContent(mEdgeContent);
+            }
+
+            return new EdgeContentLayout(mainBoxBuilder.build());
+        }
+    }
+
+    private boolean areElementsPresent(@ContentBits int elementFlag) {
+        return (getMetadataTag()[FLAG_INDEX] & elementFlag) == elementFlag;
+    }
+
+    /** Returns metadata tag set to this EdgeContentLayout. */
+    @NonNull
+    byte[] getMetadataTag() {
+        return getMetadataTagBytes(checkNotNull(checkNotNull(mImpl.getModifiers()).getMetadata()));
+    }
+
+    /** Returns the inner content from this layout. */
+    @Nullable
+    public LayoutElement getContent() {
+        if (!areElementsPresent(CONTENT_PRESENT)) {
+            return null;
+        }
+        // By tag we know that content exists. It will be at position 0 if there is no primary
+        // label, or at position 2 (primary label, spacer - content) otherwise.
+        int contentPosition = areElementsPresent(PRIMARY_LABEL_PRESENT) ? 2 : 0;
+        return ((Box) mInnerColumn.get(contentPosition)).getContents().get(0);
+    }
+
+    /** Get the primary label content from this layout. */
+    @Nullable
+    public LayoutElement getPrimaryLabelTextContent() {
+        if (!areElementsPresent(PRIMARY_LABEL_PRESENT)) {
+            return null;
+        }
+        // By tag we know that primary label exists. It will always be at position 0.
+        return mInnerColumn.get(0);
+    }
+
+    /** Get the secondary label content from this layout. */
+    @Nullable
+    public LayoutElement getSecondaryLabelTextContent() {
+        if (!areElementsPresent(SECONDARY_LABEL_PRESENT)) {
+            return null;
+        }
+        // By tag we know that secondary label exists. It will always be at last position.
+        return mInnerColumn.get(mInnerColumn.size() - 1);
+    }
+
+    /** Returns the edge content from this layout. */
+    @Nullable
+    public LayoutElement getEdgeContent() {
+        if (areElementsPresent(EDGE_CONTENT_PRESENT)) {
+            return mContents.get(1);
+        }
+        return null;
+    }
+
+    /**
+     * Returns EdgeContentLayout object from the given LayoutElement (e.g. one retrieved from a
+     * container's content with {@code container.getContents().get(index)}) if that element can be
+     * converted to EdgeContentLayout. Otherwise, it will return null.
+     */
+    @Nullable
+    public static EdgeContentLayout fromLayoutElement(@NonNull LayoutElement element) {
+        if (element instanceof EdgeContentLayout) {
+            return (EdgeContentLayout) element;
+        }
+        if (!(element instanceof Box)) {
+            return null;
+        }
+        Box boxElement = (Box) element;
+        if (!checkTag(boxElement.getModifiers(), METADATA_TAG_PREFIX, METADATA_TAG_BASE)) {
+            return null;
+        }
+        // Now we are sure that this element is a EdgeContentLayout.
+        return new EdgeContentLayout(boxElement);
+    }
+
+    @NonNull
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public LayoutElementProto.LayoutElement toLayoutElementProto() {
+        return mImpl.toLayoutElementProto();
+    }
+
+    @Nullable
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public Fingerprint getFingerprint() {
+        return mImpl.getFingerprint();
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/layouts/LayoutDefaults.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/layouts/LayoutDefaults.java
new file mode 100644
index 0000000..42a3381
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/layouts/LayoutDefaults.java
@@ -0,0 +1,134 @@
+/*
+ * 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.wear.protolayout.material.layouts;
+
+import static androidx.wear.protolayout.DimensionBuilders.dp;
+
+import androidx.wear.protolayout.DimensionBuilders.DpProp;
+import androidx.wear.protolayout.material.ButtonDefaults;
+
+/** Contains the default values used by layout templates for Tiles. */
+public class LayoutDefaults {
+    private LayoutDefaults() {}
+
+    /**
+     * The default percentage for the bottom margin for primary chip in the {@link PrimaryLayout}.
+     */
+    static final float PRIMARY_LAYOUT_MARGIN_BOTTOM_ROUND_PERCENT = 2.1f / 100;
+
+    /**
+     * The default percentage for the bottom margin for primary chip in the {@link PrimaryLayout}.
+     */
+    static final float PRIMARY_LAYOUT_MARGIN_BOTTOM_SQUARE_PERCENT = 0;
+
+    /**
+     * The default percentage for the top margin for primary chip in the {@link PrimaryLayout} on
+     * round devices.
+     */
+    static final float PRIMARY_LAYOUT_MARGIN_TOP_ROUND_PERCENT = 16.7f / 100;
+
+    /**
+     * The default percentage for the top margin for primary chip in the {@link PrimaryLayout} on
+     * square devices.
+     */
+    static final float PRIMARY_LAYOUT_MARGIN_TOP_SQUARE_PERCENT = 13.3f / 100;
+
+    /**
+     * The default spacer above primary label in {@link PrimaryLayout} to make space for Tile icon
+     * on round devices.
+     */
+    static final DpProp PRIMARY_LAYOUT_PRIMARY_LABEL_SPACER_HEIGHT_ROUND_DP = dp(0);
+
+    /**
+     * The default spacer above primary label in {@link PrimaryLayout} to make space for Tile icon
+     * on square devices.
+     */
+    static final DpProp PRIMARY_LAYOUT_PRIMARY_LABEL_SPACER_HEIGHT_SQUARE_DP = dp(4);
+
+    /**
+     * The default percentage for the horizontal margin for primary chip in the {@link
+     * PrimaryLayout}.
+     */
+    static final float PRIMARY_LAYOUT_MARGIN_HORIZONTAL_ROUND_PERCENT = 6.3f / 100;
+
+    /**
+     * The default percentage for the horizontal margin for primary chip in the {@link
+     * PrimaryLayout}.
+     */
+    static final float PRIMARY_LAYOUT_MARGIN_HORIZONTAL_SQUARE_PERCENT = 2.8f / 100;
+
+    /**
+     * The padding for the primary chip in {@link PrimaryLayout} so it doesn't bleed off screen if
+     * text is too big.
+     */
+    static final float PRIMARY_LAYOUT_CHIP_HORIZONTAL_PADDING_ROUND_DP = 30;
+
+    /**
+     * The padding for the primary chip in {@link PrimaryLayout} so it doesn't bleed off screen if
+     * text is too big.
+     */
+    static final float PRIMARY_LAYOUT_CHIP_HORIZONTAL_PADDING_SQUARE_DP = 0;
+
+    /** The default horizontal margin in the {@link EdgeContentLayout}. */
+    static final float EDGE_CONTENT_LAYOUT_MARGIN_HORIZONTAL_ROUND_DP = 14;
+
+    /** The default horizontal margin in the {@link EdgeContentLayout}. */
+    static final float EDGE_CONTENT_LAYOUT_MARGIN_HORIZONTAL_SQUARE_DP = 16;
+
+    /**
+     * The recommended padding that should be above the main content (text) in the {@link
+     * EdgeContentLayout}.
+     */
+    public static final float EDGE_CONTENT_LAYOUT_PADDING_ABOVE_MAIN_CONTENT_DP = 6;
+
+    /**
+     * The recommended padding that should be below the main content (text) in the {@link
+     * EdgeContentLayout}.
+     */
+    public static final float EDGE_CONTENT_LAYOUT_PADDING_BELOW_MAIN_CONTENT_DP = 8;
+
+    /** The default spacer width for slots in a {@link MultiSlotLayout}. */
+    public static final DpProp MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH = dp(8);
+
+    /** The recommended space between the main content and additional labels in layouts. */
+    public static final DpProp DEFAULT_VERTICAL_SPACER_HEIGHT = dp(8);
+
+    /** The maximum number of button that can be added to the {@link MultiButtonLayout}. */
+    public static final int MULTI_BUTTON_MAX_NUMBER = 7;
+
+    /**
+     * The default size of button in case when there are 3 or more buttons in the {@link
+     * MultiButtonLayout}.
+     */
+    static final DpProp MULTI_BUTTON_3_PLUS_SIZE = ButtonDefaults.DEFAULT_SIZE;
+
+    /** The default size of button in case when there 2 buttons in the {@link MultiButtonLayout}. */
+    static final DpProp MULTI_BUTTON_2_SIZE = ButtonDefaults.LARGE_SIZE;
+
+    /**
+     * The default size of button in case when there is 1 button in the {@link MultiButtonLayout}.
+     */
+    static final DpProp MULTI_BUTTON_1_SIZE = ButtonDefaults.EXTRA_LARGE_SIZE;
+
+    /** The default width for vertical spacer between buttons in the {@link MultiButtonLayout}. */
+    static final DpProp MULTI_BUTTON_SPACER_WIDTH = dp(6);
+
+    /**
+     * The default height for horizontal spacer between buttons in the {@link MultiButtonLayout}.
+     */
+    static final DpProp MULTI_BUTTON_SPACER_HEIGHT = dp(4);
+}
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/layouts/MultiButtonLayout.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/layouts/MultiButtonLayout.java
new file mode 100644
index 0000000..e4186c5
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/layouts/MultiButtonLayout.java
@@ -0,0 +1,399 @@
+/*
+ * 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.wear.protolayout.material.layouts;
+
+import static androidx.wear.protolayout.DimensionBuilders.wrap;
+import static androidx.wear.protolayout.material.Helper.checkNotNull;
+import static androidx.wear.protolayout.material.Helper.checkTag;
+import static androidx.wear.protolayout.material.Helper.getMetadataTagName;
+import static androidx.wear.protolayout.material.Helper.getTagBytes;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.MULTI_BUTTON_1_SIZE;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.MULTI_BUTTON_2_SIZE;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.MULTI_BUTTON_3_PLUS_SIZE;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.MULTI_BUTTON_MAX_NUMBER;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.MULTI_BUTTON_SPACER_HEIGHT;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.MULTI_BUTTON_SPACER_WIDTH;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.DimensionBuilders.DpProp;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.Column;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+import androidx.wear.protolayout.LayoutElementBuilders.Row;
+import androidx.wear.protolayout.LayoutElementBuilders.Spacer;
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.material.Button;
+import androidx.wear.protolayout.proto.LayoutElementProto;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Opinionated Tiles layout, that can contain between 1 and {@link
+ * LayoutDefaults#MULTI_BUTTON_MAX_NUMBER} number of buttons arranged inline with the Material
+ * guidelines. Can be used as a content passed in to the {@link PrimaryLayout}, but if there is
+ * {@link LayoutDefaults#MULTI_BUTTON_MAX_NUMBER} buttons it should be used on its own.
+ *
+ * <p>For additional examples and suggested layouts see <a
+ * href="/training/wearables/design/tiles-design-system">Tiles Design System</a>.
+ *
+ * <p>When accessing the contents of a container for testing, note that this element can't be simply
+ * casted back to the original type, i.e.:
+ *
+ * <pre>{@code
+ * MultiButtonLayout mbl = new MultiButtonLayout...
+ * Box box = new Box.Builder().addContent(mbl).build();
+ *
+ * MultiButtonLayout myMbl = (MultiButtonLayout) box.getContents().get(0);
+ * }</pre>
+ *
+ * will fail.
+ *
+ * <p>To be able to get {@link MultiButtonLayout} object from any layout element, {@link
+ * #fromLayoutElement} method should be used, i.e.:
+ *
+ * <pre>{@code
+ * MultiButtonLayout myMbl = MultiButtonLayout.fromLayoutElement(box.getContents().get(0));
+ * }</pre>
+ */
+public class MultiButtonLayout implements LayoutElement {
+    /** Tool tag for Metadata in Modifiers, so we know that Box is actually a MultiButtonLayout. */
+    static final String METADATA_TAG = "MBL";
+
+    /** Button distribution where the first row has more buttons than other rows. */
+    public static final int FIVE_BUTTON_DISTRIBUTION_TOP_HEAVY = 1;
+
+    /** Button distribution where the last row has more buttons than other rows. */
+    public static final int FIVE_BUTTON_DISTRIBUTION_BOTTOM_HEAVY = 2;
+
+    @RestrictTo(Scope.LIBRARY)
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({FIVE_BUTTON_DISTRIBUTION_TOP_HEAVY, FIVE_BUTTON_DISTRIBUTION_BOTTOM_HEAVY})
+    public @interface ButtonDistribution {}
+
+    @NonNull private final Box mElement;
+
+    MultiButtonLayout(@NonNull Box mElement) {
+        this.mElement = mElement;
+    }
+
+    /** Builder class for {@link MultiButtonLayout}. */
+    public static final class Builder implements LayoutElement.Builder {
+        @NonNull private final List<LayoutElement> mButtonsContent = new ArrayList<>();
+        private @ButtonDistribution int mFiveButtonDistribution =
+                FIVE_BUTTON_DISTRIBUTION_BOTTOM_HEAVY;
+
+        /**
+         * Creates a builder for the {@link MultiButtonLayout}. Content inside of it can later be
+         * added with {@link #addButtonContent}.
+         */
+        public Builder() {}
+
+        /**
+         * Add one new button to the layout. Note that it is accepted to pass in any {@link
+         * LayoutElement}, but it is strongly recommended to add a {@link Button} as the layout is
+         * optimized for it. Any button added after {@link LayoutDefaults#MULTI_BUTTON_MAX_NUMBER}
+         * is reached will be discarded.
+         */
+        @NonNull
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        // There is no direct matching getter for this setter, but there is a getter that gets all
+        // added buttons.
+        public Builder addButtonContent(@NonNull LayoutElement buttonContent) {
+            mButtonsContent.add(buttonContent);
+            return this;
+        }
+
+        /**
+         * Sets the button distribution for this layout. Button distribution is used in case when
+         * there is 5 buttons in the layout to determine whether the 3 buttons row is at the top or
+         * bottom.
+         */
+        @NonNull
+        public Builder setFiveButtonDistribution(@ButtonDistribution int fiveButtonDistribution) {
+            this.mFiveButtonDistribution = fiveButtonDistribution;
+            return this;
+        }
+
+        /** Constructs and returns {@link MultiButtonLayout} with the provided content and look. */
+        @NonNull
+        @Override
+        public MultiButtonLayout build() {
+            int buttonNum = mButtonsContent.size();
+            if (buttonNum > MULTI_BUTTON_MAX_NUMBER) {
+                throw new IllegalArgumentException(
+                        "Too many buttons are added. Maximum number is "
+                                + MULTI_BUTTON_MAX_NUMBER
+                                + ".");
+            }
+
+            LayoutElement buttons = buildButtons(buttonNum);
+            Box.Builder elementBuilder =
+                    new Box.Builder()
+                        .setModifiers(
+                            new Modifiers.Builder()
+                                .setMetadata(
+                                    new ElementMetadata.Builder()
+                                        .setTagData(getTagBytes(METADATA_TAG))
+                                        .build())
+                                .build())
+                        .addContent(buttons);
+
+            return new MultiButtonLayout(elementBuilder.build());
+        }
+
+        @NonNull
+        private LayoutElement buildButtons(int buttonNum) {
+            switch (buttonNum) {
+                case 1:
+                    return wrapButton(mButtonsContent.get(0), MULTI_BUTTON_1_SIZE);
+                case 2:
+                    return build2ButtonRow(
+                            mButtonsContent.get(0), mButtonsContent.get(1), MULTI_BUTTON_2_SIZE);
+                case 3:
+                    return build3ButtonRow(
+                            mButtonsContent.get(0), mButtonsContent.get(1), mButtonsContent.get(2));
+                case 4:
+                    return new Column.Builder()
+                            .addContent(
+                                    build2ButtonRow(
+                                            mButtonsContent.get(0),
+                                            mButtonsContent.get(1),
+                                            MULTI_BUTTON_3_PLUS_SIZE))
+                            .addContent(buildVerticalSpacer())
+                            .addContent(
+                                    build2ButtonRow(
+                                            mButtonsContent.get(2),
+                                            mButtonsContent.get(3),
+                                            MULTI_BUTTON_3_PLUS_SIZE))
+                            .build();
+                case 5:
+                    return new Column.Builder()
+                            .addContent(
+                                    mFiveButtonDistribution == FIVE_BUTTON_DISTRIBUTION_TOP_HEAVY
+                                            ? build3ButtonRow(
+                                                    mButtonsContent.get(0),
+                                                    mButtonsContent.get(1),
+                                                    mButtonsContent.get(2))
+                                            : build2ButtonRow(
+                                                    mButtonsContent.get(0),
+                                                    mButtonsContent.get(1),
+                                                    MULTI_BUTTON_3_PLUS_SIZE))
+                            .addContent(buildVerticalSpacer())
+                            .addContent(
+                                    mFiveButtonDistribution == FIVE_BUTTON_DISTRIBUTION_TOP_HEAVY
+                                            ? build2ButtonRow(
+                                                    mButtonsContent.get(3),
+                                                    mButtonsContent.get(4),
+                                                    MULTI_BUTTON_3_PLUS_SIZE)
+                                            : build3ButtonRow(
+                                                    mButtonsContent.get(2),
+                                                    mButtonsContent.get(3),
+                                                    mButtonsContent.get(4)))
+                            .build();
+                case 6:
+                    return new Column.Builder()
+                            .addContent(
+                                    build3ButtonRow(
+                                            mButtonsContent.get(0),
+                                            mButtonsContent.get(1),
+                                            mButtonsContent.get(2)))
+                            .addContent(buildVerticalSpacer())
+                            .addContent(
+                                    build3ButtonRow(
+                                            mButtonsContent.get(3),
+                                            mButtonsContent.get(4),
+                                            mButtonsContent.get(5)))
+                            .build();
+                case 7:
+                    return new Column.Builder()
+                            .addContent(
+                                    build2ButtonRow(
+                                            mButtonsContent.get(0),
+                                            mButtonsContent.get(1),
+                                            MULTI_BUTTON_3_PLUS_SIZE))
+                            .addContent(buildVerticalSpacer())
+                            .addContent(
+                                    build3ButtonRow(
+                                            mButtonsContent.get(2),
+                                            mButtonsContent.get(3),
+                                            mButtonsContent.get(4)))
+                            .addContent(buildVerticalSpacer())
+                            .addContent(
+                                    build2ButtonRow(
+                                            mButtonsContent.get(5),
+                                            mButtonsContent.get(6),
+                                            MULTI_BUTTON_3_PLUS_SIZE))
+                            .build();
+            }
+            // This shouldn't happen, but return an empty Box instead of having this method nullable
+            // and checks above.
+            return new Box.Builder().build();
+        }
+
+        @NonNull
+        private Row build3ButtonRow(
+                @NonNull LayoutElement button1,
+                @NonNull LayoutElement button2,
+                @NonNull LayoutElement button3) {
+            return new Row.Builder()
+                    .setWidth(wrap())
+                    .setHeight(wrap())
+                    .addContent(wrapButton(button1, MULTI_BUTTON_3_PLUS_SIZE))
+                    .addContent(buildHorizontalSpacer())
+                    .addContent(wrapButton(button2, MULTI_BUTTON_3_PLUS_SIZE))
+                    .addContent(buildHorizontalSpacer())
+                    .addContent(wrapButton(button3, MULTI_BUTTON_3_PLUS_SIZE))
+                    .build();
+        }
+
+        @NonNull
+        private Row build2ButtonRow(
+                @NonNull LayoutElement button1,
+                @NonNull LayoutElement button2,
+                @NonNull DpProp size) {
+            return new Row.Builder()
+                    .setWidth(wrap())
+                    .setHeight(wrap())
+                    .addContent(wrapButton(button1, size))
+                    .addContent(buildHorizontalSpacer())
+                    .addContent(wrapButton(button2, size))
+                    .build();
+        }
+
+        @NonNull
+        private Spacer buildHorizontalSpacer() {
+            return new Spacer.Builder().setWidth(MULTI_BUTTON_SPACER_WIDTH).build();
+        }
+
+        @NonNull
+        private Spacer buildVerticalSpacer() {
+            return new Spacer.Builder().setHeight(MULTI_BUTTON_SPACER_HEIGHT).build();
+        }
+
+        @NonNull
+        private Box wrapButton(@NonNull LayoutElement button, @NonNull DpProp size) {
+            return new Box.Builder().setWidth(size).setHeight(size).addContent(button).build();
+        }
+    }
+
+    /** Gets the content from this layout, containing all buttons that were added. */
+    @NonNull
+    public List<LayoutElement> getButtonContents() {
+        List<LayoutElement> buttons = new ArrayList<>();
+        List<LayoutElement> contents = mElement.getContents();
+        if (contents.isEmpty()) {
+            return buttons;
+        }
+        LayoutElement innerContent = contents.get(0);
+        if (innerContent instanceof Column) {
+            for (LayoutElement row : ((Column) innerContent).getContents()) {
+                if (row instanceof Row) {
+                    buttons.addAll(getButtonsFromRow((Row) row));
+                }
+            }
+        } else if (innerContent instanceof Row) {
+            return getButtonsFromRow((Row) innerContent);
+        } else if (innerContent instanceof Box) {
+            buttons.add(((Box) innerContent).getContents().get(0));
+        }
+
+        return buttons;
+    }
+
+    /** Returns metadata tag set to this MultiButtonLayouts. */
+    @NonNull
+    String getMetadataTag() {
+        return getMetadataTagName(
+                checkNotNull(checkNotNull(mElement.getModifiers()).getMetadata()));
+    }
+
+    /**
+     * Gets the button distribution from this layout for the case when there is 5 buttons in the
+     * layout. If there is more or less buttons than 5, default {@link
+     * #FIVE_BUTTON_DISTRIBUTION_BOTTOM_HEAVY} will be returned.
+     */
+    public int getFiveButtonDistribution() {
+        List<LayoutElement> contents = mElement.getContents();
+        if (getButtonContents().size() != 5) {
+            return FIVE_BUTTON_DISTRIBUTION_BOTTOM_HEAVY;
+        }
+        LayoutElement innerContent = contents.get(0);
+        if (innerContent instanceof Column && ((Column) innerContent).getContents().size() == 3) {
+            // 1st and 3rd row are buttons. Check whether the first row has 5 (3 buttons + 2 spacer)
+            // - top heavy or 3 (2 buttons + spacer) - bottom heavy elements.
+            LayoutElement firstElement = ((Column) innerContent).getContents().get(0);
+            if (firstElement instanceof Row && ((Row) firstElement).getContents().size() == 5) {
+                return FIVE_BUTTON_DISTRIBUTION_TOP_HEAVY;
+            }
+        }
+        return FIVE_BUTTON_DISTRIBUTION_BOTTOM_HEAVY;
+    }
+
+    private List<LayoutElement> getButtonsFromRow(Row row) {
+        List<LayoutElement> buttons = new ArrayList<>();
+        for (LayoutElement element : row.getContents()) {
+            if (element instanceof Box) {
+                buttons.add(((Box) element).getContents().get(0));
+            }
+        }
+        return buttons;
+    }
+
+    /**
+     * Returns MultiButtonLayout object from the given LayoutElement (e.g. one retrieved from a
+     * container's content with {@code container.getContents().get(index)}) if that element can be
+     * converted to MultiButtonLayout. Otherwise, it will return null.
+     */
+    @Nullable
+    public static MultiButtonLayout fromLayoutElement(@NonNull LayoutElement element) {
+        if (element instanceof MultiButtonLayout) {
+            return (MultiButtonLayout) element;
+        }
+        if (!(element instanceof Box)) {
+            return null;
+        }
+        Box boxElement = (Box) element;
+        if (!checkTag(boxElement.getModifiers(), METADATA_TAG)) {
+            return null;
+        }
+        // Now we are sure that this element is a MultiButtonLayout.
+        return new MultiButtonLayout(boxElement);
+    }
+
+    @NonNull
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public LayoutElementProto.LayoutElement toLayoutElementProto() {
+        return mElement.toLayoutElementProto();
+    }
+
+    @Nullable
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public Fingerprint getFingerprint() {
+        return mElement.getFingerprint();
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/layouts/MultiSlotLayout.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/layouts/MultiSlotLayout.java
new file mode 100644
index 0000000..8f9e903
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/layouts/MultiSlotLayout.java
@@ -0,0 +1,237 @@
+/*
+ * 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.wear.protolayout.material.layouts;
+
+import static androidx.annotation.Dimension.DP;
+import static androidx.wear.protolayout.DimensionBuilders.dp;
+import static androidx.wear.protolayout.DimensionBuilders.wrap;
+import static androidx.wear.protolayout.material.Helper.checkNotNull;
+import static androidx.wear.protolayout.material.Helper.checkTag;
+import static androidx.wear.protolayout.material.Helper.getMetadataTagName;
+import static androidx.wear.protolayout.material.Helper.getTagBytes;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH;
+
+import android.annotation.SuppressLint;
+
+import androidx.annotation.Dimension;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.DimensionBuilders.DpProp;
+import androidx.wear.protolayout.DimensionBuilders.SpacerDimension;
+import androidx.wear.protolayout.LayoutElementBuilders;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+import androidx.wear.protolayout.LayoutElementBuilders.Row;
+import androidx.wear.protolayout.LayoutElementBuilders.Spacer;
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.proto.LayoutElementProto;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Opinionated Tiles layout, row like style with horizontally aligned and spaced slots (for icons or
+ * other small content). Should be used as a content passed in to the {@link PrimaryLayout}.
+ *
+ * <p>Recommended number of added slots is 1 to 3. Their width will be the width of an element
+ * passed in, with the {@link LayoutDefaults#MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH} space
+ * between.
+ *
+ * <p>For additional examples and suggested layouts see <a
+ * href="/training/wearables/design/tiles-design-system">Tiles Design System</a>.
+ *
+ * <p>When accessing the contents of a container for testing, note that this element can't be simply
+ * casted back to the original type, i.e.:
+ *
+ * <pre>{@code
+ * MultiSlotLayout msl = new MultiSlotLayout...
+ * Box box = new Box.Builder().addContent(msl).build();
+ *
+ * MultiSlotLayout myMsl = (MultiSlotLayout) box.getContents().get(0);
+ * }</pre>
+ *
+ * will fail.
+ *
+ * <p>To be able to get {@link MultiSlotLayout} object from any layout element, {@link
+ * #fromLayoutElement} method should be used, i.e.:
+ *
+ * <pre>{@code
+ * MultiSlotLayout myMsl = MultiSlotLayout.fromLayoutElement(box.getContents().get(0));
+ * }</pre>
+ */
+public class MultiSlotLayout implements LayoutElement {
+    /** Tool tag for Metadata in Modifiers, so we know that Row is actually a MultiSlotLayout. */
+    static final String METADATA_TAG = "MSL";
+
+    @NonNull private final Row mElement;
+
+    MultiSlotLayout(@NonNull Row mElement) {
+        this.mElement = mElement;
+    }
+
+    /** Builder class for {@link MultiSlotLayout}. */
+    public static final class Builder implements LayoutElement.Builder {
+
+        @NonNull private final List<LayoutElement> mSlotsContent = new ArrayList<>();
+        @NonNull private DpProp mHorizontalSpacerWidth = MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH;
+
+        /**
+         * Creates a builder for the {@link MultiSlotLayout}. Content inside of it can later be
+         * added with {@link #addSlotContent}.
+         */
+        public Builder() {}
+
+        /** Add one new slot to the layout with the given content inside. */
+        @NonNull
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        // There is no direct matching getter for this setter, but there is a getter that gets all
+        // added slots.
+        public Builder addSlotContent(@NonNull LayoutElement slotContent) {
+            mSlotsContent.add(slotContent);
+            return this;
+        }
+
+        /**
+         * Sets the horizontal spacer width which is used as a space between slots if there is more
+         * than one slot. If not set, {@link
+         * LayoutDefaults#MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH} will be used.
+         */
+        @NonNull
+        public Builder setHorizontalSpacerWidth(@Dimension(unit = DP) float width) {
+            this.mHorizontalSpacerWidth = dp(width);
+            return this;
+        }
+
+        /** Constructs and returns {@link MultiSlotLayout} with the provided content and look. */
+        @NonNull
+        @Override
+        // The @Dimension(unit = DP) on mVerticalSpacerHeight.getValue() is seemingly being ignored,
+        // so lint complains that we're passing PX to something expecting DP. Just suppress the
+        // warning for now.
+        @SuppressLint("ResourceType")
+        public MultiSlotLayout build() {
+            Row.Builder rowBuilder =
+                    new Row.Builder()
+                            .setHeight(wrap())
+                            .setVerticalAlignment(LayoutElementBuilders.VERTICAL_ALIGN_CENTER)
+                            .setWidth(wrap())
+                            .setModifiers(
+                                    new Modifiers.Builder()
+                                            .setMetadata(
+                                                    new ElementMetadata.Builder()
+                                                            .setTagData(getTagBytes(METADATA_TAG))
+                                                            .build())
+                                            .build());
+            if (!mSlotsContent.isEmpty()) {
+
+                boolean isFirst = true;
+                for (LayoutElement slot : mSlotsContent) {
+                    if (!isFirst) {
+                        rowBuilder.addContent(
+                                new Spacer.Builder().setWidth(mHorizontalSpacerWidth).build());
+                    } else {
+                        isFirst = false;
+                    }
+                    rowBuilder.addContent(
+                            new Box.Builder()
+                                    .setWidth(wrap())
+                                    .setHeight(wrap())
+                                    .addContent(slot)
+                                    .build());
+                }
+            }
+
+            return new MultiSlotLayout(rowBuilder.build());
+        }
+    }
+
+    /** Gets the content from this layout, containing all slots that were added. */
+    @NonNull
+    public List<LayoutElement> getSlotContents() {
+        List<LayoutElement> slots = new ArrayList<>();
+        for (LayoutElement slot : mElement.getContents()) {
+            if (slot instanceof Box) {
+                slots.add(((Box) slot).getContents().get(0));
+            }
+        }
+        return slots;
+    }
+
+    /** Gets the width of horizontal spacer that is between slots. */
+    // The @Dimension(unit = DP) on getLinearDimension.getValue() is seemingly being ignored, so
+    // lint complains that we're passing PX to something expecting DP. Just suppress the warning for
+    // now.
+    @SuppressLint("ResourceType")
+    @Dimension(unit = DP)
+    public float getHorizontalSpacerWidth() {
+        for (LayoutElement slot : mElement.getContents()) {
+            if (slot instanceof Spacer) {
+                SpacerDimension width = ((Spacer) slot).getWidth();
+                if (width instanceof DpProp) {
+                    return ((DpProp) width).getValue();
+                }
+            }
+        }
+        return LayoutDefaults.MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH.getValue();
+    }
+
+    /** Returns metadata tag set to this MultiSlotLayout. */
+    @NonNull
+    String getMetadataTag() {
+        return getMetadataTagName(
+                checkNotNull(checkNotNull(mElement.getModifiers()).getMetadata()));
+    }
+
+    /**
+     * Returns MultiSlotLayout object from the given LayoutElement (e.g. one retrieved from a
+     * container's content with {@code container.getContents().get(index)}) if that element can be
+     * converted to MultiSlotLayout. Otherwise, it will return null.
+     */
+    @Nullable
+    public static MultiSlotLayout fromLayoutElement(@NonNull LayoutElement element) {
+        if (element instanceof MultiSlotLayout) {
+            return (MultiSlotLayout) element;
+        }
+        if (!(element instanceof Row)) {
+            return null;
+        }
+        Row rowElement = (Row) element;
+        if (!checkTag(rowElement.getModifiers(), METADATA_TAG)) {
+            return null;
+        }
+        // Now we are sure that this element is a MultiSlotLayout.
+        return new MultiSlotLayout(rowElement);
+    }
+
+    @NonNull
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public LayoutElementProto.LayoutElement toLayoutElementProto() {
+        return mElement.toLayoutElementProto();
+    }
+
+    @Nullable
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public Fingerprint getFingerprint() {
+        return mElement.getFingerprint();
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/layouts/PrimaryLayout.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/layouts/PrimaryLayout.java
new file mode 100644
index 0000000..2bbbac5
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/layouts/PrimaryLayout.java
@@ -0,0 +1,540 @@
+/*
+ * 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.wear.protolayout.material.layouts;
+
+import static androidx.annotation.Dimension.DP;
+import static androidx.wear.protolayout.DimensionBuilders.dp;
+import static androidx.wear.protolayout.DimensionBuilders.expand;
+import static androidx.wear.protolayout.DimensionBuilders.wrap;
+import static androidx.wear.protolayout.material.ChipDefaults.COMPACT_HEIGHT_TAPPABLE;
+import static androidx.wear.protolayout.material.Helper.checkNotNull;
+import static androidx.wear.protolayout.material.Helper.checkTag;
+import static androidx.wear.protolayout.material.Helper.getMetadataTagBytes;
+import static androidx.wear.protolayout.material.Helper.getTagBytes;
+import static androidx.wear.protolayout.material.Helper.isRoundDevice;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.DEFAULT_VERTICAL_SPACER_HEIGHT;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.PRIMARY_LAYOUT_CHIP_HORIZONTAL_PADDING_ROUND_DP;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.PRIMARY_LAYOUT_CHIP_HORIZONTAL_PADDING_SQUARE_DP;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.PRIMARY_LAYOUT_MARGIN_BOTTOM_ROUND_PERCENT;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.PRIMARY_LAYOUT_MARGIN_BOTTOM_SQUARE_PERCENT;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.PRIMARY_LAYOUT_MARGIN_HORIZONTAL_ROUND_PERCENT;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.PRIMARY_LAYOUT_MARGIN_HORIZONTAL_SQUARE_PERCENT;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.PRIMARY_LAYOUT_MARGIN_TOP_ROUND_PERCENT;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.PRIMARY_LAYOUT_MARGIN_TOP_SQUARE_PERCENT;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.PRIMARY_LAYOUT_PRIMARY_LABEL_SPACER_HEIGHT_ROUND_DP;
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.PRIMARY_LAYOUT_PRIMARY_LABEL_SPACER_HEIGHT_SQUARE_DP;
+
+import android.annotation.SuppressLint;
+
+import androidx.annotation.Dimension;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.protolayout.DimensionBuilders.DpProp;
+import androidx.wear.protolayout.DimensionBuilders.SpacerDimension;
+import androidx.wear.protolayout.LayoutElementBuilders;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.Column;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+import androidx.wear.protolayout.LayoutElementBuilders.Spacer;
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+import androidx.wear.protolayout.ModifiersBuilders.Padding;
+import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.material.CompactChip;
+import androidx.wear.protolayout.proto.LayoutElementProto;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tiles layout that represents a suggested layout style for Material Tiles with the primary
+ * (compact) chip at the bottom with the given content in the center and the recommended margin and
+ * padding applied. There is a fixed slot for an optional primary label above or optional secondary
+ * label below the main content area.
+ *
+ * <p>It is highly recommended that main content has max lines between 2 and 4 (dependant on labels
+ * present), i.e.: * No labels are present: content with max 4 lines, * 1 label is present: content
+ * with max 3 lines, * 2 labels are present: content with max 2 lines.
+ *
+ * <p>For additional examples and suggested layouts see <a
+ * href="/training/wearables/design/tiles-design-system">Tiles Design System</a>.
+ *
+ * <p>When accessing the contents of a container for testing, note that this element can't be simply
+ * casted back to the original type, i.e.:
+ *
+ * <pre>{@code
+ * PrimaryLayout pl = new PrimaryLayout...
+ * Box box = new Box.Builder().addContent(pl).build();
+ *
+ * PrimaryLayout myPl = (PrimaryLayout) box.getContents().get(0);
+ * }</pre>
+ *
+ * will fail.
+ *
+ * <p>To be able to get {@link PrimaryLayout} object from any layout element, {@link
+ * #fromLayoutElement} method should be used, i.e.:
+ *
+ * <pre>{@code
+ * PrimaryLayout myPl = PrimaryLayout.fromLayoutElement(box.getContents().get(0));
+ * }</pre>
+ */
+public class PrimaryLayout implements LayoutElement {
+    /**
+     * Prefix tool tag for Metadata in Modifiers, so we know that Box is actually a PrimaryLayout.
+     */
+    static final String METADATA_TAG_PREFIX = "PL_";
+
+    /** Index for byte array that contains bits to check whether the contents are present or not. */
+    static final int FLAG_INDEX = METADATA_TAG_PREFIX.length();
+
+    /**
+     * Base tool tag for Metadata in Modifiers, so we know that Box is actually a PrimaryLayout and
+     * what optional content is added.
+     */
+    static final byte[] METADATA_TAG_BASE =
+            Arrays.copyOf(getTagBytes(METADATA_TAG_PREFIX), FLAG_INDEX + 1);
+
+    /**
+     * Bit position in a byte on {@link #FLAG_INDEX} index in metadata byte array to check whether
+     * the primary chip is present or not.
+     */
+    static final int CHIP_PRESENT = 0x1;
+    /**
+     * Bit position in a byte on {@link #FLAG_INDEX} index in metadata byte array to check whether
+     * the primary label is present or not.
+     */
+    static final int PRIMARY_LABEL_PRESENT = 0x2;
+    /**
+     * Bit position in a byte on {@link #FLAG_INDEX} index in metadata byte array to check whether
+     * the secondary label is present or not.
+     */
+    static final int SECONDARY_LABEL_PRESENT = 0x4;
+    /**
+     * Bit position in a byte on {@link #FLAG_INDEX} index in metadata byte array to check whether
+     * the content is present or not.
+     */
+    static final int CONTENT_PRESENT = 0x8;
+
+    /** Position of the primary label in its own inner column if exists. */
+    static final int PRIMARY_LABEL_POSITION = 1;
+    /** Position of the content in its own inner column. */
+    static final int CONTENT_ONLY_POSITION = 0;
+    /** Position of the primary chip in main layout column. */
+    static final int PRIMARY_CHIP_POSITION = 1;
+
+    @RestrictTo(Scope.LIBRARY)
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            flag = true,
+            value = {CHIP_PRESENT, PRIMARY_LABEL_PRESENT, SECONDARY_LABEL_PRESENT, CONTENT_PRESENT})
+    @interface ContentBits {}
+
+    @NonNull private final Box mImpl;
+
+    // This contains inner columns and primary chip.
+    @NonNull private final List<LayoutElement> mAllContent;
+    // This contains optional labels, spacers and main content.
+    @NonNull private final List<LayoutElement> mPrimaryLabel;
+    // This contains optional labels, spacers and main content.
+    @NonNull private final List<LayoutElement> mContentAndSecondaryLabel;
+
+    PrimaryLayout(@NonNull Box layoutElement) {
+        this.mImpl = layoutElement;
+        this.mAllContent = ((Column) layoutElement.getContents().get(0)).getContents();
+        List<LayoutElement> innerContent = ((Column) mAllContent.get(0)).getContents();
+        this.mPrimaryLabel = ((Column) innerContent.get(0)).getContents();
+        this.mContentAndSecondaryLabel =
+                ((Column) ((Box) innerContent.get(1)).getContents().get(0)).getContents();
+    }
+
+    /** Builder class for {@link PrimaryLayout}. */
+    public static final class Builder implements LayoutElement.Builder {
+        @NonNull private final DeviceParameters mDeviceParameters;
+        @Nullable private LayoutElement mPrimaryChip = null;
+        @Nullable private LayoutElement mPrimaryLabelText = null;
+        @Nullable private LayoutElement mSecondaryLabelText = null;
+        @NonNull private LayoutElement mContent = new Box.Builder().build();
+        @NonNull private DpProp mVerticalSpacerHeight = DEFAULT_VERTICAL_SPACER_HEIGHT;
+        private byte mMetadataContentByte = 0;
+
+        /**
+         * Creates a builder for the {@link PrimaryLayout} from the given content. Content inside of
+         * it can later be set with {@link #setContent}, {@link #setPrimaryChipContent}, {@link
+         * #setPrimaryLabelTextContent} and {@link #setSecondaryLabelTextContent}.
+         */
+        public Builder(@NonNull DeviceParameters deviceParameters) {
+            this.mDeviceParameters = deviceParameters;
+        }
+
+        /**
+         * Sets the element which is in the slot at the bottom of the layout. Note that it is
+         * accepted to pass in any {@link LayoutElement}, but it is strongly recommended to add a
+         * {@link CompactChip} as the layout is optimized for it.
+         */
+        @NonNull
+        public Builder setPrimaryChipContent(@NonNull LayoutElement compactChip) {
+            this.mPrimaryChip = compactChip;
+            mMetadataContentByte = (byte) (mMetadataContentByte | CHIP_PRESENT);
+            return this;
+        }
+
+        /** Sets the content in the primary label slot which will be above the main content. */
+        @NonNull
+        public Builder setPrimaryLabelTextContent(@NonNull LayoutElement primaryLabelText) {
+            this.mPrimaryLabelText = primaryLabelText;
+            mMetadataContentByte = (byte) (mMetadataContentByte | PRIMARY_LABEL_PRESENT);
+            return this;
+        }
+
+        /**
+         * Sets the content in the primary label slot which will be below the main content. It is
+         * highly recommended to have primary label set when having secondary label.
+         */
+        @NonNull
+        public Builder setSecondaryLabelTextContent(@NonNull LayoutElement secondaryLabelText) {
+            this.mSecondaryLabelText = secondaryLabelText;
+            mMetadataContentByte = (byte) (mMetadataContentByte | SECONDARY_LABEL_PRESENT);
+            return this;
+        }
+
+        /**
+         * Sets the additional content to this layout, above the primary chip.
+         *
+         * The content slot will wrap the elements' height, so the height of the given content must
+         * be fixed or set to wrap ({@code expand} can't be used).
+         *
+         * This layout has built-in horizontal margins, so the given content should have width set
+         * to {@code expand} to use all the available space, rather than an explicit width which may
+         * lead to clipping.
+         */
+        @NonNull
+        public Builder setContent(@NonNull LayoutElement content) {
+            this.mContent = content;
+            mMetadataContentByte = (byte) (mMetadataContentByte | CONTENT_PRESENT);
+            return this;
+        }
+
+        /**
+         * Sets the vertical spacer height which is used as a space between main content and
+         * secondary label if there is any. If not set, {@link
+         * LayoutDefaults#DEFAULT_VERTICAL_SPACER_HEIGHT} will be used.
+         */
+        @NonNull
+        // The @Dimension(unit = DP) on dp() is seemingly being ignored, so lint complains that
+        // we're passing PX to something expecting DP. Just suppress the warning for now.
+        @SuppressLint("ResourceType")
+        public Builder setVerticalSpacerHeight(@Dimension(unit = DP) float height) {
+            this.mVerticalSpacerHeight = dp(height);
+            return this;
+        }
+
+        /** Constructs and returns {@link PrimaryLayout} with the provided content and look. */
+        // The @Dimension(unit = DP) on dp() is seemingly being ignored, so lint complains that
+        // we're passing DP to something expecting PX. Just suppress the warning for now.
+        @SuppressLint("ResourceType")
+        @NonNull
+        @Override
+        public PrimaryLayout build() {
+            float topPadding = getTopPadding();
+            float bottomPadding = getBottomPadding();
+            float horizontalPadding = getHorizontalPadding();
+            float horizontalChipPadding = getChipHorizontalPadding();
+
+            float primaryChipHeight = mPrimaryChip != null ? COMPACT_HEIGHT_TAPPABLE.getValue() : 0;
+
+            DpProp mainContentHeight =
+                    dp(
+                            mDeviceParameters.getScreenHeightDp()
+                                    - primaryChipHeight
+                                    - bottomPadding
+                                    - topPadding);
+
+            // Layout organization: column(column(primary label + spacer + (box(column(content +
+            // secondary label))) + chip)
+
+            // First column that has all other content and chip.
+            Column.Builder layoutBuilder = new Column.Builder();
+
+            // Contains primary label, main content and secondary label. Primary label will be
+            // wrapped, while other content will be expanded so it can be centered in the remaining
+            // space.
+            Column.Builder contentAreaBuilder =
+                    new Column.Builder()
+                            .setWidth(expand())
+                            .setHeight(mainContentHeight)
+                            .setHorizontalAlignment(LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER);
+
+            // Contains main content and secondary label with wrapped height so it can be put inside
+            // of the Box to be centered.
+            Column.Builder contentSecondaryLabelBuilder =
+                    new Column.Builder()
+                            .setWidth(expand())
+                            .setHeight(wrap())
+                            .setHorizontalAlignment(LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER);
+
+            // Needs to be in column because of the spacers.
+            Column.Builder primaryLabelBuilder =
+                    new Column.Builder().setWidth(expand()).setHeight(wrap());
+
+            if (mPrimaryLabelText != null) {
+                primaryLabelBuilder.addContent(
+                        new Spacer.Builder().setHeight(getPrimaryLabelTopSpacerHeight()).build());
+                primaryLabelBuilder.addContent(mPrimaryLabelText);
+            }
+
+            contentAreaBuilder.addContent(primaryLabelBuilder.build());
+
+            contentSecondaryLabelBuilder.addContent(
+                    new Box.Builder()
+                            .setVerticalAlignment(LayoutElementBuilders.VERTICAL_ALIGN_CENTER)
+                            .setWidth(expand())
+                            .setHeight(wrap())
+                            .addContent(mContent)
+                            .build());
+
+            if (mSecondaryLabelText != null) {
+                contentSecondaryLabelBuilder.addContent(
+                        new Spacer.Builder().setHeight(mVerticalSpacerHeight).build());
+                contentSecondaryLabelBuilder.addContent(mSecondaryLabelText);
+            }
+
+            contentAreaBuilder.addContent(
+                    new Box.Builder()
+                            .setVerticalAlignment(LayoutElementBuilders.VERTICAL_ALIGN_CENTER)
+                            .setWidth(expand())
+                            .setHeight(expand())
+                            .addContent(contentSecondaryLabelBuilder.build())
+                            .build());
+
+            layoutBuilder
+                    .setModifiers(
+                            new Modifiers.Builder()
+                                    .setPadding(
+                                            new Padding.Builder()
+                                                    .setStart(dp(horizontalPadding))
+                                                    .setEnd(dp(horizontalPadding))
+                                                    .setTop(dp(topPadding))
+                                                    .setBottom(dp(bottomPadding))
+                                                    .build())
+                                    .build())
+                    .setWidth(expand())
+                    .setHeight(expand())
+                    .setHorizontalAlignment(LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER);
+
+            layoutBuilder.addContent(contentAreaBuilder.build());
+
+            if (mPrimaryChip != null) {
+                layoutBuilder.addContent(
+                        new Box.Builder()
+                                .setVerticalAlignment(LayoutElementBuilders.VERTICAL_ALIGN_BOTTOM)
+                                .setWidth(expand())
+                                .setHeight(wrap())
+                                .setModifiers(
+                                        new Modifiers.Builder()
+                                                .setPadding(
+                                                        new Padding.Builder()
+                                                                .setStart(dp(horizontalChipPadding))
+                                                                .setEnd(dp(horizontalChipPadding))
+                                                                .build())
+                                                .build())
+                                .addContent(mPrimaryChip)
+                                .build());
+            }
+
+            byte[] metadata = METADATA_TAG_BASE.clone();
+            metadata[FLAG_INDEX] = mMetadataContentByte;
+
+            Box.Builder element =
+                    new Box.Builder()
+                            .setWidth(expand())
+                            .setHeight(expand())
+                            .setModifiers(
+                                    new Modifiers.Builder()
+                                            .setMetadata(
+                                                    new ElementMetadata.Builder()
+                                                            .setTagData(metadata)
+                                                            .build())
+                                            .build())
+                            .setVerticalAlignment(LayoutElementBuilders.VERTICAL_ALIGN_BOTTOM)
+                            .addContent(layoutBuilder.build());
+
+            return new PrimaryLayout(element.build());
+        }
+
+        /**
+         * Returns the recommended bottom padding, based on percentage values in {@link
+         * LayoutDefaults}.
+         */
+        private float getBottomPadding() {
+            return mPrimaryChip != null
+                    ? (mDeviceParameters.getScreenHeightDp()
+                            * (isRoundDevice(mDeviceParameters)
+                                    ? PRIMARY_LAYOUT_MARGIN_BOTTOM_ROUND_PERCENT
+                                    : PRIMARY_LAYOUT_MARGIN_BOTTOM_SQUARE_PERCENT))
+                    : getTopPadding();
+        }
+
+        /**
+         * Returns the recommended top padding, based on percentage values in {@link
+         * LayoutDefaults}.
+         */
+        @Dimension(unit = DP)
+        private float getTopPadding() {
+            return mDeviceParameters.getScreenHeightDp()
+                    * (isRoundDevice(mDeviceParameters)
+                            ? PRIMARY_LAYOUT_MARGIN_TOP_ROUND_PERCENT
+                            : PRIMARY_LAYOUT_MARGIN_TOP_SQUARE_PERCENT);
+        }
+
+        /**
+         * Returns the recommended horizontal padding, based on percentage values in {@link
+         * LayoutDefaults}.
+         */
+        @Dimension(unit = DP)
+        private float getHorizontalPadding() {
+            return mDeviceParameters.getScreenWidthDp()
+                    * (isRoundDevice(mDeviceParameters)
+                            ? PRIMARY_LAYOUT_MARGIN_HORIZONTAL_ROUND_PERCENT
+                            : PRIMARY_LAYOUT_MARGIN_HORIZONTAL_SQUARE_PERCENT);
+        }
+
+        /**
+         * Returns the recommended horizontal padding for primary chip, based on percentage values
+         * and DP values in {@link LayoutDefaults}.
+         */
+        @Dimension(unit = DP)
+        private float getChipHorizontalPadding() {
+            return isRoundDevice(mDeviceParameters)
+                    ? PRIMARY_LAYOUT_CHIP_HORIZONTAL_PADDING_ROUND_DP
+                    : PRIMARY_LAYOUT_CHIP_HORIZONTAL_PADDING_SQUARE_DP;
+        }
+
+        /** Returns the spacer height to be placed above primary label to accommodate Tile icon. */
+        @NonNull
+        private DpProp getPrimaryLabelTopSpacerHeight() {
+            return isRoundDevice(mDeviceParameters)
+                    ? PRIMARY_LAYOUT_PRIMARY_LABEL_SPACER_HEIGHT_ROUND_DP
+                    : PRIMARY_LAYOUT_PRIMARY_LABEL_SPACER_HEIGHT_SQUARE_DP;
+        }
+    }
+
+    /** Get the primary label content from this layout. */
+    @Nullable
+    public LayoutElement getPrimaryLabelTextContent() {
+        if (!areElementsPresent(PRIMARY_LABEL_PRESENT)) {
+            return null;
+        }
+        return mPrimaryLabel.get(PRIMARY_LABEL_POSITION);
+    }
+
+    /** Get the secondary label content from this layout. */
+    @Nullable
+    public LayoutElement getSecondaryLabelTextContent() {
+        if (!areElementsPresent(SECONDARY_LABEL_PRESENT)) {
+            return null;
+        }
+        // By tag we know that secondary label exists. It will always be at last position.
+        return mContentAndSecondaryLabel.get(mContentAndSecondaryLabel.size() - 1);
+    }
+
+    /** Get the inner content from this layout. */
+    @Nullable
+    public LayoutElement getContent() {
+        if (!areElementsPresent(CONTENT_PRESENT)) {
+            return null;
+        }
+        return ((Box) mContentAndSecondaryLabel.get(CONTENT_ONLY_POSITION)).getContents().get(0);
+    }
+
+    /** Get the primary chip content from this layout. */
+    @Nullable
+    public LayoutElement getPrimaryChipContent() {
+        if (areElementsPresent(CHIP_PRESENT)) {
+            return ((Box) mAllContent.get(PRIMARY_CHIP_POSITION)).getContents().get(0);
+        }
+        return null;
+    }
+
+    /** Get the vertical spacer height from this layout. */
+    // The @Dimension(unit = DP) on getValue() is seemingly being ignored, so lint complains that
+    // we're passing PX to something expecting DP. Just suppress the warning for now.
+    @SuppressLint("ResourceType")
+    @Dimension(unit = DP)
+    public float getVerticalSpacerHeight() {
+        if (areElementsPresent(SECONDARY_LABEL_PRESENT)) {
+            LayoutElement element = mContentAndSecondaryLabel.get(CONTENT_ONLY_POSITION + 1);
+            if (element instanceof Spacer) {
+                SpacerDimension height = ((Spacer) element).getHeight();
+                if (height instanceof DpProp) {
+                    return ((DpProp) height).getValue();
+                }
+            }
+        }
+        return DEFAULT_VERTICAL_SPACER_HEIGHT.getValue();
+    }
+
+    private boolean areElementsPresent(@ContentBits int elementFlag) {
+        return (getMetadataTag()[FLAG_INDEX] & elementFlag) == elementFlag;
+    }
+
+    /** Returns metadata tag set to this PrimaryLayout. */
+    @NonNull
+    byte[] getMetadataTag() {
+        return getMetadataTagBytes(checkNotNull(checkNotNull(mImpl.getModifiers()).getMetadata()));
+    }
+
+    /**
+     * Returns PrimaryLayout object from the given LayoutElement (e.g. one retrieved from a
+     * container's content with {@code container.getContents().get(index)}) if that element can be
+     * converted to PrimaryLayout. Otherwise, it will return null.
+     */
+    @Nullable
+    public static PrimaryLayout fromLayoutElement(@NonNull LayoutElement element) {
+        if (element instanceof PrimaryLayout) {
+            return (PrimaryLayout) element;
+        }
+        if (!(element instanceof Box)) {
+            return null;
+        }
+        Box boxElement = (Box) element;
+        if (!checkTag(boxElement.getModifiers(), METADATA_TAG_PREFIX, METADATA_TAG_BASE)) {
+            return null;
+        }
+        // Now we are sure that this element is a PrimaryLayout.
+        return new PrimaryLayout(boxElement);
+    }
+
+    @NonNull
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public LayoutElementProto.LayoutElement toLayoutElementProto() {
+        return mImpl.toLayoutElementProto();
+    }
+
+    @Nullable
+    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public Fingerprint getFingerprint() {
+        return mImpl.getFingerprint();
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/main/res/drawable/avatar.png b/wear/protolayout/protolayout-material/src/main/res/drawable/avatar.png
new file mode 100644
index 0000000..a6da988
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/res/drawable/avatar.png
Binary files differ
diff --git a/wear/protolayout/protolayout-material/src/main/res/drawable/icon.xml b/wear/protolayout/protolayout-material/src/main/res/drawable/icon.xml
new file mode 100644
index 0000000..21eb853
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/main/res/drawable/icon.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:fillColor="#000"
+      android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+</vector>
diff --git a/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/ButtonColorsTest.java b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/ButtonColorsTest.java
new file mode 100644
index 0000000..fcbf8b2
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/ButtonColorsTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.wear.protolayout.ColorBuilders.argb;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.wear.protolayout.ColorBuilders.ColorProp;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(AndroidJUnit4.class)
+@DoNotInstrument
+public class ButtonColorsTest {
+    private static final int ARGB_BACKGROUND_COLOR = 0x12345678;
+    private static final int ARGB_CONTENT_COLOR = 0x11223344;
+    private static final ColorProp BACKGROUND_COLOR = argb(ARGB_BACKGROUND_COLOR);
+    private static final ColorProp CONTENT_COLOR = argb(ARGB_CONTENT_COLOR);
+    private static final Colors COLORS = new Colors(0x123, 0x234, 0x345, 0x456);
+
+    @Test
+    public void testCreateButtonColorsFromArgb() {
+        ButtonColors buttonColors = new ButtonColors(ARGB_BACKGROUND_COLOR, ARGB_CONTENT_COLOR);
+
+        assertThat(buttonColors.getBackgroundColor().getArgb())
+                .isEqualTo(BACKGROUND_COLOR.getArgb());
+        assertThat(buttonColors.getContentColor().getArgb()).isEqualTo(CONTENT_COLOR.getArgb());
+    }
+
+    @Test
+    public void testCreateButtonColorsFromColorProp() {
+        ButtonColors buttonColors = new ButtonColors(BACKGROUND_COLOR, CONTENT_COLOR);
+
+        assertThat(buttonColors.getBackgroundColor().getArgb())
+                .isEqualTo(BACKGROUND_COLOR.getArgb());
+        assertThat(buttonColors.getContentColor().getArgb()).isEqualTo(CONTENT_COLOR.getArgb());
+    }
+
+    @Test
+    public void testCreateButtonColorsFromHelperPrimary() {
+        ButtonColors buttonColors = ButtonColors.primaryButtonColors(COLORS);
+
+        assertThat(buttonColors.getBackgroundColor().getArgb()).isEqualTo(COLORS.getPrimary());
+        assertThat(buttonColors.getContentColor().getArgb()).isEqualTo(COLORS.getOnPrimary());
+    }
+
+    @Test
+    public void testCreateButtonColorsFromHelperSurface() {
+        ButtonColors buttonColors = ButtonColors.secondaryButtonColors(COLORS);
+
+        assertThat(buttonColors.getBackgroundColor().getArgb()).isEqualTo(COLORS.getSurface());
+        assertThat(buttonColors.getContentColor().getArgb()).isEqualTo(COLORS.getOnSurface());
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/ButtonTest.java b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/ButtonTest.java
new file mode 100644
index 0000000..1cdede2
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/ButtonTest.java
@@ -0,0 +1,355 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.wear.protolayout.ColorBuilders.argb;
+import static androidx.wear.protolayout.DimensionBuilders.dp;
+import static androidx.wear.protolayout.material.ButtonDefaults.DEFAULT_SIZE;
+import static androidx.wear.protolayout.material.ButtonDefaults.EXTRA_LARGE_SIZE;
+import static androidx.wear.protolayout.material.ButtonDefaults.LARGE_SIZE;
+import static androidx.wear.protolayout.material.ButtonDefaults.PRIMARY_COLORS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.wear.protolayout.ActionBuilders.LaunchAction;
+import androidx.wear.protolayout.DimensionBuilders.DpProp;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.Column;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+import androidx.wear.protolayout.ModifiersBuilders.Clickable;
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(AndroidJUnit4.class)
+@DoNotInstrument
+public class ButtonTest {
+    private static final String RESOURCE_ID = "icon";
+    private static final String TEXT = "ABC";
+    private static final String CONTENT_DESCRIPTION = "clickable button";
+    private static final Clickable CLICKABLE =
+            new Clickable.Builder()
+                    .setOnClick(new LaunchAction.Builder().build())
+                    .setId("action_id")
+                    .build();
+    private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
+    private static final LayoutElement CONTENT =
+            new Text.Builder(CONTEXT, "ABC").setColor(argb(0)).build();
+
+    @Test
+    public void testButtonCustomAddedContentNoContentDesc() {
+        Button button = new Button.Builder(CONTEXT, CLICKABLE).setCustomContent(CONTENT).build();
+
+        assertButton(
+                button,
+                DEFAULT_SIZE,
+                new ButtonColors(Colors.PRIMARY, 0),
+                null,
+                Button.METADATA_TAG_CUSTOM_CONTENT,
+                null,
+                null,
+                null,
+                CONTENT);
+    }
+
+    @Test
+    public void testButtonCustom() {
+        DpProp mSize = LARGE_SIZE;
+        ButtonColors mButtonColors = new ButtonColors(0x11223344, 0);
+
+        Button button =
+                new Button.Builder(CONTEXT, CLICKABLE)
+                        .setCustomContent(CONTENT)
+                        .setSize(mSize)
+                        .setButtonColors(mButtonColors)
+                        .setContentDescription(CONTENT_DESCRIPTION)
+                        .build();
+
+        assertButton(
+                button,
+                mSize,
+                mButtonColors,
+                CONTENT_DESCRIPTION,
+                Button.METADATA_TAG_CUSTOM_CONTENT,
+                null,
+                null,
+                null,
+                CONTENT);
+    }
+
+    @Test
+    public void testButtonSetIcon() {
+
+        Button button =
+                new Button.Builder(CONTEXT, CLICKABLE)
+                        .setIconContent(RESOURCE_ID)
+                        .setContentDescription(CONTENT_DESCRIPTION)
+                        .build();
+
+        assertButton(
+                button,
+                DEFAULT_SIZE,
+                PRIMARY_COLORS,
+                CONTENT_DESCRIPTION,
+                Button.METADATA_TAG_ICON,
+                null,
+                RESOURCE_ID,
+                null,
+                null);
+    }
+
+    @Test
+    public void testButtonSetIconSetSize() {
+        Button button =
+                new Button.Builder(CONTEXT, CLICKABLE)
+                        .setIconContent(RESOURCE_ID)
+                        .setSize(LARGE_SIZE)
+                        .setContentDescription(CONTENT_DESCRIPTION)
+                        .build();
+
+        assertButton(
+                button,
+                LARGE_SIZE,
+                PRIMARY_COLORS,
+                CONTENT_DESCRIPTION,
+                Button.METADATA_TAG_ICON,
+                null,
+                RESOURCE_ID,
+                null,
+                null);
+    }
+
+    @Test
+    public void testButtonSetIconCustomSize() {
+        DpProp mSize = dp(36);
+
+        Button button =
+                new Button.Builder(CONTEXT, CLICKABLE)
+                        .setIconContent(RESOURCE_ID, mSize)
+                        .setContentDescription(CONTENT_DESCRIPTION)
+                        .build();
+
+        assertButton(
+                button,
+                DEFAULT_SIZE,
+                PRIMARY_COLORS,
+                CONTENT_DESCRIPTION,
+                Button.METADATA_TAG_ICON,
+                null,
+                RESOURCE_ID,
+                null,
+                null);
+    }
+
+    @Test
+    public void testButtonSetText() {
+        Button button =
+                new Button.Builder(CONTEXT, CLICKABLE)
+                        .setTextContent(TEXT)
+                        .setContentDescription(CONTENT_DESCRIPTION)
+                        .build();
+
+        assertButton(
+                button,
+                DEFAULT_SIZE,
+                PRIMARY_COLORS,
+                CONTENT_DESCRIPTION,
+                Button.METADATA_TAG_TEXT,
+                TEXT,
+                null,
+                null,
+                null);
+    }
+
+    @Test
+    public void testButtonSetTextSetSize() {
+        Button button =
+                new Button.Builder(CONTEXT, CLICKABLE)
+                        .setTextContent(TEXT)
+                        .setContentDescription(CONTENT_DESCRIPTION)
+                        .setSize(EXTRA_LARGE_SIZE)
+                        .build();
+
+        assertButton(
+                button,
+                EXTRA_LARGE_SIZE,
+                PRIMARY_COLORS,
+                CONTENT_DESCRIPTION,
+                Button.METADATA_TAG_TEXT,
+                TEXT,
+                null,
+                null,
+                null);
+    }
+
+    @Test
+    public void testWrongElementForButton() {
+        Column box = new Column.Builder().build();
+
+        assertThat(Button.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongBoxForButton() {
+        Box box = new Box.Builder().build();
+
+        assertThat(Button.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongTagForButton() {
+        Box box =
+                new Box.Builder()
+                        .setModifiers(
+                                new Modifiers.Builder()
+                                        .setMetadata(
+                                                new ElementMetadata.Builder()
+                                                        .setTagData("test".getBytes(UTF_8))
+                                                        .build())
+                                        .build())
+                        .build();
+
+        assertThat(Button.fromLayoutElement(box)).isNull();
+    }
+
+    private void assertButton(
+            @NonNull Button actualButton,
+            @NonNull DpProp expectedSize,
+            @NonNull ButtonColors expectedButtonColors,
+            @Nullable String expectedContentDescription,
+            @NonNull String expectedMetadataTag,
+            @Nullable String expectedTextContent,
+            @Nullable String expectedIconContent,
+            @Nullable String expectedImageContent,
+            @Nullable LayoutElement expectedCustomContent) {
+        assertButtonIsEqual(
+                actualButton,
+                expectedSize,
+                expectedButtonColors,
+                expectedContentDescription,
+                expectedMetadataTag,
+                expectedTextContent,
+                expectedIconContent,
+                expectedImageContent,
+                expectedCustomContent);
+
+        assertFromLayoutElementButtonIsEqual(
+                actualButton,
+                expectedSize,
+                expectedButtonColors,
+                expectedContentDescription,
+                expectedMetadataTag,
+                expectedTextContent,
+                expectedIconContent,
+                expectedImageContent,
+                expectedCustomContent);
+
+        assertThat(Button.fromLayoutElement(actualButton)).isEqualTo(actualButton);
+    }
+
+    private void assertButtonIsEqual(
+            @NonNull Button actualButton,
+            @NonNull DpProp expectedSize,
+            @NonNull ButtonColors expectedButtonColors,
+            @Nullable String expectedContentDescription,
+            @NonNull String expectedMetadataTag,
+            @Nullable String expectedTextContent,
+            @Nullable String expectedIconContent,
+            @Nullable String expectedImageContent,
+            @Nullable LayoutElement expectedCustomContent) {
+        // Mandatory
+        assertThat(actualButton.getMetadataTag()).isEqualTo(expectedMetadataTag);
+        assertThat(actualButton.getClickable().toProto()).isEqualTo(CLICKABLE.toProto());
+        assertThat(actualButton.getSize().toContainerDimensionProto())
+                .isEqualTo(expectedSize.toContainerDimensionProto());
+        assertThat(actualButton.getButtonColors().getBackgroundColor().getArgb())
+                .isEqualTo(expectedButtonColors.getBackgroundColor().getArgb());
+        assertThat(actualButton.getButtonColors().getContentColor().getArgb())
+                .isEqualTo(expectedButtonColors.getContentColor().getArgb());
+
+        // Nullable
+        if (expectedContentDescription == null) {
+            assertThat(actualButton.getContentDescription()).isNull();
+        } else {
+            assertThat(actualButton.getContentDescription().toString())
+                    .isEqualTo(expectedContentDescription);
+        }
+
+        if (expectedTextContent == null) {
+            assertThat(actualButton.getTextContent()).isNull();
+        } else {
+            assertThat(actualButton.getTextContent()).isEqualTo(expectedTextContent);
+        }
+
+        if (expectedIconContent == null) {
+            assertThat(actualButton.getIconContent()).isNull();
+        } else {
+            assertThat(actualButton.getIconContent()).isEqualTo(expectedIconContent);
+        }
+
+        if (expectedImageContent == null) {
+            assertThat(actualButton.getImageContent()).isNull();
+        } else {
+            assertThat(actualButton.getImageContent()).isEqualTo(expectedImageContent);
+        }
+
+        if (expectedCustomContent == null) {
+            assertThat(actualButton.getCustomContent()).isNull();
+        } else {
+            assertThat(actualButton.getCustomContent().toLayoutElementProto())
+                    .isEqualTo(expectedCustomContent.toLayoutElementProto());
+        }
+    }
+
+    private void assertFromLayoutElementButtonIsEqual(
+            @NonNull Button button,
+            @NonNull DpProp expectedSize,
+            @NonNull ButtonColors expectedButtonColors,
+            @Nullable String expectedContentDescription,
+            @NonNull String expectedMetadataTag,
+            @Nullable String expectedTextContent,
+            @Nullable String expectedIconContent,
+            @Nullable String expectedImageContent,
+            @Nullable LayoutElement expectedCustomContent) {
+        Box box = new Box.Builder().addContent(button).build();
+
+        Button newButton = Button.fromLayoutElement(box.getContents().get(0));
+
+        assertThat(newButton).isNotNull();
+        assertButtonIsEqual(
+                newButton,
+                expectedSize,
+                expectedButtonColors,
+                expectedContentDescription,
+                expectedMetadataTag,
+                expectedTextContent,
+                expectedIconContent,
+                expectedImageContent,
+                expectedCustomContent);
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/ChipColorsTest.java b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/ChipColorsTest.java
new file mode 100644
index 0000000..5bf4319
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/ChipColorsTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.wear.protolayout.ColorBuilders.argb;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.wear.protolayout.ColorBuilders.ColorProp;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(AndroidJUnit4.class)
+@DoNotInstrument
+public class ChipColorsTest {
+    private static final int ARGB_BACKGROUND_COLOR = 0x12345678;
+    private static final int ARGB_CONTENT_COLOR = 0x11223344;
+    private static final int ARGB_SECONDARY_CONTENT_COLOR = 0x11223355;
+    private static final int ARGB_ICON_COLOR = 0x11223366;
+    private static final ColorProp BACKGROUND_COLOR = argb(ARGB_BACKGROUND_COLOR);
+    private static final ColorProp CONTENT_COLOR = argb(ARGB_CONTENT_COLOR);
+    private static final ColorProp ICON_COLOR = argb(ARGB_ICON_COLOR);
+    private static final ColorProp SECONDARY_CONTENT_COLOR = argb(ARGB_SECONDARY_CONTENT_COLOR);
+    private static final Colors COLORS = new Colors(0x123, 0x234, 0x345, 0x456);
+
+    @Test
+    public void testCreateChipColorsFromArgb() {
+        ChipColors chipColors = new ChipColors(ARGB_BACKGROUND_COLOR, ARGB_CONTENT_COLOR);
+
+        assertThat(chipColors.getBackgroundColor().getArgb()).isEqualTo(BACKGROUND_COLOR.getArgb());
+        assertThat(chipColors.getIconColor().getArgb()).isEqualTo(CONTENT_COLOR.getArgb());
+        assertThat(chipColors.getContentColor().getArgb()).isEqualTo(CONTENT_COLOR.getArgb());
+        assertThat(chipColors.getSecondaryContentColor().getArgb())
+                .isEqualTo(CONTENT_COLOR.getArgb());
+    }
+
+    @Test
+    public void testCreateChipColorsFromColorProp() {
+        ChipColors chipColors = new ChipColors(BACKGROUND_COLOR, CONTENT_COLOR);
+
+        assertThat(chipColors.getBackgroundColor().getArgb()).isEqualTo(BACKGROUND_COLOR.getArgb());
+        assertThat(chipColors.getIconColor().getArgb()).isEqualTo(CONTENT_COLOR.getArgb());
+        assertThat(chipColors.getContentColor().getArgb()).isEqualTo(CONTENT_COLOR.getArgb());
+        assertThat(chipColors.getSecondaryContentColor().getArgb())
+                .isEqualTo(CONTENT_COLOR.getArgb());
+    }
+
+    @Test
+    public void testCreateChipColorsFullFromArgb() {
+        ChipColors chipColors =
+                new ChipColors(
+                        ARGB_BACKGROUND_COLOR,
+                        ARGB_ICON_COLOR,
+                        ARGB_CONTENT_COLOR,
+                        ARGB_SECONDARY_CONTENT_COLOR);
+
+        assertThat(chipColors.getBackgroundColor().getArgb()).isEqualTo(BACKGROUND_COLOR.getArgb());
+        assertThat(chipColors.getIconColor().getArgb()).isEqualTo(ICON_COLOR.getArgb());
+        assertThat(chipColors.getContentColor().getArgb()).isEqualTo(CONTENT_COLOR.getArgb());
+        assertThat(chipColors.getSecondaryContentColor().getArgb())
+                .isEqualTo(SECONDARY_CONTENT_COLOR.getArgb());
+    }
+
+    @Test
+    public void testCreateChipColorsFullFromColorProp() {
+        ChipColors chipColors =
+                new ChipColors(
+                        BACKGROUND_COLOR, ICON_COLOR, CONTENT_COLOR, SECONDARY_CONTENT_COLOR);
+
+        assertThat(chipColors.getBackgroundColor().getArgb()).isEqualTo(BACKGROUND_COLOR.getArgb());
+        assertThat(chipColors.getIconColor().getArgb()).isEqualTo(ICON_COLOR.getArgb());
+        assertThat(chipColors.getContentColor().getArgb()).isEqualTo(CONTENT_COLOR.getArgb());
+        assertThat(chipColors.getSecondaryContentColor().getArgb())
+                .isEqualTo(SECONDARY_CONTENT_COLOR.getArgb());
+    }
+
+    @Test
+    public void testCreateChipColorsFromHelperPrimary() {
+        ChipColors chipColors = ChipColors.primaryChipColors(COLORS);
+
+        assertThat(chipColors.getBackgroundColor().getArgb()).isEqualTo(COLORS.getPrimary());
+        assertThat(chipColors.getIconColor().getArgb()).isEqualTo(COLORS.getOnPrimary());
+        assertThat(chipColors.getContentColor().getArgb()).isEqualTo(COLORS.getOnPrimary());
+        assertThat(chipColors.getSecondaryContentColor().getArgb())
+                .isEqualTo(COLORS.getOnPrimary());
+    }
+
+    @Test
+    public void testCreateChipColorsFromHelperSurface() {
+        ChipColors chipColors = ChipColors.secondaryChipColors(COLORS);
+
+        assertThat(chipColors.getBackgroundColor().getArgb()).isEqualTo(COLORS.getSurface());
+        assertThat(chipColors.getIconColor().getArgb()).isEqualTo(COLORS.getOnSurface());
+        assertThat(chipColors.getContentColor().getArgb()).isEqualTo(COLORS.getOnSurface());
+        assertThat(chipColors.getSecondaryContentColor().getArgb())
+                .isEqualTo(COLORS.getOnSurface());
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/ChipTest.java b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/ChipTest.java
new file mode 100644
index 0000000..316b904
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/ChipTest.java
@@ -0,0 +1,319 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.wear.protolayout.ColorBuilders.argb;
+import static androidx.wear.protolayout.DimensionBuilders.dp;
+import static androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER;
+import static androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_START;
+import static androidx.wear.protolayout.material.Utils.areChipColorsEqual;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.content.Context;
+import android.graphics.Color;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.wear.protolayout.ActionBuilders.LaunchAction;
+import androidx.wear.protolayout.ColorBuilders.ColorProp;
+import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.protolayout.DimensionBuilders.DpProp;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.Column;
+import androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignment;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+import androidx.wear.protolayout.LayoutElementBuilders.Row;
+import androidx.wear.protolayout.ModifiersBuilders.Clickable;
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(AndroidJUnit4.class)
+@DoNotInstrument
+public class ChipTest {
+    private static final String MAIN_TEXT = "Primary text";
+    private static final Clickable CLICKABLE =
+            new Clickable.Builder()
+                    .setOnClick(new LaunchAction.Builder().build())
+                    .setId("action_id")
+                    .build();
+    private static final DeviceParameters DEVICE_PARAMETERS =
+            new DeviceParameters.Builder().setScreenWidthDp(192).setScreenHeightDp(192).build();
+    private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
+    private static final DpProp EXPECTED_WIDTH =
+            dp(
+                    DEVICE_PARAMETERS.getScreenWidthDp()
+                            * (100 - 2 * ChipDefaults.DEFAULT_MARGIN_PERCENT)
+                            / 100);
+
+    @Test
+    public void testChip() {
+        String contentDescription = "Chip";
+        Chip chip =
+                new Chip.Builder(CONTEXT, CLICKABLE, DEVICE_PARAMETERS)
+                        .setPrimaryLabelContent(MAIN_TEXT)
+                        .setHorizontalAlignment(HORIZONTAL_ALIGN_CENTER)
+                        .setContentDescription(contentDescription)
+                        .build();
+        assertChip(
+                chip,
+                HORIZONTAL_ALIGN_CENTER,
+                ChipDefaults.PRIMARY_COLORS,
+                contentDescription,
+                Chip.METADATA_TAG_TEXT,
+                MAIN_TEXT,
+                null,
+                null,
+                null);
+    }
+
+    @Test
+    public void testFullChipColors() {
+        ChipColors colors = new ChipColors(Color.YELLOW, Color.WHITE, Color.BLUE, Color.MAGENTA);
+        String secondaryLabel = "Label";
+        Chip chip =
+                new Chip.Builder(CONTEXT, CLICKABLE, DEVICE_PARAMETERS)
+                        .setChipColors(colors)
+                        .setPrimaryLabelContent(MAIN_TEXT)
+                        .setSecondaryLabelContent(secondaryLabel)
+                        .setIconContent("ICON_ID")
+                        .build();
+        assertChip(
+                chip,
+                HORIZONTAL_ALIGN_START,
+                colors,
+                MAIN_TEXT + "\n" + secondaryLabel,
+                Chip.METADATA_TAG_ICON,
+                MAIN_TEXT,
+                secondaryLabel,
+                "ICON_ID",
+                null);
+    }
+
+    @Test
+    public void testChipLeftAligned() {
+        Chip chip =
+                new Chip.Builder(CONTEXT, CLICKABLE, DEVICE_PARAMETERS)
+                        .setHorizontalAlignment(HORIZONTAL_ALIGN_START)
+                        .setPrimaryLabelContent(MAIN_TEXT)
+                        .build();
+        assertChip(
+                chip,
+                HORIZONTAL_ALIGN_START,
+                ChipDefaults.PRIMARY_COLORS,
+                MAIN_TEXT,
+                Chip.METADATA_TAG_TEXT,
+                MAIN_TEXT,
+                null,
+                null,
+                null);
+    }
+
+    @Test
+    public void testChipCustomContent() {
+        ColorProp yellow = argb(Color.YELLOW);
+        ColorProp blue = argb(Color.BLUE);
+        LayoutElement content =
+                new Row.Builder()
+                        .addContent(
+                                new Text.Builder(CONTEXT, "text1")
+                                        .setTypography(Typography.TYPOGRAPHY_TITLE3)
+                                        .setColor(yellow)
+                                        .setItalic(true)
+                                        .build())
+                        .addContent(
+                                new Text.Builder(CONTEXT, "text2")
+                                        .setTypography(Typography.TYPOGRAPHY_TITLE2)
+                                        .setColor(blue)
+                                        .build())
+                        .build();
+
+        String contentDescription = "Custom chip";
+        Chip chip =
+                new Chip.Builder(CONTEXT, CLICKABLE, DEVICE_PARAMETERS)
+                        .setCustomContent(content)
+                        .setHorizontalAlignment(HORIZONTAL_ALIGN_START)
+                        .setContentDescription(contentDescription)
+                        .build();
+
+        assertChip(
+                chip,
+                HORIZONTAL_ALIGN_START,
+                new ChipColors(
+                        ChipDefaults.PRIMARY_COLORS.getBackgroundColor(),
+                        new ColorProp.Builder(0).build()),
+                contentDescription,
+                Chip.METADATA_TAG_CUSTOM_CONTENT,
+                null,
+                null,
+                null,
+                content);
+        assertThat(chip.getCustomContent().toLayoutElementProto())
+                .isEqualTo(content.toLayoutElementProto());
+    }
+
+    private void assertChip(
+            @NonNull Chip actualChip,
+            @HorizontalAlignment int hAlign,
+            @NonNull ChipColors colors,
+            @Nullable String expectedContDesc,
+            @NonNull String expectedMetadata,
+            @Nullable String expectedPrimaryText,
+            @Nullable String expectedLabel,
+            @Nullable String expectedIcon,
+            @Nullable LayoutElement expectedCustomContent) {
+        assertChipIsEqual(
+                actualChip,
+                hAlign,
+                colors,
+                expectedContDesc,
+                expectedMetadata,
+                expectedPrimaryText,
+                expectedLabel,
+                expectedIcon,
+                expectedCustomContent);
+
+        assertFromLayoutElementChipIsEqual(
+                actualChip,
+                hAlign,
+                colors,
+                expectedContDesc,
+                expectedMetadata,
+                expectedPrimaryText,
+                expectedLabel,
+                expectedIcon,
+                expectedCustomContent);
+
+        assertThat(Chip.fromLayoutElement(actualChip)).isEqualTo(actualChip);
+    }
+
+    @Test
+    public void testWrongElement() {
+        Column box = new Column.Builder().build();
+
+        assertThat(Chip.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongBox() {
+        Box box = new Box.Builder().build();
+
+        assertThat(Chip.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongTag() {
+        Box box =
+                new Box.Builder()
+                        .setModifiers(
+                                new Modifiers.Builder()
+                                        .setMetadata(
+                                                new ElementMetadata.Builder()
+                                                        .setTagData("test".getBytes(UTF_8))
+                                                        .build())
+                                        .build())
+                        .build();
+
+        assertThat(Chip.fromLayoutElement(box)).isNull();
+    }
+
+    private void assertFromLayoutElementChipIsEqual(
+            @NonNull Chip chip,
+            @HorizontalAlignment int hAlign,
+            @NonNull ChipColors colors,
+            @Nullable String expectedContDesc,
+            @NonNull String expectedMetadata,
+            @Nullable String expectedPrimaryText,
+            @Nullable String expectedLabel,
+            @Nullable String expectedIcon,
+            @Nullable LayoutElement expectedCustomContent) {
+        Box box = new Box.Builder().addContent(chip).build();
+
+        Chip newChip = Chip.fromLayoutElement(box.getContents().get(0));
+
+        assertThat(newChip).isNotNull();
+        assertChipIsEqual(
+                newChip,
+                hAlign,
+                colors,
+                expectedContDesc,
+                expectedMetadata,
+                expectedPrimaryText,
+                expectedLabel,
+                expectedIcon,
+                expectedCustomContent);
+    }
+
+    private void assertChipIsEqual(
+            @NonNull Chip actualChip,
+            @HorizontalAlignment int hAlign,
+            @NonNull ChipColors colors,
+            @Nullable String expectedContDesc,
+            @NonNull String expectedMetadata,
+            @Nullable String expectedPrimaryText,
+            @Nullable String expectedLabel,
+            @Nullable String expectedIcon,
+            @Nullable LayoutElement expectedCustomContent) {
+        assertThat(actualChip.getMetadataTag()).isEqualTo(expectedMetadata);
+        assertThat(actualChip.getClickable().toProto()).isEqualTo(CLICKABLE.toProto());
+        assertThat(actualChip.getWidth().toContainerDimensionProto())
+                .isEqualTo(EXPECTED_WIDTH.toContainerDimensionProto());
+        assertThat(actualChip.getHeight().toContainerDimensionProto())
+                .isEqualTo(ChipDefaults.DEFAULT_HEIGHT.toContainerDimensionProto());
+        assertThat(areChipColorsEqual(actualChip.getChipColors(), colors)).isTrue();
+        assertThat(actualChip.getHorizontalAlignment()).isEqualTo(hAlign);
+
+        if (expectedContDesc == null) {
+            assertThat(actualChip.getContentDescription()).isNull();
+        } else {
+            assertThat(actualChip.getContentDescription().toString()).isEqualTo(expectedContDesc);
+        }
+
+        if (expectedPrimaryText == null) {
+            assertThat(actualChip.getPrimaryLabelContent()).isNull();
+        } else {
+            assertThat(actualChip.getPrimaryLabelContent()).isEqualTo(expectedPrimaryText);
+        }
+
+        if (expectedLabel == null) {
+            assertThat(actualChip.getSecondaryLabelContent()).isNull();
+        } else {
+            assertThat(actualChip.getSecondaryLabelContent()).isEqualTo(expectedLabel);
+        }
+
+        if (expectedIcon == null) {
+            assertThat(actualChip.getIconContent()).isNull();
+        } else {
+            assertThat(actualChip.getIconContent()).isEqualTo(expectedIcon);
+        }
+
+        if (expectedCustomContent == null) {
+            assertThat(actualChip.getCustomContent()).isNull();
+        } else {
+            assertThat(actualChip.getCustomContent().toLayoutElementProto())
+                    .isEqualTo(expectedCustomContent.toLayoutElementProto());
+        }
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/CircularProgressIndicatorTest.java b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/CircularProgressIndicatorTest.java
new file mode 100644
index 0000000..f956e06
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/CircularProgressIndicatorTest.java
@@ -0,0 +1,200 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.wear.protolayout.material.ProgressIndicatorDefaults.DEFAULT_COLORS;
+import static androidx.wear.protolayout.material.ProgressIndicatorDefaults.DEFAULT_END_ANGLE;
+import static androidx.wear.protolayout.material.ProgressIndicatorDefaults.DEFAULT_START_ANGLE;
+import static androidx.wear.protolayout.material.ProgressIndicatorDefaults.DEFAULT_STROKE_WIDTH;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.graphics.Color;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.Column;
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(AndroidJUnit4.class)
+@DoNotInstrument
+public class CircularProgressIndicatorTest {
+    @Test
+    public void testOpenRingIndicatorDefault() {
+        CircularProgressIndicator circularProgressIndicator =
+                new CircularProgressIndicator.Builder().build();
+
+        assertProgressIndicator(
+                circularProgressIndicator,
+                0,
+                DEFAULT_START_ANGLE,
+                DEFAULT_END_ANGLE,
+                DEFAULT_COLORS,
+                DEFAULT_STROKE_WIDTH.getValue(),
+                null);
+    }
+
+    @Test
+    public void testProgressIndicatorCustom() {
+        float progress = 0.25f;
+        String contentDescription = "60 degrees progress";
+        ProgressIndicatorColors colors = new ProgressIndicatorColors(Color.YELLOW, Color.BLACK);
+        int thickness = 16;
+        float startAngle = -24;
+        float endAngle = 24;
+
+        CircularProgressIndicator circularProgressIndicator =
+                new CircularProgressIndicator.Builder()
+                        .setProgress(progress)
+                        .setStartAngle(startAngle)
+                        .setEndAngle(endAngle)
+                        .setCircularProgressIndicatorColors(colors)
+                        .setStrokeWidth(thickness)
+                        .setContentDescription(contentDescription)
+                        .build();
+
+        assertProgressIndicator(
+                circularProgressIndicator,
+                progress,
+                startAngle,
+                endAngle,
+                colors,
+                thickness,
+                contentDescription);
+    }
+
+    @Test
+    public void testWrongElement() {
+        Column box = new Column.Builder().build();
+
+        assertThat(CircularProgressIndicator.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongBox() {
+        Box box = new Box.Builder().build();
+
+        assertThat(CircularProgressIndicator.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongTag() {
+        Box box =
+                new Box.Builder()
+                        .setModifiers(
+                                new Modifiers.Builder()
+                                        .setMetadata(
+                                                new ElementMetadata.Builder()
+                                                        .setTagData("test".getBytes(UTF_8))
+                                                        .build())
+                                        .build())
+                        .build();
+
+        assertThat(CircularProgressIndicator.fromLayoutElement(box)).isNull();
+    }
+
+    private void assertProgressIndicator(
+            @NonNull CircularProgressIndicator actualCircularProgressIndicator,
+            float expectedProgress,
+            float expectedStartAngle,
+            float expectedEndAngle,
+            @NonNull ProgressIndicatorColors expectedColors,
+            float expectedThickness,
+            @Nullable String expectedContentDescription) {
+        assertProgressIndicatorIsEqual(
+                actualCircularProgressIndicator,
+                expectedProgress,
+                expectedStartAngle,
+                expectedEndAngle,
+                expectedColors,
+                expectedThickness,
+                expectedContentDescription);
+
+        Box box = new Box.Builder().addContent(actualCircularProgressIndicator).build();
+
+        CircularProgressIndicator newCpi =
+                CircularProgressIndicator.fromLayoutElement(box.getContents().get(0));
+
+        assertThat(newCpi).isNotNull();
+        assertProgressIndicatorIsEqual(
+                actualCircularProgressIndicator,
+                expectedProgress,
+                expectedStartAngle,
+                expectedEndAngle,
+                expectedColors,
+                expectedThickness,
+                expectedContentDescription);
+
+        assertThat(CircularProgressIndicator.fromLayoutElement(actualCircularProgressIndicator))
+                .isEqualTo(actualCircularProgressIndicator);
+    }
+
+    private void assertProgressIndicatorIsEqual(
+            @NonNull CircularProgressIndicator actualCircularProgressIndicator,
+            float expectedProgress,
+            float expectedStartAngle,
+            float expectedEndAngle,
+            @NonNull ProgressIndicatorColors expectedColors,
+            float expectedThickness,
+            @Nullable String expectedContentDescription) {
+        float total =
+                expectedEndAngle
+                        + (expectedEndAngle <= expectedStartAngle ? 360 : 0)
+                        - expectedStartAngle;
+        assertThat(actualCircularProgressIndicator.getMetadataTag())
+                .isEqualTo(CircularProgressIndicator.METADATA_TAG);
+        assertThat(actualCircularProgressIndicator.getProgress().getValue())
+                .isWithin(0.01f)
+                .of(expectedProgress * total);
+        assertThat(actualCircularProgressIndicator.getStartAngle().getValue())
+                .isWithin(0.01f)
+                .of(expectedStartAngle);
+        assertThat(actualCircularProgressIndicator.getEndAngle().getValue())
+                .isWithin(0.01f)
+                .of(expectedEndAngle);
+        assertThat(
+                        actualCircularProgressIndicator
+                                .getCircularProgressIndicatorColors()
+                                .getIndicatorColor()
+                                .getArgb())
+                .isEqualTo(expectedColors.getIndicatorColor().getArgb());
+        assertThat(
+                        actualCircularProgressIndicator
+                                .getCircularProgressIndicatorColors()
+                                .getTrackColor()
+                                .getArgb())
+                .isEqualTo(expectedColors.getTrackColor().getArgb());
+        assertThat(actualCircularProgressIndicator.getStrokeWidth().getValue())
+                .isEqualTo(expectedThickness);
+
+        if (expectedContentDescription == null) {
+            assertThat(actualCircularProgressIndicator.getContentDescription()).isNull();
+        } else {
+            assertThat(actualCircularProgressIndicator.getContentDescription().toString())
+                    .isEqualTo(expectedContentDescription);
+        }
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/CompactChipTest.java b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/CompactChipTest.java
new file mode 100644
index 0000000..f3834d6
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/CompactChipTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.wear.protolayout.material.Utils.areChipColorsEqual;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.content.Context;
+import android.graphics.Color;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.wear.protolayout.ActionBuilders.LaunchAction;
+import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.Column;
+import androidx.wear.protolayout.ModifiersBuilders.Clickable;
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(AndroidJUnit4.class)
+@DoNotInstrument
+public class CompactChipTest {
+    private static final String MAIN_TEXT = "Action";
+    private static final Clickable CLICKABLE =
+            new Clickable.Builder()
+                    .setOnClick(new LaunchAction.Builder().build())
+                    .setId("action_id")
+                    .build();
+    private static final DeviceParameters DEVICE_PARAMETERS =
+            new DeviceParameters.Builder().setScreenWidthDp(192).setScreenHeightDp(192).build();
+    private static final ChipColors COLORS = new ChipColors(Color.YELLOW, Color.BLUE);
+    private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
+
+    @Test
+    public void testCompactChipDefault() {
+        CompactChip compactChip =
+                new CompactChip.Builder(CONTEXT, MAIN_TEXT, CLICKABLE, DEVICE_PARAMETERS).build();
+
+        assertChip(compactChip, ChipDefaults.COMPACT_PRIMARY_COLORS);
+    }
+
+    @Test
+    public void testCompactChipCustomColor() {
+        CompactChip compactChip =
+                new CompactChip.Builder(CONTEXT, MAIN_TEXT, CLICKABLE, DEVICE_PARAMETERS)
+                        .setChipColors(COLORS)
+                        .build();
+
+        assertChip(compactChip, COLORS);
+    }
+
+    @Test
+    public void testWrongElement() {
+        Column box = new Column.Builder().build();
+
+        assertThat(CompactChip.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongBox() {
+        Box box = new Box.Builder().build();
+
+        assertThat(CompactChip.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongTag() {
+        Box box =
+                new Box.Builder()
+                        .setModifiers(
+                                new Modifiers.Builder()
+                                        .setMetadata(
+                                                new ElementMetadata.Builder()
+                                                        .setTagData("test".getBytes(UTF_8))
+                                                        .build())
+                                        .build())
+                        .build();
+
+        assertThat(CompactChip.fromLayoutElement(box)).isNull();
+    }
+
+    private void assertChip(CompactChip actualCompactChip, ChipColors colors) {
+        assertChipIsEqual(actualCompactChip, colors);
+        assertFromLayoutElementChipIsEqual(actualCompactChip, colors);
+        assertThat(CompactChip.fromLayoutElement(actualCompactChip)).isEqualTo(actualCompactChip);
+    }
+
+    private void assertChipIsEqual(CompactChip actualCompactChip, ChipColors colors) {
+        assertThat(actualCompactChip.getMetadataTag()).isEqualTo(CompactChip.METADATA_TAG);
+        assertThat(actualCompactChip.getClickable().toProto()).isEqualTo(CLICKABLE.toProto());
+        assertThat(areChipColorsEqual(actualCompactChip.getChipColors(), colors)).isTrue();
+        assertThat(actualCompactChip.getText()).isEqualTo(MAIN_TEXT);
+    }
+
+    private void assertFromLayoutElementChipIsEqual(CompactChip chip, ChipColors colors) {
+        Box box = new Box.Builder().addContent(chip).build();
+
+        CompactChip newChip = CompactChip.fromLayoutElement(box.getContents().get(0));
+
+        assertThat(newChip).isNotNull();
+        assertChipIsEqual(newChip, colors);
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/ProgressIndicatorColorsTest.java b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/ProgressIndicatorColorsTest.java
new file mode 100644
index 0000000..485a4f1
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/ProgressIndicatorColorsTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.wear.protolayout.ColorBuilders.argb;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.wear.protolayout.ColorBuilders.ColorProp;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(AndroidJUnit4.class)
+@DoNotInstrument
+public class ProgressIndicatorColorsTest {
+    private static final int ARGB_BACKGROUND_COLOR = 0x12345678;
+    private static final int ARGB_CONTENT_COLOR = 0x11223344;
+    private static final ColorProp TRACK_COLOR = argb(ARGB_BACKGROUND_COLOR);
+    private static final ColorProp INDICATOR_COLOR = argb(ARGB_CONTENT_COLOR);
+    private static final Colors COLORS = new Colors(0x123, 0x234, 0x345, 0x456);
+
+    @Test
+    public void testCreateProgressIndicatorColorsFromArgb() {
+        ProgressIndicatorColors progressIndicatorColors =
+                new ProgressIndicatorColors(ARGB_CONTENT_COLOR, ARGB_BACKGROUND_COLOR);
+
+        assertThat(progressIndicatorColors.getTrackColor().getArgb())
+                .isEqualTo(TRACK_COLOR.getArgb());
+        assertThat(progressIndicatorColors.getIndicatorColor().getArgb())
+                .isEqualTo(INDICATOR_COLOR.getArgb());
+    }
+
+    @Test
+    public void testCreateProgressIndicatorColorsFromColorProp() {
+        ProgressIndicatorColors progressIndicatorColors =
+                new ProgressIndicatorColors(INDICATOR_COLOR, TRACK_COLOR);
+
+        assertThat(progressIndicatorColors.getTrackColor().getArgb())
+                .isEqualTo(TRACK_COLOR.getArgb());
+        assertThat(progressIndicatorColors.getIndicatorColor().getArgb())
+                .isEqualTo(INDICATOR_COLOR.getArgb());
+    }
+
+    @Test
+    public void testCreateProgressIndicatorColorsFromHelper() {
+        ProgressIndicatorColors progressIndicatorColors =
+                ProgressIndicatorColors.progressIndicatorColors(COLORS);
+
+        assertThat(progressIndicatorColors.getTrackColor().getArgb())
+                .isEqualTo(COLORS.getSurface());
+        assertThat(progressIndicatorColors.getIndicatorColor().getArgb())
+                .isEqualTo(COLORS.getPrimary());
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/TextTest.java b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/TextTest.java
new file mode 100644
index 0000000..3f424d0
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/TextTest.java
@@ -0,0 +1,197 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.wear.protolayout.ColorBuilders.argb;
+import static androidx.wear.protolayout.LayoutElementBuilders.FONT_WEIGHT_BOLD;
+import static androidx.wear.protolayout.LayoutElementBuilders.FONT_WEIGHT_MEDIUM;
+import static androidx.wear.protolayout.LayoutElementBuilders.FONT_WEIGHT_NORMAL;
+import static androidx.wear.protolayout.LayoutElementBuilders.TEXT_ALIGN_END;
+import static androidx.wear.protolayout.LayoutElementBuilders.TEXT_OVERFLOW_ELLIPSIZE_END;
+import static androidx.wear.protolayout.material.Typography.TYPOGRAPHY_BODY1;
+import static androidx.wear.protolayout.material.Typography.TYPOGRAPHY_CAPTION2;
+import static androidx.wear.protolayout.material.Typography.TYPOGRAPHY_TITLE1;
+import static androidx.wear.protolayout.material.Typography.getFontStyleBuilder;
+import static androidx.wear.protolayout.material.Typography.getLineHeightForTypography;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.content.Context;
+import android.graphics.Color;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.Column;
+import androidx.wear.protolayout.LayoutElementBuilders.FontStyle;
+import androidx.wear.protolayout.ModifiersBuilders.Background;
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(AndroidJUnit4.class)
+@DoNotInstrument
+public class TextTest {
+
+    public static final int NUM_OF_FONT_STYLE_CONST = 12;
+    private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
+
+    @Test
+    public void testTypography_incorrectTypography_negativeValue() {
+        assertThrows(IllegalArgumentException.class, () -> getFontStyleBuilder(-1, CONTEXT));
+    }
+
+    @Test
+    public void testTypography_incorrectTypography_positiveValue() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> getFontStyleBuilder(NUM_OF_FONT_STYLE_CONST + 1, CONTEXT));
+    }
+
+    @Test
+    public void testLineHeight_incorrectTypography_negativeValue() {
+        assertThrows(IllegalArgumentException.class, () -> getLineHeightForTypography(-1));
+    }
+
+    @Test
+    public void testLineHeight_incorrectTypography_positiveValue() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> getLineHeightForTypography(NUM_OF_FONT_STYLE_CONST + 1));
+    }
+
+    @Test
+    public void testTypography_body1() {
+        FontStyle fontStyle = getFontStyleBuilder(TYPOGRAPHY_BODY1, CONTEXT).build();
+        assertFontStyle(fontStyle, 16, FONT_WEIGHT_NORMAL, 0.01f, 20, TYPOGRAPHY_BODY1);
+    }
+
+    @Test
+    public void testTypography_caption2() {
+        FontStyle fontStyle = getFontStyleBuilder(TYPOGRAPHY_CAPTION2, CONTEXT).build();
+        assertFontStyle(fontStyle, 12, FONT_WEIGHT_MEDIUM, 0.01f, 16, TYPOGRAPHY_CAPTION2);
+    }
+
+    @Test
+    public void testWrongElement() {
+        Column box = new Column.Builder().build();
+
+        assertThat(Text.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongBox() {
+        Box box = new Box.Builder().build();
+
+        assertThat(Text.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongTag() {
+        Box box =
+                new Box.Builder()
+                    .setModifiers(
+                        new Modifiers.Builder()
+                            .setMetadata(
+                                new ElementMetadata.Builder()
+                                    .setTagData("test".getBytes(UTF_8))
+                                    .build())
+                            .build())
+                    .build();
+
+        assertThat(Text.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testText() {
+        String textContent = "Testing text.";
+        Modifiers modifiers =
+                new Modifiers.Builder()
+                        .setBackground(new Background.Builder().setColor(argb(Color.BLUE)).build())
+                        .build();
+        int color = Color.YELLOW;
+        Text text =
+                new Text.Builder(CONTEXT, textContent)
+                        .setItalic(true)
+                        .setColor(argb(color))
+                        .setTypography(TYPOGRAPHY_TITLE1)
+                        .setUnderline(true)
+                        .setMaxLines(2)
+                        .setModifiers(modifiers)
+                        .setOverflow(TEXT_OVERFLOW_ELLIPSIZE_END)
+                        .setMultilineAlignment(TEXT_ALIGN_END)
+                        .setWeight(FONT_WEIGHT_BOLD)
+                        .build();
+
+        FontStyle expectedFontStyle =
+                getFontStyleBuilder(TYPOGRAPHY_TITLE1, CONTEXT)
+                        .setItalic(true)
+                        .setUnderline(true)
+                        .setColor(argb(color))
+                        .setWeight(FONT_WEIGHT_BOLD)
+                        .build();
+
+        assertTextIsEqual(text, textContent, modifiers, color, expectedFontStyle);
+
+        Box box = new Box.Builder().addContent(text).build();
+        Text newText = Text.fromLayoutElement(box.getContents().get(0));
+        assertThat(newText).isNotNull();
+        assertTextIsEqual(newText, textContent, modifiers, color, expectedFontStyle);
+
+        assertThat(Text.fromLayoutElement(text)).isEqualTo(text);
+    }
+
+    private void assertTextIsEqual(
+            Text actualText,
+            String expectedTextContent,
+            Modifiers expectedModifiers,
+            int expectedColor,
+            FontStyle expectedFontStyle) {
+        assertThat(actualText.getFontStyle().toProto()).isEqualTo(expectedFontStyle.toProto());
+        assertThat(actualText.getText()).isEqualTo(expectedTextContent);
+        assertThat(actualText.getColor().getArgb()).isEqualTo(expectedColor);
+        assertThat(actualText.getOverflow()).isEqualTo(TEXT_OVERFLOW_ELLIPSIZE_END);
+        assertThat(actualText.getMultilineAlignment()).isEqualTo(TEXT_ALIGN_END);
+        assertThat(actualText.getMaxLines()).isEqualTo(2);
+        assertThat(actualText.getLineHeight())
+                .isEqualTo(getLineHeightForTypography(TYPOGRAPHY_TITLE1).getValue());
+    }
+
+    private void assertFontStyle(
+            FontStyle actualFontStyle,
+            int expectedSize,
+            int expectedWeight,
+            float expectedLetterSpacing,
+            float expectedLineHeight,
+            int typographyCode) {
+        assertThat(actualFontStyle.getSize()).isNotNull();
+        assertThat(actualFontStyle.getWeight()).isNotNull();
+        assertThat(actualFontStyle.getLetterSpacing()).isNotNull();
+        assertThat(actualFontStyle.getSize().getValue()).isEqualTo(expectedSize);
+        assertThat(actualFontStyle.getWeight().getValue()).isEqualTo(expectedWeight);
+        assertThat(actualFontStyle.getLetterSpacing().getValue()).isEqualTo(expectedLetterSpacing);
+        assertThat(getLineHeightForTypography(typographyCode).getValue())
+                .isEqualTo(expectedLineHeight);
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/TitleChipTest.java b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/TitleChipTest.java
new file mode 100644
index 0000000..408484f
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/TitleChipTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.wear.protolayout.material;
+
+import static androidx.wear.protolayout.DimensionBuilders.dp;
+import static androidx.wear.protolayout.material.Utils.areChipColorsEqual;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.content.Context;
+import android.graphics.Color;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.wear.protolayout.ActionBuilders.LaunchAction;
+import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.protolayout.DimensionBuilders.DpProp;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.Column;
+import androidx.wear.protolayout.ModifiersBuilders.Clickable;
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(AndroidJUnit4.class)
+@DoNotInstrument
+public class TitleChipTest {
+    private static final String MAIN_TEXT = "Action";
+    private static final Clickable CLICKABLE =
+            new Clickable.Builder()
+                    .setOnClick(new LaunchAction.Builder().build())
+                    .setId("action_id")
+                    .build();
+    private static final DeviceParameters DEVICE_PARAMETERS =
+            new DeviceParameters.Builder().setScreenWidthDp(192).setScreenHeightDp(192).build();
+    private static final ChipColors COLORS = new ChipColors(Color.YELLOW, Color.BLUE);
+    private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
+    private static final DpProp EXPECTED_WIDTH =
+            dp(
+                    DEVICE_PARAMETERS.getScreenWidthDp()
+                            * (100 - 2 * ChipDefaults.DEFAULT_MARGIN_PERCENT)
+                            / 100);
+
+    @Test
+    public void testTitleChipDefault() {
+        TitleChip titleChip =
+                new TitleChip.Builder(CONTEXT, MAIN_TEXT, CLICKABLE, DEVICE_PARAMETERS).build();
+
+        assertChip(titleChip, ChipDefaults.TITLE_PRIMARY_COLORS, EXPECTED_WIDTH);
+    }
+
+    @Test
+    public void testTitleChipCustom() {
+        DpProp width = dp(150);
+        TitleChip titleChip =
+                new TitleChip.Builder(CONTEXT, MAIN_TEXT, CLICKABLE, DEVICE_PARAMETERS)
+                        .setChipColors(COLORS)
+                        .setWidth(width)
+                        .build();
+
+        assertChip(titleChip, COLORS, width);
+    }
+
+    @Test
+    public void testWrongElement() {
+        Column box = new Column.Builder().build();
+
+        assertThat(TitleChip.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongBox() {
+        Box box = new Box.Builder().build();
+
+        assertThat(TitleChip.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongTag() {
+        Box box =
+                new Box.Builder()
+                        .setModifiers(
+                                new Modifiers.Builder()
+                                        .setMetadata(
+                                                new ElementMetadata.Builder()
+                                                        .setTagData("test".getBytes(UTF_8))
+                                                        .build())
+                                        .build())
+                        .build();
+
+        assertThat(TitleChip.fromLayoutElement(box)).isNull();
+    }
+
+    private void assertChip(TitleChip actualTitleChip, ChipColors colors, DpProp width) {
+        assertChipIsEqual(actualTitleChip, colors, width);
+        assertFromLayoutElementChipIsEqual(actualTitleChip, colors, width);
+        assertThat(TitleChip.fromLayoutElement(actualTitleChip)).isEqualTo(actualTitleChip);
+    }
+
+    private void assertChipIsEqual(TitleChip actualTitleChip, ChipColors colors, DpProp width) {
+        assertThat(actualTitleChip.getMetadataTag()).isEqualTo(TitleChip.METADATA_TAG);
+        assertThat(actualTitleChip.getClickable().toProto()).isEqualTo(CLICKABLE.toProto());
+        assertThat(actualTitleChip.getWidth().toContainerDimensionProto())
+                .isEqualTo(width.toContainerDimensionProto());
+        assertThat(areChipColorsEqual(actualTitleChip.getChipColors(), colors)).isTrue();
+        assertThat(actualTitleChip.getText()).isEqualTo(MAIN_TEXT);
+    }
+
+    private void assertFromLayoutElementChipIsEqual(
+            TitleChip chip, ChipColors colors, DpProp width) {
+        Box box = new Box.Builder().addContent(chip).build();
+
+        TitleChip newChip = TitleChip.fromLayoutElement(box.getContents().get(0));
+
+        assertThat(newChip).isNotNull();
+        assertChipIsEqual(newChip, colors, width);
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/Utils.java b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/Utils.java
new file mode 100644
index 0000000..363a204
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/Utils.java
@@ -0,0 +1,37 @@
+/*
+ * 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.wear.protolayout.material;
+
+import androidx.annotation.Dimension;
+
+public final class Utils {
+    /** Returns true if the given ChipColors have the same colored content. */
+    static boolean areChipColorsEqual(ChipColors colors1, ChipColors colors2) {
+        return colors1.getBackgroundColor().getArgb() == colors2.getBackgroundColor().getArgb()
+                && colors1.getContentColor().getArgb() == colors2.getContentColor().getArgb()
+                && colors1.getSecondaryContentColor().getArgb()
+                        == colors2.getSecondaryContentColor().getArgb()
+                && colors1.getIconColor().getArgb() == colors2.getIconColor().getArgb();
+    }
+
+    @Dimension(unit = Dimension.DP)
+    public static int pxToDp(int px, float scale) {
+        return (int) ((px - 0.5f) / scale);
+    }
+
+    private Utils() {}
+}
diff --git a/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/layouts/EdgeContentLayoutTest.java b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/layouts/EdgeContentLayoutTest.java
new file mode 100644
index 0000000..c673b23
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/layouts/EdgeContentLayoutTest.java
@@ -0,0 +1,227 @@
+/*
+ * 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.wear.protolayout.material.layouts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.Column;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+import androidx.wear.protolayout.material.CircularProgressIndicator;
+import androidx.wear.protolayout.material.Text;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(AndroidJUnit4.class)
+@DoNotInstrument
+public class EdgeContentLayoutTest {
+    private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
+    private static final DeviceParameters DEVICE_PARAMETERS =
+            new DeviceParameters.Builder().setScreenWidthDp(192).setScreenHeightDp(192).build();
+    private static final Text PRIMARY_LABEL = new Text.Builder(CONTEXT, "Primary label").build();
+    private static final Text SECONDARY_LABEL =
+            new Text.Builder(CONTEXT, "Secondary label").build();
+
+    @Test
+    public void testAll() {
+        LayoutElement content = new Box.Builder().build();
+        CircularProgressIndicator progressIndicator =
+                new CircularProgressIndicator.Builder().build();
+        EdgeContentLayout layout =
+                new EdgeContentLayout.Builder(DEVICE_PARAMETERS)
+                        .setContent(content)
+                        .setEdgeContent(progressIndicator)
+                        .setPrimaryLabelTextContent(PRIMARY_LABEL)
+                        .setSecondaryLabelTextContent(SECONDARY_LABEL)
+                        .build();
+
+        assertLayout(layout, progressIndicator, content, PRIMARY_LABEL, SECONDARY_LABEL);
+    }
+
+    @Test
+    public void testContentOnly() {
+        LayoutElement content = new Box.Builder().build();
+        EdgeContentLayout layout =
+                new EdgeContentLayout.Builder(DEVICE_PARAMETERS).setContent(content).build();
+
+        assertLayout(layout, null, content, null, null);
+    }
+
+    @Test
+    public void testIndicatorOnly() {
+        CircularProgressIndicator progressIndicator =
+                new CircularProgressIndicator.Builder().build();
+        EdgeContentLayout layout =
+                new EdgeContentLayout.Builder(DEVICE_PARAMETERS)
+                        .setEdgeContent(progressIndicator)
+                        .build();
+
+        assertLayout(layout, progressIndicator, null, null, null);
+    }
+
+    @Test
+    public void testEmpty() {
+        EdgeContentLayout layout = new EdgeContentLayout.Builder(DEVICE_PARAMETERS).build();
+
+        assertLayout(layout, null, null, null, null);
+    }
+
+    @Test
+    public void testWrongElement() {
+        Column box = new Column.Builder().build();
+
+        assertThat(EdgeContentLayout.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongBox() {
+        Box box = new Box.Builder().build();
+
+        assertThat(EdgeContentLayout.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongTag() {
+        Box box =
+                new Box.Builder()
+                        .setModifiers(
+                                new Modifiers.Builder()
+                                        .setMetadata(
+                                                new ElementMetadata.Builder()
+                                                        .setTagData("test".getBytes(UTF_8))
+                                                        .build())
+                                        .build())
+                        .build();
+
+        assertThat(EdgeContentLayout.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongLengthTag() {
+        Box box =
+                new Box.Builder()
+                        .setModifiers(
+                                new Modifiers.Builder()
+                                        .setMetadata(
+                                                new ElementMetadata.Builder()
+                                                        .setTagData(
+                                                                EdgeContentLayout
+                                                                        .METADATA_TAG_PREFIX
+                                                                        .getBytes(UTF_8))
+                                                        .build())
+                                        .build())
+                        .build();
+
+        assertThat(EdgeContentLayout.fromLayoutElement(box)).isNull();
+    }
+
+    private void assertLayout(
+            @NonNull EdgeContentLayout actualLayout,
+            @Nullable LayoutElement expectedProgressIndicator,
+            @Nullable LayoutElement expectedContent,
+            @Nullable LayoutElement expectedPrimaryLabel,
+            @Nullable LayoutElement expectedSecondaryLabel) {
+        assertLayoutIsEqual(
+                actualLayout,
+                expectedProgressIndicator,
+                expectedContent,
+                expectedPrimaryLabel,
+                expectedSecondaryLabel);
+
+        Box box = new Box.Builder().addContent(actualLayout).build();
+
+        EdgeContentLayout newLayout = EdgeContentLayout.fromLayoutElement(box.getContents().get(0));
+
+        assertThat(newLayout).isNotNull();
+        assertLayoutIsEqual(
+                newLayout,
+                expectedProgressIndicator,
+                expectedContent,
+                expectedPrimaryLabel,
+                expectedSecondaryLabel);
+
+        assertThat(EdgeContentLayout.fromLayoutElement(actualLayout)).isEqualTo(actualLayout);
+    }
+
+    private void assertLayoutIsEqual(
+            @NonNull EdgeContentLayout actualLayout,
+            @Nullable LayoutElement expectedProgressIndicator,
+            @Nullable LayoutElement expectedContent,
+            @Nullable LayoutElement expectedPrimaryLabel,
+            @Nullable LayoutElement expectedSecondaryLabel) {
+        byte[] expectedMetadata = EdgeContentLayout.METADATA_TAG_BASE.clone();
+
+        if (expectedProgressIndicator == null) {
+            assertThat(actualLayout.getEdgeContent()).isNull();
+        } else {
+            assertThat(actualLayout.getEdgeContent().toLayoutElementProto())
+                    .isEqualTo(expectedProgressIndicator.toLayoutElementProto());
+            expectedMetadata[EdgeContentLayout.FLAG_INDEX] =
+                    (byte)
+                            (expectedMetadata[EdgeContentLayout.FLAG_INDEX]
+                                    | EdgeContentLayout.EDGE_CONTENT_PRESENT);
+        }
+
+        if (expectedContent == null) {
+            assertThat(actualLayout.getContent()).isNull();
+        } else {
+            assertThat(actualLayout.getContent().toLayoutElementProto())
+                    .isEqualTo(expectedContent.toLayoutElementProto());
+            expectedMetadata[EdgeContentLayout.FLAG_INDEX] =
+                    (byte)
+                            (expectedMetadata[EdgeContentLayout.FLAG_INDEX]
+                                    | EdgeContentLayout.CONTENT_PRESENT);
+        }
+
+        if (expectedPrimaryLabel == null) {
+            assertThat(actualLayout.getPrimaryLabelTextContent()).isNull();
+        } else {
+            assertThat(actualLayout.getPrimaryLabelTextContent().toLayoutElementProto())
+                    .isEqualTo(expectedPrimaryLabel.toLayoutElementProto());
+            expectedMetadata[EdgeContentLayout.FLAG_INDEX] =
+                    (byte)
+                            (expectedMetadata[EdgeContentLayout.FLAG_INDEX]
+                                    | EdgeContentLayout.PRIMARY_LABEL_PRESENT);
+        }
+
+        if (expectedSecondaryLabel == null) {
+            assertThat(actualLayout.getSecondaryLabelTextContent()).isNull();
+        } else {
+            assertThat(actualLayout.getSecondaryLabelTextContent().toLayoutElementProto())
+                    .isEqualTo(expectedSecondaryLabel.toLayoutElementProto());
+            expectedMetadata[EdgeContentLayout.FLAG_INDEX] =
+                    (byte)
+                            (expectedMetadata[EdgeContentLayout.FLAG_INDEX]
+                                    | EdgeContentLayout.SECONDARY_LABEL_PRESENT);
+        }
+
+        assertThat(actualLayout.getMetadataTag()).isEqualTo(expectedMetadata);
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/layouts/MultiButtonLayoutTest.java b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/layouts/MultiButtonLayoutTest.java
new file mode 100644
index 0000000..085b61d
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/layouts/MultiButtonLayoutTest.java
@@ -0,0 +1,168 @@
+/*
+ * 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.wear.protolayout.material.layouts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.wear.protolayout.ActionBuilders.LaunchAction;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.Column;
+import androidx.wear.protolayout.ModifiersBuilders.Clickable;
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+import androidx.wear.protolayout.material.Button;
+import androidx.wear.protolayout.material.ButtonDefaults;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@DoNotInstrument
+public class MultiButtonLayoutTest {
+    private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
+    private static final Clickable CLICKABLE =
+            new Clickable.Builder()
+                    .setOnClick(new LaunchAction.Builder().build())
+                    .setId("action_id")
+                    .build();
+
+    @Test
+    public void test_1button() {
+        Button button1 =
+                new Button.Builder(CONTEXT, CLICKABLE)
+                        .setTextContent("1")
+                        .setSize(ButtonDefaults.EXTRA_LARGE_SIZE)
+                        .build();
+
+        MultiButtonLayout layout =
+                new MultiButtonLayout.Builder().addButtonContent(button1).build();
+
+        assertLayout(layout, Arrays.asList(button1));
+    }
+
+    @Test
+    public void test_2buttons() {
+        Button button1 = new Button.Builder(CONTEXT, CLICKABLE).setTextContent("1").build();
+        Button button2 = new Button.Builder(CONTEXT, CLICKABLE).setTextContent("2").build();
+
+        MultiButtonLayout layout =
+                new MultiButtonLayout.Builder()
+                        .addButtonContent(button1)
+                        .addButtonContent(button2)
+                        .build();
+
+        assertLayout(layout, Arrays.asList(button1, button2));
+    }
+
+    @Test
+    public void test_5buttons() {
+        List<Button> buttons = new ArrayList<>();
+        int size = 5;
+        for (int i = 0; i < size; i++) {
+            buttons.add(new Button.Builder(CONTEXT, CLICKABLE).setTextContent("" + i).build());
+        }
+
+        MultiButtonLayout.Builder layoutBuilder = new MultiButtonLayout.Builder();
+        for (Button b : buttons) {
+            layoutBuilder.addButtonContent(b);
+        }
+        layoutBuilder.setFiveButtonDistribution(
+                MultiButtonLayout.FIVE_BUTTON_DISTRIBUTION_TOP_HEAVY);
+        MultiButtonLayout layout = layoutBuilder.build();
+
+        assertLayout(layout, buttons);
+        assertThat(layout.getFiveButtonDistribution())
+                .isEqualTo(MultiButtonLayout.FIVE_BUTTON_DISTRIBUTION_TOP_HEAVY);
+    }
+
+    @Test
+    public void test_too_many_button() {
+        Button button = new Button.Builder(CONTEXT, CLICKABLE).setTextContent("1").build();
+        MultiButtonLayout.Builder layoutBuilder = new MultiButtonLayout.Builder();
+        for (int i = 0; i < LayoutDefaults.MULTI_BUTTON_MAX_NUMBER + 1; i++) {
+            layoutBuilder.addButtonContent(button);
+        }
+
+        assertThrows(IllegalArgumentException.class, layoutBuilder::build);
+    }
+
+    @Test
+    public void testWrongElement() {
+        Column box = new Column.Builder().build();
+
+        assertThat(MultiButtonLayout.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongBox() {
+        Box box = new Box.Builder().build();
+
+        assertThat(MultiButtonLayout.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongTag() {
+        Box box =
+                new Box.Builder()
+                    .setModifiers(
+                        new Modifiers.Builder()
+                            .setMetadata(
+                                new ElementMetadata.Builder()
+                                    .setTagData("test".getBytes(UTF_8))
+                                    .build())
+                            .build())
+                    .build();
+
+        assertThat(MultiButtonLayout.fromLayoutElement(box)).isNull();
+    }
+
+    private void assertLayout(MultiButtonLayout actualLayout, List<Button> expectedButtons) {
+        assertLayoutIsEqual(actualLayout, expectedButtons);
+
+        Box box = new Box.Builder().addContent(actualLayout).build();
+
+        MultiButtonLayout newLayout = MultiButtonLayout.fromLayoutElement(box.getContents().get(0));
+
+        assertThat(newLayout).isNotNull();
+        assertLayoutIsEqual(newLayout, expectedButtons);
+
+        assertThat(MultiButtonLayout.fromLayoutElement(actualLayout)).isEqualTo(actualLayout);
+    }
+
+    private void assertLayoutIsEqual(MultiButtonLayout actualLayout, List<Button> expectedButtons) {
+        int size = expectedButtons.size();
+        assertThat(actualLayout.getMetadataTag()).isEqualTo(MultiButtonLayout.METADATA_TAG);
+        assertThat(actualLayout.getButtonContents()).hasSize(size);
+        for (int i = 0; i < size; i++) {
+            assertThat(actualLayout.getButtonContents().get(i).toLayoutElementProto())
+                    .isEqualTo(expectedButtons.get(i).toLayoutElementProto());
+        }
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/layouts/MultiSlotLayoutTest.java b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/layouts/MultiSlotLayoutTest.java
new file mode 100644
index 0000000..db1e4c7
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/layouts/MultiSlotLayoutTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.wear.protolayout.material.layouts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.Column;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+import androidx.wear.protolayout.LayoutElementBuilders.Row;
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(AndroidJUnit4.class)
+@DoNotInstrument
+public class MultiSlotLayoutTest {
+
+    @Test
+    public void test() {
+        LayoutElement content1 = new Box.Builder().build();
+        LayoutElement content2 = new Row.Builder().build();
+        float spacerWidth = 10f;
+
+        MultiSlotLayout layout =
+                new MultiSlotLayout.Builder()
+                        .addSlotContent(content1)
+                        .addSlotContent(content2)
+                        .setHorizontalSpacerWidth(spacerWidth)
+                        .build();
+
+        assertLayoutIsEqual(content1, content2, spacerWidth, layout);
+
+        Box box = new Box.Builder().addContent(layout).build();
+
+        MultiSlotLayout newLayout = MultiSlotLayout.fromLayoutElement(box.getContents().get(0));
+
+        assertThat(newLayout).isNotNull();
+        assertLayoutIsEqual(content1, content2, spacerWidth, newLayout);
+
+        assertThat(MultiSlotLayout.fromLayoutElement(layout)).isEqualTo(layout);
+    }
+
+    @Test
+    public void testWrongElement() {
+        Column box = new Column.Builder().build();
+
+        assertThat(MultiSlotLayout.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongBox() {
+        Box box = new Box.Builder().build();
+
+        assertThat(MultiSlotLayout.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongTag() {
+        Box box =
+                new Box.Builder()
+                    .setModifiers(
+                        new Modifiers.Builder()
+                            .setMetadata(
+                                new ElementMetadata.Builder()
+                                    .setTagData("test".getBytes(UTF_8))
+                                    .build())
+                            .build())
+                        .build();
+
+        assertThat(MultiSlotLayout.fromLayoutElement(box)).isNull();
+    }
+
+    private void assertLayoutIsEqual(
+            LayoutElement content1,
+            LayoutElement content2,
+            float spacerWidth,
+            MultiSlotLayout layout) {
+        assertThat(layout.getSlotContents()).hasSize(2);
+        assertThat(layout.getMetadataTag()).isEqualTo(MultiSlotLayout.METADATA_TAG);
+        assertThat(layout.getSlotContents().get(0).toLayoutElementProto())
+                .isEqualTo(content1.toLayoutElementProto());
+        assertThat(layout.getSlotContents().get(1).toLayoutElementProto())
+                .isEqualTo(content2.toLayoutElementProto());
+        assertThat(layout.getHorizontalSpacerWidth()).isEqualTo(spacerWidth);
+    }
+}
diff --git a/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/layouts/PrimaryLayoutTest.java b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/layouts/PrimaryLayoutTest.java
new file mode 100644
index 0000000..6669cda
--- /dev/null
+++ b/wear/protolayout/protolayout-material/src/test/java/androidx/wear/protolayout/material/layouts/PrimaryLayoutTest.java
@@ -0,0 +1,274 @@
+/*
+ * 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.wear.protolayout.material.layouts;
+
+import static androidx.wear.protolayout.material.layouts.LayoutDefaults.DEFAULT_VERTICAL_SPACER_HEIGHT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.wear.protolayout.ActionBuilders.LaunchAction;
+import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.protolayout.LayoutElementBuilders.Box;
+import androidx.wear.protolayout.LayoutElementBuilders.Column;
+import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement;
+import androidx.wear.protolayout.ModifiersBuilders.Clickable;
+import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata;
+import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
+import androidx.wear.protolayout.material.CompactChip;
+import androidx.wear.protolayout.material.Text;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(AndroidJUnit4.class)
+@DoNotInstrument
+public class PrimaryLayoutTest {
+    private static final Clickable CLICKABLE =
+            new Clickable.Builder()
+                    .setOnClick(new LaunchAction.Builder().build())
+                    .setId("action_id")
+                    .build();
+    private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
+    private static final DeviceParameters DEVICE_PARAMETERS =
+            new DeviceParameters.Builder().setScreenWidthDp(192).setScreenHeightDp(192).build();
+    private static final LayoutElement CONTENT = new Box.Builder().build();
+    private static final CompactChip PRIMARY_CHIP =
+            new CompactChip.Builder(CONTEXT, "Compact", CLICKABLE, DEVICE_PARAMETERS).build();
+    private static final Text PRIMARY_LABEL = new Text.Builder(CONTEXT, "Primary label").build();
+    private static final Text SECONDARY_LABEL =
+            new Text.Builder(CONTEXT, "Secondary label").build();
+
+    @Test
+    public void testOnlyContent() {
+        PrimaryLayout layout =
+                new PrimaryLayout.Builder(DEVICE_PARAMETERS).setContent(CONTENT).build();
+
+        assertLayout(DEFAULT_VERTICAL_SPACER_HEIGHT.getValue(), layout, CONTENT, null, null, null);
+    }
+
+    @Test
+    public void testContentChip() {
+        PrimaryLayout layout =
+                new PrimaryLayout.Builder(DEVICE_PARAMETERS)
+                        .setContent(CONTENT)
+                        .setPrimaryChipContent(PRIMARY_CHIP)
+                        .build();
+
+        assertLayout(
+                DEFAULT_VERTICAL_SPACER_HEIGHT.getValue(),
+                layout,
+                CONTENT,
+                PRIMARY_CHIP,
+                null,
+                null);
+    }
+
+    @Test
+    public void testContentPrimaryLabel() {
+        PrimaryLayout layout =
+                new PrimaryLayout.Builder(DEVICE_PARAMETERS)
+                        .setContent(CONTENT)
+                        .setPrimaryLabelTextContent(PRIMARY_LABEL)
+                        .build();
+
+        assertLayout(
+                DEFAULT_VERTICAL_SPACER_HEIGHT.getValue(),
+                layout,
+                CONTENT,
+                null,
+                PRIMARY_LABEL,
+                null);
+    }
+
+    @Test
+    public void testContentSecondaryLabel() {
+        PrimaryLayout layout =
+                new PrimaryLayout.Builder(DEVICE_PARAMETERS)
+                        .setContent(CONTENT)
+                        .setSecondaryLabelTextContent(SECONDARY_LABEL)
+                        .build();
+
+        assertLayout(
+                DEFAULT_VERTICAL_SPACER_HEIGHT.getValue(),
+                layout,
+                CONTENT,
+                null,
+                null,
+                SECONDARY_LABEL);
+    }
+
+    @Test
+    public void testAll() {
+        float height = 12;
+        PrimaryLayout layout =
+                new PrimaryLayout.Builder(DEVICE_PARAMETERS)
+                        .setContent(CONTENT)
+                        .setPrimaryChipContent(PRIMARY_CHIP)
+                        .setPrimaryLabelTextContent(PRIMARY_LABEL)
+                        .setSecondaryLabelTextContent(SECONDARY_LABEL)
+                        .setVerticalSpacerHeight(height)
+                        .build();
+
+        assertLayout(height, layout, CONTENT, PRIMARY_CHIP, PRIMARY_LABEL, SECONDARY_LABEL);
+    }
+
+    @Test
+    public void testWrongElement() {
+        Column box = new Column.Builder().build();
+
+        assertThat(PrimaryLayout.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongBox() {
+        Box box = new Box.Builder().build();
+
+        assertThat(PrimaryLayout.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongTag() {
+        Box box =
+                new Box.Builder()
+                        .setModifiers(
+                                new Modifiers.Builder()
+                                        .setMetadata(
+                                                new ElementMetadata.Builder()
+                                                        .setTagData("test".getBytes(UTF_8))
+                                                        .build())
+                                        .build())
+                        .build();
+
+        assertThat(PrimaryLayout.fromLayoutElement(box)).isNull();
+    }
+
+    @Test
+    public void testWrongLengthTag() {
+        Box box =
+                new Box.Builder()
+                        .setModifiers(
+                                new Modifiers.Builder()
+                                        .setMetadata(
+                                                new ElementMetadata.Builder()
+                                                        .setTagData(
+                                                                PrimaryLayout.METADATA_TAG_PREFIX
+                                                                        .getBytes(UTF_8))
+                                                        .build())
+                                        .build())
+                        .build();
+
+        assertThat(PrimaryLayout.fromLayoutElement(box)).isNull();
+    }
+
+    private void assertLayout(
+            float height,
+            @NonNull PrimaryLayout actualLayout,
+            @Nullable LayoutElement expectedContent,
+            @Nullable LayoutElement expectedPrimaryChip,
+            @Nullable LayoutElement expectedPrimaryLabel,
+            @Nullable LayoutElement expectedSecondaryLabel) {
+        assertLayoutIsEqual(
+                height,
+                actualLayout,
+                expectedContent,
+                expectedPrimaryChip,
+                expectedPrimaryLabel,
+                expectedSecondaryLabel);
+
+        Box box = new Box.Builder().addContent(actualLayout).build();
+
+        PrimaryLayout newLayout = PrimaryLayout.fromLayoutElement(box.getContents().get(0));
+
+        assertThat(newLayout).isNotNull();
+        assertLayoutIsEqual(
+                height,
+                newLayout,
+                expectedContent,
+                expectedPrimaryChip,
+                expectedPrimaryLabel,
+                expectedSecondaryLabel);
+
+        assertThat(PrimaryLayout.fromLayoutElement(actualLayout)).isEqualTo(actualLayout);
+    }
+
+    private void assertLayoutIsEqual(
+            float height,
+            @NonNull PrimaryLayout actualLayout,
+            @Nullable LayoutElement expectedContent,
+            @Nullable LayoutElement expectedPrimaryChip,
+            @Nullable LayoutElement expectedPrimaryLabel,
+            @Nullable LayoutElement expectedSecondaryLabel) {
+        byte[] expectedMetadata = PrimaryLayout.METADATA_TAG_BASE.clone();
+
+        if (expectedContent == null) {
+            assertThat(actualLayout.getContent()).isNull();
+        } else {
+            assertThat(actualLayout.getContent().toLayoutElementProto())
+                    .isEqualTo(expectedContent.toLayoutElementProto());
+            expectedMetadata[PrimaryLayout.FLAG_INDEX] =
+                    (byte)
+                            (expectedMetadata[PrimaryLayout.FLAG_INDEX]
+                                    | PrimaryLayout.CONTENT_PRESENT);
+        }
+
+        if (expectedPrimaryChip == null) {
+            assertThat(actualLayout.getPrimaryChipContent()).isNull();
+        } else {
+            assertThat(actualLayout.getPrimaryChipContent().toLayoutElementProto())
+                    .isEqualTo(expectedPrimaryChip.toLayoutElementProto());
+            expectedMetadata[PrimaryLayout.FLAG_INDEX] =
+                    (byte)
+                            (expectedMetadata[PrimaryLayout.FLAG_INDEX]
+                                    | PrimaryLayout.CHIP_PRESENT);
+        }
+
+        assertThat(actualLayout.getVerticalSpacerHeight()).isEqualTo(height);
+
+        if (expectedPrimaryLabel == null) {
+            assertThat(actualLayout.getPrimaryLabelTextContent()).isNull();
+        } else {
+            assertThat(actualLayout.getPrimaryLabelTextContent().toLayoutElementProto())
+                    .isEqualTo(expectedPrimaryLabel.toLayoutElementProto());
+            expectedMetadata[PrimaryLayout.FLAG_INDEX] =
+                    (byte)
+                            (expectedMetadata[PrimaryLayout.FLAG_INDEX]
+                                    | PrimaryLayout.PRIMARY_LABEL_PRESENT);
+        }
+
+        if (expectedSecondaryLabel == null) {
+            assertThat(actualLayout.getSecondaryLabelTextContent()).isNull();
+        } else {
+            assertThat(actualLayout.getSecondaryLabelTextContent().toLayoutElementProto())
+                    .isEqualTo(expectedSecondaryLabel.toLayoutElementProto());
+            expectedMetadata[PrimaryLayout.FLAG_INDEX] =
+                    (byte)
+                            (expectedMetadata[PrimaryLayout.FLAG_INDEX]
+                                    | PrimaryLayout.SECONDARY_LABEL_PRESENT);
+        }
+
+        assertThat(actualLayout.getMetadataTag()).isEqualTo(expectedMetadata);
+    }
+}
diff --git a/wear/protolayout/protolayout-proto/src/main/proto/dimension.proto b/wear/protolayout/protolayout-proto/src/main/proto/dimension.proto
index 6c69366..2ce9669 100644
--- a/wear/protolayout/protolayout-proto/src/main/proto/dimension.proto
+++ b/wear/protolayout/protolayout-proto/src/main/proto/dimension.proto
@@ -18,7 +18,9 @@
   // The dynamic value, in dp.
   androidx.wear.protolayout.expression.proto.DynamicFloat dynamic_value = 2;
 
-  float value_for_layout = 3;
+  oneof optional_value_for_layout {
+    float value_for_layout = 3;
+  }
 
   oneof align {
     // Vertical alignment of the actual content within the space reserved by
@@ -81,7 +83,9 @@
   // The dynamic value, in degrees.
   androidx.wear.protolayout.expression.proto.DynamicFloat dynamic_value = 2;
 
-  float value_for_layout = 3;
+  oneof optional_value_for_layout {
+    float value_for_layout = 3;
+  }
   AngularAlignment angular_alignment_for_layout = 4;
 }
 
diff --git a/wear/protolayout/protolayout-proto/src/main/proto/types.proto b/wear/protolayout/protolayout-proto/src/main/proto/types.proto
index c9ddacc..9d9227f 100644
--- a/wear/protolayout/protolayout-proto/src/main/proto/types.proto
+++ b/wear/protolayout/protolayout-proto/src/main/proto/types.proto
@@ -20,15 +20,20 @@
 
 // A string type.
 message StringProp {
-  // The value.
-  string value = 1;
+
+  oneof optional_value {
+    // The value.
+    string value = 1;
+  }
 
   // The dynamic value.
   androidx.wear.protolayout.expression.proto.DynamicString dynamic_value = 2;
 
-  // When used as a layout-changing data bind, the string to measure, when
-  // considering how wide the element should be in the layout.
-  string value_for_layout = 3;
+  oneof optional_value_for_layout {
+    // When used as a layout-changing data bind, the string to measure, when
+    // considering how wide the element should be in the layout.
+    string value_for_layout = 3;
+  }
 
   // Alignment alignment of the actual text within the space reserved by
   // value_for_layout. If not specified, defaults to center alignment.
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java
index 3a01156..11ff17f 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java
@@ -141,10 +141,10 @@
         if (enableAnimations) {
             this.mEvaluator =
                     new DynamicTypeEvaluator(
-                            canUpdateGateways, sensorGateway, stateStore, animationQuotaManager);
+                            canUpdateGateways, stateStore, animationQuotaManager, sensorGateway);
         } else {
             this.mEvaluator =
-                    new DynamicTypeEvaluator(canUpdateGateways, sensorGateway, stateStore);
+                    new DynamicTypeEvaluator(canUpdateGateways, stateStore, sensorGateway);
         }
     }
 
diff --git a/wear/protolayout/protolayout/api/current.txt b/wear/protolayout/protolayout/api/current.txt
index b4afdb2..4c667bd 100644
--- a/wear/protolayout/protolayout/api/current.txt
+++ b/wear/protolayout/protolayout/api/current.txt
@@ -180,33 +180,48 @@
     method public static androidx.wear.protolayout.DimensionBuilders.DpProp dp(@Dimension(unit=androidx.annotation.Dimension.DP) float);
     method public static androidx.wear.protolayout.DimensionBuilders.EmProp em(int);
     method public static androidx.wear.protolayout.DimensionBuilders.EmProp em(float);
+    method public static androidx.wear.protolayout.DimensionBuilders.ExpandedDimensionProp expand();
     method public static androidx.wear.protolayout.DimensionBuilders.SpProp sp(@Dimension(unit=androidx.annotation.Dimension.SP) float);
+    method public static androidx.wear.protolayout.DimensionBuilders.WrappedDimensionProp wrap();
+  }
+
+  public static final class DimensionBuilders.AngularLayoutConstraint {
+    method public int getAngularAlignment();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getValue();
+  }
+
+  public static final class DimensionBuilders.AngularLayoutConstraint.Builder {
+    ctor public DimensionBuilders.AngularLayoutConstraint.Builder(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint build();
+    method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint.Builder setAngularAlignment(int);
   }
 
   public static interface DimensionBuilders.ContainerDimension {
   }
 
-  public static interface DimensionBuilders.ContainerDimension.Builder {
-    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension build();
-  }
-
   public static final class DimensionBuilders.DegreesProp {
+    method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getDynamicValue();
     method public float getValue();
   }
 
   public static final class DimensionBuilders.DegreesProp.Builder {
-    ctor public DimensionBuilders.DegreesProp.Builder();
+    ctor @Deprecated public DimensionBuilders.DegreesProp.Builder();
+    ctor public DimensionBuilders.DegreesProp.Builder(float);
     method public androidx.wear.protolayout.DimensionBuilders.DegreesProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp.Builder setDynamicValue(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat);
     method public androidx.wear.protolayout.DimensionBuilders.DegreesProp.Builder setValue(float);
   }
 
   public static final class DimensionBuilders.DpProp implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension androidx.wear.protolayout.DimensionBuilders.ImageDimension androidx.wear.protolayout.DimensionBuilders.SpacerDimension {
+    method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getDynamicValue();
     method @Dimension(unit=androidx.annotation.Dimension.DP) public float getValue();
   }
 
-  public static final class DimensionBuilders.DpProp.Builder implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension.Builder androidx.wear.protolayout.DimensionBuilders.ImageDimension.Builder androidx.wear.protolayout.DimensionBuilders.SpacerDimension.Builder {
-    ctor public DimensionBuilders.DpProp.Builder();
+  public static final class DimensionBuilders.DpProp.Builder {
+    ctor @Deprecated public DimensionBuilders.DpProp.Builder();
+    ctor public DimensionBuilders.DpProp.Builder(@Dimension(unit=androidx.annotation.Dimension.DP) float);
     method public androidx.wear.protolayout.DimensionBuilders.DpProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp.Builder setDynamicValue(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat);
     method public androidx.wear.protolayout.DimensionBuilders.DpProp.Builder setValue(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
@@ -220,11 +235,28 @@
     method public androidx.wear.protolayout.DimensionBuilders.EmProp.Builder setValue(float);
   }
 
-  public static interface DimensionBuilders.ImageDimension {
+  public static final class DimensionBuilders.ExpandedDimensionProp implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension androidx.wear.protolayout.DimensionBuilders.ImageDimension {
+    method public androidx.wear.protolayout.TypeBuilders.FloatProp? getLayoutWeight();
   }
 
-  public static interface DimensionBuilders.ImageDimension.Builder {
-    method public androidx.wear.protolayout.DimensionBuilders.ImageDimension build();
+  public static final class DimensionBuilders.ExpandedDimensionProp.Builder {
+    ctor public DimensionBuilders.ExpandedDimensionProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.ExpandedDimensionProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.ExpandedDimensionProp.Builder setLayoutWeight(androidx.wear.protolayout.TypeBuilders.FloatProp);
+  }
+
+  public static final class DimensionBuilders.HorizontalLayoutConstraint {
+    method public int getHorizontalAlignment();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getValue();
+  }
+
+  public static final class DimensionBuilders.HorizontalLayoutConstraint.Builder {
+    ctor public DimensionBuilders.HorizontalLayoutConstraint.Builder(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    method public androidx.wear.protolayout.DimensionBuilders.HorizontalLayoutConstraint build();
+    method public androidx.wear.protolayout.DimensionBuilders.HorizontalLayoutConstraint.Builder setHorizontalAlignment(int);
+  }
+
+  public static interface DimensionBuilders.ImageDimension {
   }
 
   public static final class DimensionBuilders.ProportionalDimensionProp implements androidx.wear.protolayout.DimensionBuilders.ImageDimension {
@@ -232,7 +264,7 @@
     method @IntRange(from=0) public int getAspectRatioWidth();
   }
 
-  public static final class DimensionBuilders.ProportionalDimensionProp.Builder implements androidx.wear.protolayout.DimensionBuilders.ImageDimension.Builder {
+  public static final class DimensionBuilders.ProportionalDimensionProp.Builder {
     ctor public DimensionBuilders.ProportionalDimensionProp.Builder();
     method public androidx.wear.protolayout.DimensionBuilders.ProportionalDimensionProp build();
     method public androidx.wear.protolayout.DimensionBuilders.ProportionalDimensionProp.Builder setAspectRatioHeight(@IntRange(from=0) int);
@@ -252,11 +284,32 @@
   public static interface DimensionBuilders.SpacerDimension {
   }
 
-  public static interface DimensionBuilders.SpacerDimension.Builder {
-    method public androidx.wear.protolayout.DimensionBuilders.SpacerDimension build();
+  public static final class DimensionBuilders.VerticalLayoutConstraint {
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getValue();
+    method public int getVerticalAlignment();
+  }
+
+  public static final class DimensionBuilders.VerticalLayoutConstraint.Builder {
+    ctor public DimensionBuilders.VerticalLayoutConstraint.Builder(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    method public androidx.wear.protolayout.DimensionBuilders.VerticalLayoutConstraint build();
+    method public androidx.wear.protolayout.DimensionBuilders.VerticalLayoutConstraint.Builder setVerticalAlignment(int);
+  }
+
+  public static final class DimensionBuilders.WrappedDimensionProp implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension {
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getMinimumSize();
+  }
+
+  public static final class DimensionBuilders.WrappedDimensionProp.Builder {
+    ctor public DimensionBuilders.WrappedDimensionProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.WrappedDimensionProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.WrappedDimensionProp.Builder setMinimumSize(androidx.wear.protolayout.DimensionBuilders.DpProp);
   }
 
   public final class LayoutElementBuilders {
+    field public static final int ANGULAR_ALIGNMENT_CENTER = 2; // 0x2
+    field public static final int ANGULAR_ALIGNMENT_END = 3; // 0x3
+    field public static final int ANGULAR_ALIGNMENT_START = 1; // 0x1
+    field public static final int ANGULAR_ALIGNMENT_UNDEFINED = 0; // 0x0
     field public static final int ARC_ANCHOR_CENTER = 2; // 0x2
     field public static final int ARC_ANCHOR_END = 3; // 0x3
     field public static final int ARC_ANCHOR_START = 1; // 0x1
@@ -297,6 +350,7 @@
     method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getAnchorAngle();
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp? getAnchorType();
     method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement!> getContents();
+    method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint? getLayoutConstraintsForDynamicAnchorAngle();
     method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
     method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp? getVerticalAlign();
   }
@@ -308,6 +362,7 @@
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorAngle(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setLayoutConstraintsForDynamicAnchorAngle(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(int);
@@ -345,6 +400,7 @@
 
   public static final class LayoutElementBuilders.ArcLine implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement {
     method public androidx.wear.protolayout.ColorBuilders.ColorProp? getColor();
+    method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint? getLayoutConstraintsForDynamicLength();
     method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getLength();
     method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers? getModifiers();
     method public androidx.wear.protolayout.DimensionBuilders.DpProp? getThickness();
@@ -354,6 +410,7 @@
     ctor public LayoutElementBuilders.ArcLine.Builder();
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine build();
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setLayoutConstraintsForDynamicLength(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint);
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setLength(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.ArcModifiers);
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setThickness(androidx.wear.protolayout.DimensionBuilders.DpProp);
@@ -567,6 +624,8 @@
 
   public static final class LayoutElementBuilders.Spacer implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
     method public androidx.wear.protolayout.DimensionBuilders.SpacerDimension? getHeight();
+    method public androidx.wear.protolayout.DimensionBuilders.VerticalLayoutConstraint? getLayoutConstraintsForDynamicHeight();
+    method public androidx.wear.protolayout.DimensionBuilders.HorizontalLayoutConstraint? getLayoutConstraintsForDynamicWidth();
     method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
     method public androidx.wear.protolayout.DimensionBuilders.SpacerDimension? getWidth();
   }
@@ -575,6 +634,8 @@
     ctor public LayoutElementBuilders.Spacer.Builder();
     method public androidx.wear.protolayout.LayoutElementBuilders.Spacer build();
     method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.SpacerDimension);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setLayoutConstraintsForDynamicHeight(androidx.wear.protolayout.DimensionBuilders.VerticalLayoutConstraint);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setLayoutConstraintsForDynamicWidth(androidx.wear.protolayout.DimensionBuilders.HorizontalLayoutConstraint);
     method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
     method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.SpacerDimension);
   }
@@ -656,6 +717,7 @@
 
   public static final class LayoutElementBuilders.Text implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
     method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle? getFontStyle();
+    method public androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint? getLayoutConstraintsForDynamicText();
     method public androidx.wear.protolayout.DimensionBuilders.SpProp? getLineHeight();
     method public androidx.wear.protolayout.TypeBuilders.Int32Prop? getMaxLines();
     method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
@@ -668,6 +730,7 @@
     ctor public LayoutElementBuilders.Text.Builder();
     method public androidx.wear.protolayout.LayoutElementBuilders.Text build();
     method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setFontStyle(androidx.wear.protolayout.LayoutElementBuilders.FontStyle);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setLayoutConstraintsForDynamicText(androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint);
     method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setLineHeight(androidx.wear.protolayout.DimensionBuilders.SpProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setMaxLines(androidx.wear.protolayout.TypeBuilders.Int32Prop);
     method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setMaxLines(@IntRange(from=1) int);
@@ -1042,13 +1105,27 @@
     method public androidx.wear.protolayout.TypeBuilders.Int32Prop.Builder setValue(int);
   }
 
+  public static final class TypeBuilders.StringLayoutConstraint {
+    method public int getAlignment();
+    method public String getPatternForLayout();
+  }
+
+  public static final class TypeBuilders.StringLayoutConstraint.Builder {
+    ctor public TypeBuilders.StringLayoutConstraint.Builder(String);
+    method public androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint build();
+    method public androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint.Builder setAlignment(int);
+  }
+
   public static final class TypeBuilders.StringProp {
+    method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicString? getDynamicValue();
     method public String getValue();
   }
 
   public static final class TypeBuilders.StringProp.Builder {
-    ctor public TypeBuilders.StringProp.Builder();
+    ctor @Deprecated public TypeBuilders.StringProp.Builder();
+    ctor public TypeBuilders.StringProp.Builder(String);
     method public androidx.wear.protolayout.TypeBuilders.StringProp build();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp.Builder setDynamicValue(androidx.wear.protolayout.expression.DynamicBuilders.DynamicString);
     method public androidx.wear.protolayout.TypeBuilders.StringProp.Builder setValue(String);
   }
 
diff --git a/wear/protolayout/protolayout/api/public_plus_experimental_current.txt b/wear/protolayout/protolayout/api/public_plus_experimental_current.txt
index 19bb4e0..ec7bdbf 100644
--- a/wear/protolayout/protolayout/api/public_plus_experimental_current.txt
+++ b/wear/protolayout/protolayout/api/public_plus_experimental_current.txt
@@ -192,33 +192,48 @@
     method public static androidx.wear.protolayout.DimensionBuilders.DpProp dp(@Dimension(unit=androidx.annotation.Dimension.DP) float);
     method public static androidx.wear.protolayout.DimensionBuilders.EmProp em(int);
     method public static androidx.wear.protolayout.DimensionBuilders.EmProp em(float);
+    method public static androidx.wear.protolayout.DimensionBuilders.ExpandedDimensionProp expand();
     method public static androidx.wear.protolayout.DimensionBuilders.SpProp sp(@Dimension(unit=androidx.annotation.Dimension.SP) float);
+    method public static androidx.wear.protolayout.DimensionBuilders.WrappedDimensionProp wrap();
+  }
+
+  public static final class DimensionBuilders.AngularLayoutConstraint {
+    method public int getAngularAlignment();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getValue();
+  }
+
+  public static final class DimensionBuilders.AngularLayoutConstraint.Builder {
+    ctor public DimensionBuilders.AngularLayoutConstraint.Builder(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint build();
+    method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint.Builder setAngularAlignment(int);
   }
 
   public static interface DimensionBuilders.ContainerDimension {
   }
 
-  public static interface DimensionBuilders.ContainerDimension.Builder {
-    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension build();
-  }
-
   public static final class DimensionBuilders.DegreesProp {
+    method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getDynamicValue();
     method public float getValue();
   }
 
   public static final class DimensionBuilders.DegreesProp.Builder {
-    ctor public DimensionBuilders.DegreesProp.Builder();
+    ctor @Deprecated public DimensionBuilders.DegreesProp.Builder();
+    ctor public DimensionBuilders.DegreesProp.Builder(float);
     method public androidx.wear.protolayout.DimensionBuilders.DegreesProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp.Builder setDynamicValue(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat);
     method public androidx.wear.protolayout.DimensionBuilders.DegreesProp.Builder setValue(float);
   }
 
   public static final class DimensionBuilders.DpProp implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension androidx.wear.protolayout.DimensionBuilders.ImageDimension androidx.wear.protolayout.DimensionBuilders.SpacerDimension {
+    method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getDynamicValue();
     method @Dimension(unit=androidx.annotation.Dimension.DP) public float getValue();
   }
 
-  public static final class DimensionBuilders.DpProp.Builder implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension.Builder androidx.wear.protolayout.DimensionBuilders.ImageDimension.Builder androidx.wear.protolayout.DimensionBuilders.SpacerDimension.Builder {
-    ctor public DimensionBuilders.DpProp.Builder();
+  public static final class DimensionBuilders.DpProp.Builder {
+    ctor @Deprecated public DimensionBuilders.DpProp.Builder();
+    ctor public DimensionBuilders.DpProp.Builder(@Dimension(unit=androidx.annotation.Dimension.DP) float);
     method public androidx.wear.protolayout.DimensionBuilders.DpProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp.Builder setDynamicValue(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat);
     method public androidx.wear.protolayout.DimensionBuilders.DpProp.Builder setValue(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
@@ -232,11 +247,28 @@
     method public androidx.wear.protolayout.DimensionBuilders.EmProp.Builder setValue(float);
   }
 
-  public static interface DimensionBuilders.ImageDimension {
+  public static final class DimensionBuilders.ExpandedDimensionProp implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension androidx.wear.protolayout.DimensionBuilders.ImageDimension {
+    method public androidx.wear.protolayout.TypeBuilders.FloatProp? getLayoutWeight();
   }
 
-  public static interface DimensionBuilders.ImageDimension.Builder {
-    method public androidx.wear.protolayout.DimensionBuilders.ImageDimension build();
+  public static final class DimensionBuilders.ExpandedDimensionProp.Builder {
+    ctor public DimensionBuilders.ExpandedDimensionProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.ExpandedDimensionProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.ExpandedDimensionProp.Builder setLayoutWeight(androidx.wear.protolayout.TypeBuilders.FloatProp);
+  }
+
+  public static final class DimensionBuilders.HorizontalLayoutConstraint {
+    method public int getHorizontalAlignment();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getValue();
+  }
+
+  public static final class DimensionBuilders.HorizontalLayoutConstraint.Builder {
+    ctor public DimensionBuilders.HorizontalLayoutConstraint.Builder(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    method public androidx.wear.protolayout.DimensionBuilders.HorizontalLayoutConstraint build();
+    method public androidx.wear.protolayout.DimensionBuilders.HorizontalLayoutConstraint.Builder setHorizontalAlignment(int);
+  }
+
+  public static interface DimensionBuilders.ImageDimension {
   }
 
   public static final class DimensionBuilders.ProportionalDimensionProp implements androidx.wear.protolayout.DimensionBuilders.ImageDimension {
@@ -244,7 +276,7 @@
     method @IntRange(from=0) public int getAspectRatioWidth();
   }
 
-  public static final class DimensionBuilders.ProportionalDimensionProp.Builder implements androidx.wear.protolayout.DimensionBuilders.ImageDimension.Builder {
+  public static final class DimensionBuilders.ProportionalDimensionProp.Builder {
     ctor public DimensionBuilders.ProportionalDimensionProp.Builder();
     method public androidx.wear.protolayout.DimensionBuilders.ProportionalDimensionProp build();
     method public androidx.wear.protolayout.DimensionBuilders.ProportionalDimensionProp.Builder setAspectRatioHeight(@IntRange(from=0) int);
@@ -264,11 +296,32 @@
   public static interface DimensionBuilders.SpacerDimension {
   }
 
-  public static interface DimensionBuilders.SpacerDimension.Builder {
-    method public androidx.wear.protolayout.DimensionBuilders.SpacerDimension build();
+  public static final class DimensionBuilders.VerticalLayoutConstraint {
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getValue();
+    method public int getVerticalAlignment();
+  }
+
+  public static final class DimensionBuilders.VerticalLayoutConstraint.Builder {
+    ctor public DimensionBuilders.VerticalLayoutConstraint.Builder(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    method public androidx.wear.protolayout.DimensionBuilders.VerticalLayoutConstraint build();
+    method public androidx.wear.protolayout.DimensionBuilders.VerticalLayoutConstraint.Builder setVerticalAlignment(int);
+  }
+
+  public static final class DimensionBuilders.WrappedDimensionProp implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension {
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getMinimumSize();
+  }
+
+  public static final class DimensionBuilders.WrappedDimensionProp.Builder {
+    ctor public DimensionBuilders.WrappedDimensionProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.WrappedDimensionProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.WrappedDimensionProp.Builder setMinimumSize(androidx.wear.protolayout.DimensionBuilders.DpProp);
   }
 
   public final class LayoutElementBuilders {
+    field public static final int ANGULAR_ALIGNMENT_CENTER = 2; // 0x2
+    field public static final int ANGULAR_ALIGNMENT_END = 3; // 0x3
+    field public static final int ANGULAR_ALIGNMENT_START = 1; // 0x1
+    field public static final int ANGULAR_ALIGNMENT_UNDEFINED = 0; // 0x0
     field public static final int ARC_ANCHOR_CENTER = 2; // 0x2
     field public static final int ARC_ANCHOR_END = 3; // 0x3
     field public static final int ARC_ANCHOR_START = 1; // 0x1
@@ -310,6 +363,7 @@
     method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getAnchorAngle();
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp? getAnchorType();
     method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement!> getContents();
+    method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint? getLayoutConstraintsForDynamicAnchorAngle();
     method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
     method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp? getVerticalAlign();
   }
@@ -321,6 +375,7 @@
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorAngle(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setLayoutConstraintsForDynamicAnchorAngle(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(int);
@@ -358,6 +413,7 @@
 
   public static final class LayoutElementBuilders.ArcLine implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement {
     method public androidx.wear.protolayout.ColorBuilders.ColorProp? getColor();
+    method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint? getLayoutConstraintsForDynamicLength();
     method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getLength();
     method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers? getModifiers();
     method public androidx.wear.protolayout.DimensionBuilders.DpProp? getThickness();
@@ -367,6 +423,7 @@
     ctor public LayoutElementBuilders.ArcLine.Builder();
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine build();
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setLayoutConstraintsForDynamicLength(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint);
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setLength(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.ArcModifiers);
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setThickness(androidx.wear.protolayout.DimensionBuilders.DpProp);
@@ -595,6 +652,8 @@
 
   public static final class LayoutElementBuilders.Spacer implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
     method public androidx.wear.protolayout.DimensionBuilders.SpacerDimension? getHeight();
+    method public androidx.wear.protolayout.DimensionBuilders.VerticalLayoutConstraint? getLayoutConstraintsForDynamicHeight();
+    method public androidx.wear.protolayout.DimensionBuilders.HorizontalLayoutConstraint? getLayoutConstraintsForDynamicWidth();
     method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
     method public androidx.wear.protolayout.DimensionBuilders.SpacerDimension? getWidth();
   }
@@ -603,6 +662,8 @@
     ctor public LayoutElementBuilders.Spacer.Builder();
     method public androidx.wear.protolayout.LayoutElementBuilders.Spacer build();
     method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.SpacerDimension);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setLayoutConstraintsForDynamicHeight(androidx.wear.protolayout.DimensionBuilders.VerticalLayoutConstraint);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setLayoutConstraintsForDynamicWidth(androidx.wear.protolayout.DimensionBuilders.HorizontalLayoutConstraint);
     method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
     method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.SpacerDimension);
   }
@@ -684,6 +745,7 @@
 
   public static final class LayoutElementBuilders.Text implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
     method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle? getFontStyle();
+    method public androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint? getLayoutConstraintsForDynamicText();
     method public androidx.wear.protolayout.DimensionBuilders.SpProp? getLineHeight();
     method public androidx.wear.protolayout.TypeBuilders.Int32Prop? getMaxLines();
     method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
@@ -696,6 +758,7 @@
     ctor public LayoutElementBuilders.Text.Builder();
     method public androidx.wear.protolayout.LayoutElementBuilders.Text build();
     method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setFontStyle(androidx.wear.protolayout.LayoutElementBuilders.FontStyle);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setLayoutConstraintsForDynamicText(androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint);
     method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setLineHeight(androidx.wear.protolayout.DimensionBuilders.SpProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setMaxLines(androidx.wear.protolayout.TypeBuilders.Int32Prop);
     method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setMaxLines(@IntRange(from=1) int);
@@ -1190,13 +1253,27 @@
     method public androidx.wear.protolayout.TypeBuilders.Int32Prop.Builder setValue(int);
   }
 
+  public static final class TypeBuilders.StringLayoutConstraint {
+    method public int getAlignment();
+    method public String getPatternForLayout();
+  }
+
+  public static final class TypeBuilders.StringLayoutConstraint.Builder {
+    ctor public TypeBuilders.StringLayoutConstraint.Builder(String);
+    method public androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint build();
+    method public androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint.Builder setAlignment(int);
+  }
+
   public static final class TypeBuilders.StringProp {
+    method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicString? getDynamicValue();
     method public String getValue();
   }
 
   public static final class TypeBuilders.StringProp.Builder {
-    ctor public TypeBuilders.StringProp.Builder();
+    ctor @Deprecated public TypeBuilders.StringProp.Builder();
+    ctor public TypeBuilders.StringProp.Builder(String);
     method public androidx.wear.protolayout.TypeBuilders.StringProp build();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp.Builder setDynamicValue(androidx.wear.protolayout.expression.DynamicBuilders.DynamicString);
     method public androidx.wear.protolayout.TypeBuilders.StringProp.Builder setValue(String);
   }
 
diff --git a/wear/protolayout/protolayout/api/restricted_current.txt b/wear/protolayout/protolayout/api/restricted_current.txt
index b4afdb2..4c667bd 100644
--- a/wear/protolayout/protolayout/api/restricted_current.txt
+++ b/wear/protolayout/protolayout/api/restricted_current.txt
@@ -180,33 +180,48 @@
     method public static androidx.wear.protolayout.DimensionBuilders.DpProp dp(@Dimension(unit=androidx.annotation.Dimension.DP) float);
     method public static androidx.wear.protolayout.DimensionBuilders.EmProp em(int);
     method public static androidx.wear.protolayout.DimensionBuilders.EmProp em(float);
+    method public static androidx.wear.protolayout.DimensionBuilders.ExpandedDimensionProp expand();
     method public static androidx.wear.protolayout.DimensionBuilders.SpProp sp(@Dimension(unit=androidx.annotation.Dimension.SP) float);
+    method public static androidx.wear.protolayout.DimensionBuilders.WrappedDimensionProp wrap();
+  }
+
+  public static final class DimensionBuilders.AngularLayoutConstraint {
+    method public int getAngularAlignment();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getValue();
+  }
+
+  public static final class DimensionBuilders.AngularLayoutConstraint.Builder {
+    ctor public DimensionBuilders.AngularLayoutConstraint.Builder(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint build();
+    method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint.Builder setAngularAlignment(int);
   }
 
   public static interface DimensionBuilders.ContainerDimension {
   }
 
-  public static interface DimensionBuilders.ContainerDimension.Builder {
-    method public androidx.wear.protolayout.DimensionBuilders.ContainerDimension build();
-  }
-
   public static final class DimensionBuilders.DegreesProp {
+    method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getDynamicValue();
     method public float getValue();
   }
 
   public static final class DimensionBuilders.DegreesProp.Builder {
-    ctor public DimensionBuilders.DegreesProp.Builder();
+    ctor @Deprecated public DimensionBuilders.DegreesProp.Builder();
+    ctor public DimensionBuilders.DegreesProp.Builder(float);
     method public androidx.wear.protolayout.DimensionBuilders.DegreesProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.DegreesProp.Builder setDynamicValue(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat);
     method public androidx.wear.protolayout.DimensionBuilders.DegreesProp.Builder setValue(float);
   }
 
   public static final class DimensionBuilders.DpProp implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension androidx.wear.protolayout.DimensionBuilders.ImageDimension androidx.wear.protolayout.DimensionBuilders.SpacerDimension {
+    method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getDynamicValue();
     method @Dimension(unit=androidx.annotation.Dimension.DP) public float getValue();
   }
 
-  public static final class DimensionBuilders.DpProp.Builder implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension.Builder androidx.wear.protolayout.DimensionBuilders.ImageDimension.Builder androidx.wear.protolayout.DimensionBuilders.SpacerDimension.Builder {
-    ctor public DimensionBuilders.DpProp.Builder();
+  public static final class DimensionBuilders.DpProp.Builder {
+    ctor @Deprecated public DimensionBuilders.DpProp.Builder();
+    ctor public DimensionBuilders.DpProp.Builder(@Dimension(unit=androidx.annotation.Dimension.DP) float);
     method public androidx.wear.protolayout.DimensionBuilders.DpProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp.Builder setDynamicValue(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat);
     method public androidx.wear.protolayout.DimensionBuilders.DpProp.Builder setValue(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
@@ -220,11 +235,28 @@
     method public androidx.wear.protolayout.DimensionBuilders.EmProp.Builder setValue(float);
   }
 
-  public static interface DimensionBuilders.ImageDimension {
+  public static final class DimensionBuilders.ExpandedDimensionProp implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension androidx.wear.protolayout.DimensionBuilders.ImageDimension {
+    method public androidx.wear.protolayout.TypeBuilders.FloatProp? getLayoutWeight();
   }
 
-  public static interface DimensionBuilders.ImageDimension.Builder {
-    method public androidx.wear.protolayout.DimensionBuilders.ImageDimension build();
+  public static final class DimensionBuilders.ExpandedDimensionProp.Builder {
+    ctor public DimensionBuilders.ExpandedDimensionProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.ExpandedDimensionProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.ExpandedDimensionProp.Builder setLayoutWeight(androidx.wear.protolayout.TypeBuilders.FloatProp);
+  }
+
+  public static final class DimensionBuilders.HorizontalLayoutConstraint {
+    method public int getHorizontalAlignment();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getValue();
+  }
+
+  public static final class DimensionBuilders.HorizontalLayoutConstraint.Builder {
+    ctor public DimensionBuilders.HorizontalLayoutConstraint.Builder(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    method public androidx.wear.protolayout.DimensionBuilders.HorizontalLayoutConstraint build();
+    method public androidx.wear.protolayout.DimensionBuilders.HorizontalLayoutConstraint.Builder setHorizontalAlignment(int);
+  }
+
+  public static interface DimensionBuilders.ImageDimension {
   }
 
   public static final class DimensionBuilders.ProportionalDimensionProp implements androidx.wear.protolayout.DimensionBuilders.ImageDimension {
@@ -232,7 +264,7 @@
     method @IntRange(from=0) public int getAspectRatioWidth();
   }
 
-  public static final class DimensionBuilders.ProportionalDimensionProp.Builder implements androidx.wear.protolayout.DimensionBuilders.ImageDimension.Builder {
+  public static final class DimensionBuilders.ProportionalDimensionProp.Builder {
     ctor public DimensionBuilders.ProportionalDimensionProp.Builder();
     method public androidx.wear.protolayout.DimensionBuilders.ProportionalDimensionProp build();
     method public androidx.wear.protolayout.DimensionBuilders.ProportionalDimensionProp.Builder setAspectRatioHeight(@IntRange(from=0) int);
@@ -252,11 +284,32 @@
   public static interface DimensionBuilders.SpacerDimension {
   }
 
-  public static interface DimensionBuilders.SpacerDimension.Builder {
-    method public androidx.wear.protolayout.DimensionBuilders.SpacerDimension build();
+  public static final class DimensionBuilders.VerticalLayoutConstraint {
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getValue();
+    method public int getVerticalAlignment();
+  }
+
+  public static final class DimensionBuilders.VerticalLayoutConstraint.Builder {
+    ctor public DimensionBuilders.VerticalLayoutConstraint.Builder(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    method public androidx.wear.protolayout.DimensionBuilders.VerticalLayoutConstraint build();
+    method public androidx.wear.protolayout.DimensionBuilders.VerticalLayoutConstraint.Builder setVerticalAlignment(int);
+  }
+
+  public static final class DimensionBuilders.WrappedDimensionProp implements androidx.wear.protolayout.DimensionBuilders.ContainerDimension {
+    method public androidx.wear.protolayout.DimensionBuilders.DpProp? getMinimumSize();
+  }
+
+  public static final class DimensionBuilders.WrappedDimensionProp.Builder {
+    ctor public DimensionBuilders.WrappedDimensionProp.Builder();
+    method public androidx.wear.protolayout.DimensionBuilders.WrappedDimensionProp build();
+    method public androidx.wear.protolayout.DimensionBuilders.WrappedDimensionProp.Builder setMinimumSize(androidx.wear.protolayout.DimensionBuilders.DpProp);
   }
 
   public final class LayoutElementBuilders {
+    field public static final int ANGULAR_ALIGNMENT_CENTER = 2; // 0x2
+    field public static final int ANGULAR_ALIGNMENT_END = 3; // 0x3
+    field public static final int ANGULAR_ALIGNMENT_START = 1; // 0x1
+    field public static final int ANGULAR_ALIGNMENT_UNDEFINED = 0; // 0x0
     field public static final int ARC_ANCHOR_CENTER = 2; // 0x2
     field public static final int ARC_ANCHOR_END = 3; // 0x3
     field public static final int ARC_ANCHOR_START = 1; // 0x1
@@ -297,6 +350,7 @@
     method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getAnchorAngle();
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp? getAnchorType();
     method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement!> getContents();
+    method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint? getLayoutConstraintsForDynamicAnchorAngle();
     method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
     method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp? getVerticalAlign();
   }
@@ -308,6 +362,7 @@
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorAngle(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(int);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setLayoutConstraintsForDynamicAnchorAngle(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(int);
@@ -345,6 +400,7 @@
 
   public static final class LayoutElementBuilders.ArcLine implements androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement {
     method public androidx.wear.protolayout.ColorBuilders.ColorProp? getColor();
+    method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint? getLayoutConstraintsForDynamicLength();
     method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getLength();
     method public androidx.wear.protolayout.ModifiersBuilders.ArcModifiers? getModifiers();
     method public androidx.wear.protolayout.DimensionBuilders.DpProp? getThickness();
@@ -354,6 +410,7 @@
     ctor public LayoutElementBuilders.ArcLine.Builder();
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine build();
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setColor(androidx.wear.protolayout.ColorBuilders.ColorProp);
+    method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setLayoutConstraintsForDynamicLength(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint);
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setLength(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.ArcModifiers);
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcLine.Builder setThickness(androidx.wear.protolayout.DimensionBuilders.DpProp);
@@ -567,6 +624,8 @@
 
   public static final class LayoutElementBuilders.Spacer implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
     method public androidx.wear.protolayout.DimensionBuilders.SpacerDimension? getHeight();
+    method public androidx.wear.protolayout.DimensionBuilders.VerticalLayoutConstraint? getLayoutConstraintsForDynamicHeight();
+    method public androidx.wear.protolayout.DimensionBuilders.HorizontalLayoutConstraint? getLayoutConstraintsForDynamicWidth();
     method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
     method public androidx.wear.protolayout.DimensionBuilders.SpacerDimension? getWidth();
   }
@@ -575,6 +634,8 @@
     ctor public LayoutElementBuilders.Spacer.Builder();
     method public androidx.wear.protolayout.LayoutElementBuilders.Spacer build();
     method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setHeight(androidx.wear.protolayout.DimensionBuilders.SpacerDimension);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setLayoutConstraintsForDynamicHeight(androidx.wear.protolayout.DimensionBuilders.VerticalLayoutConstraint);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setLayoutConstraintsForDynamicWidth(androidx.wear.protolayout.DimensionBuilders.HorizontalLayoutConstraint);
     method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
     method public androidx.wear.protolayout.LayoutElementBuilders.Spacer.Builder setWidth(androidx.wear.protolayout.DimensionBuilders.SpacerDimension);
   }
@@ -656,6 +717,7 @@
 
   public static final class LayoutElementBuilders.Text implements androidx.wear.protolayout.LayoutElementBuilders.LayoutElement {
     method public androidx.wear.protolayout.LayoutElementBuilders.FontStyle? getFontStyle();
+    method public androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint? getLayoutConstraintsForDynamicText();
     method public androidx.wear.protolayout.DimensionBuilders.SpProp? getLineHeight();
     method public androidx.wear.protolayout.TypeBuilders.Int32Prop? getMaxLines();
     method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
@@ -668,6 +730,7 @@
     ctor public LayoutElementBuilders.Text.Builder();
     method public androidx.wear.protolayout.LayoutElementBuilders.Text build();
     method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setFontStyle(androidx.wear.protolayout.LayoutElementBuilders.FontStyle);
+    method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setLayoutConstraintsForDynamicText(androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint);
     method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setLineHeight(androidx.wear.protolayout.DimensionBuilders.SpProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setMaxLines(androidx.wear.protolayout.TypeBuilders.Int32Prop);
     method public androidx.wear.protolayout.LayoutElementBuilders.Text.Builder setMaxLines(@IntRange(from=1) int);
@@ -1042,13 +1105,27 @@
     method public androidx.wear.protolayout.TypeBuilders.Int32Prop.Builder setValue(int);
   }
 
+  public static final class TypeBuilders.StringLayoutConstraint {
+    method public int getAlignment();
+    method public String getPatternForLayout();
+  }
+
+  public static final class TypeBuilders.StringLayoutConstraint.Builder {
+    ctor public TypeBuilders.StringLayoutConstraint.Builder(String);
+    method public androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint build();
+    method public androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint.Builder setAlignment(int);
+  }
+
   public static final class TypeBuilders.StringProp {
+    method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicString? getDynamicValue();
     method public String getValue();
   }
 
   public static final class TypeBuilders.StringProp.Builder {
-    ctor public TypeBuilders.StringProp.Builder();
+    ctor @Deprecated public TypeBuilders.StringProp.Builder();
+    ctor public TypeBuilders.StringProp.Builder(String);
     method public androidx.wear.protolayout.TypeBuilders.StringProp build();
+    method public androidx.wear.protolayout.TypeBuilders.StringProp.Builder setDynamicValue(androidx.wear.protolayout.expression.DynamicBuilders.DynamicString);
     method public androidx.wear.protolayout.TypeBuilders.StringProp.Builder setValue(String);
   }
 
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/DimensionBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/DimensionBuilders.java
index 4cae976..fe21d0a 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/DimensionBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/DimensionBuilders.java
@@ -18,8 +18,7 @@
 
 import static androidx.annotation.Dimension.DP;
 import static androidx.annotation.Dimension.SP;
-
-import android.annotation.SuppressLint;
+import static androidx.wear.protolayout.expression.Preconditions.checkNotNull;
 
 import androidx.annotation.Dimension;
 import androidx.annotation.IntRange;
@@ -27,18 +26,23 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.TypeBuilders.FloatProp;
+import androidx.wear.protolayout.expression.DynamicBuilders;
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat;
 import androidx.wear.protolayout.expression.Fingerprint;
 import androidx.wear.protolayout.proto.DimensionProto;
-import androidx.wear.protolayout.proto.TypesProto;
 
 /** Builders for dimensions for layout elements. */
 public final class DimensionBuilders {
     private DimensionBuilders() {}
 
+    private static final ExpandedDimensionProp EXPAND = new ExpandedDimensionProp.Builder().build();
+    private static final WrappedDimensionProp WRAP = new WrappedDimensionProp.Builder().build();
+
     /** Shortcut for building a {@link DpProp} using a measurement in DP. */
     @NonNull
     public static DpProp dp(@Dimension(unit = DP) float valueDp) {
-        return new DpProp.Builder().setValue(valueDp).build();
+        return new DpProp.Builder(valueDp).build();
     }
 
     /** Shortcut for building a {@link SpProp} using a measurement in SP. */
@@ -47,22 +51,56 @@
         return new SpProp.Builder().setValue(valueSp).build();
     }
 
-    /** Shortcut for building a {@link EmProp} using a measurement in EM. */
+    /**
+     * Shortcut for building a {@link EmProp} using a measurement in EM.
+     *
+     * @since 1.0
+     */
     @NonNull
     public static EmProp em(int valueEm) {
         return new EmProp.Builder().setValue(valueEm).build();
     }
 
-    /** Shortcut for building a {@link EmProp} using a measurement in EM. */
+    /**
+     * Shortcut for building a {@link EmProp} using a measurement in EM.
+     *
+     * @since 1.0
+     */
     @NonNull
     public static EmProp em(float valueEm) {
         return new EmProp.Builder().setValue(valueEm).build();
     }
 
-    /** Shortcut for building an {@link DegreesProp} using a measurement in degrees. */
+    /**
+     * Shortcut for building an {@link DegreesProp} using a measurement in degrees.
+     *
+     * @since 1.0
+     */
     @NonNull
     public static DegreesProp degrees(float valueDegrees) {
-        return new DegreesProp.Builder().setValue(valueDegrees).build();
+        return new DegreesProp.Builder(valueDegrees).build();
+    }
+
+    /**
+     * Shortcut for building an {@link ExpandedDimensionProp} that will expand to the size of its
+     * parent.
+     *
+     * @since 1.0
+     */
+    @NonNull
+    public static ExpandedDimensionProp expand() {
+        return EXPAND;
+    }
+
+    /**
+     * Shortcut for building an {@link WrappedDimensionProp} that will shrink to the size of its
+     * children.
+     *
+     * @since 1.0
+     */
+    @NonNull
+    public static WrappedDimensionProp wrap() {
+        return WRAP;
     }
 
     /**
@@ -81,7 +119,7 @@
         }
 
         /**
-         * Gets the value, in dp.
+         * Gets the static value, in dp.
          *
          * @since 1.0
          */
@@ -90,6 +128,20 @@
             return mImpl.getValue();
         }
 
+        /**
+         * Gets the dynamic value, in dp.
+         *
+         * @since 1.2
+         */
+        @Nullable
+        public DynamicFloat getDynamicValue() {
+            if (mImpl.hasDynamicValue()) {
+                return DynamicBuilders.dynamicFloatFromProto(mImpl.getDynamicValue());
+            } else {
+                return null;
+            }
+        }
+
         @Override
         @RestrictTo(Scope.LIBRARY_GROUP)
         @Nullable
@@ -97,12 +149,22 @@
             return mFingerprint;
         }
 
+        /** Creates a new wrapper instance from the proto. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
-        static DpProp fromProto(@NonNull DimensionProto.DpProp proto) {
-            return new DpProp(proto, null);
+        public static DpProp fromProto(
+                @NonNull DimensionProto.DpProp proto, @Nullable Fingerprint fingerprint) {
+            return new DpProp(proto, fingerprint);
         }
 
         @NonNull
+        static DpProp fromProto(@NonNull DimensionProto.DpProp proto) {
+            return fromProto(proto, null);
+        }
+
+        /** Returns the internal proto instance. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
         DimensionProto.DpProp toProto() {
             return mImpl;
         }
@@ -131,7 +193,7 @@
         @Override
         @NonNull
         public String toString() {
-            return "DpProp{" + "value=" + getValue() + "}";
+            return "DpProp{" + "value=" + getValue() + ", dynamicValue=" + getDynamicValue() + "}";
         }
 
         /** Builder for {@link DpProp}. */
@@ -142,28 +204,258 @@
             private final DimensionProto.DpProp.Builder mImpl = DimensionProto.DpProp.newBuilder();
             private final Fingerprint mFingerprint = new Fingerprint(756413087);
 
+            /**
+             * @deprecated Use {@link #Builder(float)} instead.
+             */
+            @Deprecated
             public Builder() {}
 
             /**
-             * Sets the value, in dp.
+             * Creates a instance of {@link Builder}.
+             *
+             * @param staticValue the static value, in dp.
+             */
+            public Builder(@Dimension(unit = DP) float staticValue) {
+                setValue(staticValue);
+            }
+
+            /**
+             * Sets the static value, in dp. If a dynamic value is also set and the renderer
+             * supports dynamic values for the corresponding field, this static value will be
+             * ignored.
              *
              * @since 1.0
              */
             @NonNull
-            public Builder setValue(@Dimension(unit = DP) float value) {
-                mImpl.setValue(value);
-                mFingerprint.recordPropertyUpdate(1, Float.floatToIntBits(value));
+            public Builder setValue(@Dimension(unit = DP) float staticValue) {
+                mImpl.setValue(staticValue);
+                mFingerprint.recordPropertyUpdate(1, Float.floatToIntBits(staticValue));
+                return this;
+            }
+
+            /**
+             * Sets the dynamic value, in dp. Note that when setting this value, the static value is
+             * still required to be set to support older renderers that only read the static value.
+             *
+             * @since 1.2
+             */
+            @NonNull
+            public Builder setDynamicValue(@NonNull DynamicFloat dynamicValue) {
+                mImpl.setDynamicValue(dynamicValue.toDynamicFloatProto());
+                mFingerprint.recordPropertyUpdate(
+                        2, checkNotNull(dynamicValue.getFingerprint()).aggregateValueAsInt());
                 return this;
             }
 
             @Override
             @NonNull
             public DpProp build() {
+                if (mImpl.hasDynamicValue() && !mImpl.hasValue()) {
+                    throw new IllegalStateException("Static value is missing.");
+                }
                 return new DpProp(mImpl.build(), mFingerprint);
             }
         }
     }
 
+    private static class DpPropLayoutConstraint {
+        protected final DimensionProto.DpProp mImpl;
+        @Nullable protected final Fingerprint mFingerprint;
+
+        protected DpPropLayoutConstraint(
+                DimensionProto.DpProp impl, @Nullable Fingerprint fingerprint) {
+            this.mImpl = impl;
+            this.mFingerprint = fingerprint;
+        }
+
+        /**
+         * Gets the value to use when laying out components which can have a dynamic value.
+         * Constrains the layout so that components are not changing size or location regardless
+         * of the dynamic value that is being provided.
+         *
+         * @since 1.2
+         */
+        @SuppressWarnings("Unused")
+        @Dimension(unit = DP)
+        public float getValue() {
+            return mImpl.getValueForLayout();
+        }
+
+        @SuppressWarnings("Unused")
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Nullable
+        public Fingerprint getFingerprint() {
+            return mFingerprint;
+        }
+
+        @SuppressWarnings("Unused")
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public DimensionProto.SpacerDimension toSpacerDimensionProto() {
+            return DimensionProto.SpacerDimension.newBuilder().setLinearDimension(mImpl).build();
+        }
+
+        /** Builder for {@link DpPropLayoutConstraint}. */
+        protected static class Builder {
+            protected final DimensionProto.DpProp.Builder mImpl =
+                    DimensionProto.DpProp.newBuilder();
+            protected final Fingerprint mFingerprint = new Fingerprint(756413088);
+
+            /**
+             * Creates a new builder for {@link DpPropLayoutConstraint}.
+             *
+             * @param value Sets the value to use when laying out components which can have a
+             *              dynamic value. Constrains the layout so that components are not
+             *              changing size or location regardless of the dynamic value that is
+             *              being provided.
+             * @since 1.2
+             */
+            protected Builder(@Dimension(unit = DP) float value) {
+                setValue(value);
+            }
+
+            /**
+             * Sets the value to use when laying out components which can have a dynamic value.
+             * Constrains the layout so that components are not changing size or location
+             * regardless of the dynamic value that is being provided.
+             *
+             * @since 1.2
+             */
+            @NonNull
+            private Builder setValue(@Dimension(unit = DP) float value) {
+                mImpl.setValueForLayout(value);
+                mFingerprint.recordPropertyUpdate(3, Float.floatToIntBits(value));
+                return this;
+            }
+        }
+    }
+
+    /**
+     * A type for specifying horizontal layout constraints when using {@link DpProp} on a data
+     * bindable layout element.
+     *
+     * @since 1.2
+     */
+    public static final class HorizontalLayoutConstraint extends DpPropLayoutConstraint {
+        HorizontalLayoutConstraint(DimensionProto.DpProp impl, @Nullable Fingerprint fingerprint) {
+            super(impl, fingerprint);
+        }
+
+        /**
+         * Gets the horizontal alignment of the actual content within the space reserved by value.
+         *
+         * @since 1.2
+         */
+        @LayoutElementBuilders.HorizontalAlignment
+        public int getHorizontalAlignment() {
+            return mImpl.getHorizontalAlignmentForLayoutValue();
+        }
+
+        @NonNull
+        static HorizontalLayoutConstraint fromProto(@NonNull DimensionProto.DpProp proto) {
+            return new HorizontalLayoutConstraint(proto, null);
+        }
+
+        /** Builder for {@link HorizontalLayoutConstraint}. */
+        public static final class Builder extends DpPropLayoutConstraint.Builder {
+            /**
+             * Creates a new builder for {@link HorizontalLayoutConstraint}.
+             *
+             * @param value Sets the value to use when laying out components which can have a
+             *              dynamic value. Constrains the layout so that components are not
+             *              changing size or location regardless of the dynamic value that is
+             *              being provided.
+             * @since 1.2
+             */
+            public Builder(@Dimension(unit = DP) float value) {
+                super(value);
+            }
+
+            /**
+             * Sets the horizontal alignment of the actual content within the space reserved by
+             * value. If not specified, defaults to center alignment.
+             *
+             * @since 1.2
+             */
+            @NonNull
+            public Builder setHorizontalAlignment(
+                    @LayoutElementBuilders.HorizontalAlignment int horizontalAlignment) {
+                mImpl.setHorizontalAlignmentForLayoutValue(horizontalAlignment);
+                mFingerprint.recordPropertyUpdate(5, horizontalAlignment);
+                return this;
+            }
+
+            /** Builds an instance of {@link HorizontalLayoutConstraint}. */
+            @NonNull
+            public HorizontalLayoutConstraint build() {
+                return new HorizontalLayoutConstraint(mImpl.build(), mFingerprint);
+            }
+        }
+    }
+
+    /**
+     * A type for specifying vertical layout constraints when using {@link DpProp} on a data
+     * bindable layout element.
+     *
+     * @since 1.2
+     */
+    public static final class VerticalLayoutConstraint extends DpPropLayoutConstraint {
+        VerticalLayoutConstraint(DimensionProto.DpProp impl, @Nullable Fingerprint fingerprint) {
+            super(impl, fingerprint);
+        }
+
+        /**
+         * Gets the vertical alignment of the actual content within the space reserved by value.
+         *
+         * @since 1.2
+         */
+        @LayoutElementBuilders.VerticalAlignment
+        public int getVerticalAlignment() {
+            return mImpl.getVerticalAlignmentForLayoutValue();
+        }
+
+        @NonNull
+        static VerticalLayoutConstraint fromProto(@NonNull DimensionProto.DpProp proto) {
+            return new VerticalLayoutConstraint(proto, null);
+        }
+
+        /** Builder for {@link VerticalLayoutConstraint}. */
+        public static final class Builder extends DpPropLayoutConstraint.Builder {
+            /**
+             * Creates a new builder for {@link VerticalLayoutConstraint}.
+             *
+             * @param value Sets the value to use when laying out components which can have a
+             *              dynamic value. Constrains the layout so that components are not
+             *              changing size or location regardless of the dynamic value that is
+             *              being provided.
+             * @since 1.2
+             */
+            public Builder(@Dimension(unit = DP) float value) {
+                super(value);
+            }
+
+            /**
+             * Sets the vertical alignment of the actual content within the space reserved by value.
+             * If not specified, defaults to center alignment.
+             *
+             * @since 1.2
+             */
+            @NonNull
+            public Builder setVerticalAlignment(
+                    @LayoutElementBuilders.VerticalAlignment int verticalAlignment) {
+                mImpl.setVerticalAlignmentForLayoutValue(verticalAlignment);
+                mFingerprint.recordPropertyUpdate(4, verticalAlignment);
+                return this;
+            }
+
+            /** Builds an instance of {@link VerticalLayoutConstraint}. */
+            @NonNull
+            public VerticalLayoutConstraint build() {
+                return new VerticalLayoutConstraint(mImpl.build(), mFingerprint);
+            }
+        }
+    }
+
     /**
      * A type for font sizes, measured in sp.
      *
@@ -195,16 +487,32 @@
             return mFingerprint;
         }
 
+        /** Creates a new wrapper instance from the proto. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
-        static SpProp fromProto(@NonNull DimensionProto.SpProp proto) {
-            return new SpProp(proto, null);
+        public static SpProp fromProto(
+                @NonNull DimensionProto.SpProp proto, @Nullable Fingerprint fingerprint) {
+            return new SpProp(proto, fingerprint);
         }
 
         @NonNull
-        DimensionProto.SpProp toProto() {
+        static SpProp fromProto(@NonNull DimensionProto.SpProp proto) {
+            return fromProto(proto, null);
+        }
+
+        /** Returns the internal proto instance. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public DimensionProto.SpProp toProto() {
             return mImpl;
         }
 
+        @Override
+        @NonNull
+        public String toString() {
+            return "SpProp{" + "value=" + getValue() + "}";
+        }
+
         /** Builder for {@link SpProp} */
         public static final class Builder {
             private final DimensionProto.SpProp.Builder mImpl = DimensionProto.SpProp.newBuilder();
@@ -213,7 +521,8 @@
             public Builder() {}
 
             /**
-             * Sets the value, in sp.
+             * Sets the value, in sp. If a dynamic value is also set and the renderer supports
+             * dynamic values for the corresponding field, this static value will be ignored.
              *
              * @since 1.0
              */
@@ -262,16 +571,32 @@
             return mFingerprint;
         }
 
+        /** Creates a new wrapper instance from the proto. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
-        static EmProp fromProto(@NonNull DimensionProto.EmProp proto) {
-            return new EmProp(proto, null);
+        public static EmProp fromProto(
+                @NonNull DimensionProto.EmProp proto, @Nullable Fingerprint fingerprint) {
+            return new EmProp(proto, fingerprint);
         }
 
         @NonNull
-        DimensionProto.EmProp toProto() {
+        static EmProp fromProto(@NonNull DimensionProto.EmProp proto) {
+            return fromProto(proto, null);
+        }
+
+        /** Returns the internal proto instance. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public DimensionProto.EmProp toProto() {
             return mImpl;
         }
 
+        @Override
+        @NonNull
+        public String toString() {
+            return "EmProp{" + "value=" + getValue() + "}";
+        }
+
         /** Builder for {@link EmProp} */
         public static final class Builder {
             private final DimensionProto.EmProp.Builder mImpl = DimensionProto.EmProp.newBuilder();
@@ -314,7 +639,7 @@
         }
 
         /**
-         * Gets the value, in degrees.
+         * Gets the static value, in degrees.
          *
          * @since 1.0
          */
@@ -322,6 +647,20 @@
             return mImpl.getValue();
         }
 
+        /**
+         * Gets the dynamic value, in degrees.
+         *
+         * @since 1.2
+         */
+        @Nullable
+        public DynamicFloat getDynamicValue() {
+            if (mImpl.hasDynamicValue()) {
+                return DynamicBuilders.dynamicFloatFromProto(mImpl.getDynamicValue());
+            } else {
+                return null;
+            }
+        }
+
         /** Get the fingerprint for this object, or null if unknown. */
         @RestrictTo(Scope.LIBRARY_GROUP)
         @Nullable
@@ -329,51 +668,209 @@
             return mFingerprint;
         }
 
+        /** Creates a new wrapper instance from the proto. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
-        static DegreesProp fromProto(@NonNull DimensionProto.DegreesProp proto) {
-            return new DegreesProp(proto, null);
+        public static DegreesProp fromProto(
+                @NonNull DimensionProto.DegreesProp proto, @Nullable Fingerprint fingerprint) {
+            return new DegreesProp(proto, fingerprint);
         }
 
         @NonNull
-        DimensionProto.DegreesProp toProto() {
+        static DegreesProp fromProto(@NonNull DimensionProto.DegreesProp proto) {
+            return fromProto(proto, null);
+        }
+
+        /** Returns the internal proto instance. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public DimensionProto.DegreesProp toProto() {
             return mImpl;
         }
 
+        @Override
+        @NonNull
+        public String toString() {
+            return "DegreesProp{"
+                    + "value="
+                    + getValue()
+                    + ", dynamicValue="
+                    + getDynamicValue()
+                    + "}";
+        }
+
         /** Builder for {@link DegreesProp} */
         public static final class Builder {
             private final DimensionProto.DegreesProp.Builder mImpl =
                     DimensionProto.DegreesProp.newBuilder();
             private final Fingerprint mFingerprint = new Fingerprint(-1927567665);
 
+            /**
+             * @deprecated Use {@link #Builder(float)} instead.
+             */
+            @Deprecated
             public Builder() {}
 
             /**
-             * Sets the value, in degrees.
+             * Creates a instance of {@link Builder}.
+             *
+             * @param staticValue the static value, in degrees.
+             */
+            public Builder(float staticValue) {
+                setValue(staticValue);
+            }
+
+            /**
+             * Sets the static value, in degrees. If a dynamic value is also set and the renderer
+             * supports dynamic values for the corresponding field, this static value will be
+             * ignored.
              *
              * @since 1.0
              */
             @NonNull
-            public Builder setValue(float value) {
-                mImpl.setValue(value);
-                mFingerprint.recordPropertyUpdate(1, Float.floatToIntBits(value));
+            public Builder setValue(float staticValue) {
+                mImpl.setValue(staticValue);
+                mFingerprint.recordPropertyUpdate(1, Float.floatToIntBits(staticValue));
+                return this;
+            }
+
+            /**
+             * Sets the dynamic value, in degrees. Note that when setting this value, the static
+             * value is still required to be set to support older renderers that only read the
+             * static value.
+             *
+             * @since 1.2
+             */
+            @NonNull
+            public Builder setDynamicValue(@NonNull DynamicFloat dynamicValue) {
+                mImpl.setDynamicValue(dynamicValue.toDynamicFloatProto());
+                mFingerprint.recordPropertyUpdate(
+                        2, checkNotNull(dynamicValue.getFingerprint()).aggregateValueAsInt());
                 return this;
             }
 
             /** Builds an instance from accumulated values. */
             @NonNull
             public DegreesProp build() {
+                if (mImpl.hasDynamicValue() && !mImpl.hasValue()) {
+                    throw new IllegalStateException("Static value is missing.");
+                }
                 return new DegreesProp(mImpl.build(), mFingerprint);
             }
         }
     }
 
     /**
+     * A type for specifying layout constraints when using {@link DegreesProp} on a data bindable
+     * layout element.
+     *
+     * @since 1.2
+     */
+    public static final class AngularLayoutConstraint {
+        private final DimensionProto.DegreesProp mImpl;
+        @Nullable private final Fingerprint mFingerprint;
+
+        AngularLayoutConstraint(
+                DimensionProto.DegreesProp impl, @Nullable Fingerprint fingerprint) {
+            this.mImpl = impl;
+            this.mFingerprint = fingerprint;
+        }
+
+        /**
+         * Gets the fixed value to reserve the space when used on a layout-changing data bind. If
+         * not set defaults to the static value of the associated {@link DegreesProp} field.
+         *
+         * @since 1.2
+         */
+        @Dimension(unit = DP)
+        public float getValue() {
+            return mImpl.getValueForLayout();
+        }
+
+        /**
+         * Gets angular alignment of the actual content within the space reserved by value.
+         *
+         * @since 1.2
+         */
+        @LayoutElementBuilders.AngularAlignment
+        public int getAngularAlignment() {
+            return mImpl.getAngularAlignmentForLayoutValue();
+        }
+
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Nullable
+        public Fingerprint getFingerprint() {
+            return mFingerprint;
+        }
+
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public DimensionProto.DegreesProp toProto() {
+            return mImpl;
+        }
+
+        @NonNull
+        static AngularLayoutConstraint fromProto(@NonNull DimensionProto.DegreesProp proto) {
+            return new AngularLayoutConstraint(proto, null);
+        }
+
+        /** Builder for {@link AngularLayoutConstraint}. */
+        public static final class Builder {
+            private final DimensionProto.DegreesProp.Builder mImpl =
+                    DimensionProto.DegreesProp.newBuilder();
+            private final Fingerprint mFingerprint = new Fingerprint(-1927567664);
+
+            /**
+             * Creates a new builder for {@link AngularLayoutConstraint}.
+             *
+             * @param value Sets the fixed value to reserve the space when used on a layout-changing
+             *     data bind.
+             * @since 1.2
+             */
+            public Builder(@Dimension(unit = DP) float value) {
+                setValue(value);
+            }
+
+            /**
+             * Sets the fixed value to reserve the space when used on a layout-changing data bind.
+             *
+             * @since 1.2
+             */
+            @NonNull
+            private Builder setValue(@Dimension(unit = DP) float value) {
+                mImpl.setValueForLayout(value);
+                mFingerprint.recordPropertyUpdate(3, Float.floatToIntBits(value));
+                return this;
+            }
+
+            /**
+             * Sets angular alignment of the actual content within the space reserved by value. If
+             * not specified, defaults to center alignment.
+             *
+             * @since 1.2
+             */
+            @NonNull
+            public Builder setAngularAlignment(
+                    @LayoutElementBuilders.AngularAlignment int angularAlignment) {
+                mImpl.setAngularAlignmentForLayoutValue(angularAlignment);
+                mFingerprint.recordPropertyUpdate(4, angularAlignment);
+                return this;
+            }
+
+            /** Builds an instance of {@link AngularLayoutConstraint}. */
+            @NonNull
+            public AngularLayoutConstraint build() {
+                return new AngularLayoutConstraint(mImpl.build(), mFingerprint);
+            }
+        }
+    }
+
+    /**
      * A type for a dimension that fills all the space it can (i.e. MATCH_PARENT in Android
      * parlance).
      *
      * @since 1.0
      */
-    @RestrictTo(Scope.LIBRARY_GROUP)
     public static final class ExpandedDimensionProp implements ContainerDimension, ImageDimension {
         private final DimensionProto.ExpandedDimensionProp mImpl;
         @Nullable private final Fingerprint mFingerprint;
@@ -394,8 +891,13 @@
          *
          * @since 1.2
          */
-        public float getLayoutWeight() {
-            return mImpl.getLayoutWeight().getValue();
+        @Nullable
+        public FloatProp getLayoutWeight() {
+            if (mImpl.hasLayoutWeight()) {
+                return FloatProp.fromProto(mImpl.getLayoutWeight());
+            } else {
+                return null;
+            }
         }
 
         @Override
@@ -405,12 +907,23 @@
             return mFingerprint;
         }
 
+        /** Creates a new wrapper instance from the proto. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public static ExpandedDimensionProp fromProto(
+                @NonNull DimensionProto.ExpandedDimensionProp proto,
+                @Nullable Fingerprint fingerprint) {
+            return new ExpandedDimensionProp(proto, fingerprint);
+        }
+
         @NonNull
         static ExpandedDimensionProp fromProto(
                 @NonNull DimensionProto.ExpandedDimensionProp proto) {
-            return new ExpandedDimensionProp(proto, null);
+            return fromProto(proto, null);
         }
 
+        /** Returns the internal proto instance. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
         DimensionProto.ExpandedDimensionProp toProto() {
             return mImpl;
@@ -425,6 +938,7 @@
                     .build();
         }
 
+        /* */
         @Override
         @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
@@ -452,9 +966,10 @@
              * @since 1.2
              */
             @NonNull
-            public Builder setLayoutWeight(float layoutWeight) {
-                mImpl.setLayoutWeight(TypesProto.FloatProp.newBuilder().setValue(layoutWeight));
-                mFingerprint.recordPropertyUpdate(1, Float.floatToIntBits(layoutWeight));
+            public Builder setLayoutWeight(@NonNull FloatProp layoutWeight) {
+                mImpl.setLayoutWeight(layoutWeight.toProto());
+                mFingerprint.recordPropertyUpdate(
+                        1, checkNotNull(layoutWeight.getFingerprint()).aggregateValueAsInt());
                 return this;
             }
 
@@ -472,7 +987,6 @@
      *
      * @since 1.0
      */
-    @RestrictTo(Scope.LIBRARY_GROUP)
     public static final class WrappedDimensionProp implements ContainerDimension {
         private final DimensionProto.WrappedDimensionProp mImpl;
         @Nullable private final Fingerprint mFingerprint;
@@ -488,9 +1002,13 @@
          *
          * @since 1.2
          */
-        @Dimension(unit = DP)
-        public float getMinimumSizeDp() {
-            return mImpl.getMinimumSize().getValue();
+        @Nullable
+        public DpProp getMinimumSize() {
+            if (mImpl.hasMinimumSize()) {
+                return DpProp.fromProto(mImpl.getMinimumSize());
+            } else {
+                return null;
+            }
         }
 
         @Override
@@ -500,12 +1018,23 @@
             return mFingerprint;
         }
 
+        /** Creates a new wrapper instance from the proto. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
-        static WrappedDimensionProp fromProto(@NonNull DimensionProto.WrappedDimensionProp proto) {
-            return new WrappedDimensionProp(proto, null);
+        public static WrappedDimensionProp fromProto(
+                @NonNull DimensionProto.WrappedDimensionProp proto,
+                @Nullable Fingerprint fingerprint) {
+            return new WrappedDimensionProp(proto, fingerprint);
         }
 
         @NonNull
+        static WrappedDimensionProp fromProto(@NonNull DimensionProto.WrappedDimensionProp proto) {
+            return fromProto(proto, null);
+        }
+
+        /** Returns the internal proto instance. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
         DimensionProto.WrappedDimensionProp toProto() {
             return mImpl;
         }
@@ -519,6 +1048,12 @@
                     .build();
         }
 
+        @Override
+        @NonNull
+        public String toString() {
+            return "WrappedDimensionProp{" + "minimumSize=" + getMinimumSize() + "}";
+        }
+
         /** Builder for {@link WrappedDimensionProp}. */
         public static final class Builder implements ContainerDimension.Builder {
             private final DimensionProto.WrappedDimensionProp.Builder mImpl =
@@ -530,12 +1065,20 @@
             /**
              * Sets the minimum size of this dimension. If not set, then there is no minimum size.
              *
+             * <p>Note that this field only supports static values.
+             *
              * @since 1.2
              */
             @NonNull
-            public Builder setMinimumSizeDp(@Dimension(unit = DP) float minimumSize) {
-                mImpl.setMinimumSize(DimensionProto.DpProp.newBuilder().setValue(minimumSize));
-                mFingerprint.recordPropertyUpdate(1, Float.floatToIntBits(minimumSize));
+            public Builder setMinimumSize(@NonNull DpProp minimumSize) {
+                if (minimumSize.getDynamicValue() != null) {
+                    throw new IllegalArgumentException(
+                            "setMinimumSize doesn't support dynamic values.");
+                }
+
+                mImpl.setMinimumSize(minimumSize.toProto());
+                mFingerprint.recordPropertyUpdate(
+                        1, checkNotNull(minimumSize.getFingerprint()).aggregateValueAsInt());
                 return this;
             }
 
@@ -595,12 +1138,23 @@
             return mFingerprint;
         }
 
+        /** Creates a new wrapper instance from the proto. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public static ProportionalDimensionProp fromProto(
+                @NonNull DimensionProto.ProportionalDimensionProp proto,
+                @Nullable Fingerprint fingerprint) {
+            return new ProportionalDimensionProp(proto, fingerprint);
+        }
+
         @NonNull
         static ProportionalDimensionProp fromProto(
                 @NonNull DimensionProto.ProportionalDimensionProp proto) {
-            return new ProportionalDimensionProp(proto, null);
+            return fromProto(proto, null);
         }
 
+        /** Returns the internal proto instance. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
         DimensionProto.ProportionalDimensionProp toProto() {
             return mImpl;
@@ -615,6 +1169,17 @@
                     .build();
         }
 
+        @Override
+        @NonNull
+        public String toString() {
+            return "ProportionalDimensionProp{"
+                    + "aspectRatioWidth="
+                    + getAspectRatioWidth()
+                    + ", aspectRatioHeight="
+                    + getAspectRatioHeight()
+                    + "}";
+        }
+
         /** Builder for {@link ProportionalDimensionProp}. */
         public static final class Builder implements ImageDimension.Builder {
             private final DimensionProto.ProportionalDimensionProp.Builder mImpl =
@@ -672,7 +1237,7 @@
         Fingerprint getFingerprint();
 
         /** Builder to create {@link ContainerDimension} objects. */
-        @SuppressLint("StaticFinalBuilder")
+        @RestrictTo(Scope.LIBRARY_GROUP)
         interface Builder {
 
             /** Builds an instance with values accumulated in this Builder. */
@@ -681,22 +1246,30 @@
         }
     }
 
+    /** Creates a new wrapper instance from the proto. */
+    @RestrictTo(Scope.LIBRARY_GROUP)
     @NonNull
-    static ContainerDimension containerDimensionFromProto(
-            @NonNull DimensionProto.ContainerDimension proto) {
+    public static ContainerDimension containerDimensionFromProto(
+            @NonNull DimensionProto.ContainerDimension proto, @Nullable Fingerprint fingerprint) {
         if (proto.hasLinearDimension()) {
-            return DpProp.fromProto(proto.getLinearDimension());
+            return DpProp.fromProto(proto.getLinearDimension(), fingerprint);
         }
         if (proto.hasExpandedDimension()) {
-            return ExpandedDimensionProp.fromProto(proto.getExpandedDimension());
+            return ExpandedDimensionProp.fromProto(proto.getExpandedDimension(), fingerprint);
         }
         if (proto.hasWrappedDimension()) {
-            return WrappedDimensionProp.fromProto(proto.getWrappedDimension());
+            return WrappedDimensionProp.fromProto(proto.getWrappedDimension(), fingerprint);
         }
         throw new IllegalStateException(
                 "Proto was not a recognised instance of ContainerDimension");
     }
 
+    @NonNull
+    static ContainerDimension containerDimensionFromProto(
+            @NonNull DimensionProto.ContainerDimension proto) {
+        return containerDimensionFromProto(proto, null);
+    }
+
     /**
      * Interface defining a dimension that can be applied to an image.
      *
@@ -714,7 +1287,7 @@
         Fingerprint getFingerprint();
 
         /** Builder to create {@link ImageDimension} objects. */
-        @SuppressLint("StaticFinalBuilder")
+        @RestrictTo(Scope.LIBRARY_GROUP)
         interface Builder {
 
             /** Builds an instance with values accumulated in this Builder. */
@@ -723,20 +1296,29 @@
         }
     }
 
+    /** Creates a new wrapper instance from the proto. */
+    @RestrictTo(Scope.LIBRARY_GROUP)
     @NonNull
-    static ImageDimension imageDimensionFromProto(@NonNull DimensionProto.ImageDimension proto) {
+    public static ImageDimension imageDimensionFromProto(
+            @NonNull DimensionProto.ImageDimension proto, @Nullable Fingerprint fingerprint) {
         if (proto.hasLinearDimension()) {
-            return DpProp.fromProto(proto.getLinearDimension());
+            return DpProp.fromProto(proto.getLinearDimension(), fingerprint);
         }
         if (proto.hasExpandedDimension()) {
-            return ExpandedDimensionProp.fromProto(proto.getExpandedDimension());
+            return ExpandedDimensionProp.fromProto(proto.getExpandedDimension(), fingerprint);
         }
         if (proto.hasProportionalDimension()) {
-            return ProportionalDimensionProp.fromProto(proto.getProportionalDimension());
+            return ProportionalDimensionProp.fromProto(
+                    proto.getProportionalDimension(), fingerprint);
         }
         throw new IllegalStateException("Proto was not a recognised instance of ImageDimension");
     }
 
+    @NonNull
+    static ImageDimension imageDimensionFromProto(@NonNull DimensionProto.ImageDimension proto) {
+        return imageDimensionFromProto(proto, null);
+    }
+
     /**
      * Interface defining a dimension that can be applied to a spacer.
      *
@@ -754,7 +1336,7 @@
         Fingerprint getFingerprint();
 
         /** Builder to create {@link SpacerDimension} objects. */
-        @SuppressLint("StaticFinalBuilder")
+        @RestrictTo(Scope.LIBRARY_GROUP)
         interface Builder {
 
             /** Builds an instance with values accumulated in this Builder. */
@@ -763,11 +1345,19 @@
         }
     }
 
+    /** Creates a new wrapper instance from the proto. */
+    @RestrictTo(Scope.LIBRARY_GROUP)
     @NonNull
-    static SpacerDimension spacerDimensionFromProto(@NonNull DimensionProto.SpacerDimension proto) {
+    public static SpacerDimension spacerDimensionFromProto(
+            @NonNull DimensionProto.SpacerDimension proto, @Nullable Fingerprint fingerprint) {
         if (proto.hasLinearDimension()) {
-            return DpProp.fromProto(proto.getLinearDimension());
+            return DpProp.fromProto(proto.getLinearDimension(), fingerprint);
         }
         throw new IllegalStateException("Proto was not a recognised instance of SpacerDimension");
     }
+
+    @NonNull
+    static SpacerDimension spacerDimensionFromProto(@NonNull DimensionProto.SpacerDimension proto) {
+        return spacerDimensionFromProto(proto, null);
+    }
 }
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
index 3aa4228..3800b46 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
@@ -28,22 +28,27 @@
 import androidx.annotation.RestrictTo.Scope;
 import androidx.wear.protolayout.ColorBuilders.ColorProp;
 import androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters;
+import androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint;
 import androidx.wear.protolayout.DimensionBuilders.ContainerDimension;
 import androidx.wear.protolayout.DimensionBuilders.DegreesProp;
 import androidx.wear.protolayout.DimensionBuilders.DpProp;
 import androidx.wear.protolayout.DimensionBuilders.EmProp;
+import androidx.wear.protolayout.DimensionBuilders.HorizontalLayoutConstraint;
 import androidx.wear.protolayout.DimensionBuilders.ImageDimension;
 import androidx.wear.protolayout.DimensionBuilders.SpProp;
 import androidx.wear.protolayout.DimensionBuilders.SpacerDimension;
+import androidx.wear.protolayout.DimensionBuilders.VerticalLayoutConstraint;
 import androidx.wear.protolayout.ModifiersBuilders.ArcModifiers;
 import androidx.wear.protolayout.ModifiersBuilders.Modifiers;
 import androidx.wear.protolayout.ModifiersBuilders.SpanModifiers;
 import androidx.wear.protolayout.TypeBuilders.BoolProp;
 import androidx.wear.protolayout.TypeBuilders.Int32Prop;
+import androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint;
 import androidx.wear.protolayout.TypeBuilders.StringProp;
 import androidx.wear.protolayout.expression.Fingerprint;
 import androidx.wear.protolayout.expression.ProtoLayoutExperimental;
 import androidx.wear.protolayout.proto.AlignmentProto;
+import androidx.wear.protolayout.proto.DimensionProto;
 import androidx.wear.protolayout.proto.FingerprintProto;
 import androidx.wear.protolayout.proto.FingerprintProto.TreeFingerprint;
 import androidx.wear.protolayout.proto.LayoutElementProto;
@@ -549,7 +554,15 @@
                 return this;
             }
 
-            /** Sets the text color. If not defined, defaults to white. */
+            /**
+             * Sets the text color. If not defined, defaults to white.
+             *
+             * <p>This field is made bindable from version 1.2 and will use the dynamic value (if
+             * set). Older renderers will still consider this field as non-bindable and will use the
+             * static value.
+             *
+             * @since 1.0
+             */
             @NonNull
             public Builder setColor(@NonNull ColorProp color) {
                 mImpl.setColor(color.toProto());
@@ -705,6 +718,21 @@
         }
 
         /**
+         * Gets the bounding constraints for the layout affected by the dynamic value from {@link
+         * #getText()}.
+         *
+         * @since 1.2
+         */
+        @Nullable
+        public StringLayoutConstraint getLayoutConstraintsForDynamicText() {
+            if (mImpl.hasText()) {
+                return StringLayoutConstraint.fromProto(mImpl.getText());
+            } else {
+                return null;
+            }
+        }
+
+        /**
          * Gets the style of font to use (size, bold etc). If not specified, defaults to the
          * platform's default body font. Intended for testing purposes only.
          */
@@ -718,8 +746,8 @@
         }
 
         /**
-         * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended
-         * for testing purposes only.
+         * Gets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+         * Intended for testing purposes only.
          */
         @Nullable
         public Modifiers getModifiers() {
@@ -822,15 +850,50 @@
 
             public Builder() {}
 
-            /** Sets the text to render. */
+            /**
+             * Sets the text to render.
+             *
+             * <p>This field is made bindable from version 1.2 and will use the dynamic value (if
+             * set). Older renderers will still consider this field as non-bindable and will use the
+             * static value.
+             *
+             * <p>When using a dynamic value, make sure to specify the bounding constraints for the
+             * affected layout element through {@link
+             * #setLayoutConstraintsForDynamicText(StringLayoutConstraint)} otherwise {@link
+             * #build()} fails.
+             *
+             * @since 1.0
+             */
             @NonNull
             public Builder setText(@NonNull StringProp text) {
-                mImpl.setText(text.toProto());
+                mImpl.mergeText(text.toProto());
                 mFingerprint.recordPropertyUpdate(
                         1, checkNotNull(text.getFingerprint()).aggregateValueAsInt());
                 return this;
             }
-            /** Sets the text to render. */
+
+            /**
+             * Sets the bounding constraints for the layout affected by the dynamic value from
+             * {@link #setText(StringProp)}}.
+             *
+             * @since 1.2
+             */
+            @NonNull
+            public Builder setLayoutConstraintsForDynamicText(
+                    @NonNull StringLayoutConstraint stringLayoutConstraint) {
+                mImpl.mergeText(stringLayoutConstraint.toProto());
+                mFingerprint.recordPropertyUpdate(
+                        1,
+                        checkNotNull(stringLayoutConstraint.getFingerprint())
+                                .aggregateValueAsInt());
+                return this;
+            }
+
+            /**
+             * Sets the static text to render.
+             *
+             * @since 1.0
+             */
             @NonNull
             public Builder setText(@NonNull String text) {
                 mImpl.setText(TypesProto.StringProp.newBuilder().setValue(text));
@@ -849,7 +912,9 @@
                 return this;
             }
 
-            /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+            /**
+             * Sets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+             */
             @NonNull
             public Builder setModifiers(@NonNull Modifiers modifiers) {
                 mImpl.setModifiers(modifiers.toProto());
@@ -955,6 +1020,12 @@
             @Override
             @NonNull
             public Text build() {
+                TypesProto.StringProp text = mImpl.getText();
+                if (text.hasDynamicValue() && !text.hasValueForLayout()) {
+                    throw new IllegalStateException(
+                            "text with dynamic value requires "
+                                    + "layoutConstraintsForDynamicText to be present.");
+                }
                 return new Text(mImpl.build(), mFingerprint);
             }
         }
@@ -1078,6 +1149,10 @@
              *
              * <p>Note that only Android image resources can be tinted; Inline images will not be
              * tinted, and this property will have no effect.
+             *
+             * <p>This field is made bindable from version 1.2 and will use the dynamic value (if
+             * set). Older renderers will still consider this field as non-bindable and will use the
+             * static value.
              */
             @NonNull
             public Builder setTint(@NonNull ColorProp tint) {
@@ -1165,8 +1240,8 @@
         }
 
         /**
-         * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended
-         * for testing purposes only.
+         * Gets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+         * Intended for testing purposes only.
          */
         @Nullable
         public Modifiers getModifiers() {
@@ -1286,7 +1361,9 @@
                 return this;
             }
 
-            /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+            /**
+             * Sets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+             */
             @NonNull
             public Builder setModifiers(@NonNull Modifiers modifiers) {
                 mImpl.setModifiers(modifiers.toProto());
@@ -1327,7 +1404,7 @@
         /**
          * Gets the width of this {@link Spacer}. When this is added as the direct child of an
          * {@link Arc}, this must be specified as an angular dimension, otherwise a linear dimension
-         * must be used. If not defined, defaults to 0. Intended for testing purposes only.
+         * must be used. If not defined, defaults to 0.
          */
         @Nullable
         public SpacerDimension getWidth() {
@@ -1339,10 +1416,22 @@
         }
 
         /**
-         * Gets the height of this spacer. If not defined, defaults to 0. Intended for testing
-         * purposes only.
+         * Gets the bounding constraints for the layout affected by the dynamic value from {@link
+         * #getWidth()}.
+         *
+         * @since 1.2
          */
         @Nullable
+        public HorizontalLayoutConstraint getLayoutConstraintsForDynamicWidth() {
+            if (mImpl.getWidth().hasLinearDimension()) {
+                return HorizontalLayoutConstraint.fromProto(mImpl.getWidth().getLinearDimension());
+            } else {
+                return null;
+            }
+        }
+
+        /** Gets the height of this spacer. If not defined, defaults to 0. */
+        @Nullable
         public SpacerDimension getHeight() {
             if (mImpl.hasHeight()) {
                 return DimensionBuilders.spacerDimensionFromProto(mImpl.getHeight());
@@ -1352,8 +1441,23 @@
         }
 
         /**
-         * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended
-         * for testing purposes only.
+         * Gets the bounding constraints for the layout affected by the dynamic value from {@link
+         * #getHeight()}.
+         *
+         * @since 1.2
+         */
+        @Nullable
+        public VerticalLayoutConstraint getLayoutConstraintsForDynamicHeight() {
+            if (mImpl.getHeight().hasLinearDimension()) {
+                return VerticalLayoutConstraint.fromProto(mImpl.getHeight().getLinearDimension());
+            } else {
+                return null;
+            }
+        }
+
+        /**
+         * Gets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+         * Intended for testing purposes only.
          */
         @Nullable
         public Modifiers getModifiers() {
@@ -1400,16 +1504,62 @@
              * Sets the width of this {@link Spacer}. When this is added as the direct child of an
              * {@link Arc}, this must be specified as an angular dimension, otherwise a linear
              * dimension must be used. If not defined, defaults to 0.
+             *
+             * <p>This field is made bindable from version 1.2 and will use the dynamic value (if
+             * set). Older renderers will still consider this field as non-bindable and will use the
+             * static value.
+             *
+             * <p>When using a dynamic value, make sure to specify the bounding constraints for the
+             * affected layout element through {@link
+             * #setLayoutConstraintsForDynamicWidth(HorizontalLayoutConstraint)} otherwise {@link
+             * #build()} fails.
              */
             @NonNull
             public Builder setWidth(@NonNull SpacerDimension width) {
-                mImpl.setWidth(width.toSpacerDimensionProto());
+                mImpl.mergeWidth(width.toSpacerDimensionProto());
                 mFingerprint.recordPropertyUpdate(
                         1, checkNotNull(width.getFingerprint()).aggregateValueAsInt());
                 return this;
             }
 
-            /** Sets the height of this spacer. If not defined, defaults to 0. */
+            /**
+             * Sets the bounding constraints for the layout affected by the dynamic value from
+             * {@link #setWidth(SpacerDimension)}. If the {@link SpacerDimension} does not have a
+             * dynamic value, this will be ignored.
+             *
+             * @since 1.2
+             */
+            @NonNull
+            public Builder setLayoutConstraintsForDynamicWidth(
+                    @NonNull HorizontalLayoutConstraint horizontalLayoutConstraint) {
+                switch (mImpl.getWidth().getInnerCase()) {
+                    case INNER_NOT_SET:
+                    case LINEAR_DIMENSION:
+                        mImpl.mergeWidth(horizontalLayoutConstraint.toSpacerDimensionProto());
+                        mFingerprint.recordPropertyUpdate(
+                                1,
+                                checkNotNull(horizontalLayoutConstraint.getFingerprint())
+                                        .aggregateValueAsInt());
+                        break;
+                    default:
+                }
+                return this;
+            }
+
+            /**
+             * Sets the height of this spacer. If not defined, defaults to 0.
+             *
+             * <p>This field is made bindable from version 1.2 and will use the dynamic value (if
+             * set). Older renderers will still consider this field as non-bindable and will use the
+             * static value.
+             *
+             * <p>When using a dynamic value, make sure to specify the bounding constraints for the
+             * affected layout element through {@link
+             * #setLayoutConstraintsForDynamicWidth(HorizontalLayoutConstraint)} otherwise {@link
+             * #build()} fails.
+             *
+             * @since 1.0
+             */
             @NonNull
             public Builder setHeight(@NonNull SpacerDimension height) {
                 mImpl.setHeight(height.toSpacerDimensionProto());
@@ -1418,7 +1568,33 @@
                 return this;
             }
 
-            /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+            /**
+             * Sets the bounding constraints for the layout affected by the dynamic value from
+             * {@link #setHeight(SpacerDimension)}. If the {@link SpacerDimension} does not have a
+             * dynamic value, this will be ignored.
+             *
+             * @since 1.2
+             */
+            @NonNull
+            public Builder setLayoutConstraintsForDynamicHeight(
+                    @NonNull VerticalLayoutConstraint verticalLayoutConstraint) {
+                switch (mImpl.getHeight().getInnerCase()) {
+                    case INNER_NOT_SET:
+                    case LINEAR_DIMENSION:
+                        mImpl.mergeHeight(verticalLayoutConstraint.toSpacerDimensionProto());
+                        mFingerprint.recordPropertyUpdate(
+                                2,
+                                checkNotNull(verticalLayoutConstraint.getFingerprint())
+                                        .aggregateValueAsInt());
+                        break;
+                    default:
+                }
+                return this;
+            }
+
+            /**
+             * Sets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+             */
             @NonNull
             public Builder setModifiers(@NonNull Modifiers modifiers) {
                 mImpl.setModifiers(modifiers.toProto());
@@ -1430,6 +1606,18 @@
             @Override
             @NonNull
             public Spacer build() {
+                DimensionProto.DpProp width = mImpl.getWidth().getLinearDimension();
+                if (width.hasDynamicValue() && !width.hasValueForLayout()) {
+                    throw new IllegalStateException(
+                            "width with dynamic value requires "
+                                    + "layoutConstraintsForDynamicWidth to be present.");
+                }
+                DimensionProto.DpProp height = mImpl.getHeight().getLinearDimension();
+                if (height.hasDynamicValue() && !height.hasValueForLayout()) {
+                    throw new IllegalStateException(
+                            "height with dynamic value requires "
+                                    + "layoutConstraintsForDynamicHeight to be present.");
+                }
                 return new Spacer(mImpl.build(), mFingerprint);
             }
         }
@@ -1511,8 +1699,8 @@
         }
 
         /**
-         * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended
-         * for testing purposes only.
+         * Gets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+         * Intended for testing purposes only.
          */
         @Nullable
         public Modifiers getModifiers() {
@@ -1640,7 +1828,9 @@
                 return this;
             }
 
-            /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+            /**
+             * Sets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+             */
             @NonNull
             public Builder setModifiers(@NonNull Modifiers modifiers) {
                 mImpl.setModifiers(modifiers.toProto());
@@ -1695,8 +1885,8 @@
         }
 
         /**
-         * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended
-         * for testing purposes only.
+         * Gets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+         * Intended for testing purposes only.
          */
         @Nullable
         public SpanModifiers getModifiers() {
@@ -1739,9 +1929,19 @@
 
             public Builder() {}
 
-            /** Sets the text to render. */
+            /**
+             * Sets the text to render.
+             *
+             * <p>Note that this field only supports static values.
+             *
+             * @since 1.0
+             */
             @NonNull
             public Builder setText(@NonNull StringProp text) {
+                if (text.getDynamicValue() != null) {
+                    throw new IllegalArgumentException(
+                            "SpanText.Builder.setText doesn't support dynamic values.");
+                }
                 mImpl.setText(text.toProto());
                 mFingerprint.recordPropertyUpdate(
                         1, checkNotNull(text.getFingerprint()).aggregateValueAsInt());
@@ -1766,7 +1966,9 @@
                 return this;
             }
 
-            /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+            /**
+             * Sets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+             */
             @NonNull
             public Builder setModifiers(@NonNull SpanModifiers modifiers) {
                 mImpl.setModifiers(modifiers.toProto());
@@ -1833,8 +2035,8 @@
         }
 
         /**
-         * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended
-         * for testing purposes only.
+         * Gets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+         * Intended for testing purposes only.
          */
         @Nullable
         public SpanModifiers getModifiers() {
@@ -1929,7 +2131,9 @@
                 return this;
             }
 
-            /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+            /**
+             * Sets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+             */
             @NonNull
             public Builder setModifiers(@NonNull SpanModifiers modifiers) {
                 mImpl.setModifiers(modifiers.toProto());
@@ -2037,8 +2241,8 @@
         }
 
         /**
-         * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended
-         * for testing purposes only.
+         * Gets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+         * Intended for testing purposes only.
          */
         @Nullable
         public Modifiers getModifiers() {
@@ -2151,7 +2355,9 @@
                 return this;
             }
 
-            /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+            /**
+             * Sets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+             */
             @NonNull
             public Builder setModifiers(@NonNull Modifiers modifiers) {
                 mImpl.setModifiers(modifiers.toProto());
@@ -2338,8 +2544,8 @@
         }
 
         /**
-         * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended
-         * for testing purposes only.
+         * Gets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+         * Intended for testing purposes only.
          */
         @Nullable
         public Modifiers getModifiers() {
@@ -2444,7 +2650,9 @@
                 return this;
             }
 
-            /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+            /**
+             * Sets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+             */
             @NonNull
             public Builder setModifiers(@NonNull Modifiers modifiers) {
                 mImpl.setModifiers(modifiers.toProto());
@@ -2534,8 +2742,8 @@
         }
 
         /**
-         * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended
-         * for testing purposes only.
+         * Gets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+         * Intended for testing purposes only.
          */
         @Nullable
         public Modifiers getModifiers() {
@@ -2636,7 +2844,9 @@
                 return this;
             }
 
-            /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+            /**
+             * Sets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+             */
             @NonNull
             public Builder setModifiers(@NonNull Modifiers modifiers) {
                 mImpl.setModifiers(modifiers.toProto());
@@ -2696,6 +2906,21 @@
         }
 
         /**
+         * Gets the bounding constraints for the layout affected by the dynamic value from {@link
+         * #getAnchorAngle()}.
+         *
+         * @since 1.2
+         */
+        @Nullable
+        public AngularLayoutConstraint getLayoutConstraintsForDynamicAnchorAngle() {
+            if (mImpl.hasAnchorAngle()) {
+                return AngularLayoutConstraint.fromProto(mImpl.getAnchorAngle());
+            } else {
+                return null;
+            }
+        }
+
+        /**
          * Gets how to align the contents of this container relative to anchor_angle. If not
          * defined, defaults to ARC_ANCHOR_CENTER. Intended for testing purposes only.
          */
@@ -2724,8 +2949,8 @@
         }
 
         /**
-         * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended
-         * for testing purposes only.
+         * Gets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+         * Intended for testing purposes only.
          */
         @Nullable
         public Modifiers getModifiers() {
@@ -2784,6 +3009,17 @@
              * <p>Values do not have to be clamped to the range 0-360; values less than 0 degrees
              * will sweep anti-clockwise (i.e. -90 degrees is equivalent to 270 degrees), and values
              * >360 will be be placed at X mod 360 degrees.
+             *
+             * <p>This field is made bindable from version 1.2 and will use the dynamic value (if
+             * set). Older renderers will still consider this field as non-bindable and will use the
+             * static value.
+             *
+             * <p>When using a dynamic value, make sure to specify the bounding constraints for the
+             * affected layout element through {@link
+             * #setLayoutConstraintsForDynamicAnchorAngle(AngularLayoutConstraint)} otherwise {@link
+             * #build()} fails.
+             *
+             * @since 1.0
              */
             @NonNull
             public Builder setAnchorAngle(@NonNull DegreesProp anchorAngle) {
@@ -2794,6 +3030,23 @@
             }
 
             /**
+             * Sets the bounding constraints for the layout affected by the dynamic value from
+             * {@link #setAnchorAngle(DegreesProp)}}.
+             *
+             * @since 1.2
+             */
+            @NonNull
+            public Builder setLayoutConstraintsForDynamicAnchorAngle(
+                    @NonNull DimensionBuilders.AngularLayoutConstraint angularLayoutConstraint) {
+                mImpl.mergeAnchorAngle(angularLayoutConstraint.toProto());
+                mFingerprint.recordPropertyUpdate(
+                        2,
+                        checkNotNull(angularLayoutConstraint.getFingerprint())
+                                .aggregateValueAsInt());
+                return this;
+            }
+
+            /**
              * Sets how to align the contents of this container relative to anchor_angle. If not
              * defined, defaults to ARC_ANCHOR_CENTER.
              */
@@ -2845,7 +3098,9 @@
                 return this;
             }
 
-            /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+            /**
+             * Sets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+             */
             @NonNull
             public Builder setModifiers(@NonNull Modifiers modifiers) {
                 mImpl.setModifiers(modifiers.toProto());
@@ -2857,6 +3112,12 @@
             @Override
             @NonNull
             public Arc build() {
+                DimensionProto.DegreesProp anchorAngle = mImpl.getAnchorAngle();
+                if (anchorAngle.hasDynamicValue() && !anchorAngle.hasValueForLayout()) {
+                    throw new IllegalStateException(
+                            "anchorAngle with dynamic value requires "
+                                    + "layoutConstraintsForDynamicAnchorAngle to be present.");
+                }
                 return new Arc(mImpl.build(), mFingerprint);
             }
         }
@@ -2896,8 +3157,8 @@
         }
 
         /**
-         * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended
-         * for testing purposes only.
+         * Gets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+         * Intended for testing purposes only.
          */
         @Nullable
         public ArcModifiers getModifiers() {
@@ -2940,9 +3201,19 @@
 
             public Builder() {}
 
-            /** Sets the text to render. */
+            /**
+             * Sets the text to render.
+             *
+             * <p>Note that this field only supports static values.
+             *
+             * @since 1.0
+             */
             @NonNull
             public Builder setText(@NonNull StringProp text) {
+                if (text.getDynamicValue() != null) {
+                    throw new IllegalArgumentException(
+                            "ArcText.Builder.setText doesn't support dynamic values.");
+                }
                 mImpl.setText(text.toProto());
                 mFingerprint.recordPropertyUpdate(
                         1, checkNotNull(text.getFingerprint()).aggregateValueAsInt());
@@ -2968,7 +3239,9 @@
                 return this;
             }
 
-            /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+            /**
+             * Sets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+             */
             @NonNull
             public Builder setModifiers(@NonNull ArcModifiers modifiers) {
                 mImpl.setModifiers(modifiers.toProto());
@@ -3009,6 +3282,21 @@
         }
 
         /**
+         * Gets the bounding constraints for the layout affected by the dynamic value from {@link
+         * #getLength()}.
+         *
+         * @since 1.2
+         */
+        @Nullable
+        public AngularLayoutConstraint getLayoutConstraintsForDynamicLength() {
+            if (mImpl.hasLength()) {
+                return AngularLayoutConstraint.fromProto(mImpl.getLength());
+            } else {
+                return null;
+            }
+        }
+
+        /**
          * Gets the thickness of this line. If not defined, defaults to 0. Intended for testing
          * purposes only.
          */
@@ -3032,8 +3320,8 @@
         }
 
         /**
-         * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended
-         * for testing purposes only.
+         * Gets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+         * Intended for testing purposes only.
          */
         @Nullable
         public ArcModifiers getModifiers() {
@@ -3076,15 +3364,45 @@
 
             public Builder() {}
 
-            /** Sets the length of this line, in degrees. If not defined, defaults to 0. */
+            /**
+             * Sets the length of this line, in degrees. If not defined, defaults to 0.
+             *
+             * <p>This field is made bindable from version 1.2 and will use the dynamic value (if
+             * set). Older renderers will still consider this field as non-bindable and will use the
+             * static value.
+             *
+             * <p>When using a dynamic value, make sure to specify the bounding constraints for the
+             * affected layout element through {@link
+             * #setLayoutConstraintsForDynamicLength(AngularLayoutConstraint)} otherwise {@link
+             * #build()} fails.
+             *
+             * @since 1.0
+             */
             @NonNull
             public Builder setLength(@NonNull DegreesProp length) {
-                mImpl.setLength(length.toProto());
+                mImpl.mergeLength(length.toProto());
                 mFingerprint.recordPropertyUpdate(
                         1, checkNotNull(length.getFingerprint()).aggregateValueAsInt());
                 return this;
             }
 
+            /**
+             * Sets the bounding constraints for the layout affected by the dynamic value from
+             * {@link #setLength(DegreesProp)}.
+             *
+             * @since 1.2
+             */
+            @NonNull
+            public Builder setLayoutConstraintsForDynamicLength(
+                    @NonNull DimensionBuilders.AngularLayoutConstraint angularLayoutConstraint) {
+                mImpl.mergeLength(angularLayoutConstraint.toProto());
+                mFingerprint.recordPropertyUpdate(
+                        1,
+                        checkNotNull(angularLayoutConstraint.getFingerprint())
+                                .aggregateValueAsInt());
+                return this;
+            }
+
             /** Sets the thickness of this line. If not defined, defaults to 0. */
             @NonNull
             public Builder setThickness(@NonNull DpProp thickness) {
@@ -3094,7 +3412,15 @@
                 return this;
             }
 
-            /** Sets the color of this line. */
+            /**
+             * Sets the color of this line.
+             *
+             * <p>This field is made bindable from version 1.2 and will use the dynamic value (if
+             * set). Older renderers will still consider this field as non-bindable and will use the
+             * static value.
+             *
+             * @since 1.0
+             */
             @NonNull
             public Builder setColor(@NonNull ColorProp color) {
                 mImpl.setColor(color.toProto());
@@ -3103,7 +3429,9 @@
                 return this;
             }
 
-            /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+            /**
+             * Sets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+             */
             @NonNull
             public Builder setModifiers(@NonNull ArcModifiers modifiers) {
                 mImpl.setModifiers(modifiers.toProto());
@@ -3115,6 +3443,12 @@
             @Override
             @NonNull
             public ArcLine build() {
+                DimensionProto.DegreesProp length = mImpl.getLength();
+                if (length.hasDynamicValue() && !length.hasValueForLayout()) {
+                    throw new IllegalStateException(
+                            "length with dynamic value requires "
+                                    + "layoutConstraintsForDynamicLength to be present.");
+                }
                 return new ArcLine(mImpl.build(), mFingerprint);
             }
         }
@@ -3157,8 +3491,8 @@
         }
 
         /**
-         * Gets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. Intended
-         * for testing purposes only.
+         * Gets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+         * Intended for testing purposes only.
          */
         @Nullable
         public ArcModifiers getModifiers() {
@@ -3219,7 +3553,9 @@
                 return this;
             }
 
-            /** Sets {@link androidx.wear.tiles.ModifiersBuilders.Modifiers} for this element. */
+            /**
+             * Sets {@link androidx.wear.protolayout.ModifiersBuilders.Modifiers} for this element.
+             */
             @NonNull
             public Builder setModifiers(@NonNull ArcModifiers modifiers) {
                 mImpl.setModifiers(modifiers.toProto());
@@ -3615,32 +3951,34 @@
     public static final int TEXT_ALIGN_UNDEFINED = 0;
 
     /**
-     * Align to the "start" of the {@link androidx.wear.tiles.LayoutElementBuilders.Text} element
-     * (left in LTR layouts, right in RTL layouts).
+     * Align to the "start" of the {@link androidx.wear.protolayout.LayoutElementBuilders.Text}
+     * element (left in LTR layouts, right in RTL layouts).
      */
     public static final int TEXT_ALIGN_START = 1;
 
     /**
-     * Align to the center of the {@link androidx.wear.tiles.LayoutElementBuilders.Text} element.
+     * Align to the center of the {@link androidx.wear.protolayout.LayoutElementBuilders.Text}
+     * element.
      */
     public static final int TEXT_ALIGN_CENTER = 2;
 
     /**
-     * Align to the "end" of the {@link androidx.wear.tiles.LayoutElementBuilders.Text} element
-     * (right in LTR layouts, left in RTL layouts).
+     * Align to the "end" of the {@link androidx.wear.protolayout.LayoutElementBuilders.Text}
+     * element (right in LTR layouts, left in RTL layouts).
      */
     public static final int TEXT_ALIGN_END = 3;
 
     /**
-     * The anchor position of an {@link androidx.wear.tiles.LayoutElementBuilders.Arc}'s elements.
-     * This is used to specify how elements added to an {@link
-     * androidx.wear.tiles.LayoutElementBuilders.Arc} should be laid out with respect to
+     * The anchor position of an {@link androidx.wear.protolayout.LayoutElementBuilders.Arc}'s
+     * elements. This is used to specify how elements added to an {@link
+     * androidx.wear.protolayout.LayoutElementBuilders.Arc} should be laid out with respect to
      * anchor_angle.
      *
      * <p>As an example, assume that the following diagrams are wrapped to an arc, and each
-     * represents an {@link androidx.wear.tiles.LayoutElementBuilders.Arc} element containing a
-     * single {@link androidx.wear.tiles.LayoutElementBuilders.Text} element. The {@link
-     * androidx.wear.tiles.LayoutElementBuilders.Text} element's anchor_angle is "0" for all cases.
+     * represents an {@link androidx.wear.protolayout.LayoutElementBuilders.Arc} element containing
+     * a single {@link androidx.wear.protolayout.LayoutElementBuilders.Text} element. The {@link
+     * androidx.wear.protolayout.LayoutElementBuilders.Text} element's anchor_angle is "0" for all
+     * cases.
      *
      * <pre>{@code
      * ARC_ANCHOR_START:
@@ -3684,6 +4022,58 @@
      */
     public static final int ARC_ANCHOR_END = 3;
 
+    /**
+     * How to lay out components in a {@link androidx.wear.protolayout.LayoutElementBuilders.Arc}
+     * context when they are smaller than their container. This would be similar to {@code
+     * HorizontalAlignment} in a {@link androidx.wear.protolayout.LayoutElementBuilders.Box} or
+     * {@link androidx.wear.protolayout.LayoutElementBuilders.Column}.
+     *
+     * @since 1.2
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @IntDef({
+        ANGULAR_ALIGNMENT_UNDEFINED,
+        ANGULAR_ALIGNMENT_START,
+        ANGULAR_ALIGNMENT_CENTER,
+        ANGULAR_ALIGNMENT_END
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AngularAlignment {}
+
+    /**
+     * Angular alignment is undefined.
+     *
+     * @since 1.2
+     */
+    public static final int ANGULAR_ALIGNMENT_UNDEFINED = 0;
+
+    /**
+     * Align to the start of the container. As an example, if the container starts at 90 degrees and
+     * has 180 degrees of sweep, the element within would draw from 90 degrees, clockwise.
+     *
+     * @since 1.2
+     */
+    public static final int ANGULAR_ALIGNMENT_START = 1;
+
+    /**
+     * Align to the center of the container. As an example, if the container starts at 90 degrees,
+     * and has 180 degrees of sweep, and the contained element has 90 degrees of sweep, the element
+     * would draw between 135 and 225 degrees.
+     *
+     * @since 1.2
+     */
+    public static final int ANGULAR_ALIGNMENT_CENTER = 2;
+
+    /**
+     * Align to the end of the container. As an example, if the container starts at 90 degrees and
+     * has 180 degrees of sweep, and the contained element has 90 degrees of sweep, the element
+     * would draw between 180 and 270 degrees.
+     *
+     * @since 1.2
+     */
+    public static final int ANGULAR_ALIGNMENT_END = 3;
+
     /** An extensible {@code HorizontalAlignment} property. */
     public static final class HorizontalAlignmentProp {
         private final AlignmentProto.HorizontalAlignmentProp mImpl;
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ModifiersBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ModifiersBuilders.java
index 4a65eeb..adb3aaa 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ModifiersBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ModifiersBuilders.java
@@ -663,9 +663,8 @@
             /**
              * Sets the color of the border.
              *
-             * <p>
-             * This field is made bindable and will use the dynamic value (if set) from version 1.2
-             * Older renderers will still consider this field as non-bindable and will use the
+             * <p>This field is made bindable and will use the dynamic value (if set) from version
+             * 1.2 Older renderers will still consider this field as non-bindable and will use the
              * static value.
              */
             @NonNull
@@ -684,7 +683,7 @@
         }
     }
 
-    /** The corner of a {@link androidx.wear.tiles.LayoutElementBuilders.Box} element. */
+    /** The corner of a {@link androidx.wear.protolayout.LayoutElementBuilders.Box} element. */
     public static final class Corner {
         private final ModifiersProto.Corner mImpl;
         @Nullable private final Fingerprint mFingerprint;
@@ -810,10 +809,10 @@
             /**
              * Sets the background color for this element. If not defined, defaults to being
              * transparent.
-             * <p>
-             * This field is made bindable and supports dynamic colors from version 1.2
-             * Older renderers will still consider this field as non-bindable and will use the
-             * static value.
+             *
+             * <p>This field is made bindable and supports dynamic colors from version 1.2 Older
+             * renderers will still consider this field as non-bindable and will use the static
+             * value.
              */
             @NonNull
             public Builder setColor(@NonNull ColorProp color) {
@@ -2312,8 +2311,8 @@
 
     /**
      * {@link Modifiers} that can be used with {@link
-     * androidx.wear.tiles.LayoutElementBuilders.Span} elements. These may change the way they are
-     * drawn, or change their behaviour.
+     * androidx.wear.protolayout.LayoutElementBuilders.Span} elements. These may change the way they
+     * are drawn, or change their behaviour.
      */
     public static final class SpanModifiers {
         private final ModifiersProto.SpanModifiers mImpl;
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TypeBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TypeBuilders.java
index f445db0..4ce0f30 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TypeBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TypeBuilders.java
@@ -16,303 +16,447 @@
 
 package androidx.wear.protolayout;
 
+import static androidx.wear.protolayout.expression.Preconditions.checkNotNull;
+
 import android.annotation.SuppressLint;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.expression.DynamicBuilders;
 import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.proto.AlignmentProto;
 import androidx.wear.protolayout.proto.TypesProto;
 
 /** Builders for extensible primitive types used by layout elements. */
 public final class TypeBuilders {
-  private TypeBuilders() {}
-
-  /**
-   * An int32 type.
-   *
-   * @since 1.0
-   */
-  public static final class Int32Prop {
-    private final TypesProto.Int32Prop mImpl;
-    @Nullable private final Fingerprint mFingerprint;
-
-    Int32Prop(TypesProto.Int32Prop impl, @Nullable Fingerprint fingerprint) {
-      this.mImpl = impl;
-      this.mFingerprint = fingerprint;
-    }
+    private TypeBuilders() {}
 
     /**
-     * Gets the value.
+     * An int32 type.
      *
      * @since 1.0
      */
-    public int getValue() {
-      return mImpl.getValue();
+    public static final class Int32Prop {
+        private final TypesProto.Int32Prop mImpl;
+        @Nullable private final Fingerprint mFingerprint;
+
+        Int32Prop(TypesProto.Int32Prop impl, @Nullable Fingerprint fingerprint) {
+            this.mImpl = impl;
+            this.mFingerprint = fingerprint;
+        }
+
+        /**
+         * Gets the value.
+         *
+         * @since 1.0
+         */
+        public int getValue() {
+            return mImpl.getValue();
+        }
+
+        /** Get the fingerprint for this object, or null if unknown. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Nullable
+        public Fingerprint getFingerprint() {
+            return mFingerprint;
+        }
+
+        @NonNull
+        static Int32Prop fromProto(@NonNull TypesProto.Int32Prop proto) {
+            return new Int32Prop(proto, null);
+        }
+
+        @NonNull
+        TypesProto.Int32Prop toProto() {
+            return mImpl;
+        }
+
+        /** Builder for {@link Int32Prop} */
+        public static final class Builder {
+            private final TypesProto.Int32Prop.Builder mImpl = TypesProto.Int32Prop.newBuilder();
+            private final Fingerprint mFingerprint = new Fingerprint(-1360212989);
+
+            public Builder() {}
+
+            /**
+             * Sets the value.
+             *
+             * @since 1.0
+             */
+            @NonNull
+            public Builder setValue(int value) {
+                mImpl.setValue(value);
+                mFingerprint.recordPropertyUpdate(1, value);
+                return this;
+            }
+
+            /** Builds an instance from accumulated values. */
+            @NonNull
+            public Int32Prop build() {
+                return new Int32Prop(mImpl.build(), mFingerprint);
+            }
+        }
     }
 
     /**
-     * Get the fingerprint for this object, or null if unknown.
-     *
-     */
-    @RestrictTo(Scope.LIBRARY_GROUP)
-    @Nullable
-    public Fingerprint getFingerprint() {
-      return mFingerprint;
-    }
-
-    @NonNull
-    static Int32Prop fromProto(@NonNull TypesProto.Int32Prop proto) {
-      return new Int32Prop(proto, null);
-    }
-
-    @NonNull
-    TypesProto.Int32Prop toProto() {
-      return mImpl;
-    }
-
-    /** Builder for {@link Int32Prop} */
-    public static final class Builder {
-      private final TypesProto.Int32Prop.Builder mImpl = TypesProto.Int32Prop.newBuilder();
-      private final Fingerprint mFingerprint = new Fingerprint(-1360212989);
-
-      public Builder() {}
-
-      /**
-       * Sets the value.
-       *
-       * @since 1.0
-       */
-      @NonNull
-      public Builder setValue(int value) {
-        mImpl.setValue(value);
-        mFingerprint.recordPropertyUpdate(1, value);
-        return this;
-      }
-
-      /** Builds an instance from accumulated values. */
-      @NonNull
-      public Int32Prop build() {
-        return new Int32Prop(mImpl.build(), mFingerprint);
-      }
-    }
-  }
-
-  /**
-   * A string type.
-   *
-   * @since 1.0
-   */
-  public static final class StringProp {
-    private final TypesProto.StringProp mImpl;
-    @Nullable private final Fingerprint mFingerprint;
-
-    StringProp(TypesProto.StringProp impl, @Nullable Fingerprint fingerprint) {
-      this.mImpl = impl;
-      this.mFingerprint = fingerprint;
-    }
-
-    /**
-     * Gets the value.
+     * A string type.
      *
      * @since 1.0
      */
-    @NonNull
-    public String getValue() {
-      return mImpl.getValue();
+    public static final class StringProp {
+        private final TypesProto.StringProp mImpl;
+        @Nullable private final Fingerprint mFingerprint;
+
+        StringProp(TypesProto.StringProp impl, @Nullable Fingerprint fingerprint) {
+            this.mImpl = impl;
+            this.mFingerprint = fingerprint;
+        }
+
+        /**
+         * Gets the static value.
+         *
+         * @since 1.0
+         */
+        @NonNull
+        public String getValue() {
+            return mImpl.getValue();
+        }
+
+        /**
+         * Gets the dynamic value.
+         *
+         * @since 1.2
+         */
+        @Nullable
+        public DynamicBuilders.DynamicString getDynamicValue() {
+            if (mImpl.hasDynamicValue()) {
+                return DynamicBuilders.dynamicStringFromProto(mImpl.getDynamicValue());
+            } else {
+                return null;
+            }
+        }
+
+        /** Get the fingerprint for this object, or null if unknown. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Nullable
+        public Fingerprint getFingerprint() {
+            return mFingerprint;
+        }
+
+        @NonNull
+        static StringProp fromProto(@NonNull TypesProto.StringProp proto) {
+            return new StringProp(proto, null);
+        }
+
+        @NonNull
+        TypesProto.StringProp toProto() {
+            return mImpl;
+        }
+
+        /** Builder for {@link StringProp} */
+        public static final class Builder {
+            private final TypesProto.StringProp.Builder mImpl = TypesProto.StringProp.newBuilder();
+            private final Fingerprint mFingerprint = new Fingerprint(327834307);
+
+            /**
+             * Creates an instance of {@link Builder}.
+             *
+             * @deprecated use {@link Builder(String)}
+             */
+            @Deprecated
+            public Builder() {}
+
+            /**
+             * Creates an instance of {@link Builder}.
+             *
+             * @param staticValue the static value.
+             */
+            public Builder(@NonNull String staticValue) {
+                setValue(staticValue);
+            }
+
+            /**
+             * Sets the static value. If a dynamic value is also set and the renderer supports
+             * dynamic values for the corresponding field, this static value will be ignored.
+             *
+             * @since 1.0
+             */
+            @NonNull
+            public Builder setValue(@NonNull String value) {
+                mImpl.setValue(value);
+                mFingerprint.recordPropertyUpdate(1, value.hashCode());
+                return this;
+            }
+
+            /**
+             * Sets the dynamic value. Note that when setting this value, the static value is still
+             * required to be set to support older renderers that only read the static value.
+             *
+             * @since 1.2
+             */
+            @NonNull
+            public Builder setDynamicValue(@NonNull DynamicBuilders.DynamicString dynamicValue) {
+                mImpl.setDynamicValue(dynamicValue.toDynamicStringProto());
+                mFingerprint.recordPropertyUpdate(
+                        2, checkNotNull(dynamicValue.getFingerprint()).aggregateValueAsInt());
+                return this;
+            }
+
+            /** Builds an instance from accumulated values. */
+            @NonNull
+            public StringProp build() {
+                if (mImpl.hasDynamicValue() && !mImpl.hasValue()) {
+                    throw new IllegalStateException("Static value is missing.");
+                }
+                return new StringProp(mImpl.build(), mFingerprint);
+            }
+        }
     }
 
     /**
-     * Get the fingerprint for this object, or null if unknown.
+     * A type for specifying layout constraints when using {@link StringProp} on a data bindable
+     * layout element.
      *
+     * @since 1.2
      */
-    @RestrictTo(Scope.LIBRARY_GROUP)
-    @Nullable
-    public Fingerprint getFingerprint() {
-      return mFingerprint;
-    }
+    public static final class StringLayoutConstraint {
+        private final TypesProto.StringProp mImpl;
+        @Nullable private final Fingerprint mFingerprint;
 
-    @NonNull
-    static StringProp fromProto(@NonNull TypesProto.StringProp proto) {
-      return new StringProp(proto, null);
-    }
+        StringLayoutConstraint(TypesProto.StringProp impl, @Nullable Fingerprint fingerprint) {
+            this.mImpl = impl;
+            this.mFingerprint = fingerprint;
+        }
 
-    @NonNull
-    TypesProto.StringProp toProto() {
-      return mImpl;
-    }
+        /**
+         * Gets the text string to use as the pattern for the largest text that can be laid out.
+         * Used to ensure that the layout is of a known size during the layout pass.
+         *
+         * @since 1.2
+         */
+        @NonNull
+        public String getPatternForLayout() {
+            return mImpl.getValueForLayout();
+        }
 
-    /** Builder for {@link StringProp} */
-    public static final class Builder {
-      private final TypesProto.StringProp.Builder mImpl = TypesProto.StringProp.newBuilder();
-      private final Fingerprint mFingerprint = new Fingerprint(327834307);
+        /**
+         * Gets angular alignment of the actual content within the space reserved by value.
+         *
+         * @since 1.2
+         */
+        @LayoutElementBuilders.TextAlignment
+        public int getAlignment() {
+            return mImpl.getTextAlignmentForLayoutValue();
+        }
 
-      public Builder() {}
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Nullable
+        public Fingerprint getFingerprint() {
+            return mFingerprint;
+        }
 
-      /**
-       * Sets the value.
-       *
-       * @since 1.0
-       */
-      @NonNull
-      public Builder setValue(@NonNull String value) {
-        mImpl.setValue(value);
-        mFingerprint.recordPropertyUpdate(1, value.hashCode());
-        return this;
-      }
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        public TypesProto.StringProp toProto() {
+            return mImpl;
+        }
 
-      /** Builds an instance from accumulated values. */
-      @NonNull
-      public StringProp build() {
-        return new StringProp(mImpl.build(), mFingerprint);
-      }
-    }
-  }
+        @NonNull
+        static StringLayoutConstraint fromProto(@NonNull TypesProto.StringProp proto) {
+            return new StringLayoutConstraint(proto, null);
+        }
 
-  /**
-   * A float type.
-   *
-   * @since 1.0
-   */
-  public static final class FloatProp {
-    private final TypesProto.FloatProp mImpl;
-    @Nullable private final Fingerprint mFingerprint;
+        /** Builder for {@link StringLayoutConstraint}. */
+        public static final class Builder {
+            private final TypesProto.StringProp.Builder mImpl = TypesProto.StringProp.newBuilder();
+            private final Fingerprint mFingerprint = new Fingerprint(-1927567664);
 
-    FloatProp(TypesProto.FloatProp impl, @Nullable Fingerprint fingerprint) {
-      this.mImpl = impl;
-      this.mFingerprint = fingerprint;
+            /**
+             * Creates a new builder for {@link StringLayoutConstraint}.
+             *
+             * @param patternForLayout Sets the text string to use as the pattern for the largest
+             *                        text that can be laid out. Used to ensure that the layout
+             *                        is of a known size during the layout pass.
+             * @since 1.2
+             */
+            public Builder(@NonNull String patternForLayout) {
+                setValue(patternForLayout);
+            }
+
+            /**
+             * Sets the text string to use as the pattern for the largest text that can be laid out.
+             * Used to ensure that the layout is of a known size during the layout pass.
+             *
+             * @since 1.2
+             */
+            @NonNull
+            private Builder setValue(@NonNull String patternForLayout) {
+                mImpl.setValueForLayout(patternForLayout);
+                mFingerprint.recordPropertyUpdate(3, patternForLayout.hashCode());
+                return this;
+            }
+
+            /**
+             * Sets alignment of the actual text within the space reserved by patternForLayout.
+             * If not specified, defaults to center alignment.
+             *
+             * @since 1.2
+             */
+            @NonNull
+            public Builder setAlignment(@LayoutElementBuilders.TextAlignment int alignment) {
+                mImpl.setTextAlignmentForLayout(AlignmentProto.TextAlignment.forNumber(alignment));
+                mFingerprint.recordPropertyUpdate(4, alignment);
+                return this;
+            }
+
+            /** Builds an instance of {@link StringLayoutConstraint}. */
+            @NonNull
+            public StringLayoutConstraint build() {
+                return new StringLayoutConstraint(mImpl.build(), mFingerprint);
+            }
+        }
     }
 
     /**
-     * Gets the value.
+     * A float type.
      *
      * @since 1.0
      */
-    public float getValue() {
-      return mImpl.getValue();
+    public static final class FloatProp {
+        private final TypesProto.FloatProp mImpl;
+        @Nullable private final Fingerprint mFingerprint;
+
+        FloatProp(TypesProto.FloatProp impl, @Nullable Fingerprint fingerprint) {
+            this.mImpl = impl;
+            this.mFingerprint = fingerprint;
+        }
+
+        /**
+         * Gets the value.
+         *
+         * @since 1.0
+         */
+        public float getValue() {
+            return mImpl.getValue();
+        }
+
+        /** Get the fingerprint for this object, or null if unknown. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Nullable
+        public Fingerprint getFingerprint() {
+            return mFingerprint;
+        }
+
+        @NonNull
+        static FloatProp fromProto(@NonNull TypesProto.FloatProp proto) {
+            return new FloatProp(proto, null);
+        }
+
+        @NonNull
+        TypesProto.FloatProp toProto() {
+            return mImpl;
+        }
+
+        /** Builder for {@link FloatProp} */
+        public static final class Builder {
+            private final TypesProto.FloatProp.Builder mImpl = TypesProto.FloatProp.newBuilder();
+            private final Fingerprint mFingerprint = new Fingerprint(-641088370);
+
+            public Builder() {}
+
+            /**
+             * Sets the value.
+             *
+             * @since 1.0
+             */
+            @NonNull
+            public Builder setValue(float value) {
+                mImpl.setValue(value);
+                mFingerprint.recordPropertyUpdate(1, Float.floatToIntBits(value));
+                return this;
+            }
+
+            /** Builds an instance from accumulated values. */
+            @NonNull
+            public FloatProp build() {
+                return new FloatProp(mImpl.build(), mFingerprint);
+            }
+        }
     }
 
     /**
-     * Get the fingerprint for this object, or null if unknown.
-     *
-     */
-    @RestrictTo(Scope.LIBRARY_GROUP)
-    @Nullable
-    public Fingerprint getFingerprint() {
-      return mFingerprint;
-    }
-
-    @NonNull
-    static FloatProp fromProto(@NonNull TypesProto.FloatProp proto) {
-      return new FloatProp(proto, null);
-    }
-
-    @NonNull
-    TypesProto.FloatProp toProto() {
-      return mImpl;
-    }
-
-    /** Builder for {@link FloatProp} */
-    public static final class Builder {
-      private final TypesProto.FloatProp.Builder mImpl = TypesProto.FloatProp.newBuilder();
-      private final Fingerprint mFingerprint = new Fingerprint(-641088370);
-
-      public Builder() {}
-
-      /**
-       * Sets the value.
-       *
-       * @since 1.0
-       */
-      @NonNull
-      public Builder setValue(float value) {
-        mImpl.setValue(value);
-        mFingerprint.recordPropertyUpdate(1, Float.floatToIntBits(value));
-        return this;
-      }
-
-      /** Builds an instance from accumulated values. */
-      @NonNull
-      public FloatProp build() {
-        return new FloatProp(mImpl.build(), mFingerprint);
-      }
-    }
-  }
-
-  /**
-   * A boolean type.
-   *
-   * @since 1.0
-   */
-  public static final class BoolProp {
-    private final TypesProto.BoolProp mImpl;
-    @Nullable private final Fingerprint mFingerprint;
-
-    BoolProp(TypesProto.BoolProp impl, @Nullable Fingerprint fingerprint) {
-      this.mImpl = impl;
-      this.mFingerprint = fingerprint;
-    }
-
-    /**
-     * Gets the value.
+     * A boolean type.
      *
      * @since 1.0
      */
-    public boolean getValue() {
-      return mImpl.getValue();
+    public static final class BoolProp {
+        private final TypesProto.BoolProp mImpl;
+        @Nullable private final Fingerprint mFingerprint;
+
+        BoolProp(TypesProto.BoolProp impl, @Nullable Fingerprint fingerprint) {
+            this.mImpl = impl;
+            this.mFingerprint = fingerprint;
+        }
+
+        /**
+         * Gets the value.
+         *
+         * @since 1.0
+         */
+        public boolean getValue() {
+            return mImpl.getValue();
+        }
+
+        /** Get the fingerprint for this object, or null if unknown. */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Nullable
+        public Fingerprint getFingerprint() {
+            return mFingerprint;
+        }
+
+        @NonNull
+        static BoolProp fromProto(@NonNull TypesProto.BoolProp proto) {
+            return new BoolProp(proto, null);
+        }
+
+        @NonNull
+        TypesProto.BoolProp toProto() {
+            return mImpl;
+        }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return "BoolProp{" + "value=" + getValue() + "}";
+        }
+
+        /** Builder for {@link BoolProp} */
+        public static final class Builder {
+            private final TypesProto.BoolProp.Builder mImpl = TypesProto.BoolProp.newBuilder();
+            private final Fingerprint mFingerprint = new Fingerprint(1691257528);
+
+            public Builder() {}
+
+            /**
+             * Sets the value.
+             *
+             * @since 1.0
+             */
+            @SuppressLint("MissingGetterMatchingBuilder")
+            @NonNull
+            public Builder setValue(boolean value) {
+                mImpl.setValue(value);
+                mFingerprint.recordPropertyUpdate(1, Boolean.hashCode(value));
+                return this;
+            }
+
+            /** Builds an instance from accumulated values. */
+            @NonNull
+            public BoolProp build() {
+                return new BoolProp(mImpl.build(), mFingerprint);
+            }
+        }
     }
-
-    /**
-     * Get the fingerprint for this object, or null if unknown.
-     *
-     */
-    @RestrictTo(Scope.LIBRARY_GROUP)
-    @Nullable
-    public Fingerprint getFingerprint() {
-      return mFingerprint;
-    }
-
-    @NonNull
-    static BoolProp fromProto(@NonNull TypesProto.BoolProp proto) {
-      return new BoolProp(proto, null);
-    }
-
-    @NonNull
-    TypesProto.BoolProp toProto() {
-      return mImpl;
-    }
-
-    @Override
-    @NonNull
-    public String toString() {
-      return "BoolProp{" + "value=" + getValue() + "}";
-    }
-
-    /** Builder for {@link BoolProp} */
-    public static final class Builder {
-      private final TypesProto.BoolProp.Builder mImpl = TypesProto.BoolProp.newBuilder();
-      private final Fingerprint mFingerprint = new Fingerprint(1691257528);
-
-      public Builder() {}
-
-      /**
-       * Sets the value.
-       *
-       * @since 1.0
-       */
-      @SuppressLint("MissingGetterMatchingBuilder")
-      @NonNull
-      public Builder setValue(boolean value) {
-        mImpl.setValue(value);
-        mFingerprint.recordPropertyUpdate(1, Boolean.hashCode(value));
-        return this;
-      }
-
-      /** Builds an instance from accumulated values. */
-      @NonNull
-      public BoolProp build() {
-        return new BoolProp(mImpl.build(), mFingerprint);
-      }
-    }
-  }
 }
diff --git a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/ColorBuildersTest.java b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/ColorBuildersTest.java
index d83ef0d..078ae25 100644
--- a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/ColorBuildersTest.java
+++ b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/ColorBuildersTest.java
@@ -52,7 +52,7 @@
     }
 
     @Test
-    public void borderSetColor_withoutStaticValue_throws() {
+    public void colorProp_withoutStaticValue_throws() {
         assertThrows(IllegalStateException.class, COLOR_BUILDER_WITHOUT_STATIC_VALUE::build);
     }
 }
diff --git a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/DimensionBuildersTest.java b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/DimensionBuildersTest.java
index 2f54d7b..81f3b67 100644
--- a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/DimensionBuildersTest.java
+++ b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/DimensionBuildersTest.java
@@ -16,8 +16,13 @@
 
 package androidx.wear.protolayout;
 
+import static androidx.wear.protolayout.DimensionBuilders.dp;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertThrows;
+
+import androidx.wear.protolayout.expression.DynamicBuilders;
 import androidx.wear.protolayout.proto.DimensionProto;
 
 import org.junit.Test;
@@ -26,31 +31,94 @@
 
 @RunWith(RobolectricTestRunner.class)
 public class DimensionBuildersTest {
+    private static final String STATE_KEY = "state-key";
+    private static final DimensionBuilders.DpProp DP_PROP =
+            new DimensionBuilders.DpProp.Builder(3.14f)
+                    .setDynamicValue(DynamicBuilders.DynamicFloat.fromState(STATE_KEY))
+                    .build();
+
+    @SuppressWarnings("deprecation")
+    private static final DimensionBuilders.DpProp.Builder DP_PROP_WITHOUT_STATIC_VALUE =
+            new DimensionBuilders.DpProp.Builder()
+                    .setDynamicValue(DynamicBuilders.DynamicFloat.fromState(STATE_KEY));
+
+    private static final DimensionBuilders.DegreesProp DEGREES_PROP =
+            new DimensionBuilders.DegreesProp.Builder(3.14f)
+                    .setDynamicValue(DynamicBuilders.DynamicFloat.fromState(STATE_KEY))
+                    .build();
+
+    @SuppressWarnings("deprecation")
+    private static final DimensionBuilders.DegreesProp.Builder DEGREES_PROP_WITHOUT_STATIC_VALUE =
+            new DimensionBuilders.DegreesProp.Builder()
+                    .setDynamicValue(DynamicBuilders.DynamicFloat.fromState(STATE_KEY));
+
+    @Test
+    public void dpPropSupportsDynamicValue() {
+        DimensionProto.DpProp dpPropProto = DP_PROP.toProto();
+
+        assertThat(dpPropProto.getValue()).isEqualTo(DP_PROP.getValue());
+        assertThat(dpPropProto.getDynamicValue().getStateSource().getSourceKey())
+                .isEqualTo(STATE_KEY);
+    }
+
+    @Test
+    public void dpProp_withoutStaticValue_throws() {
+        assertThrows(IllegalStateException.class, DP_PROP_WITHOUT_STATIC_VALUE::build);
+    }
+
+    public void degreesPropSupportsDynamicValue() {
+        DimensionProto.DegreesProp degreesPropProto = DEGREES_PROP.toProto();
+
+        assertThat(degreesPropProto.getValue()).isEqualTo(DEGREES_PROP.getValue());
+        assertThat(degreesPropProto.getDynamicValue().getStateSource().getSourceKey())
+                .isEqualTo(STATE_KEY);
+    }
+
+    @Test
+    public void degreesProp_withoutStaticValue_throws() {
+        assertThrows(IllegalStateException.class, DEGREES_PROP_WITHOUT_STATIC_VALUE::build);
+    }
 
     @Test
     public void expandedLayoutWeight() {
-        float layoutWeight = 3.14f;
+        TypeBuilders.FloatProp layoutWeight =
+                new TypeBuilders.FloatProp.Builder().setValue(3.14f).build();
         DimensionBuilders.ContainerDimension dimensionProp =
-                new DimensionBuilders.ExpandedDimensionProp.Builder().setLayoutWeight(layoutWeight)
+                new DimensionBuilders.ExpandedDimensionProp.Builder()
+                        .setLayoutWeight(layoutWeight)
                         .build();
 
         DimensionProto.ContainerDimension dimensionProto =
                 dimensionProp.toContainerDimensionProto();
         assertThat(dimensionProto.getExpandedDimension().getLayoutWeight().getValue())
-                .isWithin(.001f).of(layoutWeight);
+                .isWithin(.001f)
+                .of(layoutWeight.getValue());
     }
 
-
     @Test
     public void wrappedMinSize() {
-        int minSizeDp = 42;
+        DimensionBuilders.DpProp minSize = dp(42);
         DimensionBuilders.ContainerDimension dimensionProp =
-                new DimensionBuilders.WrappedDimensionProp.Builder().setMinimumSizeDp(minSizeDp)
+                new DimensionBuilders.WrappedDimensionProp.Builder()
+                        .setMinimumSize(minSize)
                         .build();
 
         DimensionProto.ContainerDimension dimensionProto =
                 dimensionProp.toContainerDimensionProto();
         assertThat(dimensionProto.getWrappedDimension().getMinimumSize().getValue())
-                .isEqualTo(minSizeDp);
+                .isEqualTo(minSize.getValue());
+    }
+
+    @Test
+    public void wrappedMinSize_throwsWhenSetToDynamicValue() {
+        DimensionBuilders.DpProp minSizeDynamic =
+                new DimensionBuilders.DpProp.Builder(42)
+                        .setDynamicValue(DynamicBuilders.DynamicFloat.fromState("some-state"))
+                        .build();
+        assertThrows(
+                IllegalArgumentException.class,
+                () ->
+                        new DimensionBuilders.WrappedDimensionProp.Builder()
+                                .setMinimumSize(minSizeDynamic));
     }
 }
diff --git a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java
new file mode 100644
index 0000000..78945dd
--- /dev/null
+++ b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import androidx.wear.protolayout.expression.DynamicBuilders;
+import androidx.wear.protolayout.proto.DimensionProto;
+import androidx.wear.protolayout.proto.TypesProto;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class LayoutElementBuildersTest {
+    private static final String STATE_KEY = "state-key";
+    private static final DimensionBuilders.DegreesProp DEGREES_PROP =
+            new DimensionBuilders.DegreesProp.Builder(10)
+                    .setDynamicValue(DynamicBuilders.DynamicFloat.fromState(STATE_KEY))
+                    .build();
+    private static final DimensionBuilders.AngularLayoutConstraint DEGREES_PROP_CONSTRAINT =
+            new DimensionBuilders.AngularLayoutConstraint.Builder(20)
+                    .setAngularAlignment(LayoutElementBuilders.ANGULAR_ALIGNMENT_END)
+                    .build();
+    private static final DimensionBuilders.DpProp DP_PROP =
+            new DimensionBuilders.DpProp.Builder(10)
+                    .setDynamicValue(DynamicBuilders.DynamicFloat.fromState(STATE_KEY))
+                    .build();
+    private static final DimensionBuilders.HorizontalLayoutConstraint HORIZONTAL_LAYOUT_CONSTRAINT =
+            new DimensionBuilders.HorizontalLayoutConstraint.Builder(20)
+                    .setHorizontalAlignment(LayoutElementBuilders.HORIZONTAL_ALIGN_END)
+                    .build();
+    private static final DimensionBuilders.VerticalLayoutConstraint VERTICAL_LAYOUT_CONSTRAINT =
+            new DimensionBuilders.VerticalLayoutConstraint.Builder(20)
+                    .setVerticalAlignment(LayoutElementBuilders.VERTICAL_ALIGN_BOTTOM)
+                    .build();
+    private static final TypeBuilders.StringProp STRING_PROP =
+            new TypeBuilders.StringProp.Builder("string")
+                    .setDynamicValue(DynamicBuilders.DynamicString.fromState(STATE_KEY))
+                    .build();
+    private static final TypeBuilders.StringLayoutConstraint STRING_LAYOUT_CONSTRAINT =
+            new TypeBuilders.StringLayoutConstraint.Builder("pattern")
+                    .setAlignment(LayoutElementBuilders.TEXT_ALIGN_END)
+                    .build();
+
+    @Test
+    public void testArcLineSetLength() {
+        LayoutElementBuilders.ArcLine arcLine =
+                new LayoutElementBuilders.ArcLine.Builder()
+                        .setLength(DEGREES_PROP)
+                        .setLayoutConstraintsForDynamicLength(DEGREES_PROP_CONSTRAINT)
+                        .build();
+
+        DimensionProto.DegreesProp lengthProto = arcLine.toProto().getLength();
+
+        assertThat(lengthProto.getValue()).isEqualTo(DEGREES_PROP.getValue());
+        assertThat(lengthProto.getDynamicValue().getStateSource().getSourceKey())
+                .isEqualTo(STATE_KEY);
+        assertThat(lengthProto.getValueForLayout()).isEqualTo(DEGREES_PROP_CONSTRAINT.getValue());
+        assertThat(lengthProto.getAngularAlignmentForLayoutValue())
+                .isEqualTo(DEGREES_PROP_CONSTRAINT.getAngularAlignment());
+    }
+
+    @Test
+    public void arcLineSetLength_withoutLayoutConstraint_throws() {
+        assertThrows(
+                IllegalStateException.class,
+                () -> new LayoutElementBuilders.ArcLine.Builder().setLength(DEGREES_PROP).build());
+    }
+
+    @Test
+    public void testArcSetAnchorAngle() {
+        LayoutElementBuilders.Arc arc =
+                new LayoutElementBuilders.Arc.Builder()
+                        .setAnchorAngle(DEGREES_PROP)
+                        .setLayoutConstraintsForDynamicAnchorAngle(DEGREES_PROP_CONSTRAINT)
+                        .build();
+
+        DimensionProto.DegreesProp anchorAngleProto = arc.toProto().getAnchorAngle();
+
+        assertThat(anchorAngleProto.getValue()).isEqualTo(DEGREES_PROP.getValue());
+        assertThat(anchorAngleProto.getDynamicValue().getStateSource().getSourceKey())
+                .isEqualTo(STATE_KEY);
+        assertThat(anchorAngleProto.getValueForLayout())
+                .isEqualTo(DEGREES_PROP_CONSTRAINT.getValue());
+        assertThat(anchorAngleProto.getAngularAlignmentForLayoutValue())
+                .isEqualTo(DEGREES_PROP_CONSTRAINT.getAngularAlignment());
+    }
+
+    @Test
+    public void arcSetAnchorAngle_withoutLayoutConstraint_throws() {
+        assertThrows(
+                IllegalStateException.class,
+                () -> new LayoutElementBuilders.Arc.Builder().setAnchorAngle(DEGREES_PROP).build());
+    }
+
+    @Test
+    public void testSpacerSetWidthLinear() {
+        LayoutElementBuilders.Spacer spacer =
+                new LayoutElementBuilders.Spacer.Builder()
+                        .setWidth(DP_PROP)
+                        .setLayoutConstraintsForDynamicWidth(HORIZONTAL_LAYOUT_CONSTRAINT)
+                        .build();
+
+        DimensionProto.DpProp spacerWidth = spacer.toProto().getWidth().getLinearDimension();
+
+        assertThat(spacerWidth.getValue()).isEqualTo(DP_PROP.getValue());
+        assertThat(spacerWidth.getDynamicValue().getStateSource().getSourceKey())
+                .isEqualTo(STATE_KEY);
+        assertThat(spacerWidth.getValueForLayout())
+                .isEqualTo(HORIZONTAL_LAYOUT_CONSTRAINT.getValue());
+        assertThat(spacerWidth.getHorizontalAlignmentForLayoutValue())
+                .isEqualTo(HORIZONTAL_LAYOUT_CONSTRAINT.getHorizontalAlignment());
+    }
+
+    @Test
+    public void spacerSetWidth_withoutLayoutConstraint_throws() {
+        assertThrows(
+                IllegalStateException.class,
+                () -> new LayoutElementBuilders.Spacer.Builder().setWidth(DP_PROP).build());
+    }
+
+    @Test
+    public void testSpacerSetHeightLinear() {
+        LayoutElementBuilders.Spacer spacer =
+                new LayoutElementBuilders.Spacer.Builder()
+                        .setHeight(DP_PROP)
+                        .setLayoutConstraintsForDynamicHeight(VERTICAL_LAYOUT_CONSTRAINT)
+                        .build();
+
+        DimensionProto.DpProp spacerHeight = spacer.toProto().getHeight().getLinearDimension();
+
+        assertThat(spacerHeight.getValue()).isEqualTo(DP_PROP.getValue());
+        assertThat(spacerHeight.getDynamicValue().getStateSource().getSourceKey())
+                .isEqualTo(STATE_KEY);
+        assertThat(spacerHeight.getValueForLayout())
+                .isEqualTo(VERTICAL_LAYOUT_CONSTRAINT.getValue());
+        assertThat(spacerHeight.getVerticalAlignmentForLayoutValue())
+                .isEqualTo(VERTICAL_LAYOUT_CONSTRAINT.getVerticalAlignment());
+    }
+
+    @Test
+    public void spacerSetHeight_withoutLayoutConstraint_throws() {
+        assertThrows(
+                IllegalStateException.class,
+                () -> new LayoutElementBuilders.Spacer.Builder().setHeight(DP_PROP).build());
+    }
+
+    @Test
+    public void testTextSetText() {
+        LayoutElementBuilders.Text text =
+                new LayoutElementBuilders.Text.Builder()
+                        .setText(STRING_PROP)
+                        .setLayoutConstraintsForDynamicText(STRING_LAYOUT_CONSTRAINT)
+                        .build();
+
+        TypesProto.StringProp textProto = text.toProto().getText();
+
+        assertThat(textProto.getValue()).isEqualTo(STRING_PROP.getValue());
+        assertThat(textProto.getDynamicValue().getStateSource().getSourceKey())
+                .isEqualTo(STATE_KEY);
+        assertThat(textProto.getValueForLayout())
+                .isEqualTo(STRING_LAYOUT_CONSTRAINT.getPatternForLayout());
+        assertThat(textProto.getTextAlignmentForLayoutValue())
+                .isEqualTo(STRING_LAYOUT_CONSTRAINT.getAlignment());
+    }
+
+    @Test
+    public void textSetText_withoutLayoutConstraint_throws() {
+        assertThrows(
+                IllegalStateException.class,
+                () -> new LayoutElementBuilders.Text.Builder().setText(STRING_PROP).build());
+    }
+}
diff --git a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/TypeBuildersTest.java b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/TypeBuildersTest.java
new file mode 100644
index 0000000..4de0b0a
--- /dev/null
+++ b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/TypeBuildersTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import androidx.wear.protolayout.expression.DynamicBuilders;
+import androidx.wear.protolayout.proto.TypesProto;
+
+import org.junit.Test;
+
+public class TypeBuildersTest {
+    private static final String STATE_KEY = "state-key";
+    private static final TypeBuilders.StringProp STRING_PROP =
+            new TypeBuilders.StringProp.Builder("string")
+                    .setDynamicValue(DynamicBuilders.DynamicString.fromState(STATE_KEY))
+                    .build();
+
+    @SuppressWarnings("deprecation")
+    private static final TypeBuilders.StringProp.Builder STRING_PROP_BUILDER_WITHOUT_STATIC_VALUE =
+            new TypeBuilders.StringProp.Builder()
+                    .setDynamicValue(DynamicBuilders.DynamicString.fromState(STATE_KEY));
+
+    @Test
+    public void stringPropSupportsDynamicString() {
+        TypesProto.StringProp stringPropProto = STRING_PROP.toProto();
+
+        assertThat(stringPropProto.getValue()).isEqualTo(STRING_PROP.getValue());
+        assertThat(stringPropProto.getDynamicValue().getStateSource().getSourceKey())
+                .isEqualTo(STATE_KEY);
+    }
+
+    @Test
+    public void stringProp_withoutStaticValue_throws() {
+        assertThrows(IllegalStateException.class, STRING_PROP_BUILDER_WITHOUT_STATIC_VALUE::build);
+    }
+
+    @Test
+    public void spanSetText_throwsWhenSetToDynamicValue() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> new LayoutElementBuilders.SpanText.Builder().setText(STRING_PROP));
+    }
+
+    @Test
+    public void arcTextSetText_throwsWhenSetToDynamicValue() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> new LayoutElementBuilders.ArcText.Builder().setText(STRING_PROP));
+    }
+}
diff --git a/wear/tiles/tiles-material/api/current.txt b/wear/tiles/tiles-material/api/current.txt
index aecb80e..a798a8f 100644
--- a/wear/tiles/tiles-material/api/current.txt
+++ b/wear/tiles/tiles-material/api/current.txt
@@ -1,297 +1,297 @@
 // Signature format: 4.0
 package androidx.wear.tiles.material {
 
-  public class Button implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.Button? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.ButtonColors getButtonColors();
-    method public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
-    method public CharSequence? getContentDescription();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getCustomContent();
-    method public String? getIconContent();
-    method public String? getImageContent();
-    method public androidx.wear.tiles.DimensionBuilders.ContainerDimension getSize();
-    method public String? getTextContent();
+  @Deprecated public class Button implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.Button? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.ButtonColors getButtonColors();
+    method @Deprecated public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
+    method @Deprecated public CharSequence? getContentDescription();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getCustomContent();
+    method @Deprecated public String? getIconContent();
+    method @Deprecated public String? getImageContent();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.ContainerDimension getSize();
+    method @Deprecated public String? getTextContent();
   }
 
-  public static final class Button.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public Button.Builder(android.content.Context, androidx.wear.tiles.ModifiersBuilders.Clickable);
-    method public androidx.wear.tiles.material.Button build();
-    method public androidx.wear.tiles.material.Button.Builder setButtonColors(androidx.wear.tiles.material.ButtonColors);
-    method public androidx.wear.tiles.material.Button.Builder setContentDescription(CharSequence);
-    method public androidx.wear.tiles.material.Button.Builder setCustomContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.Button.Builder setIconContent(String, androidx.wear.tiles.DimensionBuilders.DpProp);
-    method public androidx.wear.tiles.material.Button.Builder setIconContent(String);
-    method public androidx.wear.tiles.material.Button.Builder setImageContent(String);
-    method public androidx.wear.tiles.material.Button.Builder setSize(androidx.wear.tiles.DimensionBuilders.DpProp);
-    method public androidx.wear.tiles.material.Button.Builder setSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
-    method public androidx.wear.tiles.material.Button.Builder setTextContent(String);
-    method public androidx.wear.tiles.material.Button.Builder setTextContent(String, int);
+  @Deprecated public static final class Button.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public Button.Builder(android.content.Context, androidx.wear.tiles.ModifiersBuilders.Clickable);
+    method @Deprecated public androidx.wear.tiles.material.Button build();
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setButtonColors(androidx.wear.tiles.material.ButtonColors);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setContentDescription(CharSequence);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setCustomContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setIconContent(String, androidx.wear.tiles.DimensionBuilders.DpProp);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setIconContent(String);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setImageContent(String);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setSize(androidx.wear.tiles.DimensionBuilders.DpProp);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setTextContent(String);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setTextContent(String, int);
   }
 
-  public class ButtonColors {
-    ctor public ButtonColors(@ColorInt int, @ColorInt int);
-    ctor public ButtonColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getBackgroundColor();
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getContentColor();
-    method public static androidx.wear.tiles.material.ButtonColors primaryButtonColors(androidx.wear.tiles.material.Colors);
-    method public static androidx.wear.tiles.material.ButtonColors secondaryButtonColors(androidx.wear.tiles.material.Colors);
+  @Deprecated public class ButtonColors {
+    ctor @Deprecated public ButtonColors(@ColorInt int, @ColorInt int);
+    ctor @Deprecated public ButtonColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getBackgroundColor();
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getContentColor();
+    method @Deprecated public static androidx.wear.tiles.material.ButtonColors primaryButtonColors(androidx.wear.tiles.material.Colors);
+    method @Deprecated public static androidx.wear.tiles.material.ButtonColors secondaryButtonColors(androidx.wear.tiles.material.Colors);
   }
 
-  public class ButtonDefaults {
-    method public static androidx.wear.tiles.DimensionBuilders.DpProp recommendedIconSize(androidx.wear.tiles.DimensionBuilders.DpProp);
-    method public static androidx.wear.tiles.DimensionBuilders.DpProp recommendedIconSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
-    field public static final androidx.wear.tiles.DimensionBuilders.DpProp DEFAULT_SIZE;
-    field public static final androidx.wear.tiles.DimensionBuilders.DpProp EXTRA_LARGE_SIZE;
-    field public static final androidx.wear.tiles.DimensionBuilders.DpProp LARGE_SIZE;
-    field public static final androidx.wear.tiles.material.ButtonColors PRIMARY_COLORS;
-    field public static final androidx.wear.tiles.material.ButtonColors SECONDARY_COLORS;
+  @Deprecated public class ButtonDefaults {
+    method @Deprecated public static androidx.wear.tiles.DimensionBuilders.DpProp recommendedIconSize(androidx.wear.tiles.DimensionBuilders.DpProp);
+    method @Deprecated public static androidx.wear.tiles.DimensionBuilders.DpProp recommendedIconSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    field @Deprecated public static final androidx.wear.tiles.DimensionBuilders.DpProp DEFAULT_SIZE;
+    field @Deprecated public static final androidx.wear.tiles.DimensionBuilders.DpProp EXTRA_LARGE_SIZE;
+    field @Deprecated public static final androidx.wear.tiles.DimensionBuilders.DpProp LARGE_SIZE;
+    field @Deprecated public static final androidx.wear.tiles.material.ButtonColors PRIMARY_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.material.ButtonColors SECONDARY_COLORS;
   }
 
-  public class Chip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.Chip? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.ChipColors getChipColors();
-    method public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
-    method public CharSequence? getContentDescription();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getCustomContent();
-    method public androidx.wear.tiles.DimensionBuilders.ContainerDimension getHeight();
-    method public int getHorizontalAlignment();
-    method public String? getIconContent();
-    method public String? getPrimaryLabelContent();
-    method public String? getSecondaryLabelContent();
-    method public androidx.wear.tiles.DimensionBuilders.ContainerDimension getWidth();
+  @Deprecated public class Chip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.Chip? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.ChipColors getChipColors();
+    method @Deprecated public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
+    method @Deprecated public CharSequence? getContentDescription();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getCustomContent();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.ContainerDimension getHeight();
+    method @Deprecated public int getHorizontalAlignment();
+    method @Deprecated public String? getIconContent();
+    method @Deprecated public String? getPrimaryLabelContent();
+    method @Deprecated public String? getSecondaryLabelContent();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.ContainerDimension getWidth();
   }
 
-  public static final class Chip.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public Chip.Builder(android.content.Context, androidx.wear.tiles.ModifiersBuilders.Clickable, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
-    method public androidx.wear.tiles.material.Chip build();
-    method public androidx.wear.tiles.material.Chip.Builder setChipColors(androidx.wear.tiles.material.ChipColors);
-    method public androidx.wear.tiles.material.Chip.Builder setContentDescription(CharSequence);
-    method public androidx.wear.tiles.material.Chip.Builder setCustomContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.Chip.Builder setHorizontalAlignment(int);
-    method public androidx.wear.tiles.material.Chip.Builder setIconContent(String);
-    method public androidx.wear.tiles.material.Chip.Builder setPrimaryLabelContent(String);
-    method public androidx.wear.tiles.material.Chip.Builder setSecondaryLabelContent(String);
-    method public androidx.wear.tiles.material.Chip.Builder setWidth(androidx.wear.tiles.DimensionBuilders.ContainerDimension);
-    method public androidx.wear.tiles.material.Chip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  @Deprecated public static final class Chip.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public Chip.Builder(android.content.Context, androidx.wear.tiles.ModifiersBuilders.Clickable, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method @Deprecated public androidx.wear.tiles.material.Chip build();
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setChipColors(androidx.wear.tiles.material.ChipColors);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setContentDescription(CharSequence);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setCustomContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setHorizontalAlignment(int);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setIconContent(String);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setPrimaryLabelContent(String);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setSecondaryLabelContent(String);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setWidth(androidx.wear.tiles.DimensionBuilders.ContainerDimension);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
-  public class ChipColors {
-    ctor public ChipColors(@ColorInt int, @ColorInt int, @ColorInt int, @ColorInt int);
-    ctor public ChipColors(@ColorInt int, @ColorInt int);
-    ctor public ChipColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
-    ctor public ChipColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getBackgroundColor();
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getContentColor();
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getIconColor();
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getSecondaryContentColor();
-    method public static androidx.wear.tiles.material.ChipColors primaryChipColors(androidx.wear.tiles.material.Colors);
-    method public static androidx.wear.tiles.material.ChipColors secondaryChipColors(androidx.wear.tiles.material.Colors);
+  @Deprecated public class ChipColors {
+    ctor @Deprecated public ChipColors(@ColorInt int, @ColorInt int, @ColorInt int, @ColorInt int);
+    ctor @Deprecated public ChipColors(@ColorInt int, @ColorInt int);
+    ctor @Deprecated public ChipColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
+    ctor @Deprecated public ChipColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getBackgroundColor();
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getContentColor();
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getIconColor();
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getSecondaryContentColor();
+    method @Deprecated public static androidx.wear.tiles.material.ChipColors primaryChipColors(androidx.wear.tiles.material.Colors);
+    method @Deprecated public static androidx.wear.tiles.material.ChipColors secondaryChipColors(androidx.wear.tiles.material.Colors);
   }
 
-  public class ChipDefaults {
-    field public static final androidx.wear.tiles.material.ChipColors COMPACT_PRIMARY_COLORS;
-    field public static final androidx.wear.tiles.material.ChipColors COMPACT_SECONDARY_COLORS;
-    field public static final androidx.wear.tiles.material.ChipColors PRIMARY_COLORS;
-    field public static final androidx.wear.tiles.material.ChipColors SECONDARY_COLORS;
-    field public static final androidx.wear.tiles.material.ChipColors TITLE_PRIMARY_COLORS;
-    field public static final androidx.wear.tiles.material.ChipColors TITLE_SECONDARY_COLORS;
+  @Deprecated public class ChipDefaults {
+    field @Deprecated public static final androidx.wear.tiles.material.ChipColors COMPACT_PRIMARY_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.material.ChipColors COMPACT_SECONDARY_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.material.ChipColors PRIMARY_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.material.ChipColors SECONDARY_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.material.ChipColors TITLE_PRIMARY_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.material.ChipColors TITLE_SECONDARY_COLORS;
   }
 
-  public class CircularProgressIndicator implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.CircularProgressIndicator? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.ProgressIndicatorColors getCircularProgressIndicatorColors();
-    method public CharSequence? getContentDescription();
-    method public androidx.wear.tiles.DimensionBuilders.DegreesProp getEndAngle();
-    method public androidx.wear.tiles.DimensionBuilders.DegreesProp getProgress();
-    method public androidx.wear.tiles.DimensionBuilders.DegreesProp getStartAngle();
-    method public androidx.wear.tiles.DimensionBuilders.DpProp getStrokeWidth();
+  @Deprecated public class CircularProgressIndicator implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.CircularProgressIndicator? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.ProgressIndicatorColors getCircularProgressIndicatorColors();
+    method @Deprecated public CharSequence? getContentDescription();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.DegreesProp getEndAngle();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.DegreesProp getProgress();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.DegreesProp getStartAngle();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.DpProp getStrokeWidth();
   }
 
-  public static final class CircularProgressIndicator.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public CircularProgressIndicator.Builder();
-    method public androidx.wear.tiles.material.CircularProgressIndicator build();
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setCircularProgressIndicatorColors(androidx.wear.tiles.material.ProgressIndicatorColors);
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setContentDescription(CharSequence);
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setEndAngle(float);
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setProgress(@FloatRange(from=0, to=1) float);
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setStartAngle(float);
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setStrokeWidth(androidx.wear.tiles.DimensionBuilders.DpProp);
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setStrokeWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  @Deprecated public static final class CircularProgressIndicator.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public CircularProgressIndicator.Builder();
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator build();
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setCircularProgressIndicatorColors(androidx.wear.tiles.material.ProgressIndicatorColors);
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setContentDescription(CharSequence);
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setEndAngle(float);
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setProgress(@FloatRange(from=0, to=1) float);
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setStartAngle(float);
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setStrokeWidth(androidx.wear.tiles.DimensionBuilders.DpProp);
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setStrokeWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
-  public class Colors {
-    ctor public Colors(@ColorInt int, @ColorInt int, @ColorInt int, @ColorInt int);
-    method @ColorInt public int getOnPrimary();
-    method @ColorInt public int getOnSurface();
-    method @ColorInt public int getPrimary();
-    method @ColorInt public int getSurface();
-    field public static final androidx.wear.tiles.material.Colors DEFAULT;
+  @Deprecated public class Colors {
+    ctor @Deprecated public Colors(@ColorInt int, @ColorInt int, @ColorInt int, @ColorInt int);
+    method @Deprecated @ColorInt public int getOnPrimary();
+    method @Deprecated @ColorInt public int getOnSurface();
+    method @Deprecated @ColorInt public int getPrimary();
+    method @Deprecated @ColorInt public int getSurface();
+    field @Deprecated public static final androidx.wear.tiles.material.Colors DEFAULT;
   }
 
-  public class CompactChip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.CompactChip? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.ChipColors getChipColors();
-    method public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
-    method public String getText();
+  @Deprecated public class CompactChip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.CompactChip? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.ChipColors getChipColors();
+    method @Deprecated public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
+    method @Deprecated public String getText();
   }
 
-  public static final class CompactChip.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public CompactChip.Builder(android.content.Context, String, androidx.wear.tiles.ModifiersBuilders.Clickable, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
-    method public androidx.wear.tiles.material.CompactChip build();
-    method public androidx.wear.tiles.material.CompactChip.Builder setChipColors(androidx.wear.tiles.material.ChipColors);
+  @Deprecated public static final class CompactChip.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public CompactChip.Builder(android.content.Context, String, androidx.wear.tiles.ModifiersBuilders.Clickable, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method @Deprecated public androidx.wear.tiles.material.CompactChip build();
+    method @Deprecated public androidx.wear.tiles.material.CompactChip.Builder setChipColors(androidx.wear.tiles.material.ChipColors);
   }
 
-  public class ProgressIndicatorColors {
-    ctor public ProgressIndicatorColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
-    ctor public ProgressIndicatorColors(@ColorInt int, @ColorInt int);
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getIndicatorColor();
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getTrackColor();
-    method public static androidx.wear.tiles.material.ProgressIndicatorColors progressIndicatorColors(androidx.wear.tiles.material.Colors);
+  @Deprecated public class ProgressIndicatorColors {
+    ctor @Deprecated public ProgressIndicatorColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
+    ctor @Deprecated public ProgressIndicatorColors(@ColorInt int, @ColorInt int);
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getIndicatorColor();
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getTrackColor();
+    method @Deprecated public static androidx.wear.tiles.material.ProgressIndicatorColors progressIndicatorColors(androidx.wear.tiles.material.Colors);
   }
 
-  public class ProgressIndicatorDefaults {
-    field public static final androidx.wear.tiles.material.ProgressIndicatorColors DEFAULT_COLORS;
-    field public static final androidx.wear.tiles.DimensionBuilders.DpProp DEFAULT_STROKE_WIDTH;
-    field public static final float GAP_END_ANGLE = 156.1f;
-    field public static final float GAP_START_ANGLE = -156.1f;
+  @Deprecated public class ProgressIndicatorDefaults {
+    field @Deprecated public static final androidx.wear.tiles.material.ProgressIndicatorColors DEFAULT_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.DimensionBuilders.DpProp DEFAULT_STROKE_WIDTH;
+    field @Deprecated public static final float GAP_END_ANGLE = 156.1f;
+    field @Deprecated public static final float GAP_START_ANGLE = -156.1f;
   }
 
-  public class Text implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.Text? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getColor();
-    method public androidx.wear.tiles.LayoutElementBuilders.FontStyle getFontStyle();
-    method public float getLineHeight();
-    method public int getMaxLines();
-    method public androidx.wear.tiles.ModifiersBuilders.Modifiers getModifiers();
-    method public int getMultilineAlignment();
-    method public int getOverflow();
-    method public String getText();
-    method public int getWeight();
-    method public boolean isItalic();
-    method public boolean isUnderline();
+  @Deprecated public class Text implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.Text? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getColor();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle getFontStyle();
+    method @Deprecated public float getLineHeight();
+    method @Deprecated public int getMaxLines();
+    method @Deprecated public androidx.wear.tiles.ModifiersBuilders.Modifiers getModifiers();
+    method @Deprecated public int getMultilineAlignment();
+    method @Deprecated public int getOverflow();
+    method @Deprecated public String getText();
+    method @Deprecated public int getWeight();
+    method @Deprecated public boolean isItalic();
+    method @Deprecated public boolean isUnderline();
   }
 
-  public static final class Text.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public Text.Builder(android.content.Context, String);
-    method public androidx.wear.tiles.material.Text build();
-    method public androidx.wear.tiles.material.Text.Builder setColor(androidx.wear.tiles.ColorBuilders.ColorProp);
-    method public androidx.wear.tiles.material.Text.Builder setItalic(boolean);
-    method public androidx.wear.tiles.material.Text.Builder setMaxLines(@IntRange(from=1) int);
-    method public androidx.wear.tiles.material.Text.Builder setModifiers(androidx.wear.tiles.ModifiersBuilders.Modifiers);
-    method public androidx.wear.tiles.material.Text.Builder setMultilineAlignment(int);
-    method public androidx.wear.tiles.material.Text.Builder setOverflow(int);
-    method public androidx.wear.tiles.material.Text.Builder setTypography(int);
-    method public androidx.wear.tiles.material.Text.Builder setUnderline(boolean);
-    method public androidx.wear.tiles.material.Text.Builder setWeight(int);
+  @Deprecated public static final class Text.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public Text.Builder(android.content.Context, String);
+    method @Deprecated public androidx.wear.tiles.material.Text build();
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setColor(androidx.wear.tiles.ColorBuilders.ColorProp);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setItalic(boolean);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setMaxLines(@IntRange(from=1) int);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setModifiers(androidx.wear.tiles.ModifiersBuilders.Modifiers);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setMultilineAlignment(int);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setOverflow(int);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setTypography(int);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setUnderline(boolean);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setWeight(int);
   }
 
-  public class TitleChip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.TitleChip? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.ChipColors getChipColors();
-    method public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
-    method public int getHorizontalAlignment();
-    method public String getText();
-    method public androidx.wear.tiles.DimensionBuilders.ContainerDimension getWidth();
+  @Deprecated public class TitleChip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.TitleChip? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.ChipColors getChipColors();
+    method @Deprecated public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
+    method @Deprecated public int getHorizontalAlignment();
+    method @Deprecated public String getText();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.ContainerDimension getWidth();
   }
 
-  public static final class TitleChip.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public TitleChip.Builder(android.content.Context, String, androidx.wear.tiles.ModifiersBuilders.Clickable, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
-    method public androidx.wear.tiles.material.TitleChip build();
-    method public androidx.wear.tiles.material.TitleChip.Builder setChipColors(androidx.wear.tiles.material.ChipColors);
-    method public androidx.wear.tiles.material.TitleChip.Builder setHorizontalAlignment(int);
-    method public androidx.wear.tiles.material.TitleChip.Builder setWidth(androidx.wear.tiles.DimensionBuilders.ContainerDimension);
-    method public androidx.wear.tiles.material.TitleChip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  @Deprecated public static final class TitleChip.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public TitleChip.Builder(android.content.Context, String, androidx.wear.tiles.ModifiersBuilders.Clickable, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method @Deprecated public androidx.wear.tiles.material.TitleChip build();
+    method @Deprecated public androidx.wear.tiles.material.TitleChip.Builder setChipColors(androidx.wear.tiles.material.ChipColors);
+    method @Deprecated public androidx.wear.tiles.material.TitleChip.Builder setHorizontalAlignment(int);
+    method @Deprecated public androidx.wear.tiles.material.TitleChip.Builder setWidth(androidx.wear.tiles.DimensionBuilders.ContainerDimension);
+    method @Deprecated public androidx.wear.tiles.material.TitleChip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
-  public class Typography {
-    field public static final int TYPOGRAPHY_BODY1 = 7; // 0x7
-    field public static final int TYPOGRAPHY_BODY2 = 8; // 0x8
-    field public static final int TYPOGRAPHY_BUTTON = 9; // 0x9
-    field public static final int TYPOGRAPHY_CAPTION1 = 10; // 0xa
-    field public static final int TYPOGRAPHY_CAPTION2 = 11; // 0xb
-    field public static final int TYPOGRAPHY_CAPTION3 = 12; // 0xc
-    field public static final int TYPOGRAPHY_DISPLAY1 = 1; // 0x1
-    field public static final int TYPOGRAPHY_DISPLAY2 = 2; // 0x2
-    field public static final int TYPOGRAPHY_DISPLAY3 = 3; // 0x3
-    field public static final int TYPOGRAPHY_TITLE1 = 4; // 0x4
-    field public static final int TYPOGRAPHY_TITLE2 = 5; // 0x5
-    field public static final int TYPOGRAPHY_TITLE3 = 6; // 0x6
+  @Deprecated public class Typography {
+    field @Deprecated public static final int TYPOGRAPHY_BODY1 = 7; // 0x7
+    field @Deprecated public static final int TYPOGRAPHY_BODY2 = 8; // 0x8
+    field @Deprecated public static final int TYPOGRAPHY_BUTTON = 9; // 0x9
+    field @Deprecated public static final int TYPOGRAPHY_CAPTION1 = 10; // 0xa
+    field @Deprecated public static final int TYPOGRAPHY_CAPTION2 = 11; // 0xb
+    field @Deprecated public static final int TYPOGRAPHY_CAPTION3 = 12; // 0xc
+    field @Deprecated public static final int TYPOGRAPHY_DISPLAY1 = 1; // 0x1
+    field @Deprecated public static final int TYPOGRAPHY_DISPLAY2 = 2; // 0x2
+    field @Deprecated public static final int TYPOGRAPHY_DISPLAY3 = 3; // 0x3
+    field @Deprecated public static final int TYPOGRAPHY_TITLE1 = 4; // 0x4
+    field @Deprecated public static final int TYPOGRAPHY_TITLE2 = 5; // 0x5
+    field @Deprecated public static final int TYPOGRAPHY_TITLE3 = 6; // 0x6
   }
 
 }
 
 package androidx.wear.tiles.material.layouts {
 
-  public class EdgeContentLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.layouts.EdgeContentLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getContent();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getEdgeContent();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getPrimaryLabelTextContent();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getSecondaryLabelTextContent();
+  @Deprecated public class EdgeContentLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.layouts.EdgeContentLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getContent();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getEdgeContent();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getPrimaryLabelTextContent();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getSecondaryLabelTextContent();
   }
 
-  public static final class EdgeContentLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public EdgeContentLayout.Builder(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
-    method public androidx.wear.tiles.material.layouts.EdgeContentLayout build();
-    method public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setEdgeContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setPrimaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setSecondaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+  @Deprecated public static final class EdgeContentLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public EdgeContentLayout.Builder(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method @Deprecated public androidx.wear.tiles.material.layouts.EdgeContentLayout build();
+    method @Deprecated public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setEdgeContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setPrimaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setSecondaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
   }
 
-  public class LayoutDefaults {
-    field public static final androidx.wear.tiles.DimensionBuilders.DpProp DEFAULT_VERTICAL_SPACER_HEIGHT;
-    field public static final float EDGE_CONTENT_LAYOUT_PADDING_ABOVE_MAIN_CONTENT_DP = 6.0f;
-    field public static final float EDGE_CONTENT_LAYOUT_PADDING_BELOW_MAIN_CONTENT_DP = 8.0f;
-    field public static final int MULTI_BUTTON_MAX_NUMBER = 7; // 0x7
-    field public static final androidx.wear.tiles.DimensionBuilders.DpProp MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH;
+  @Deprecated public class LayoutDefaults {
+    field @Deprecated public static final androidx.wear.tiles.DimensionBuilders.DpProp DEFAULT_VERTICAL_SPACER_HEIGHT;
+    field @Deprecated public static final float EDGE_CONTENT_LAYOUT_PADDING_ABOVE_MAIN_CONTENT_DP = 6.0f;
+    field @Deprecated public static final float EDGE_CONTENT_LAYOUT_PADDING_BELOW_MAIN_CONTENT_DP = 8.0f;
+    field @Deprecated public static final int MULTI_BUTTON_MAX_NUMBER = 7; // 0x7
+    field @Deprecated public static final androidx.wear.tiles.DimensionBuilders.DpProp MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH;
   }
 
-  public class MultiButtonLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.layouts.MultiButtonLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public java.util.List<androidx.wear.tiles.LayoutElementBuilders.LayoutElement!> getButtonContents();
-    method public int getFiveButtonDistribution();
-    field public static final int FIVE_BUTTON_DISTRIBUTION_BOTTOM_HEAVY = 2; // 0x2
-    field public static final int FIVE_BUTTON_DISTRIBUTION_TOP_HEAVY = 1; // 0x1
+  @Deprecated public class MultiButtonLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.layouts.MultiButtonLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public java.util.List<androidx.wear.tiles.LayoutElementBuilders.LayoutElement!> getButtonContents();
+    method @Deprecated public int getFiveButtonDistribution();
+    field @Deprecated public static final int FIVE_BUTTON_DISTRIBUTION_BOTTOM_HEAVY = 2; // 0x2
+    field @Deprecated public static final int FIVE_BUTTON_DISTRIBUTION_TOP_HEAVY = 1; // 0x1
   }
 
-  public static final class MultiButtonLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public MultiButtonLayout.Builder();
-    method public androidx.wear.tiles.material.layouts.MultiButtonLayout.Builder addButtonContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.MultiButtonLayout build();
-    method public androidx.wear.tiles.material.layouts.MultiButtonLayout.Builder setFiveButtonDistribution(int);
+  @Deprecated public static final class MultiButtonLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public MultiButtonLayout.Builder();
+    method @Deprecated public androidx.wear.tiles.material.layouts.MultiButtonLayout.Builder addButtonContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.MultiButtonLayout build();
+    method @Deprecated public androidx.wear.tiles.material.layouts.MultiButtonLayout.Builder setFiveButtonDistribution(int);
   }
 
-  public class MultiSlotLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.layouts.MultiSlotLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getHorizontalSpacerWidth();
-    method public java.util.List<androidx.wear.tiles.LayoutElementBuilders.LayoutElement!> getSlotContents();
+  @Deprecated public class MultiSlotLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.layouts.MultiSlotLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated @Dimension(unit=androidx.annotation.Dimension.DP) public float getHorizontalSpacerWidth();
+    method @Deprecated public java.util.List<androidx.wear.tiles.LayoutElementBuilders.LayoutElement!> getSlotContents();
   }
 
-  public static final class MultiSlotLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public MultiSlotLayout.Builder();
-    method public androidx.wear.tiles.material.layouts.MultiSlotLayout.Builder addSlotContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.MultiSlotLayout build();
-    method public androidx.wear.tiles.material.layouts.MultiSlotLayout.Builder setHorizontalSpacerWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  @Deprecated public static final class MultiSlotLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public MultiSlotLayout.Builder();
+    method @Deprecated public androidx.wear.tiles.material.layouts.MultiSlotLayout.Builder addSlotContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.MultiSlotLayout build();
+    method @Deprecated public androidx.wear.tiles.material.layouts.MultiSlotLayout.Builder setHorizontalSpacerWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
-  public class PrimaryLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.layouts.PrimaryLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getContent();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getPrimaryChipContent();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getPrimaryLabelTextContent();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getSecondaryLabelTextContent();
-    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getVerticalSpacerHeight();
+  @Deprecated public class PrimaryLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.layouts.PrimaryLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getContent();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getPrimaryChipContent();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getPrimaryLabelTextContent();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getSecondaryLabelTextContent();
+    method @Deprecated @Dimension(unit=androidx.annotation.Dimension.DP) public float getVerticalSpacerHeight();
   }
 
-  public static final class PrimaryLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public PrimaryLayout.Builder(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
-    method public androidx.wear.tiles.material.layouts.PrimaryLayout build();
-    method public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setPrimaryChipContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setPrimaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setSecondaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setVerticalSpacerHeight(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  @Deprecated public static final class PrimaryLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public PrimaryLayout.Builder(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method @Deprecated public androidx.wear.tiles.material.layouts.PrimaryLayout build();
+    method @Deprecated public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setPrimaryChipContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setPrimaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setSecondaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setVerticalSpacerHeight(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
 }
diff --git a/wear/tiles/tiles-material/api/public_plus_experimental_current.txt b/wear/tiles/tiles-material/api/public_plus_experimental_current.txt
index aecb80e..a798a8f 100644
--- a/wear/tiles/tiles-material/api/public_plus_experimental_current.txt
+++ b/wear/tiles/tiles-material/api/public_plus_experimental_current.txt
@@ -1,297 +1,297 @@
 // Signature format: 4.0
 package androidx.wear.tiles.material {
 
-  public class Button implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.Button? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.ButtonColors getButtonColors();
-    method public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
-    method public CharSequence? getContentDescription();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getCustomContent();
-    method public String? getIconContent();
-    method public String? getImageContent();
-    method public androidx.wear.tiles.DimensionBuilders.ContainerDimension getSize();
-    method public String? getTextContent();
+  @Deprecated public class Button implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.Button? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.ButtonColors getButtonColors();
+    method @Deprecated public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
+    method @Deprecated public CharSequence? getContentDescription();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getCustomContent();
+    method @Deprecated public String? getIconContent();
+    method @Deprecated public String? getImageContent();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.ContainerDimension getSize();
+    method @Deprecated public String? getTextContent();
   }
 
-  public static final class Button.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public Button.Builder(android.content.Context, androidx.wear.tiles.ModifiersBuilders.Clickable);
-    method public androidx.wear.tiles.material.Button build();
-    method public androidx.wear.tiles.material.Button.Builder setButtonColors(androidx.wear.tiles.material.ButtonColors);
-    method public androidx.wear.tiles.material.Button.Builder setContentDescription(CharSequence);
-    method public androidx.wear.tiles.material.Button.Builder setCustomContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.Button.Builder setIconContent(String, androidx.wear.tiles.DimensionBuilders.DpProp);
-    method public androidx.wear.tiles.material.Button.Builder setIconContent(String);
-    method public androidx.wear.tiles.material.Button.Builder setImageContent(String);
-    method public androidx.wear.tiles.material.Button.Builder setSize(androidx.wear.tiles.DimensionBuilders.DpProp);
-    method public androidx.wear.tiles.material.Button.Builder setSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
-    method public androidx.wear.tiles.material.Button.Builder setTextContent(String);
-    method public androidx.wear.tiles.material.Button.Builder setTextContent(String, int);
+  @Deprecated public static final class Button.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public Button.Builder(android.content.Context, androidx.wear.tiles.ModifiersBuilders.Clickable);
+    method @Deprecated public androidx.wear.tiles.material.Button build();
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setButtonColors(androidx.wear.tiles.material.ButtonColors);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setContentDescription(CharSequence);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setCustomContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setIconContent(String, androidx.wear.tiles.DimensionBuilders.DpProp);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setIconContent(String);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setImageContent(String);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setSize(androidx.wear.tiles.DimensionBuilders.DpProp);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setTextContent(String);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setTextContent(String, int);
   }
 
-  public class ButtonColors {
-    ctor public ButtonColors(@ColorInt int, @ColorInt int);
-    ctor public ButtonColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getBackgroundColor();
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getContentColor();
-    method public static androidx.wear.tiles.material.ButtonColors primaryButtonColors(androidx.wear.tiles.material.Colors);
-    method public static androidx.wear.tiles.material.ButtonColors secondaryButtonColors(androidx.wear.tiles.material.Colors);
+  @Deprecated public class ButtonColors {
+    ctor @Deprecated public ButtonColors(@ColorInt int, @ColorInt int);
+    ctor @Deprecated public ButtonColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getBackgroundColor();
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getContentColor();
+    method @Deprecated public static androidx.wear.tiles.material.ButtonColors primaryButtonColors(androidx.wear.tiles.material.Colors);
+    method @Deprecated public static androidx.wear.tiles.material.ButtonColors secondaryButtonColors(androidx.wear.tiles.material.Colors);
   }
 
-  public class ButtonDefaults {
-    method public static androidx.wear.tiles.DimensionBuilders.DpProp recommendedIconSize(androidx.wear.tiles.DimensionBuilders.DpProp);
-    method public static androidx.wear.tiles.DimensionBuilders.DpProp recommendedIconSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
-    field public static final androidx.wear.tiles.DimensionBuilders.DpProp DEFAULT_SIZE;
-    field public static final androidx.wear.tiles.DimensionBuilders.DpProp EXTRA_LARGE_SIZE;
-    field public static final androidx.wear.tiles.DimensionBuilders.DpProp LARGE_SIZE;
-    field public static final androidx.wear.tiles.material.ButtonColors PRIMARY_COLORS;
-    field public static final androidx.wear.tiles.material.ButtonColors SECONDARY_COLORS;
+  @Deprecated public class ButtonDefaults {
+    method @Deprecated public static androidx.wear.tiles.DimensionBuilders.DpProp recommendedIconSize(androidx.wear.tiles.DimensionBuilders.DpProp);
+    method @Deprecated public static androidx.wear.tiles.DimensionBuilders.DpProp recommendedIconSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    field @Deprecated public static final androidx.wear.tiles.DimensionBuilders.DpProp DEFAULT_SIZE;
+    field @Deprecated public static final androidx.wear.tiles.DimensionBuilders.DpProp EXTRA_LARGE_SIZE;
+    field @Deprecated public static final androidx.wear.tiles.DimensionBuilders.DpProp LARGE_SIZE;
+    field @Deprecated public static final androidx.wear.tiles.material.ButtonColors PRIMARY_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.material.ButtonColors SECONDARY_COLORS;
   }
 
-  public class Chip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.Chip? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.ChipColors getChipColors();
-    method public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
-    method public CharSequence? getContentDescription();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getCustomContent();
-    method public androidx.wear.tiles.DimensionBuilders.ContainerDimension getHeight();
-    method public int getHorizontalAlignment();
-    method public String? getIconContent();
-    method public String? getPrimaryLabelContent();
-    method public String? getSecondaryLabelContent();
-    method public androidx.wear.tiles.DimensionBuilders.ContainerDimension getWidth();
+  @Deprecated public class Chip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.Chip? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.ChipColors getChipColors();
+    method @Deprecated public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
+    method @Deprecated public CharSequence? getContentDescription();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getCustomContent();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.ContainerDimension getHeight();
+    method @Deprecated public int getHorizontalAlignment();
+    method @Deprecated public String? getIconContent();
+    method @Deprecated public String? getPrimaryLabelContent();
+    method @Deprecated public String? getSecondaryLabelContent();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.ContainerDimension getWidth();
   }
 
-  public static final class Chip.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public Chip.Builder(android.content.Context, androidx.wear.tiles.ModifiersBuilders.Clickable, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
-    method public androidx.wear.tiles.material.Chip build();
-    method public androidx.wear.tiles.material.Chip.Builder setChipColors(androidx.wear.tiles.material.ChipColors);
-    method public androidx.wear.tiles.material.Chip.Builder setContentDescription(CharSequence);
-    method public androidx.wear.tiles.material.Chip.Builder setCustomContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.Chip.Builder setHorizontalAlignment(int);
-    method public androidx.wear.tiles.material.Chip.Builder setIconContent(String);
-    method public androidx.wear.tiles.material.Chip.Builder setPrimaryLabelContent(String);
-    method public androidx.wear.tiles.material.Chip.Builder setSecondaryLabelContent(String);
-    method public androidx.wear.tiles.material.Chip.Builder setWidth(androidx.wear.tiles.DimensionBuilders.ContainerDimension);
-    method public androidx.wear.tiles.material.Chip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  @Deprecated public static final class Chip.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public Chip.Builder(android.content.Context, androidx.wear.tiles.ModifiersBuilders.Clickable, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method @Deprecated public androidx.wear.tiles.material.Chip build();
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setChipColors(androidx.wear.tiles.material.ChipColors);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setContentDescription(CharSequence);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setCustomContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setHorizontalAlignment(int);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setIconContent(String);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setPrimaryLabelContent(String);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setSecondaryLabelContent(String);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setWidth(androidx.wear.tiles.DimensionBuilders.ContainerDimension);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
-  public class ChipColors {
-    ctor public ChipColors(@ColorInt int, @ColorInt int, @ColorInt int, @ColorInt int);
-    ctor public ChipColors(@ColorInt int, @ColorInt int);
-    ctor public ChipColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
-    ctor public ChipColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getBackgroundColor();
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getContentColor();
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getIconColor();
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getSecondaryContentColor();
-    method public static androidx.wear.tiles.material.ChipColors primaryChipColors(androidx.wear.tiles.material.Colors);
-    method public static androidx.wear.tiles.material.ChipColors secondaryChipColors(androidx.wear.tiles.material.Colors);
+  @Deprecated public class ChipColors {
+    ctor @Deprecated public ChipColors(@ColorInt int, @ColorInt int, @ColorInt int, @ColorInt int);
+    ctor @Deprecated public ChipColors(@ColorInt int, @ColorInt int);
+    ctor @Deprecated public ChipColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
+    ctor @Deprecated public ChipColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getBackgroundColor();
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getContentColor();
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getIconColor();
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getSecondaryContentColor();
+    method @Deprecated public static androidx.wear.tiles.material.ChipColors primaryChipColors(androidx.wear.tiles.material.Colors);
+    method @Deprecated public static androidx.wear.tiles.material.ChipColors secondaryChipColors(androidx.wear.tiles.material.Colors);
   }
 
-  public class ChipDefaults {
-    field public static final androidx.wear.tiles.material.ChipColors COMPACT_PRIMARY_COLORS;
-    field public static final androidx.wear.tiles.material.ChipColors COMPACT_SECONDARY_COLORS;
-    field public static final androidx.wear.tiles.material.ChipColors PRIMARY_COLORS;
-    field public static final androidx.wear.tiles.material.ChipColors SECONDARY_COLORS;
-    field public static final androidx.wear.tiles.material.ChipColors TITLE_PRIMARY_COLORS;
-    field public static final androidx.wear.tiles.material.ChipColors TITLE_SECONDARY_COLORS;
+  @Deprecated public class ChipDefaults {
+    field @Deprecated public static final androidx.wear.tiles.material.ChipColors COMPACT_PRIMARY_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.material.ChipColors COMPACT_SECONDARY_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.material.ChipColors PRIMARY_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.material.ChipColors SECONDARY_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.material.ChipColors TITLE_PRIMARY_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.material.ChipColors TITLE_SECONDARY_COLORS;
   }
 
-  public class CircularProgressIndicator implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.CircularProgressIndicator? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.ProgressIndicatorColors getCircularProgressIndicatorColors();
-    method public CharSequence? getContentDescription();
-    method public androidx.wear.tiles.DimensionBuilders.DegreesProp getEndAngle();
-    method public androidx.wear.tiles.DimensionBuilders.DegreesProp getProgress();
-    method public androidx.wear.tiles.DimensionBuilders.DegreesProp getStartAngle();
-    method public androidx.wear.tiles.DimensionBuilders.DpProp getStrokeWidth();
+  @Deprecated public class CircularProgressIndicator implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.CircularProgressIndicator? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.ProgressIndicatorColors getCircularProgressIndicatorColors();
+    method @Deprecated public CharSequence? getContentDescription();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.DegreesProp getEndAngle();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.DegreesProp getProgress();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.DegreesProp getStartAngle();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.DpProp getStrokeWidth();
   }
 
-  public static final class CircularProgressIndicator.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public CircularProgressIndicator.Builder();
-    method public androidx.wear.tiles.material.CircularProgressIndicator build();
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setCircularProgressIndicatorColors(androidx.wear.tiles.material.ProgressIndicatorColors);
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setContentDescription(CharSequence);
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setEndAngle(float);
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setProgress(@FloatRange(from=0, to=1) float);
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setStartAngle(float);
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setStrokeWidth(androidx.wear.tiles.DimensionBuilders.DpProp);
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setStrokeWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  @Deprecated public static final class CircularProgressIndicator.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public CircularProgressIndicator.Builder();
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator build();
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setCircularProgressIndicatorColors(androidx.wear.tiles.material.ProgressIndicatorColors);
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setContentDescription(CharSequence);
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setEndAngle(float);
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setProgress(@FloatRange(from=0, to=1) float);
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setStartAngle(float);
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setStrokeWidth(androidx.wear.tiles.DimensionBuilders.DpProp);
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setStrokeWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
-  public class Colors {
-    ctor public Colors(@ColorInt int, @ColorInt int, @ColorInt int, @ColorInt int);
-    method @ColorInt public int getOnPrimary();
-    method @ColorInt public int getOnSurface();
-    method @ColorInt public int getPrimary();
-    method @ColorInt public int getSurface();
-    field public static final androidx.wear.tiles.material.Colors DEFAULT;
+  @Deprecated public class Colors {
+    ctor @Deprecated public Colors(@ColorInt int, @ColorInt int, @ColorInt int, @ColorInt int);
+    method @Deprecated @ColorInt public int getOnPrimary();
+    method @Deprecated @ColorInt public int getOnSurface();
+    method @Deprecated @ColorInt public int getPrimary();
+    method @Deprecated @ColorInt public int getSurface();
+    field @Deprecated public static final androidx.wear.tiles.material.Colors DEFAULT;
   }
 
-  public class CompactChip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.CompactChip? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.ChipColors getChipColors();
-    method public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
-    method public String getText();
+  @Deprecated public class CompactChip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.CompactChip? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.ChipColors getChipColors();
+    method @Deprecated public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
+    method @Deprecated public String getText();
   }
 
-  public static final class CompactChip.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public CompactChip.Builder(android.content.Context, String, androidx.wear.tiles.ModifiersBuilders.Clickable, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
-    method public androidx.wear.tiles.material.CompactChip build();
-    method public androidx.wear.tiles.material.CompactChip.Builder setChipColors(androidx.wear.tiles.material.ChipColors);
+  @Deprecated public static final class CompactChip.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public CompactChip.Builder(android.content.Context, String, androidx.wear.tiles.ModifiersBuilders.Clickable, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method @Deprecated public androidx.wear.tiles.material.CompactChip build();
+    method @Deprecated public androidx.wear.tiles.material.CompactChip.Builder setChipColors(androidx.wear.tiles.material.ChipColors);
   }
 
-  public class ProgressIndicatorColors {
-    ctor public ProgressIndicatorColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
-    ctor public ProgressIndicatorColors(@ColorInt int, @ColorInt int);
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getIndicatorColor();
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getTrackColor();
-    method public static androidx.wear.tiles.material.ProgressIndicatorColors progressIndicatorColors(androidx.wear.tiles.material.Colors);
+  @Deprecated public class ProgressIndicatorColors {
+    ctor @Deprecated public ProgressIndicatorColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
+    ctor @Deprecated public ProgressIndicatorColors(@ColorInt int, @ColorInt int);
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getIndicatorColor();
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getTrackColor();
+    method @Deprecated public static androidx.wear.tiles.material.ProgressIndicatorColors progressIndicatorColors(androidx.wear.tiles.material.Colors);
   }
 
-  public class ProgressIndicatorDefaults {
-    field public static final androidx.wear.tiles.material.ProgressIndicatorColors DEFAULT_COLORS;
-    field public static final androidx.wear.tiles.DimensionBuilders.DpProp DEFAULT_STROKE_WIDTH;
-    field public static final float GAP_END_ANGLE = 156.1f;
-    field public static final float GAP_START_ANGLE = -156.1f;
+  @Deprecated public class ProgressIndicatorDefaults {
+    field @Deprecated public static final androidx.wear.tiles.material.ProgressIndicatorColors DEFAULT_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.DimensionBuilders.DpProp DEFAULT_STROKE_WIDTH;
+    field @Deprecated public static final float GAP_END_ANGLE = 156.1f;
+    field @Deprecated public static final float GAP_START_ANGLE = -156.1f;
   }
 
-  public class Text implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.Text? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getColor();
-    method public androidx.wear.tiles.LayoutElementBuilders.FontStyle getFontStyle();
-    method public float getLineHeight();
-    method public int getMaxLines();
-    method public androidx.wear.tiles.ModifiersBuilders.Modifiers getModifiers();
-    method public int getMultilineAlignment();
-    method public int getOverflow();
-    method public String getText();
-    method public int getWeight();
-    method public boolean isItalic();
-    method public boolean isUnderline();
+  @Deprecated public class Text implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.Text? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getColor();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle getFontStyle();
+    method @Deprecated public float getLineHeight();
+    method @Deprecated public int getMaxLines();
+    method @Deprecated public androidx.wear.tiles.ModifiersBuilders.Modifiers getModifiers();
+    method @Deprecated public int getMultilineAlignment();
+    method @Deprecated public int getOverflow();
+    method @Deprecated public String getText();
+    method @Deprecated public int getWeight();
+    method @Deprecated public boolean isItalic();
+    method @Deprecated public boolean isUnderline();
   }
 
-  public static final class Text.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public Text.Builder(android.content.Context, String);
-    method public androidx.wear.tiles.material.Text build();
-    method public androidx.wear.tiles.material.Text.Builder setColor(androidx.wear.tiles.ColorBuilders.ColorProp);
-    method public androidx.wear.tiles.material.Text.Builder setItalic(boolean);
-    method public androidx.wear.tiles.material.Text.Builder setMaxLines(@IntRange(from=1) int);
-    method public androidx.wear.tiles.material.Text.Builder setModifiers(androidx.wear.tiles.ModifiersBuilders.Modifiers);
-    method public androidx.wear.tiles.material.Text.Builder setMultilineAlignment(int);
-    method public androidx.wear.tiles.material.Text.Builder setOverflow(int);
-    method public androidx.wear.tiles.material.Text.Builder setTypography(int);
-    method public androidx.wear.tiles.material.Text.Builder setUnderline(boolean);
-    method public androidx.wear.tiles.material.Text.Builder setWeight(int);
+  @Deprecated public static final class Text.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public Text.Builder(android.content.Context, String);
+    method @Deprecated public androidx.wear.tiles.material.Text build();
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setColor(androidx.wear.tiles.ColorBuilders.ColorProp);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setItalic(boolean);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setMaxLines(@IntRange(from=1) int);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setModifiers(androidx.wear.tiles.ModifiersBuilders.Modifiers);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setMultilineAlignment(int);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setOverflow(int);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setTypography(int);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setUnderline(boolean);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setWeight(int);
   }
 
-  public class TitleChip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.TitleChip? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.ChipColors getChipColors();
-    method public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
-    method public int getHorizontalAlignment();
-    method public String getText();
-    method public androidx.wear.tiles.DimensionBuilders.ContainerDimension getWidth();
+  @Deprecated public class TitleChip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.TitleChip? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.ChipColors getChipColors();
+    method @Deprecated public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
+    method @Deprecated public int getHorizontalAlignment();
+    method @Deprecated public String getText();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.ContainerDimension getWidth();
   }
 
-  public static final class TitleChip.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public TitleChip.Builder(android.content.Context, String, androidx.wear.tiles.ModifiersBuilders.Clickable, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
-    method public androidx.wear.tiles.material.TitleChip build();
-    method public androidx.wear.tiles.material.TitleChip.Builder setChipColors(androidx.wear.tiles.material.ChipColors);
-    method public androidx.wear.tiles.material.TitleChip.Builder setHorizontalAlignment(int);
-    method public androidx.wear.tiles.material.TitleChip.Builder setWidth(androidx.wear.tiles.DimensionBuilders.ContainerDimension);
-    method public androidx.wear.tiles.material.TitleChip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  @Deprecated public static final class TitleChip.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public TitleChip.Builder(android.content.Context, String, androidx.wear.tiles.ModifiersBuilders.Clickable, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method @Deprecated public androidx.wear.tiles.material.TitleChip build();
+    method @Deprecated public androidx.wear.tiles.material.TitleChip.Builder setChipColors(androidx.wear.tiles.material.ChipColors);
+    method @Deprecated public androidx.wear.tiles.material.TitleChip.Builder setHorizontalAlignment(int);
+    method @Deprecated public androidx.wear.tiles.material.TitleChip.Builder setWidth(androidx.wear.tiles.DimensionBuilders.ContainerDimension);
+    method @Deprecated public androidx.wear.tiles.material.TitleChip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
-  public class Typography {
-    field public static final int TYPOGRAPHY_BODY1 = 7; // 0x7
-    field public static final int TYPOGRAPHY_BODY2 = 8; // 0x8
-    field public static final int TYPOGRAPHY_BUTTON = 9; // 0x9
-    field public static final int TYPOGRAPHY_CAPTION1 = 10; // 0xa
-    field public static final int TYPOGRAPHY_CAPTION2 = 11; // 0xb
-    field public static final int TYPOGRAPHY_CAPTION3 = 12; // 0xc
-    field public static final int TYPOGRAPHY_DISPLAY1 = 1; // 0x1
-    field public static final int TYPOGRAPHY_DISPLAY2 = 2; // 0x2
-    field public static final int TYPOGRAPHY_DISPLAY3 = 3; // 0x3
-    field public static final int TYPOGRAPHY_TITLE1 = 4; // 0x4
-    field public static final int TYPOGRAPHY_TITLE2 = 5; // 0x5
-    field public static final int TYPOGRAPHY_TITLE3 = 6; // 0x6
+  @Deprecated public class Typography {
+    field @Deprecated public static final int TYPOGRAPHY_BODY1 = 7; // 0x7
+    field @Deprecated public static final int TYPOGRAPHY_BODY2 = 8; // 0x8
+    field @Deprecated public static final int TYPOGRAPHY_BUTTON = 9; // 0x9
+    field @Deprecated public static final int TYPOGRAPHY_CAPTION1 = 10; // 0xa
+    field @Deprecated public static final int TYPOGRAPHY_CAPTION2 = 11; // 0xb
+    field @Deprecated public static final int TYPOGRAPHY_CAPTION3 = 12; // 0xc
+    field @Deprecated public static final int TYPOGRAPHY_DISPLAY1 = 1; // 0x1
+    field @Deprecated public static final int TYPOGRAPHY_DISPLAY2 = 2; // 0x2
+    field @Deprecated public static final int TYPOGRAPHY_DISPLAY3 = 3; // 0x3
+    field @Deprecated public static final int TYPOGRAPHY_TITLE1 = 4; // 0x4
+    field @Deprecated public static final int TYPOGRAPHY_TITLE2 = 5; // 0x5
+    field @Deprecated public static final int TYPOGRAPHY_TITLE3 = 6; // 0x6
   }
 
 }
 
 package androidx.wear.tiles.material.layouts {
 
-  public class EdgeContentLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.layouts.EdgeContentLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getContent();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getEdgeContent();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getPrimaryLabelTextContent();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getSecondaryLabelTextContent();
+  @Deprecated public class EdgeContentLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.layouts.EdgeContentLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getContent();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getEdgeContent();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getPrimaryLabelTextContent();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getSecondaryLabelTextContent();
   }
 
-  public static final class EdgeContentLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public EdgeContentLayout.Builder(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
-    method public androidx.wear.tiles.material.layouts.EdgeContentLayout build();
-    method public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setEdgeContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setPrimaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setSecondaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+  @Deprecated public static final class EdgeContentLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public EdgeContentLayout.Builder(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method @Deprecated public androidx.wear.tiles.material.layouts.EdgeContentLayout build();
+    method @Deprecated public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setEdgeContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setPrimaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setSecondaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
   }
 
-  public class LayoutDefaults {
-    field public static final androidx.wear.tiles.DimensionBuilders.DpProp DEFAULT_VERTICAL_SPACER_HEIGHT;
-    field public static final float EDGE_CONTENT_LAYOUT_PADDING_ABOVE_MAIN_CONTENT_DP = 6.0f;
-    field public static final float EDGE_CONTENT_LAYOUT_PADDING_BELOW_MAIN_CONTENT_DP = 8.0f;
-    field public static final int MULTI_BUTTON_MAX_NUMBER = 7; // 0x7
-    field public static final androidx.wear.tiles.DimensionBuilders.DpProp MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH;
+  @Deprecated public class LayoutDefaults {
+    field @Deprecated public static final androidx.wear.tiles.DimensionBuilders.DpProp DEFAULT_VERTICAL_SPACER_HEIGHT;
+    field @Deprecated public static final float EDGE_CONTENT_LAYOUT_PADDING_ABOVE_MAIN_CONTENT_DP = 6.0f;
+    field @Deprecated public static final float EDGE_CONTENT_LAYOUT_PADDING_BELOW_MAIN_CONTENT_DP = 8.0f;
+    field @Deprecated public static final int MULTI_BUTTON_MAX_NUMBER = 7; // 0x7
+    field @Deprecated public static final androidx.wear.tiles.DimensionBuilders.DpProp MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH;
   }
 
-  public class MultiButtonLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.layouts.MultiButtonLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public java.util.List<androidx.wear.tiles.LayoutElementBuilders.LayoutElement!> getButtonContents();
-    method public int getFiveButtonDistribution();
-    field public static final int FIVE_BUTTON_DISTRIBUTION_BOTTOM_HEAVY = 2; // 0x2
-    field public static final int FIVE_BUTTON_DISTRIBUTION_TOP_HEAVY = 1; // 0x1
+  @Deprecated public class MultiButtonLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.layouts.MultiButtonLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public java.util.List<androidx.wear.tiles.LayoutElementBuilders.LayoutElement!> getButtonContents();
+    method @Deprecated public int getFiveButtonDistribution();
+    field @Deprecated public static final int FIVE_BUTTON_DISTRIBUTION_BOTTOM_HEAVY = 2; // 0x2
+    field @Deprecated public static final int FIVE_BUTTON_DISTRIBUTION_TOP_HEAVY = 1; // 0x1
   }
 
-  public static final class MultiButtonLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public MultiButtonLayout.Builder();
-    method public androidx.wear.tiles.material.layouts.MultiButtonLayout.Builder addButtonContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.MultiButtonLayout build();
-    method public androidx.wear.tiles.material.layouts.MultiButtonLayout.Builder setFiveButtonDistribution(int);
+  @Deprecated public static final class MultiButtonLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public MultiButtonLayout.Builder();
+    method @Deprecated public androidx.wear.tiles.material.layouts.MultiButtonLayout.Builder addButtonContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.MultiButtonLayout build();
+    method @Deprecated public androidx.wear.tiles.material.layouts.MultiButtonLayout.Builder setFiveButtonDistribution(int);
   }
 
-  public class MultiSlotLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.layouts.MultiSlotLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getHorizontalSpacerWidth();
-    method public java.util.List<androidx.wear.tiles.LayoutElementBuilders.LayoutElement!> getSlotContents();
+  @Deprecated public class MultiSlotLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.layouts.MultiSlotLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated @Dimension(unit=androidx.annotation.Dimension.DP) public float getHorizontalSpacerWidth();
+    method @Deprecated public java.util.List<androidx.wear.tiles.LayoutElementBuilders.LayoutElement!> getSlotContents();
   }
 
-  public static final class MultiSlotLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public MultiSlotLayout.Builder();
-    method public androidx.wear.tiles.material.layouts.MultiSlotLayout.Builder addSlotContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.MultiSlotLayout build();
-    method public androidx.wear.tiles.material.layouts.MultiSlotLayout.Builder setHorizontalSpacerWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  @Deprecated public static final class MultiSlotLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public MultiSlotLayout.Builder();
+    method @Deprecated public androidx.wear.tiles.material.layouts.MultiSlotLayout.Builder addSlotContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.MultiSlotLayout build();
+    method @Deprecated public androidx.wear.tiles.material.layouts.MultiSlotLayout.Builder setHorizontalSpacerWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
-  public class PrimaryLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.layouts.PrimaryLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getContent();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getPrimaryChipContent();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getPrimaryLabelTextContent();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getSecondaryLabelTextContent();
-    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getVerticalSpacerHeight();
+  @Deprecated public class PrimaryLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.layouts.PrimaryLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getContent();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getPrimaryChipContent();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getPrimaryLabelTextContent();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getSecondaryLabelTextContent();
+    method @Deprecated @Dimension(unit=androidx.annotation.Dimension.DP) public float getVerticalSpacerHeight();
   }
 
-  public static final class PrimaryLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public PrimaryLayout.Builder(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
-    method public androidx.wear.tiles.material.layouts.PrimaryLayout build();
-    method public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setPrimaryChipContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setPrimaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setSecondaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setVerticalSpacerHeight(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  @Deprecated public static final class PrimaryLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public PrimaryLayout.Builder(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method @Deprecated public androidx.wear.tiles.material.layouts.PrimaryLayout build();
+    method @Deprecated public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setPrimaryChipContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setPrimaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setSecondaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setVerticalSpacerHeight(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
 }
diff --git a/wear/tiles/tiles-material/api/restricted_current.txt b/wear/tiles/tiles-material/api/restricted_current.txt
index aecb80e..a798a8f 100644
--- a/wear/tiles/tiles-material/api/restricted_current.txt
+++ b/wear/tiles/tiles-material/api/restricted_current.txt
@@ -1,297 +1,297 @@
 // Signature format: 4.0
 package androidx.wear.tiles.material {
 
-  public class Button implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.Button? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.ButtonColors getButtonColors();
-    method public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
-    method public CharSequence? getContentDescription();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getCustomContent();
-    method public String? getIconContent();
-    method public String? getImageContent();
-    method public androidx.wear.tiles.DimensionBuilders.ContainerDimension getSize();
-    method public String? getTextContent();
+  @Deprecated public class Button implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.Button? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.ButtonColors getButtonColors();
+    method @Deprecated public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
+    method @Deprecated public CharSequence? getContentDescription();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getCustomContent();
+    method @Deprecated public String? getIconContent();
+    method @Deprecated public String? getImageContent();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.ContainerDimension getSize();
+    method @Deprecated public String? getTextContent();
   }
 
-  public static final class Button.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public Button.Builder(android.content.Context, androidx.wear.tiles.ModifiersBuilders.Clickable);
-    method public androidx.wear.tiles.material.Button build();
-    method public androidx.wear.tiles.material.Button.Builder setButtonColors(androidx.wear.tiles.material.ButtonColors);
-    method public androidx.wear.tiles.material.Button.Builder setContentDescription(CharSequence);
-    method public androidx.wear.tiles.material.Button.Builder setCustomContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.Button.Builder setIconContent(String, androidx.wear.tiles.DimensionBuilders.DpProp);
-    method public androidx.wear.tiles.material.Button.Builder setIconContent(String);
-    method public androidx.wear.tiles.material.Button.Builder setImageContent(String);
-    method public androidx.wear.tiles.material.Button.Builder setSize(androidx.wear.tiles.DimensionBuilders.DpProp);
-    method public androidx.wear.tiles.material.Button.Builder setSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
-    method public androidx.wear.tiles.material.Button.Builder setTextContent(String);
-    method public androidx.wear.tiles.material.Button.Builder setTextContent(String, int);
+  @Deprecated public static final class Button.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public Button.Builder(android.content.Context, androidx.wear.tiles.ModifiersBuilders.Clickable);
+    method @Deprecated public androidx.wear.tiles.material.Button build();
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setButtonColors(androidx.wear.tiles.material.ButtonColors);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setContentDescription(CharSequence);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setCustomContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setIconContent(String, androidx.wear.tiles.DimensionBuilders.DpProp);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setIconContent(String);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setImageContent(String);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setSize(androidx.wear.tiles.DimensionBuilders.DpProp);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setTextContent(String);
+    method @Deprecated public androidx.wear.tiles.material.Button.Builder setTextContent(String, int);
   }
 
-  public class ButtonColors {
-    ctor public ButtonColors(@ColorInt int, @ColorInt int);
-    ctor public ButtonColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getBackgroundColor();
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getContentColor();
-    method public static androidx.wear.tiles.material.ButtonColors primaryButtonColors(androidx.wear.tiles.material.Colors);
-    method public static androidx.wear.tiles.material.ButtonColors secondaryButtonColors(androidx.wear.tiles.material.Colors);
+  @Deprecated public class ButtonColors {
+    ctor @Deprecated public ButtonColors(@ColorInt int, @ColorInt int);
+    ctor @Deprecated public ButtonColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getBackgroundColor();
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getContentColor();
+    method @Deprecated public static androidx.wear.tiles.material.ButtonColors primaryButtonColors(androidx.wear.tiles.material.Colors);
+    method @Deprecated public static androidx.wear.tiles.material.ButtonColors secondaryButtonColors(androidx.wear.tiles.material.Colors);
   }
 
-  public class ButtonDefaults {
-    method public static androidx.wear.tiles.DimensionBuilders.DpProp recommendedIconSize(androidx.wear.tiles.DimensionBuilders.DpProp);
-    method public static androidx.wear.tiles.DimensionBuilders.DpProp recommendedIconSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
-    field public static final androidx.wear.tiles.DimensionBuilders.DpProp DEFAULT_SIZE;
-    field public static final androidx.wear.tiles.DimensionBuilders.DpProp EXTRA_LARGE_SIZE;
-    field public static final androidx.wear.tiles.DimensionBuilders.DpProp LARGE_SIZE;
-    field public static final androidx.wear.tiles.material.ButtonColors PRIMARY_COLORS;
-    field public static final androidx.wear.tiles.material.ButtonColors SECONDARY_COLORS;
+  @Deprecated public class ButtonDefaults {
+    method @Deprecated public static androidx.wear.tiles.DimensionBuilders.DpProp recommendedIconSize(androidx.wear.tiles.DimensionBuilders.DpProp);
+    method @Deprecated public static androidx.wear.tiles.DimensionBuilders.DpProp recommendedIconSize(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+    field @Deprecated public static final androidx.wear.tiles.DimensionBuilders.DpProp DEFAULT_SIZE;
+    field @Deprecated public static final androidx.wear.tiles.DimensionBuilders.DpProp EXTRA_LARGE_SIZE;
+    field @Deprecated public static final androidx.wear.tiles.DimensionBuilders.DpProp LARGE_SIZE;
+    field @Deprecated public static final androidx.wear.tiles.material.ButtonColors PRIMARY_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.material.ButtonColors SECONDARY_COLORS;
   }
 
-  public class Chip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.Chip? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.ChipColors getChipColors();
-    method public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
-    method public CharSequence? getContentDescription();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getCustomContent();
-    method public androidx.wear.tiles.DimensionBuilders.ContainerDimension getHeight();
-    method public int getHorizontalAlignment();
-    method public String? getIconContent();
-    method public String? getPrimaryLabelContent();
-    method public String? getSecondaryLabelContent();
-    method public androidx.wear.tiles.DimensionBuilders.ContainerDimension getWidth();
+  @Deprecated public class Chip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.Chip? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.ChipColors getChipColors();
+    method @Deprecated public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
+    method @Deprecated public CharSequence? getContentDescription();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getCustomContent();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.ContainerDimension getHeight();
+    method @Deprecated public int getHorizontalAlignment();
+    method @Deprecated public String? getIconContent();
+    method @Deprecated public String? getPrimaryLabelContent();
+    method @Deprecated public String? getSecondaryLabelContent();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.ContainerDimension getWidth();
   }
 
-  public static final class Chip.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public Chip.Builder(android.content.Context, androidx.wear.tiles.ModifiersBuilders.Clickable, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
-    method public androidx.wear.tiles.material.Chip build();
-    method public androidx.wear.tiles.material.Chip.Builder setChipColors(androidx.wear.tiles.material.ChipColors);
-    method public androidx.wear.tiles.material.Chip.Builder setContentDescription(CharSequence);
-    method public androidx.wear.tiles.material.Chip.Builder setCustomContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.Chip.Builder setHorizontalAlignment(int);
-    method public androidx.wear.tiles.material.Chip.Builder setIconContent(String);
-    method public androidx.wear.tiles.material.Chip.Builder setPrimaryLabelContent(String);
-    method public androidx.wear.tiles.material.Chip.Builder setSecondaryLabelContent(String);
-    method public androidx.wear.tiles.material.Chip.Builder setWidth(androidx.wear.tiles.DimensionBuilders.ContainerDimension);
-    method public androidx.wear.tiles.material.Chip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  @Deprecated public static final class Chip.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public Chip.Builder(android.content.Context, androidx.wear.tiles.ModifiersBuilders.Clickable, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method @Deprecated public androidx.wear.tiles.material.Chip build();
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setChipColors(androidx.wear.tiles.material.ChipColors);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setContentDescription(CharSequence);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setCustomContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setHorizontalAlignment(int);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setIconContent(String);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setPrimaryLabelContent(String);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setSecondaryLabelContent(String);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setWidth(androidx.wear.tiles.DimensionBuilders.ContainerDimension);
+    method @Deprecated public androidx.wear.tiles.material.Chip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
-  public class ChipColors {
-    ctor public ChipColors(@ColorInt int, @ColorInt int, @ColorInt int, @ColorInt int);
-    ctor public ChipColors(@ColorInt int, @ColorInt int);
-    ctor public ChipColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
-    ctor public ChipColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getBackgroundColor();
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getContentColor();
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getIconColor();
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getSecondaryContentColor();
-    method public static androidx.wear.tiles.material.ChipColors primaryChipColors(androidx.wear.tiles.material.Colors);
-    method public static androidx.wear.tiles.material.ChipColors secondaryChipColors(androidx.wear.tiles.material.Colors);
+  @Deprecated public class ChipColors {
+    ctor @Deprecated public ChipColors(@ColorInt int, @ColorInt int, @ColorInt int, @ColorInt int);
+    ctor @Deprecated public ChipColors(@ColorInt int, @ColorInt int);
+    ctor @Deprecated public ChipColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
+    ctor @Deprecated public ChipColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getBackgroundColor();
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getContentColor();
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getIconColor();
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getSecondaryContentColor();
+    method @Deprecated public static androidx.wear.tiles.material.ChipColors primaryChipColors(androidx.wear.tiles.material.Colors);
+    method @Deprecated public static androidx.wear.tiles.material.ChipColors secondaryChipColors(androidx.wear.tiles.material.Colors);
   }
 
-  public class ChipDefaults {
-    field public static final androidx.wear.tiles.material.ChipColors COMPACT_PRIMARY_COLORS;
-    field public static final androidx.wear.tiles.material.ChipColors COMPACT_SECONDARY_COLORS;
-    field public static final androidx.wear.tiles.material.ChipColors PRIMARY_COLORS;
-    field public static final androidx.wear.tiles.material.ChipColors SECONDARY_COLORS;
-    field public static final androidx.wear.tiles.material.ChipColors TITLE_PRIMARY_COLORS;
-    field public static final androidx.wear.tiles.material.ChipColors TITLE_SECONDARY_COLORS;
+  @Deprecated public class ChipDefaults {
+    field @Deprecated public static final androidx.wear.tiles.material.ChipColors COMPACT_PRIMARY_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.material.ChipColors COMPACT_SECONDARY_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.material.ChipColors PRIMARY_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.material.ChipColors SECONDARY_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.material.ChipColors TITLE_PRIMARY_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.material.ChipColors TITLE_SECONDARY_COLORS;
   }
 
-  public class CircularProgressIndicator implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.CircularProgressIndicator? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.ProgressIndicatorColors getCircularProgressIndicatorColors();
-    method public CharSequence? getContentDescription();
-    method public androidx.wear.tiles.DimensionBuilders.DegreesProp getEndAngle();
-    method public androidx.wear.tiles.DimensionBuilders.DegreesProp getProgress();
-    method public androidx.wear.tiles.DimensionBuilders.DegreesProp getStartAngle();
-    method public androidx.wear.tiles.DimensionBuilders.DpProp getStrokeWidth();
+  @Deprecated public class CircularProgressIndicator implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.CircularProgressIndicator? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.ProgressIndicatorColors getCircularProgressIndicatorColors();
+    method @Deprecated public CharSequence? getContentDescription();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.DegreesProp getEndAngle();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.DegreesProp getProgress();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.DegreesProp getStartAngle();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.DpProp getStrokeWidth();
   }
 
-  public static final class CircularProgressIndicator.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public CircularProgressIndicator.Builder();
-    method public androidx.wear.tiles.material.CircularProgressIndicator build();
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setCircularProgressIndicatorColors(androidx.wear.tiles.material.ProgressIndicatorColors);
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setContentDescription(CharSequence);
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setEndAngle(float);
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setProgress(@FloatRange(from=0, to=1) float);
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setStartAngle(float);
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setStrokeWidth(androidx.wear.tiles.DimensionBuilders.DpProp);
-    method public androidx.wear.tiles.material.CircularProgressIndicator.Builder setStrokeWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  @Deprecated public static final class CircularProgressIndicator.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public CircularProgressIndicator.Builder();
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator build();
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setCircularProgressIndicatorColors(androidx.wear.tiles.material.ProgressIndicatorColors);
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setContentDescription(CharSequence);
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setEndAngle(float);
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setProgress(@FloatRange(from=0, to=1) float);
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setStartAngle(float);
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setStrokeWidth(androidx.wear.tiles.DimensionBuilders.DpProp);
+    method @Deprecated public androidx.wear.tiles.material.CircularProgressIndicator.Builder setStrokeWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
-  public class Colors {
-    ctor public Colors(@ColorInt int, @ColorInt int, @ColorInt int, @ColorInt int);
-    method @ColorInt public int getOnPrimary();
-    method @ColorInt public int getOnSurface();
-    method @ColorInt public int getPrimary();
-    method @ColorInt public int getSurface();
-    field public static final androidx.wear.tiles.material.Colors DEFAULT;
+  @Deprecated public class Colors {
+    ctor @Deprecated public Colors(@ColorInt int, @ColorInt int, @ColorInt int, @ColorInt int);
+    method @Deprecated @ColorInt public int getOnPrimary();
+    method @Deprecated @ColorInt public int getOnSurface();
+    method @Deprecated @ColorInt public int getPrimary();
+    method @Deprecated @ColorInt public int getSurface();
+    field @Deprecated public static final androidx.wear.tiles.material.Colors DEFAULT;
   }
 
-  public class CompactChip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.CompactChip? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.ChipColors getChipColors();
-    method public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
-    method public String getText();
+  @Deprecated public class CompactChip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.CompactChip? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.ChipColors getChipColors();
+    method @Deprecated public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
+    method @Deprecated public String getText();
   }
 
-  public static final class CompactChip.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public CompactChip.Builder(android.content.Context, String, androidx.wear.tiles.ModifiersBuilders.Clickable, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
-    method public androidx.wear.tiles.material.CompactChip build();
-    method public androidx.wear.tiles.material.CompactChip.Builder setChipColors(androidx.wear.tiles.material.ChipColors);
+  @Deprecated public static final class CompactChip.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public CompactChip.Builder(android.content.Context, String, androidx.wear.tiles.ModifiersBuilders.Clickable, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method @Deprecated public androidx.wear.tiles.material.CompactChip build();
+    method @Deprecated public androidx.wear.tiles.material.CompactChip.Builder setChipColors(androidx.wear.tiles.material.ChipColors);
   }
 
-  public class ProgressIndicatorColors {
-    ctor public ProgressIndicatorColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
-    ctor public ProgressIndicatorColors(@ColorInt int, @ColorInt int);
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getIndicatorColor();
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getTrackColor();
-    method public static androidx.wear.tiles.material.ProgressIndicatorColors progressIndicatorColors(androidx.wear.tiles.material.Colors);
+  @Deprecated public class ProgressIndicatorColors {
+    ctor @Deprecated public ProgressIndicatorColors(androidx.wear.tiles.ColorBuilders.ColorProp, androidx.wear.tiles.ColorBuilders.ColorProp);
+    ctor @Deprecated public ProgressIndicatorColors(@ColorInt int, @ColorInt int);
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getIndicatorColor();
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getTrackColor();
+    method @Deprecated public static androidx.wear.tiles.material.ProgressIndicatorColors progressIndicatorColors(androidx.wear.tiles.material.Colors);
   }
 
-  public class ProgressIndicatorDefaults {
-    field public static final androidx.wear.tiles.material.ProgressIndicatorColors DEFAULT_COLORS;
-    field public static final androidx.wear.tiles.DimensionBuilders.DpProp DEFAULT_STROKE_WIDTH;
-    field public static final float GAP_END_ANGLE = 156.1f;
-    field public static final float GAP_START_ANGLE = -156.1f;
+  @Deprecated public class ProgressIndicatorDefaults {
+    field @Deprecated public static final androidx.wear.tiles.material.ProgressIndicatorColors DEFAULT_COLORS;
+    field @Deprecated public static final androidx.wear.tiles.DimensionBuilders.DpProp DEFAULT_STROKE_WIDTH;
+    field @Deprecated public static final float GAP_END_ANGLE = 156.1f;
+    field @Deprecated public static final float GAP_START_ANGLE = -156.1f;
   }
 
-  public class Text implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.Text? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.ColorBuilders.ColorProp getColor();
-    method public androidx.wear.tiles.LayoutElementBuilders.FontStyle getFontStyle();
-    method public float getLineHeight();
-    method public int getMaxLines();
-    method public androidx.wear.tiles.ModifiersBuilders.Modifiers getModifiers();
-    method public int getMultilineAlignment();
-    method public int getOverflow();
-    method public String getText();
-    method public int getWeight();
-    method public boolean isItalic();
-    method public boolean isUnderline();
+  @Deprecated public class Text implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.Text? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.ColorBuilders.ColorProp getColor();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.FontStyle getFontStyle();
+    method @Deprecated public float getLineHeight();
+    method @Deprecated public int getMaxLines();
+    method @Deprecated public androidx.wear.tiles.ModifiersBuilders.Modifiers getModifiers();
+    method @Deprecated public int getMultilineAlignment();
+    method @Deprecated public int getOverflow();
+    method @Deprecated public String getText();
+    method @Deprecated public int getWeight();
+    method @Deprecated public boolean isItalic();
+    method @Deprecated public boolean isUnderline();
   }
 
-  public static final class Text.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public Text.Builder(android.content.Context, String);
-    method public androidx.wear.tiles.material.Text build();
-    method public androidx.wear.tiles.material.Text.Builder setColor(androidx.wear.tiles.ColorBuilders.ColorProp);
-    method public androidx.wear.tiles.material.Text.Builder setItalic(boolean);
-    method public androidx.wear.tiles.material.Text.Builder setMaxLines(@IntRange(from=1) int);
-    method public androidx.wear.tiles.material.Text.Builder setModifiers(androidx.wear.tiles.ModifiersBuilders.Modifiers);
-    method public androidx.wear.tiles.material.Text.Builder setMultilineAlignment(int);
-    method public androidx.wear.tiles.material.Text.Builder setOverflow(int);
-    method public androidx.wear.tiles.material.Text.Builder setTypography(int);
-    method public androidx.wear.tiles.material.Text.Builder setUnderline(boolean);
-    method public androidx.wear.tiles.material.Text.Builder setWeight(int);
+  @Deprecated public static final class Text.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public Text.Builder(android.content.Context, String);
+    method @Deprecated public androidx.wear.tiles.material.Text build();
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setColor(androidx.wear.tiles.ColorBuilders.ColorProp);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setItalic(boolean);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setMaxLines(@IntRange(from=1) int);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setModifiers(androidx.wear.tiles.ModifiersBuilders.Modifiers);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setMultilineAlignment(int);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setOverflow(int);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setTypography(int);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setUnderline(boolean);
+    method @Deprecated public androidx.wear.tiles.material.Text.Builder setWeight(int);
   }
 
-  public class TitleChip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.TitleChip? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.ChipColors getChipColors();
-    method public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
-    method public int getHorizontalAlignment();
-    method public String getText();
-    method public androidx.wear.tiles.DimensionBuilders.ContainerDimension getWidth();
+  @Deprecated public class TitleChip implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.TitleChip? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.ChipColors getChipColors();
+    method @Deprecated public androidx.wear.tiles.ModifiersBuilders.Clickable getClickable();
+    method @Deprecated public int getHorizontalAlignment();
+    method @Deprecated public String getText();
+    method @Deprecated public androidx.wear.tiles.DimensionBuilders.ContainerDimension getWidth();
   }
 
-  public static final class TitleChip.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public TitleChip.Builder(android.content.Context, String, androidx.wear.tiles.ModifiersBuilders.Clickable, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
-    method public androidx.wear.tiles.material.TitleChip build();
-    method public androidx.wear.tiles.material.TitleChip.Builder setChipColors(androidx.wear.tiles.material.ChipColors);
-    method public androidx.wear.tiles.material.TitleChip.Builder setHorizontalAlignment(int);
-    method public androidx.wear.tiles.material.TitleChip.Builder setWidth(androidx.wear.tiles.DimensionBuilders.ContainerDimension);
-    method public androidx.wear.tiles.material.TitleChip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  @Deprecated public static final class TitleChip.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public TitleChip.Builder(android.content.Context, String, androidx.wear.tiles.ModifiersBuilders.Clickable, androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method @Deprecated public androidx.wear.tiles.material.TitleChip build();
+    method @Deprecated public androidx.wear.tiles.material.TitleChip.Builder setChipColors(androidx.wear.tiles.material.ChipColors);
+    method @Deprecated public androidx.wear.tiles.material.TitleChip.Builder setHorizontalAlignment(int);
+    method @Deprecated public androidx.wear.tiles.material.TitleChip.Builder setWidth(androidx.wear.tiles.DimensionBuilders.ContainerDimension);
+    method @Deprecated public androidx.wear.tiles.material.TitleChip.Builder setWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
-  public class Typography {
-    field public static final int TYPOGRAPHY_BODY1 = 7; // 0x7
-    field public static final int TYPOGRAPHY_BODY2 = 8; // 0x8
-    field public static final int TYPOGRAPHY_BUTTON = 9; // 0x9
-    field public static final int TYPOGRAPHY_CAPTION1 = 10; // 0xa
-    field public static final int TYPOGRAPHY_CAPTION2 = 11; // 0xb
-    field public static final int TYPOGRAPHY_CAPTION3 = 12; // 0xc
-    field public static final int TYPOGRAPHY_DISPLAY1 = 1; // 0x1
-    field public static final int TYPOGRAPHY_DISPLAY2 = 2; // 0x2
-    field public static final int TYPOGRAPHY_DISPLAY3 = 3; // 0x3
-    field public static final int TYPOGRAPHY_TITLE1 = 4; // 0x4
-    field public static final int TYPOGRAPHY_TITLE2 = 5; // 0x5
-    field public static final int TYPOGRAPHY_TITLE3 = 6; // 0x6
+  @Deprecated public class Typography {
+    field @Deprecated public static final int TYPOGRAPHY_BODY1 = 7; // 0x7
+    field @Deprecated public static final int TYPOGRAPHY_BODY2 = 8; // 0x8
+    field @Deprecated public static final int TYPOGRAPHY_BUTTON = 9; // 0x9
+    field @Deprecated public static final int TYPOGRAPHY_CAPTION1 = 10; // 0xa
+    field @Deprecated public static final int TYPOGRAPHY_CAPTION2 = 11; // 0xb
+    field @Deprecated public static final int TYPOGRAPHY_CAPTION3 = 12; // 0xc
+    field @Deprecated public static final int TYPOGRAPHY_DISPLAY1 = 1; // 0x1
+    field @Deprecated public static final int TYPOGRAPHY_DISPLAY2 = 2; // 0x2
+    field @Deprecated public static final int TYPOGRAPHY_DISPLAY3 = 3; // 0x3
+    field @Deprecated public static final int TYPOGRAPHY_TITLE1 = 4; // 0x4
+    field @Deprecated public static final int TYPOGRAPHY_TITLE2 = 5; // 0x5
+    field @Deprecated public static final int TYPOGRAPHY_TITLE3 = 6; // 0x6
   }
 
 }
 
 package androidx.wear.tiles.material.layouts {
 
-  public class EdgeContentLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.layouts.EdgeContentLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getContent();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getEdgeContent();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getPrimaryLabelTextContent();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getSecondaryLabelTextContent();
+  @Deprecated public class EdgeContentLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.layouts.EdgeContentLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getContent();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getEdgeContent();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getPrimaryLabelTextContent();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getSecondaryLabelTextContent();
   }
 
-  public static final class EdgeContentLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public EdgeContentLayout.Builder(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
-    method public androidx.wear.tiles.material.layouts.EdgeContentLayout build();
-    method public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setEdgeContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setPrimaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setSecondaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+  @Deprecated public static final class EdgeContentLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public EdgeContentLayout.Builder(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method @Deprecated public androidx.wear.tiles.material.layouts.EdgeContentLayout build();
+    method @Deprecated public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setEdgeContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setPrimaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.EdgeContentLayout.Builder setSecondaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
   }
 
-  public class LayoutDefaults {
-    field public static final androidx.wear.tiles.DimensionBuilders.DpProp DEFAULT_VERTICAL_SPACER_HEIGHT;
-    field public static final float EDGE_CONTENT_LAYOUT_PADDING_ABOVE_MAIN_CONTENT_DP = 6.0f;
-    field public static final float EDGE_CONTENT_LAYOUT_PADDING_BELOW_MAIN_CONTENT_DP = 8.0f;
-    field public static final int MULTI_BUTTON_MAX_NUMBER = 7; // 0x7
-    field public static final androidx.wear.tiles.DimensionBuilders.DpProp MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH;
+  @Deprecated public class LayoutDefaults {
+    field @Deprecated public static final androidx.wear.tiles.DimensionBuilders.DpProp DEFAULT_VERTICAL_SPACER_HEIGHT;
+    field @Deprecated public static final float EDGE_CONTENT_LAYOUT_PADDING_ABOVE_MAIN_CONTENT_DP = 6.0f;
+    field @Deprecated public static final float EDGE_CONTENT_LAYOUT_PADDING_BELOW_MAIN_CONTENT_DP = 8.0f;
+    field @Deprecated public static final int MULTI_BUTTON_MAX_NUMBER = 7; // 0x7
+    field @Deprecated public static final androidx.wear.tiles.DimensionBuilders.DpProp MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH;
   }
 
-  public class MultiButtonLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.layouts.MultiButtonLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public java.util.List<androidx.wear.tiles.LayoutElementBuilders.LayoutElement!> getButtonContents();
-    method public int getFiveButtonDistribution();
-    field public static final int FIVE_BUTTON_DISTRIBUTION_BOTTOM_HEAVY = 2; // 0x2
-    field public static final int FIVE_BUTTON_DISTRIBUTION_TOP_HEAVY = 1; // 0x1
+  @Deprecated public class MultiButtonLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.layouts.MultiButtonLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public java.util.List<androidx.wear.tiles.LayoutElementBuilders.LayoutElement!> getButtonContents();
+    method @Deprecated public int getFiveButtonDistribution();
+    field @Deprecated public static final int FIVE_BUTTON_DISTRIBUTION_BOTTOM_HEAVY = 2; // 0x2
+    field @Deprecated public static final int FIVE_BUTTON_DISTRIBUTION_TOP_HEAVY = 1; // 0x1
   }
 
-  public static final class MultiButtonLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public MultiButtonLayout.Builder();
-    method public androidx.wear.tiles.material.layouts.MultiButtonLayout.Builder addButtonContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.MultiButtonLayout build();
-    method public androidx.wear.tiles.material.layouts.MultiButtonLayout.Builder setFiveButtonDistribution(int);
+  @Deprecated public static final class MultiButtonLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public MultiButtonLayout.Builder();
+    method @Deprecated public androidx.wear.tiles.material.layouts.MultiButtonLayout.Builder addButtonContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.MultiButtonLayout build();
+    method @Deprecated public androidx.wear.tiles.material.layouts.MultiButtonLayout.Builder setFiveButtonDistribution(int);
   }
 
-  public class MultiSlotLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.layouts.MultiSlotLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getHorizontalSpacerWidth();
-    method public java.util.List<androidx.wear.tiles.LayoutElementBuilders.LayoutElement!> getSlotContents();
+  @Deprecated public class MultiSlotLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.layouts.MultiSlotLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated @Dimension(unit=androidx.annotation.Dimension.DP) public float getHorizontalSpacerWidth();
+    method @Deprecated public java.util.List<androidx.wear.tiles.LayoutElementBuilders.LayoutElement!> getSlotContents();
   }
 
-  public static final class MultiSlotLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public MultiSlotLayout.Builder();
-    method public androidx.wear.tiles.material.layouts.MultiSlotLayout.Builder addSlotContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.MultiSlotLayout build();
-    method public androidx.wear.tiles.material.layouts.MultiSlotLayout.Builder setHorizontalSpacerWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  @Deprecated public static final class MultiSlotLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public MultiSlotLayout.Builder();
+    method @Deprecated public androidx.wear.tiles.material.layouts.MultiSlotLayout.Builder addSlotContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.MultiSlotLayout build();
+    method @Deprecated public androidx.wear.tiles.material.layouts.MultiSlotLayout.Builder setHorizontalSpacerWidth(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
-  public class PrimaryLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
-    method public static androidx.wear.tiles.material.layouts.PrimaryLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getContent();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getPrimaryChipContent();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getPrimaryLabelTextContent();
-    method public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getSecondaryLabelTextContent();
-    method @Dimension(unit=androidx.annotation.Dimension.DP) public float getVerticalSpacerHeight();
+  @Deprecated public class PrimaryLayout implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement {
+    method @Deprecated public static androidx.wear.tiles.material.layouts.PrimaryLayout? fromLayoutElement(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getContent();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getPrimaryChipContent();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getPrimaryLabelTextContent();
+    method @Deprecated public androidx.wear.tiles.LayoutElementBuilders.LayoutElement? getSecondaryLabelTextContent();
+    method @Deprecated @Dimension(unit=androidx.annotation.Dimension.DP) public float getVerticalSpacerHeight();
   }
 
-  public static final class PrimaryLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
-    ctor public PrimaryLayout.Builder(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
-    method public androidx.wear.tiles.material.layouts.PrimaryLayout build();
-    method public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setPrimaryChipContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setPrimaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setSecondaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
-    method public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setVerticalSpacerHeight(@Dimension(unit=androidx.annotation.Dimension.DP) float);
+  @Deprecated public static final class PrimaryLayout.Builder implements androidx.wear.tiles.LayoutElementBuilders.LayoutElement.Builder {
+    ctor @Deprecated public PrimaryLayout.Builder(androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters);
+    method @Deprecated public androidx.wear.tiles.material.layouts.PrimaryLayout build();
+    method @Deprecated public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setPrimaryChipContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setPrimaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setSecondaryLabelTextContent(androidx.wear.tiles.LayoutElementBuilders.LayoutElement);
+    method @Deprecated public androidx.wear.tiles.material.layouts.PrimaryLayout.Builder setVerticalSpacerHeight(@Dimension(unit=androidx.annotation.Dimension.DP) float);
   }
 
 }
diff --git a/wear/tiles/tiles-material/src/androidTest/java/androidx/wear/tiles/material/TestCasesGenerator.java b/wear/tiles/tiles-material/src/androidTest/java/androidx/wear/tiles/material/TestCasesGenerator.java
index 3b62f26..f42feff 100644
--- a/wear/tiles/tiles-material/src/androidTest/java/androidx/wear/tiles/material/TestCasesGenerator.java
+++ b/wear/tiles/tiles-material/src/androidTest/java/androidx/wear/tiles/material/TestCasesGenerator.java
@@ -21,8 +21,6 @@
 import static androidx.wear.tiles.LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER;
 import static androidx.wear.tiles.LayoutElementBuilders.HORIZONTAL_ALIGN_END;
 import static androidx.wear.tiles.LayoutElementBuilders.HORIZONTAL_ALIGN_START;
-import static androidx.wear.tiles.material.ProgressIndicatorDefaults.GAP_END_ANGLE;
-import static androidx.wear.tiles.material.ProgressIndicatorDefaults.GAP_START_ANGLE;
 
 import android.content.Context;
 import android.graphics.Color;
@@ -39,6 +37,7 @@
 import java.util.HashMap;
 import java.util.Map;
 
+@SuppressWarnings("deprecation")
 public class TestCasesGenerator {
     private TestCasesGenerator() {}
 
@@ -253,8 +252,8 @@
         testCases.put(
                 "default_gap_circularprogressindicator",
                 new CircularProgressIndicator.Builder()
-                        .setStartAngle(GAP_START_ANGLE)
-                        .setEndAngle(GAP_END_ANGLE)
+                        .setStartAngle(ProgressIndicatorDefaults.GAP_START_ANGLE)
+                        .setEndAngle(ProgressIndicatorDefaults.GAP_END_ANGLE)
                         .build());
         testCases.put(
                 "default_full_90_circularprogressindicator",
@@ -263,8 +262,8 @@
                 "default_gap_90_circularprogressindicator",
                 new CircularProgressIndicator.Builder()
                         .setProgress(0.25f)
-                        .setStartAngle(GAP_START_ANGLE)
-                        .setEndAngle(GAP_END_ANGLE)
+                        .setStartAngle(ProgressIndicatorDefaults.GAP_START_ANGLE)
+                        .setEndAngle(ProgressIndicatorDefaults.GAP_END_ANGLE)
                         .build());
         testCases.put(
                 "custom_gap_45_circularprogressindicator",
diff --git a/wear/tiles/tiles-material/src/androidTest/java/androidx/wear/tiles/material/layouts/TestCasesGenerator.java b/wear/tiles/tiles-material/src/androidTest/java/androidx/wear/tiles/material/layouts/TestCasesGenerator.java
index b03caa2..1ec7f57 100644
--- a/wear/tiles/tiles-material/src/androidTest/java/androidx/wear/tiles/material/layouts/TestCasesGenerator.java
+++ b/wear/tiles/tiles-material/src/androidTest/java/androidx/wear/tiles/material/layouts/TestCasesGenerator.java
@@ -51,6 +51,7 @@
 import java.util.HashMap;
 import java.util.Map;
 
+@SuppressWarnings("deprecation")
 public class TestCasesGenerator {
     private TestCasesGenerator() {}
 
diff --git a/wear/tiles/tiles-material/src/main/AndroidManifest.xml b/wear/tiles/tiles-material/src/main/AndroidManifest.xml
index 037bd65..ed8a041 100644
--- a/wear/tiles/tiles-material/src/main/AndroidManifest.xml
+++ b/wear/tiles/tiles-material/src/main/AndroidManifest.xml
@@ -14,6 +14,6 @@
   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>
 
 </manifest>
\ No newline at end of file
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Button.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Button.java
index 180f84f..966fb2a 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Button.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Button.java
@@ -37,6 +37,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.proto.LayoutElementProto;
 import androidx.wear.tiles.ColorBuilders.ColorProp;
 import androidx.wear.tiles.DimensionBuilders.ContainerDimension;
 import androidx.wear.tiles.DimensionBuilders.DpProp;
@@ -52,7 +53,6 @@
 import androidx.wear.tiles.ModifiersBuilders.Modifiers;
 import androidx.wear.tiles.ModifiersBuilders.Semantics;
 import androidx.wear.tiles.material.Typography.TypographyName;
-import androidx.wear.protolayout.proto.LayoutElementProto;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -86,7 +86,12 @@
  * <pre>{@code
  * Button myButton = Button.fromLayoutElement(box.getContents().get(0));
  * }</pre>
+ *
+ * @deprecated Use the new class {@link androidx.wear.protolayout.material.Button} which provides
+ *     the same API and functionality.
  */
+@Deprecated
+@SuppressWarnings("deprecation")
 public class Button implements LayoutElement {
     /** Tool tag for Metadata in Modifiers, so we know that Box is actually a Button with text. */
     static final String METADATA_TAG_TEXT = "TXTBTN";
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ButtonColors.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ButtonColors.java
index cee5fbd..ebf62a0 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ButtonColors.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ButtonColors.java
@@ -28,7 +28,12 @@
  * <p>See {@link ButtonDefaults#PRIMARY_COLORS} for the default colors used in a primary styled
  * {@link Button}. See {@link ButtonDefaults#SECONDARY_COLORS} for the default colors used in a
  * secondary styled {@link Button}.
+ *
+ * @deprecated Use the new class {@link androidx.wear.protolayout.material.ButtonColors} which
+ *     provides the same API and functionality.
  */
+@Deprecated
+@SuppressWarnings("deprecation")
 public class ButtonColors {
     @NonNull private final ColorProp mBackgroundColor;
     @NonNull private final ColorProp mContentColor;
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ButtonDefaults.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ButtonDefaults.java
index 3439061..7c9398c 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ButtonDefaults.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ButtonDefaults.java
@@ -23,7 +23,14 @@
 import androidx.annotation.NonNull;
 import androidx.wear.tiles.DimensionBuilders.DpProp;
 
-/** Contains the default values used by button Tiles components. */
+/**
+ * Contains the default values used by button Tiles components.
+ *
+ * @deprecated Use the new class {@link androidx.wear.protolayout.material.ButtonDefaults} which
+ *     provides the same API and functionality.
+ */
+@Deprecated
+@SuppressWarnings("deprecation")
 public class ButtonDefaults {
     private ButtonDefaults() {}
 
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Chip.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Chip.java
index 4a00f21..18ce731 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Chip.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Chip.java
@@ -41,6 +41,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.proto.LayoutElementProto;
 import androidx.wear.tiles.ColorBuilders.ColorProp;
 import androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters;
 import androidx.wear.tiles.DimensionBuilders.ContainerDimension;
@@ -62,7 +63,6 @@
 import androidx.wear.tiles.ModifiersBuilders.Padding;
 import androidx.wear.tiles.ModifiersBuilders.Semantics;
 import androidx.wear.tiles.material.Typography.TypographyName;
-import androidx.wear.protolayout.proto.LayoutElementProto;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -102,7 +102,12 @@
  *
  * @see  androidx.wear.tiles.material.layouts.PrimaryLayout.Builder#setContent if this Chip is used
  * inside of {@link androidx.wear.tiles.material.layouts.PrimaryLayout}.
+ *
+ * @deprecated Use the new class {@link androidx.wear.protolayout.material.Chip} which provides
+ *     the same API and functionality.
  */
+@Deprecated
+@SuppressWarnings("deprecation")
 public class Chip implements LayoutElement {
     /**
      * Tool tag for Metadata in Modifiers, so we know that Box is actually a Chip with only text.
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ChipColors.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ChipColors.java
index e739b1d..45b9d76 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ChipColors.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ChipColors.java
@@ -28,7 +28,12 @@
  * <p>See {@link ChipDefaults#PRIMARY_COLORS} for the default colors used in a primary styled {@link
  * Chip}. See {@link ChipDefaults#SECONDARY_COLORS} for the default colors used in a secondary
  * styled {@link Chip}.
+ *
+ * @deprecated Use the new class {@link androidx.wear.protolayout.material.ChipColors} which
+ *     provides the same API and functionality.
  */
+@Deprecated
+@SuppressWarnings("deprecation")
 public class ChipColors {
     @NonNull private final ColorProp mBackgroundColor;
     @NonNull private final ColorProp mIconColor;
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ChipDefaults.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ChipDefaults.java
index df1d845..e0b6c27 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ChipDefaults.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ChipDefaults.java
@@ -23,7 +23,14 @@
 import androidx.annotation.RestrictTo.Scope;
 import androidx.wear.tiles.DimensionBuilders.DpProp;
 
-/** Contains the default values used by chip Tiles components. */
+/**
+ * Contains the default values used by chip Tiles components.
+ *
+ * @deprecated Use the new class {@link androidx.wear.protolayout.material.ChipDefaults} which
+ *     provides the same API and functionality.
+ */
+@Deprecated
+@SuppressWarnings("deprecation")
 public class ChipDefaults {
     private ChipDefaults() {}
 
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/CircularProgressIndicator.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/CircularProgressIndicator.java
index 771d94a..27c3d8d 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/CircularProgressIndicator.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/CircularProgressIndicator.java
@@ -35,6 +35,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.proto.LayoutElementProto;
 import androidx.wear.tiles.DimensionBuilders.DegreesProp;
 import androidx.wear.tiles.DimensionBuilders.DpProp;
 import androidx.wear.tiles.LayoutElementBuilders;
@@ -47,7 +48,6 @@
 import androidx.wear.tiles.ModifiersBuilders.Modifiers;
 import androidx.wear.tiles.ModifiersBuilders.Padding;
 import androidx.wear.tiles.ModifiersBuilders.Semantics;
-import androidx.wear.protolayout.proto.LayoutElementProto;
 
 /**
  * Tiles component {@link CircularProgressIndicator} that represents circular progress indicator
@@ -83,7 +83,13 @@
  * CircularProgressIndicator myCpi =
  *   CircularProgressIndicator.fromLayoutElement(box.getContents().get(0));
  * }</pre>
+ *
+ * @deprecated Use the new class
+ *     {@link androidx.wear.protolayout.material.CircularProgressIndicator} which provides the
+ *     same API and functionality.
  */
+@Deprecated
+@SuppressWarnings("deprecation")
 public class CircularProgressIndicator implements LayoutElement {
     /**
      * Tool tag for Metadata in Modifiers, so we know that Arc is actually a
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Colors.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Colors.java
index a1efbe29..47b6f53 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Colors.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Colors.java
@@ -26,7 +26,12 @@
  * objects for all Material components.
  *
  * <p>See {@link #DEFAULT} for default color scheme.
+ *
+ * @deprecated Use the new class {@link androidx.wear.protolayout.material.Colors} which provides
+ *     the same API and functionality.
  */
+@Deprecated
+@SuppressWarnings("deprecation")
 public class Colors {
 
     /**
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/CompactChip.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/CompactChip.java
index 8c10c16..a2dadf4 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/CompactChip.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/CompactChip.java
@@ -32,6 +32,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.proto.LayoutElementProto;
 import androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters;
 import androidx.wear.tiles.LayoutElementBuilders;
 import androidx.wear.tiles.LayoutElementBuilders.Box;
@@ -39,7 +40,6 @@
 import androidx.wear.tiles.ModifiersBuilders.Clickable;
 import androidx.wear.tiles.ModifiersBuilders.ElementMetadata;
 import androidx.wear.tiles.ModifiersBuilders.Modifiers;
-import androidx.wear.protolayout.proto.LayoutElementProto;
 
 /**
  * Tiles component {@link CompactChip} that represents clickable object with the text.
@@ -70,7 +70,12 @@
  * <pre>{@code
  * CompactChip myChip = CompactChip.fromLayoutElement(box.getContents().get(0));
  * }</pre>
+ *
+ * @deprecated Use the new class {@link androidx.wear.protolayout.material.CompactChip} which
+ *     provides the same API and functionality.
  */
+@Deprecated
+@SuppressWarnings("deprecation")
 public class CompactChip implements LayoutElement {
     /** Tool tag for Metadata in Modifiers, so we know that Box is actually a CompactChip. */
     static final String METADATA_TAG = "CMPCHP";
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Helper.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Helper.java
index 90ac94c..edc727e 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Helper.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Helper.java
@@ -35,7 +35,11 @@
 /**
  * Helper class used for Tiles Material.
  *
+ * @deprecated Use the new class {@link androidx.wear.protolayout.material.Helper} which provides
+ *     the same API and functionality.
  */
+@Deprecated
+@SuppressWarnings("deprecation")
 @RestrictTo(Scope.LIBRARY_GROUP)
 public class Helper {
     private Helper() {}
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ProgressIndicatorColors.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ProgressIndicatorColors.java
index c88a9b4..d7eea6e 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ProgressIndicatorColors.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ProgressIndicatorColors.java
@@ -27,7 +27,12 @@
  *
  * <p>See {@link ProgressIndicatorDefaults#DEFAULT_COLORS} for the default colors used in a {@link
  * CircularProgressIndicator}.
+ *
+ * @deprecated Use the new class {@link androidx.wear.protolayout.material.ProgressIndicatorColors}
+ *     which provides the same API and functionality.
  */
+@Deprecated
+@SuppressWarnings("deprecation")
 public class ProgressIndicatorColors {
     @NonNull private final ColorProp mIndicatorColor;
     @NonNull private final ColorProp mTrackColor;
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ProgressIndicatorDefaults.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ProgressIndicatorDefaults.java
index 955f829..ea4446b 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ProgressIndicatorDefaults.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/ProgressIndicatorDefaults.java
@@ -23,7 +23,15 @@
 import androidx.annotation.RestrictTo.Scope;
 import androidx.wear.tiles.DimensionBuilders.DpProp;
 
-/** Contains the default values used by {@link CircularProgressIndicator} Tiles components. */
+/**
+ * Contains the default values used by {@link CircularProgressIndicator} Tiles components.
+ *
+ * @deprecated Use the new class
+ *     {@link androidx.wear.protolayout.material.CircularProgressIndicator} which provides the
+ *     same API and functionality.
+ */
+@Deprecated
+@SuppressWarnings("deprecation")
 public class ProgressIndicatorDefaults {
     private ProgressIndicatorDefaults() {}
 
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Text.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Text.java
index b51e642..4c0bdfc 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Text.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Text.java
@@ -34,6 +34,8 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.proto.LayoutElementProto;
+import androidx.wear.protolayout.proto.ModifiersProto;
 import androidx.wear.tiles.ColorBuilders.ColorProp;
 import androidx.wear.tiles.LayoutElementBuilders;
 import androidx.wear.tiles.LayoutElementBuilders.FontStyle;
@@ -44,8 +46,6 @@
 import androidx.wear.tiles.ModifiersBuilders.ElementMetadata;
 import androidx.wear.tiles.ModifiersBuilders.Modifiers;
 import androidx.wear.tiles.material.Typography.TypographyName;
-import androidx.wear.protolayout.proto.LayoutElementProto;
-import androidx.wear.protolayout.proto.ModifiersProto;
 
 /**
  * Tiles component {@link Text} that represents text object holding any information.
@@ -71,7 +71,12 @@
  * <pre>{@code
  * Text myText = Text.fromLayoutElement(box.getContents().get(0));
  * }</pre>
+ *
+ * @deprecated Use the new class {@link androidx.wear.protolayout.material.Text} which provides
+ *     the same API and functionality.
  */
+@Deprecated
+@SuppressWarnings("deprecation")
 public class Text implements LayoutElement {
     /** Tool tag for Metadata in Modifiers, so we know that Text is actually a Material Text. */
     static final String METADATA_TAG = "TXT";
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/TitleChip.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/TitleChip.java
index 3c98bf4..2777589 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/TitleChip.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/TitleChip.java
@@ -32,13 +32,13 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.proto.LayoutElementProto;
 import androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters;
 import androidx.wear.tiles.DimensionBuilders.ContainerDimension;
 import androidx.wear.tiles.LayoutElementBuilders.Box;
 import androidx.wear.tiles.LayoutElementBuilders.HorizontalAlignment;
 import androidx.wear.tiles.LayoutElementBuilders.LayoutElement;
 import androidx.wear.tiles.ModifiersBuilders.Clickable;
-import androidx.wear.protolayout.proto.LayoutElementProto;
 
 /**
  * Tiles component {@link TitleChip} that represents clickable object with the text.
@@ -71,7 +71,12 @@
  *
  * @see  androidx.wear.tiles.material.layouts.PrimaryLayout.Builder#setContent if this TitleChip is
  * used inside of {@link androidx.wear.tiles.material.layouts.PrimaryLayout}.
+ *
+ * @deprecated Use the new class {@link androidx.wear.protolayout.material.TitleChip} which provides
+ *     the same API and functionality.
  */
+@Deprecated
+@SuppressWarnings("deprecation")
 public class TitleChip implements LayoutElement {
     /** Tool tag for Metadata in Modifiers, so we know that Box is actually a TitleChip. */
     static final String METADATA_TAG = "TTLCHP";
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Typography.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Typography.java
index c972b52..faf0528 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Typography.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/Typography.java
@@ -45,7 +45,14 @@
 import java.util.HashMap;
 import java.util.Map;
 
-/** Typography styles, currently set up to match Wear's styling. */
+/**
+ * Typography styles, currently set up to match Wear's styling.
+ *
+ * @deprecated Use the new class {@link androidx.wear.protolayout.material.Typography} which
+ *     provides the same API and functionality.
+ */
+@Deprecated
+@SuppressWarnings("deprecation")
 public class Typography {
     /** Typography for large display text. */
     public static final int TYPOGRAPHY_DISPLAY1 = 1;
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/EdgeContentLayout.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/EdgeContentLayout.java
index 71582c0..e765457 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/EdgeContentLayout.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/EdgeContentLayout.java
@@ -34,6 +34,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.proto.LayoutElementProto;
 import androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters;
 import androidx.wear.tiles.DimensionBuilders.DpProp;
 import androidx.wear.tiles.LayoutElementBuilders;
@@ -45,7 +46,6 @@
 import androidx.wear.tiles.ModifiersBuilders.Modifiers;
 import androidx.wear.tiles.ModifiersBuilders.Padding;
 import androidx.wear.tiles.material.CircularProgressIndicator;
-import androidx.wear.protolayout.proto.LayoutElementProto;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -80,7 +80,13 @@
  * EdgeContentLayout myEcl =
  *   EdgeContentLayout.fromLayoutElement(box.getContents().get(0));
  * }</pre>
+ *
+ * @deprecated Use the new class
+ *     {@link androidx.wear.protolayout.material.layouts.EdgeContentLayout} which provides the
+ *     same API and functionality.
  */
+@Deprecated
+@SuppressWarnings("deprecation")
 public class EdgeContentLayout implements LayoutElement {
     /**
      * Prefix tool tag for Metadata in Modifiers, so we know that Box is actually a
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/LayoutDefaults.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/LayoutDefaults.java
index df5d7d0..72c060c 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/LayoutDefaults.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/LayoutDefaults.java
@@ -21,7 +21,15 @@
 import androidx.wear.tiles.DimensionBuilders.DpProp;
 import androidx.wear.tiles.material.ButtonDefaults;
 
-/** Contains the default values used by layout templates for Tiles. */
+/**
+ * Contains the default values used by layout templates for Tiles.
+ *
+ * @deprecated Use the new class
+ *     {@link androidx.wear.protolayout.material.layouts.LayoutDefaults} which provides the
+ *     same API and functionality.
+ */
+@Deprecated
+@SuppressWarnings("deprecation")
 public class LayoutDefaults {
     private LayoutDefaults() {}
 
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/MultiButtonLayout.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/MultiButtonLayout.java
index dcd0d7c..f7baab6c 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/MultiButtonLayout.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/MultiButtonLayout.java
@@ -33,6 +33,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.proto.LayoutElementProto;
 import androidx.wear.tiles.DimensionBuilders.DpProp;
 import androidx.wear.tiles.LayoutElementBuilders.Box;
 import androidx.wear.tiles.LayoutElementBuilders.Column;
@@ -42,7 +43,6 @@
 import androidx.wear.tiles.ModifiersBuilders.ElementMetadata;
 import androidx.wear.tiles.ModifiersBuilders.Modifiers;
 import androidx.wear.tiles.material.Button;
-import androidx.wear.protolayout.proto.LayoutElementProto;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -76,7 +76,13 @@
  * <pre>{@code
  * MultiButtonLayout myMbl = MultiButtonLayout.fromLayoutElement(box.getContents().get(0));
  * }</pre>
+ *
+ * @deprecated Use the new class
+ *     {@link androidx.wear.protolayout.material.layouts.MultiButtonLayout} which provides the
+ *     same API and functionality.
  */
+@Deprecated
+@SuppressWarnings("deprecation")
 public class MultiButtonLayout implements LayoutElement {
     /** Tool tag for Metadata in Modifiers, so we know that Box is actually a MultiButtonLayout. */
     static final String METADATA_TAG = "MBL";
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/MultiSlotLayout.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/MultiSlotLayout.java
index 99c13ed..3eb7d77 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/MultiSlotLayout.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/MultiSlotLayout.java
@@ -32,6 +32,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.proto.LayoutElementProto;
 import androidx.wear.tiles.DimensionBuilders.DpProp;
 import androidx.wear.tiles.DimensionBuilders.SpacerDimension;
 import androidx.wear.tiles.LayoutElementBuilders;
@@ -41,7 +42,6 @@
 import androidx.wear.tiles.LayoutElementBuilders.Spacer;
 import androidx.wear.tiles.ModifiersBuilders.ElementMetadata;
 import androidx.wear.tiles.ModifiersBuilders.Modifiers;
-import androidx.wear.protolayout.proto.LayoutElementProto;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -75,7 +75,13 @@
  * <pre>{@code
  * MultiSlotLayout myMsl = MultiSlotLayout.fromLayoutElement(box.getContents().get(0));
  * }</pre>
+ *
+ * @deprecated Use the new class
+ *     {@link androidx.wear.protolayout.material.layouts.MultiSlotLayout} which provides the
+ *     same API and functionality.
  */
+@Deprecated
+@SuppressWarnings("deprecation")
 public class MultiSlotLayout implements LayoutElement {
     /** Tool tag for Metadata in Modifiers, so we know that Row is actually a MultiSlotLayout. */
     static final String METADATA_TAG = "MSL";
diff --git a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/PrimaryLayout.java b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/PrimaryLayout.java
index 77bfd3c..f77cd81 100644
--- a/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/PrimaryLayout.java
+++ b/wear/tiles/tiles-material/src/main/java/androidx/wear/tiles/material/layouts/PrimaryLayout.java
@@ -46,6 +46,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.proto.LayoutElementProto;
 import androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters;
 import androidx.wear.tiles.DimensionBuilders.DpProp;
 import androidx.wear.tiles.DimensionBuilders.SpacerDimension;
@@ -58,7 +59,6 @@
 import androidx.wear.tiles.ModifiersBuilders.Modifiers;
 import androidx.wear.tiles.ModifiersBuilders.Padding;
 import androidx.wear.tiles.material.CompactChip;
-import androidx.wear.protolayout.proto.LayoutElementProto;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -96,7 +96,13 @@
  * <pre>{@code
  * PrimaryLayout myPl = PrimaryLayout.fromLayoutElement(box.getContents().get(0));
  * }</pre>
+ *
+ * @deprecated Use the new class
+ *     {@link androidx.wear.protolayout.material.layouts.PrimaryLayout} which provides the same
+ *     API and functionality.
  */
+@Deprecated
+@SuppressWarnings("deprecation")
 public class PrimaryLayout implements LayoutElement {
     /**
      * Prefix tool tag for Metadata in Modifiers, so we know that Box is actually a PrimaryLayout.
diff --git a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ButtonColorsTest.java b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ButtonColorsTest.java
index 08060bf..c57c525 100644
--- a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ButtonColorsTest.java
+++ b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ButtonColorsTest.java
@@ -29,6 +29,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @DoNotInstrument
+@SuppressWarnings("deprecation")
 public class ButtonColorsTest {
     private static final int ARGB_BACKGROUND_COLOR = 0x12345678;
     private static final int ARGB_CONTENT_COLOR = 0x11223344;
diff --git a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ButtonTest.java b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ButtonTest.java
index 57facdb..b9cec02 100644
--- a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ButtonTest.java
+++ b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ButtonTest.java
@@ -48,6 +48,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @DoNotInstrument
+@SuppressWarnings("deprecation")
 public class ButtonTest {
     private static final String RESOURCE_ID = "icon";
     private static final String TEXT = "ABC";
diff --git a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ChipColorsTest.java b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ChipColorsTest.java
index 241e514..5e2bac5 100644
--- a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ChipColorsTest.java
+++ b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ChipColorsTest.java
@@ -29,6 +29,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @DoNotInstrument
+@SuppressWarnings("deprecation")
 public class ChipColorsTest {
     private static final int ARGB_BACKGROUND_COLOR = 0x12345678;
     private static final int ARGB_CONTENT_COLOR = 0x11223344;
diff --git a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ChipTest.java b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ChipTest.java
index 4f1fc5f..7803fb7 100644
--- a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ChipTest.java
+++ b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ChipTest.java
@@ -52,6 +52,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @DoNotInstrument
+@SuppressWarnings("deprecation")
 public class ChipTest {
     private static final String MAIN_TEXT = "Primary text";
     private static final Clickable CLICKABLE =
diff --git a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/CircularProgressIndicatorTest.java b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/CircularProgressIndicatorTest.java
index 172e8e5..62065c5 100644
--- a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/CircularProgressIndicatorTest.java
+++ b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/CircularProgressIndicatorTest.java
@@ -41,6 +41,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @DoNotInstrument
+@SuppressWarnings("deprecation")
 public class CircularProgressIndicatorTest {
     @Test
     public void testOpenRingIndicatorDefault() {
diff --git a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/CompactChipTest.java b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/CompactChipTest.java
index f7fe407a..2f8c71d 100644
--- a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/CompactChipTest.java
+++ b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/CompactChipTest.java
@@ -41,6 +41,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @DoNotInstrument
+@SuppressWarnings("deprecation")
 public class CompactChipTest {
     private static final String MAIN_TEXT = "Action";
     private static final Clickable CLICKABLE =
diff --git a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ProgressIndicatorColorsTest.java b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ProgressIndicatorColorsTest.java
index e30aa5c..343ac72 100644
--- a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ProgressIndicatorColorsTest.java
+++ b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/ProgressIndicatorColorsTest.java
@@ -29,6 +29,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @DoNotInstrument
+@SuppressWarnings("deprecation")
 public class ProgressIndicatorColorsTest {
     private static final int ARGB_BACKGROUND_COLOR = 0x12345678;
     private static final int ARGB_CONTENT_COLOR = 0x11223344;
diff --git a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/TextTest.java b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/TextTest.java
index 791119d..f241b91 100644
--- a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/TextTest.java
+++ b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/TextTest.java
@@ -52,6 +52,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @DoNotInstrument
+@SuppressWarnings("deprecation")
 public class TextTest {
 
     public static final int NUM_OF_FONT_STYLE_CONST = 12;
diff --git a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/TitleChipTest.java b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/TitleChipTest.java
index e43bfb8..d2be002 100644
--- a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/TitleChipTest.java
+++ b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/TitleChipTest.java
@@ -43,6 +43,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @DoNotInstrument
+@SuppressWarnings("deprecation")
 public class TitleChipTest {
     private static final String MAIN_TEXT = "Action";
     private static final Clickable CLICKABLE =
diff --git a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/Utils.java b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/Utils.java
index a1d44a5..d37d0eb1 100644
--- a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/Utils.java
+++ b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/Utils.java
@@ -18,6 +18,7 @@
 
 import androidx.annotation.Dimension;
 
+@SuppressWarnings("deprecation")
 public final class Utils {
     /** Returns true if the given ChipColors have the same colored content. */
     static boolean areChipColorsEqual(ChipColors colors1, ChipColors colors2) {
diff --git a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/layouts/EdgeContentLayoutTest.java b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/layouts/EdgeContentLayoutTest.java
index e4e413a..ce60257 100644
--- a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/layouts/EdgeContentLayoutTest.java
+++ b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/layouts/EdgeContentLayoutTest.java
@@ -41,6 +41,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @DoNotInstrument
+@SuppressWarnings("deprecation")
 public class EdgeContentLayoutTest {
     private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
     private static final DeviceParameters DEVICE_PARAMETERS =
diff --git a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/layouts/MultiButtonLayoutTest.java b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/layouts/MultiButtonLayoutTest.java
index ec35b24..71c33f4 100644
--- a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/layouts/MultiButtonLayoutTest.java
+++ b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/layouts/MultiButtonLayoutTest.java
@@ -45,6 +45,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @DoNotInstrument
+@SuppressWarnings("deprecation")
 public class MultiButtonLayoutTest {
     private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
     private static final Clickable CLICKABLE =
diff --git a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/layouts/MultiSlotLayoutTest.java b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/layouts/MultiSlotLayoutTest.java
index a2249fc..b44c6a2 100644
--- a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/layouts/MultiSlotLayoutTest.java
+++ b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/layouts/MultiSlotLayoutTest.java
@@ -34,6 +34,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @DoNotInstrument
+@SuppressWarnings("deprecation")
 public class MultiSlotLayoutTest {
 
     @Test
diff --git a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/layouts/PrimaryLayoutTest.java b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/layouts/PrimaryLayoutTest.java
index 1f749e6..f113146 100644
--- a/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/layouts/PrimaryLayoutTest.java
+++ b/wear/tiles/tiles-material/src/test/java/androidx/wear/tiles/material/layouts/PrimaryLayoutTest.java
@@ -45,6 +45,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @DoNotInstrument
+@SuppressWarnings("deprecation")
 public class PrimaryLayoutTest {
     private static final Clickable CLICKABLE =
             new Clickable.Builder()
diff --git a/wear/tiles/tiles-renderer/build.gradle b/wear/tiles/tiles-renderer/build.gradle
index 7270552..1d8a312 100644
--- a/wear/tiles/tiles-renderer/build.gradle
+++ b/wear/tiles/tiles-renderer/build.gradle
@@ -36,6 +36,7 @@
     implementation "androidx.wear:wear:1.2.0"
 
     implementation(project(":wear:protolayout:protolayout"))
+    implementation(project(":wear:protolayout:protolayout-expression-pipeline"))
     implementation(project(":wear:protolayout:protolayout-renderer"))
     implementation(project(":wear:tiles:tiles"))
     implementation(project(":wear:tiles:tiles-proto"))
diff --git a/wear/tiles/tiles-renderer/src/main/java/androidx/wear/tiles/renderer/TileRenderer.java b/wear/tiles/tiles-renderer/src/main/java/androidx/wear/tiles/renderer/TileRenderer.java
index 1432c9e..0cc77dc 100644
--- a/wear/tiles/tiles-renderer/src/main/java/androidx/wear/tiles/renderer/TileRenderer.java
+++ b/wear/tiles/tiles-renderer/src/main/java/androidx/wear/tiles/renderer/TileRenderer.java
@@ -26,6 +26,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.StyleRes;
 import androidx.wear.protolayout.LayoutElementBuilders;
+import androidx.wear.protolayout.expression.pipeline.ObservableStateStore;
 import androidx.wear.protolayout.proto.LayoutElementProto;
 import androidx.wear.protolayout.proto.ResourceProto;
 import androidx.wear.protolayout.proto.StateProto;
@@ -34,6 +35,7 @@
 import androidx.wear.tiles.StateBuilders;
 import androidx.wear.tiles.TileService;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.MoreExecutors;
 
@@ -115,6 +117,9 @@
         ProtoLayoutViewInstance.Config.Builder config =
                 new ProtoLayoutViewInstance.Config.Builder(uiContext, mUiExecutor, mUiExecutor,
                         TileService.EXTRA_CLICKABLE_ID)
+                        .setAnimationEnabled(true)
+                        .setIsViewFullyVisible(true)
+                        .setStateStore(new ObservableStateStore(ImmutableMap.of()))
                         .setLoadActionListener(instanceListener);
         this.mInstance = new ProtoLayoutViewInstance(config.build());
     }
diff --git a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
index 00a74e1..a64d494 100644
--- a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
+++ b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
@@ -883,6 +883,7 @@
         }
     }
 
+    @Ignore("b/273482617")
     @Test
     fun addWatchFaceReadyListener_alreadyReady() {
         val wallpaperService = TestExampleCanvasAnalogWatchFaceService(context, surfaceHolder)
diff --git a/wear/watchface/watchface-complications-data-source-samples/src/main/AndroidManifest.xml b/wear/watchface/watchface-complications-data-source-samples/src/main/AndroidManifest.xml
index 49a47bc7..b7c4f89 100644
--- a/wear/watchface/watchface-complications-data-source-samples/src/main/AndroidManifest.xml
+++ b/wear/watchface/watchface-complications-data-source-samples/src/main/AndroidManifest.xml
@@ -92,6 +92,12 @@
                 android:name="android.support.wearable.complications.SUPPORTED_TYPES"
                 android:value="SHORT_TEXT,LONG_TEXT"/>
             <meta-data
+                android:name="android.support.wearable.complications.TRUSTED_SUPPORTED_TYPES"
+                android:value="ICON"/>
+            <meta-data
+                android:name="android.support.wearable.complications.SAFE_WATCH_FACES"
+                android:value="androidx.wear.watchface.samples.test/androidx.wear.watchface.samples.ExampleCanvasAnalogWatchFaceService"/>
+            <meta-data
                 android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS"
                 android:value="10000"/>
             <intent-filter>
diff --git a/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/ImmediateDataSourceService.kt b/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/ImmediateDataSourceService.kt
index 78ee6ed..5194d6f 100644
--- a/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/ImmediateDataSourceService.kt
+++ b/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/ImmediateDataSourceService.kt
@@ -16,10 +16,13 @@
 
 package androidx.wear.watchface.complications.datasource.samples
 
+import android.graphics.drawable.Icon
 import androidx.wear.watchface.complications.data.ComplicationData
 import androidx.wear.watchface.complications.data.ComplicationText
 import androidx.wear.watchface.complications.data.ComplicationType
 import androidx.wear.watchface.complications.data.LongTextComplicationData
+import androidx.wear.watchface.complications.data.MonochromaticImage
+import androidx.wear.watchface.complications.data.MonochromaticImageComplicationData
 import androidx.wear.watchface.complications.data.ShortTextComplicationData
 import androidx.wear.watchface.complications.datasource.ComplicationDataSourceService
 import androidx.wear.watchface.complications.datasource.ComplicationRequest
@@ -45,6 +48,12 @@
                             ComplicationText.EMPTY
                         )
                         .build()
+                ComplicationType.MONOCHROMATIC_IMAGE -> MonochromaticImageComplicationData.Builder(
+                    MonochromaticImage.Builder(
+                        Icon.createWithResource(this, R.drawable.heart)
+                    ).build(),
+                    ComplicationText.EMPTY
+                ).build()
                 else -> null
             }
         )
@@ -58,6 +67,12 @@
             ComplicationType.LONG_TEXT ->
                 LongTextComplicationData.Builder(plainText("hello 123"), ComplicationText.EMPTY)
                     .build()
+            ComplicationType.MONOCHROMATIC_IMAGE -> MonochromaticImageComplicationData.Builder(
+                MonochromaticImage.Builder(
+                    Icon.createWithResource(this, R.drawable.heart)
+                ).build(),
+                ComplicationText.EMPTY
+            ).build()
             else -> null
         }
 }
diff --git a/wear/watchface/watchface-complications-data-source-samples/src/main/res/drawable/heart.xml b/wear/watchface/watchface-complications-data-source-samples/src/main/res/drawable/heart.xml
new file mode 100644
index 0000000..57b5fd1
--- /dev/null
+++ b/wear/watchface/watchface-complications-data-source-samples/src/main/res/drawable/heart.xml
@@ -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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
+</vector>
\ No newline at end of file
diff --git a/wear/watchface/watchface-complications-data-source/api/current.txt b/wear/watchface/watchface-complications-data-source/api/current.txt
index d3e0caf..fde3864 100644
--- a/wear/watchface/watchface-complications-data-source/api/current.txt
+++ b/wear/watchface/watchface-complications-data-source/api/current.txt
@@ -6,6 +6,7 @@
     method public abstract androidx.wear.watchface.complications.data.ComplicationData? getPreviewData(androidx.wear.watchface.complications.data.ComplicationType type);
     method public final android.os.IBinder? onBind(android.content.Intent intent);
     method @MainThread public void onComplicationActivated(int complicationInstanceId, androidx.wear.watchface.complications.data.ComplicationType type);
+    method @MainThread @kotlin.jvm.Throws(exceptionClasses=Throwable::class) public void onComplicationDataError(Throwable throwable) throws java.lang.Throwable;
     method @MainThread public void onComplicationDeactivated(int complicationInstanceId);
     method @MainThread public abstract void onComplicationRequest(androidx.wear.watchface.complications.datasource.ComplicationRequest request, androidx.wear.watchface.complications.datasource.ComplicationDataSourceService.ComplicationRequestListener listener);
     method @MainThread public void onStartImmediateComplicationRequests(int complicationInstanceId);
@@ -20,6 +21,7 @@
     field public static final String METADATA_KEY_DATA_SOURCE_DEFAULT_CONFIG_SUPPORTED = "androidx.watchface.complications.datasource.DEFAULT_CONFIG_SUPPORTED";
     field public static final String METADATA_KEY_IMMEDIATE_UPDATE_PERIOD_MILLISECONDS = "androidx.wear.watchface.complications.data.source.IMMEDIATE_UPDATE_PERIOD_MILLISECONDS";
     field public static final String METADATA_KEY_SAFE_WATCH_FACES = "android.support.wearable.complications.SAFE_WATCH_FACES";
+    field public static final String METADATA_KEY_SAFE_WATCH_FACE_SUPPORTED_TYPES = "androidx.wear.watchface.complications.datasource.SAFE_WATCH_FACE_SUPPORTED_TYPES";
     field public static final String METADATA_KEY_SUPPORTED_TYPES = "android.support.wearable.complications.SUPPORTED_TYPES";
     field public static final String METADATA_KEY_UPDATE_PERIOD_SECONDS = "android.support.wearable.complications.UPDATE_PERIOD_SECONDS";
   }
diff --git a/wear/watchface/watchface-complications-data-source/api/public_plus_experimental_current.txt b/wear/watchface/watchface-complications-data-source/api/public_plus_experimental_current.txt
index d3e0caf..fde3864 100644
--- a/wear/watchface/watchface-complications-data-source/api/public_plus_experimental_current.txt
+++ b/wear/watchface/watchface-complications-data-source/api/public_plus_experimental_current.txt
@@ -6,6 +6,7 @@
     method public abstract androidx.wear.watchface.complications.data.ComplicationData? getPreviewData(androidx.wear.watchface.complications.data.ComplicationType type);
     method public final android.os.IBinder? onBind(android.content.Intent intent);
     method @MainThread public void onComplicationActivated(int complicationInstanceId, androidx.wear.watchface.complications.data.ComplicationType type);
+    method @MainThread @kotlin.jvm.Throws(exceptionClasses=Throwable::class) public void onComplicationDataError(Throwable throwable) throws java.lang.Throwable;
     method @MainThread public void onComplicationDeactivated(int complicationInstanceId);
     method @MainThread public abstract void onComplicationRequest(androidx.wear.watchface.complications.datasource.ComplicationRequest request, androidx.wear.watchface.complications.datasource.ComplicationDataSourceService.ComplicationRequestListener listener);
     method @MainThread public void onStartImmediateComplicationRequests(int complicationInstanceId);
@@ -20,6 +21,7 @@
     field public static final String METADATA_KEY_DATA_SOURCE_DEFAULT_CONFIG_SUPPORTED = "androidx.watchface.complications.datasource.DEFAULT_CONFIG_SUPPORTED";
     field public static final String METADATA_KEY_IMMEDIATE_UPDATE_PERIOD_MILLISECONDS = "androidx.wear.watchface.complications.data.source.IMMEDIATE_UPDATE_PERIOD_MILLISECONDS";
     field public static final String METADATA_KEY_SAFE_WATCH_FACES = "android.support.wearable.complications.SAFE_WATCH_FACES";
+    field public static final String METADATA_KEY_SAFE_WATCH_FACE_SUPPORTED_TYPES = "androidx.wear.watchface.complications.datasource.SAFE_WATCH_FACE_SUPPORTED_TYPES";
     field public static final String METADATA_KEY_SUPPORTED_TYPES = "android.support.wearable.complications.SUPPORTED_TYPES";
     field public static final String METADATA_KEY_UPDATE_PERIOD_SECONDS = "android.support.wearable.complications.UPDATE_PERIOD_SECONDS";
   }
diff --git a/wear/watchface/watchface-complications-data-source/api/restricted_current.txt b/wear/watchface/watchface-complications-data-source/api/restricted_current.txt
index d3e0caf..fde3864 100644
--- a/wear/watchface/watchface-complications-data-source/api/restricted_current.txt
+++ b/wear/watchface/watchface-complications-data-source/api/restricted_current.txt
@@ -6,6 +6,7 @@
     method public abstract androidx.wear.watchface.complications.data.ComplicationData? getPreviewData(androidx.wear.watchface.complications.data.ComplicationType type);
     method public final android.os.IBinder? onBind(android.content.Intent intent);
     method @MainThread public void onComplicationActivated(int complicationInstanceId, androidx.wear.watchface.complications.data.ComplicationType type);
+    method @MainThread @kotlin.jvm.Throws(exceptionClasses=Throwable::class) public void onComplicationDataError(Throwable throwable) throws java.lang.Throwable;
     method @MainThread public void onComplicationDeactivated(int complicationInstanceId);
     method @MainThread public abstract void onComplicationRequest(androidx.wear.watchface.complications.datasource.ComplicationRequest request, androidx.wear.watchface.complications.datasource.ComplicationDataSourceService.ComplicationRequestListener listener);
     method @MainThread public void onStartImmediateComplicationRequests(int complicationInstanceId);
@@ -20,6 +21,7 @@
     field public static final String METADATA_KEY_DATA_SOURCE_DEFAULT_CONFIG_SUPPORTED = "androidx.watchface.complications.datasource.DEFAULT_CONFIG_SUPPORTED";
     field public static final String METADATA_KEY_IMMEDIATE_UPDATE_PERIOD_MILLISECONDS = "androidx.wear.watchface.complications.data.source.IMMEDIATE_UPDATE_PERIOD_MILLISECONDS";
     field public static final String METADATA_KEY_SAFE_WATCH_FACES = "android.support.wearable.complications.SAFE_WATCH_FACES";
+    field public static final String METADATA_KEY_SAFE_WATCH_FACE_SUPPORTED_TYPES = "androidx.wear.watchface.complications.datasource.SAFE_WATCH_FACE_SUPPORTED_TYPES";
     field public static final String METADATA_KEY_SUPPORTED_TYPES = "android.support.wearable.complications.SUPPORTED_TYPES";
     field public static final String METADATA_KEY_UPDATE_PERIOD_SECONDS = "android.support.wearable.complications.UPDATE_PERIOD_SECONDS";
   }
diff --git a/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt b/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt
index 45ba95a..01f5e4f 100644
--- a/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt
+++ b/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt
@@ -22,12 +22,12 @@
 import android.content.ComponentName
 import android.content.Intent
 import android.os.Build
+import android.os.Bundle
 import android.os.Handler
 import android.os.IBinder
 import android.os.Looper
 import android.os.RemoteException
 import android.support.wearable.complications.ComplicationData as WireComplicationData
-import android.os.Bundle
 import android.support.wearable.complications.ComplicationProviderInfo
 import android.support.wearable.complications.IComplicationManager
 import android.support.wearable.complications.IComplicationProvider
@@ -45,12 +45,14 @@
 import androidx.wear.watchface.complications.data.TimeRange
 import androidx.wear.watchface.complications.datasource.ComplicationDataSourceService.Companion.METADATA_KEY_IMMEDIATE_UPDATE_PERIOD_MILLISECONDS
 import androidx.wear.watchface.complications.datasource.ComplicationDataSourceService.ComplicationRequestListener
+import java.time.Duration
 import java.util.concurrent.CountDownLatch
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.android.asCoroutineDispatcher
-import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.withTimeout
 
 /**
  * Data associated with complication request in
@@ -199,6 +201,29 @@
  * <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
  * android:value="RANGED_VALUE,SHORT_TEXT,ICON"/>
  * ```
+ *
+ * - A provider can choose to trust one or more watch faces by including the following in its
+ * manifest entry:
+ * ```
+ * <meta-data android:name="android.support.wearable.complications.SAFE_WATCH_FACES
+ * android:value="com.pkg1/com.trusted.wf1,com.pkg2/com.trusted.wf2"/>
+ * ```
+ * The listed watch faces will not need
+ * 'com.google.android.wearable.permission.RECEIVE_COMPLICATION_DATA' in order to receive
+ * complications from this provider. Also the provider may choose to serve different types to
+ * safe watch faces by including the following in its manifest:
+ *
+ * ```
+ * <meta-data android:name=
+ *     "androidx.wear.watchface.complications.datasource.SAFE_WATCH_FACE_SUPPORTED_TYPES"
+ *      android:value="ICON"/>
+ * ```
+ *
+ * In addition the provider can learn if a request is for a safe watchface by examining
+ * [ComplicationRequest.isForSafeWatchFace]. Note SAFE_WATCH_FACE_SUPPORTED_TYPES and
+ * isForSafeWatchFace are gated behind the privileged permission
+ * `com.google.wear.permission.GET_IS_FOR_SAFE_WATCH_FACE`.
+ *
  * - A ComplicationDataSourceService should include a `meta-data` tag with
  *   android.support.wearable.complications.UPDATE_PERIOD_SECONDS its manifest entry. The value of
  *   this tag is the number of seconds the complication data source would like to elapse between
@@ -274,11 +299,8 @@
  */
 public abstract class ComplicationDataSourceService : Service() {
     private var wrapper: IComplicationProviderWrapper? = null
-    private var lastExpressionEvaluator: ComplicationDataExpressionEvaluator? = null
+    private var lastExpressionEvaluationJob: Job? = null
     internal val mainThreadHandler by lazy { createMainThreadHandler() }
-    internal val mainThreadCoroutineScope by lazy {
-        CoroutineScope(mainThreadHandler.asCoroutineDispatcher())
-    }
 
     /**
      * Equivalent to [Build.VERSION.SDK_INT], but allows override for any platform-independent
@@ -307,7 +329,7 @@
 
     override fun onDestroy() {
         super.onDestroy()
-        lastExpressionEvaluator?.close()
+        lastExpressionEvaluationJob?.cancel()
     }
 
     /**
@@ -355,6 +377,32 @@
     )
 
     /**
+     * Called when sending [ComplicationData] (through [ComplicationRequestListener]) threw an
+     * error.
+     *
+     * This can be due to expression evaluation error, a [RemoteException], or something else.
+     *
+     * It's recommended to override this rather than catch exceptions directly on the
+     * [ComplicationRequestListener] call, because the implementation can be asynchronous.
+     *
+     * When the [ComplicationData] contains an expression, that expression is evaluated locally for
+     * backward compatibility with older platforms. This evaluation is asynchronous, which means an
+     * exception will not be thrown synchronously.
+     *
+     * IMPORTANT: If not overridden, the error will be propagated to the main thread (and
+     * potentially crash the process).
+     *
+     * @throws Throwable Thrown exception will be propagated to the main thread (and potentially
+     *   crash the process).
+     */
+    @MainThread
+    @SuppressWarnings("GenericException") // Error propagation.
+    @Throws(Throwable::class)
+    public open fun onComplicationDataError(throwable: Throwable) {
+        throw throwable
+    }
+
+    /**
      * A request for representative preview data for the complication, for use in the editor UI.
      * Preview data is assumed to be static per type. E.g. for a complication that displays the date
      * and time of an event, rather than returning the real time it should return a fixed date and
@@ -378,6 +426,16 @@
          * Sends the [ComplicationData] to the system. If null is passed then any previous
          * complication data will not be overwritten. Can be called on any thread. Should only be
          * called once. Note this is mutually exclusive with [onComplicationDataTimeline].
+         *
+         * Errors sending the data are provided to [onComplicationDataError], which re-throws the
+         * error, potentially crashing the main thread.
+         *
+         * As the implementation of [onComplicationData] may be asynchronous, it's better to
+         * override [onComplicationDataError] rather than catch exceptions thrown from this call.
+         *
+         * When the [ComplicationData] contains an expression, that expression is evaluated locally
+         * for backward compatibility with older platforms. This evaluation is asynchronous, which
+         * means an exception will not be thrown synchronously.
          */
         @Throws(RemoteException::class)
         public fun onComplicationData(complicationData: ComplicationData?)
@@ -387,6 +445,16 @@
          * complication data will not be overwritten. Can be called on any thread. Should only be
          * called once. Note this is mutually exclusive with [onComplicationData]. Note only
          * [ComplicationDataTimeline.defaultComplicationData] is supported by older watch faces .
+         *
+         * Errors sending the data are provided to [onComplicationDataError], which re-throws the
+         * error, potentially crashing the main thread.
+         *
+         * As the implementation of [onComplicationDataTimeline] may be asynchronous, it's better to
+         * override [onComplicationDataError] rather than catch exceptions thrown from this call.
+         *
+         * When the [ComplicationData] contains an expression, that expression is evaluated locally
+         * for backward compatibility with older platforms. This evaluation is asynchronous, which
+         * means an exception will not be thrown synchronously.
          */
         // TODO(alexclarke): Plumb a capability bit so the developers can know if timelines are
         // supported by the watch face.
@@ -441,12 +509,7 @@
     private inner class IComplicationProviderWrapper : IComplicationProvider.Stub() {
         @SuppressLint("SyntheticAccessor")
         override fun onUpdate(complicationInstanceId: Int, type: Int, manager: IBinder) {
-            onUpdate2(
-                complicationInstanceId,
-                type,
-                manager,
-                bundle = null
-            )
+            onUpdate2(complicationInstanceId, type, manager, bundle = null)
         }
 
         @SuppressLint("SyntheticAccessor")
@@ -456,10 +519,12 @@
             manager: IBinder,
             bundle: Bundle?
         ) {
-            val isForSafeWatchFace = bundle?.getInt(
-                IComplicationProvider.BUNDLE_KEY_IS_SAFE_FOR_WATCHFACE,
-                TargetWatchFaceSafety.UNKNOWN
-            ) ?: TargetWatchFaceSafety.UNKNOWN
+            val isForSafeWatchFace =
+                bundle?.getInt(
+                    IComplicationProvider.BUNDLE_KEY_IS_SAFE_FOR_WATCHFACE,
+                    TargetWatchFaceSafety.UNKNOWN
+                )
+                    ?: TargetWatchFaceSafety.UNKNOWN
             val expectedDataType = fromWireType(type)
             val iComplicationManager = IComplicationManager.Stub.asInterface(manager)
             mainThreadHandler.post {
@@ -557,27 +622,43 @@
                         }
 
                         private fun WireComplicationData?.evaluateAndUpdateManager() {
-                            lastExpressionEvaluator?.close() // Cancelling any previous evaluation.
+                            // Cancelling any previous evaluation.
+                            lastExpressionEvaluationJob?.cancel()
                             if (
-                                // Will be evaluated by the platform.
-                                // TODO(b/257422920): Set this to the exact platform version.
-                                wearPlatformVersion >= Build.VERSION_CODES.TIRAMISU + 1 ||
-                                    // When no update is needed, the data is going to be null.
-                                    this == null
+                                // When no update is needed, the data is going to be null.
+                                this == null ||
+                                    // Will be evaluated by the platform.
+                                    // TODO(b/257422920): Set this to the exact platform version.
+                                    wearPlatformVersion >= Build.VERSION_CODES.TIRAMISU + 1 ||
+                                    // Avoid async evaluation to prevent backward incompatibility
+                                    // with try/catch.
+                                    !hasExpression(this)
                             ) {
-                                iComplicationManager.updateComplicationData(
-                                    complicationInstanceId,
-                                    this
-                                )
+                                try {
+                                    iComplicationManager.updateComplicationData(
+                                        complicationInstanceId,
+                                        this
+                                    )
+                                } catch (e: Throwable) {
+                                    onComplicationDataError(e)
+                                }
                                 return
                             }
-                            lastExpressionEvaluator =
-                                ComplicationDataExpressionEvaluator(this).apply {
-                                    init()
-                                    listenAndUpdateManager(
-                                        iComplicationManager,
-                                        complicationInstanceId,
-                                    )
+                            lastExpressionEvaluationJob =
+                                CoroutineScope(Dispatchers.Main).launch {
+                                    // Doing one-off evaluation, the service will be re-invoked.
+                                    try {
+                                        iComplicationManager.updateComplicationData(
+                                            complicationInstanceId,
+                                            withTimeout(EXPRESSION_EVALUATION_TIMEOUT.toMillis()) {
+                                                ComplicationDataExpressionEvaluator()
+                                                    .evaluate(this@evaluateAndUpdateManager)
+                                                    .first()
+                                            }
+                                        )
+                                    } catch (e: Throwable) {
+                                        onComplicationDataError(e)
+                                    }
                                 }
                         }
                     }
@@ -585,20 +666,6 @@
             }
         }
 
-        private fun ComplicationDataExpressionEvaluator.listenAndUpdateManager(
-            iComplicationManager: IComplicationManager,
-            complicationInstanceId: Int,
-        ) {
-            mainThreadCoroutineScope.launch {
-                // Doing one-off evaluation, the service will be re-invoked.
-                iComplicationManager.updateComplicationData(
-                    complicationInstanceId,
-                    data.filterNotNull().first()
-                )
-                close()
-            }
-        }
-
         @SuppressLint("SyntheticAccessor")
         override fun onComplicationDeactivated(complicationInstanceId: Int) {
             mainThreadHandler.post {
@@ -664,21 +731,19 @@
         }
 
         override fun onSynchronousComplicationRequest(complicationInstanceId: Int, type: Int) =
-            onSynchronousComplicationRequest2(
-                complicationInstanceId,
-                type,
-                bundle = null
-            )
+            onSynchronousComplicationRequest2(complicationInstanceId, type, bundle = null)
 
         override fun onSynchronousComplicationRequest2(
             complicationInstanceId: Int,
             type: Int,
             bundle: Bundle?
         ): android.support.wearable.complications.ComplicationData? {
-            val isForSafeWatchFace = bundle?.getInt(
-                IComplicationProvider.BUNDLE_KEY_IS_SAFE_FOR_WATCHFACE,
-                TargetWatchFaceSafety.UNKNOWN
-            ) ?: TargetWatchFaceSafety.UNKNOWN
+            val isForSafeWatchFace =
+                bundle?.getInt(
+                    IComplicationProvider.BUNDLE_KEY_IS_SAFE_FOR_WATCHFACE,
+                    TargetWatchFaceSafety.UNKNOWN
+                )
+                    ?: TargetWatchFaceSafety.UNKNOWN
             val expectedDataType = fromWireType(type)
             val complicationType = fromWireType(type)
             val latch = CountDownLatch(1)
@@ -786,6 +851,20 @@
             "android.support.wearable.complications.SUPPORTED_TYPES"
 
         /**
+         * Metadata key used to declare supported complication types for safe watch faces.
+         *
+         * Gated behind the privileged permission
+         * `com.google.wear.permission.GET_IS_FOR_SAFE_WATCH_FACE', this overrides the
+         * [METADATA_KEY_SUPPORTED_TYPES] list for 'safe' watch faces. I.e.
+         * watch faces in the [METADATA_KEY_SAFE_WATCH_FACES] metadata list.
+         *
+         * This means for example trusted watch faces could receive [ComplicationType.SHORT_TEXT]
+         * and untrusted ones [ComplicationType.MONOCHROMATIC_IMAGE].
+         */
+        public const val METADATA_KEY_SAFE_WATCH_FACE_SUPPORTED_TYPES: String =
+            "androidx.wear.watchface.complications.datasource.SAFE_WATCH_FACE_SUPPORTED_TYPES"
+
+        /**
          * Metadata key used to declare the requested frequency of update requests.
          *
          * A [ComplicationDataSourceService] should include a `meta-data` tag with this name in its
@@ -938,5 +1017,8 @@
         @SuppressLint("ActionValue")
         public const val EXTRA_CONFIG_DATA_SOURCE_COMPONENT: String =
             "android.support.wearable.complications.EXTRA_CONFIG_PROVIDER_COMPONENT"
+
+        /** How long to allow expression evaluation to execute. */
+        private val EXPRESSION_EVALUATION_TIMEOUT = Duration.ofSeconds(10)
     }
 }
diff --git a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.kt b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.kt
index d8a518d..6435ce6 100644
--- a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.kt
+++ b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.kt
@@ -53,6 +53,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 import org.robolectric.annotation.internal.DoNotInstrument
 import org.robolectric.shadows.ShadowLog
 import org.robolectric.shadows.ShadowLooper.runUiThreadTasks
@@ -74,8 +75,11 @@
                 complicationSlotId: Int,
                 data: WireComplicationData?
             ) {
-                mRemoteManager.updateComplicationData(complicationSlotId, data)
-                mUpdateComplicationDataLatch.countDown()
+                try {
+                    mRemoteManager.updateComplicationData(complicationSlotId, data)
+                } finally {
+                    mUpdateComplicationDataLatch.countDown()
+                }
             }
         }
     private lateinit var mProvider: IComplicationProvider.Stub
@@ -109,6 +113,9 @@
         /** Last type provided to [previewData]. */
         var lastPreviewType: ComplicationType? = null
 
+        /** Last error provided to [onComplicationDataError]. */
+        var lastSendError: Throwable? = null
+
         override var wearPlatformVersion = Build.VERSION.SDK_INT
 
         override fun createMainThreadHandler(): Handler = mPretendMainThreadHandler
@@ -129,6 +136,10 @@
             }
         }
 
+        override fun onComplicationDataError(throwable: Throwable) {
+            lastSendError = throwable
+        }
+
         override fun getPreviewData(type: ComplicationType): ComplicationData? {
             lastPreviewType = type
             return previewData
@@ -332,6 +343,26 @@
     }
 
     @Test
+    fun testOnComplicationDataError() {
+        val data =
+            LongTextComplicationData.Builder(
+                    PlainComplicationText.Builder("hello").build(),
+                    ComplicationText.EMPTY
+                )
+                .build()
+        mService.responseData = data
+        val id = 123
+        val error = RuntimeException("failed!")
+        whenever(mRemoteManager.updateComplicationData(id, data.asWireComplicationData()))
+            .thenThrow(error)
+
+        mProvider.onUpdate(id, ComplicationType.LONG_TEXT.toWireComplicationType(), mLocalManager)
+        runUiThreadTasksWhileAwaitingDataLatch(1000)
+
+        assertThat(mService.lastSendError).isEqualTo(error)
+    }
+
+    @Test
     fun testGetComplicationPreviewData() {
         mService.previewData =
             LongTextComplicationData.Builder(
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluator.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluator.kt
index a4cb503..6e50a93 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluator.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluator.kt
@@ -19,8 +19,8 @@
 import android.icu.util.ULocale
 import android.support.wearable.complications.ComplicationData as WireComplicationData
 import android.support.wearable.complications.ComplicationText as WireComplicationText
+import androidx.annotation.MainThread
 import androidx.annotation.RestrictTo
-import androidx.core.util.Consumer
 import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat
 import androidx.wear.protolayout.expression.pipeline.BoundDynamicType
 import androidx.wear.protolayout.expression.pipeline.DynamicTypeEvaluator
@@ -33,15 +33,16 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.asCoroutineDispatcher
-import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.currentCoroutineContext
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emitAll
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.invoke
+import kotlinx.coroutines.launch
 
 /**
  * Evaluates a [WireComplicationData] with
@@ -51,227 +52,119 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 class ComplicationDataExpressionEvaluator(
-    val unevaluatedData: WireComplicationData,
     private val stateStore: ObservableStateStore = ObservableStateStore(emptyMap()),
     private val sensorGateway: SensorGateway? = null,
     private val keepExpression: Boolean = false,
-) : AutoCloseable {
+) {
     /**
-     * Java compatibility class for [ComplicationDataExpressionEvaluator].
+     * Returns a [Flow] that provides the evaluated [WireComplicationData].
      *
-     * Unlike [data], [listener] is not invoked until there is a value (until [data] is non-null).
+     * The expression is evaluated _separately_ on each flow collection.
      */
-    class Compat
-    internal constructor(
-        val unevaluatedData: WireComplicationData,
-        private val listener: Consumer<WireComplicationData>,
-        stateStore: ObservableStateStore,
-        sensorGateway: SensorGateway?,
-        keepExpression: Boolean,
-    ) : AutoCloseable {
-        private val evaluator =
-            ComplicationDataExpressionEvaluator(
-                unevaluatedData,
-                stateStore,
-                sensorGateway,
-                keepExpression,
-            )
-
-        /** Builder for [ComplicationDataExpressionEvaluator.Compat]. */
-        class Builder(
-            private val unevaluatedData: WireComplicationData,
-            private val listener: Consumer<WireComplicationData>,
-        ) {
-            private var stateStore: ObservableStateStore = ObservableStateStore(emptyMap())
-            private var sensorGateway: SensorGateway? = null
-            private var keepExpression: Boolean = false
-
-            fun setStateStore(value: ObservableStateStore) = apply { stateStore = value }
-            fun setSensorGateway(value: SensorGateway?) = apply { sensorGateway = value }
-            fun setKeepExpression(value: Boolean) = apply { keepExpression = value }
-
-            fun build() =
-                Compat(unevaluatedData, listener, stateStore, sensorGateway, keepExpression)
+    fun evaluate(unevaluatedData: WireComplicationData) =
+        flow<WireComplicationData> {
+            val state: MutableStateFlow<State> = unevaluatedData.buildState()
+            state.value.use {
+                val evaluatedData: Flow<WireComplicationData> =
+                    state
+                        .mapNotNull {
+                            when {
+                                // Emitting INVALID_DATA if there's an invalid receiver.
+                                it.invalidReceivers.isNotEmpty() -> INVALID_DATA
+                                // Emitting the data if all pending receivers are done and all
+                                // pre-updates are satisfied.
+                                it.pendingReceivers.isEmpty() && it.preUpdateCount == 0 -> it.data
+                                // Skipping states that are not ready for be emitted.
+                                else -> null
+                            }
+                        }
+                        .distinctUntilChanged()
+                emitAll(evaluatedData)
+            }
         }
 
-        /**
-         * @see ComplicationDataExpressionEvaluator.init, [executor] is used in place of
-         *   `coroutineScope`.
-         */
-        fun init(executor: Executor) {
-            evaluator.init(CoroutineScope(executor.asCoroutineDispatcher()))
-            evaluator.data
-                .filterNotNull()
-                .onEach(listener::accept)
-                .launchIn(CoroutineScope(executor.asCoroutineDispatcher()))
-        }
-
-        /** @see ComplicationDataExpressionEvaluator.close */
-        override fun close() {
-            evaluator.close()
-        }
-    }
-
-    private val _data = MutableStateFlow<WireComplicationData?>(null)
-
-    /**
-     * The evaluated data, or `null` if it wasn't evaluated yet, or [NoDataComplicationData] if it
-     * wasn't possible to evaluate the [unevaluatedData].
-     */
-    val data: StateFlow<WireComplicationData?> = _data.asStateFlow()
-
-    @Volatile // In case init() and close() are called on different threads.
-    private lateinit var evaluator: DynamicTypeEvaluator
-    private val state = MutableStateFlow(State(unevaluatedData))
-
-    /**
-     * Parses the expression and starts blocking evaluation.
-     *
-     * This needs to be called exactly once.
-     *
-     * @param coroutineScope used for background evaluation
-     */
-    fun init(coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.Main)) {
-        // Add all the receivers before we start binding them because binding can synchronously
-        // trigger the receiver, which would update the data before all the fields are evaluated.
-        initStateReceivers(coroutineScope)
-        initEvaluator()
-        monitorState(coroutineScope)
-    }
-
-    /**
-     * Stops evaluation.
-     *
-     * [data] will not change after this is called.
-     */
-    override fun close() {
-        for (receiver in state.value.all) receiver.close()
-        if (this::evaluator.isInitialized) evaluator.close()
-    }
-
-    /** Adds [ComplicationEvaluationResultReceiver]s to [state]. */
-    private fun initStateReceivers(coroutineScope: CoroutineScope) {
-        val receivers = mutableSetOf<ComplicationEvaluationResultReceiver<out Any>>()
-
-        if (unevaluatedData.hasRangedValueExpression()) {
-            unevaluatedData.rangedValueExpression
-                ?.buildReceiver(
-                    coroutineScope,
+    private suspend fun WireComplicationData.buildState() =
+        MutableStateFlow(State(this)).apply {
+            if (hasRangedValueExpression()) {
+                addReceiver(
+                    rangedValueExpression,
                     expressionTrimmer = { setRangedValueExpression(null) },
                     setter = { setRangedValue(it) },
                 )
-                ?.let { receivers += it }
-        }
-        if (unevaluatedData.hasLongText()) {
-            unevaluatedData.longText
-                ?.buildReceiver(coroutineScope) { setLongText(it) }
-                ?.let { receivers += it }
-        }
-        if (unevaluatedData.hasLongTitle()) {
-            unevaluatedData.longTitle
-                ?.buildReceiver(coroutineScope) { setLongTitle(it) }
-                ?.let { receivers += it }
-        }
-        if (unevaluatedData.hasShortText()) {
-            unevaluatedData.shortText
-                ?.buildReceiver(coroutineScope) { setShortText(it) }
-                ?.let { receivers += it }
-        }
-        if (unevaluatedData.hasShortTitle()) {
-            unevaluatedData.shortTitle
-                ?.buildReceiver(coroutineScope) { setShortTitle(it) }
-                ?.let { receivers += it }
-        }
-        if (unevaluatedData.hasContentDescription()) {
-            unevaluatedData.contentDescription
-                ?.buildReceiver(coroutineScope) { setContentDescription(it) }
-                ?.let { receivers += it }
+            }
+            if (hasLongText()) addReceiver(longText) { setLongText(it) }
+            if (hasLongTitle()) addReceiver(longTitle) { setLongTitle(it) }
+            if (hasShortText()) addReceiver(shortText) { setShortText(it) }
+            if (hasShortTitle()) addReceiver(shortTitle) { setShortTitle(it) }
+            if (hasContentDescription()) {
+                addReceiver(contentDescription) { setContentDescription(it) }
+            }
+            // Add all the receivers before we start binding them because binding can synchronously
+            // trigger the receiver, which would update the data before all the fields are
+            // evaluated.
+            value.initEvaluation()
         }
 
-        state.value = State(unevaluatedData, receivers)
-    }
-
-    private fun DynamicFloat.buildReceiver(
-        coroutineScope: CoroutineScope,
+    private suspend fun MutableStateFlow<State>.addReceiver(
+        expression: DynamicFloat?,
         expressionTrimmer: WireComplicationData.Builder.() -> WireComplicationData.Builder,
         setter: WireComplicationData.Builder.(Float) -> WireComplicationData.Builder,
-    ) =
-        ComplicationEvaluationResultReceiver(
-            setter = {
-                if (!keepExpression) expressionTrimmer(this)
-                setter(this, it)
-            },
-            binder = { receiver ->
-                evaluator.bind(
-                    this@buildReceiver,
-                    coroutineScope.coroutineContext.asExecutor(),
-                    receiver
+    ) {
+        expression ?: return
+        val executor = currentCoroutineContext().asExecutor()
+        update { state ->
+            state.withPendingReceiver(
+                ComplicationEvaluationResultReceiver<Float>(
+                    this,
+                    setter = { value ->
+                        if (!keepExpression) expressionTrimmer(this)
+                        setter(this, value)
+                    },
+                    binder = { receiver -> value.evaluator.bind(expression, executor, receiver) },
                 )
-            },
-        )
-
-    private fun WireComplicationText.buildReceiver(
-        coroutineScope: CoroutineScope,
-        setter: WireComplicationData.Builder.(WireComplicationText) -> WireComplicationData.Builder,
-    ) =
-        expression?.let { expression ->
-            ComplicationEvaluationResultReceiver<String>(
-                setter = {
-                    setter(
-                        if (keepExpression) {
-                            WireComplicationText(it, expression)
-                        } else {
-                            WireComplicationText(it)
-                        }
-                    )
-                },
-                binder = { receiver ->
-                    evaluator.bind(
-                        expression,
-                        ULocale.getDefault(),
-                        coroutineScope.coroutineContext.asExecutor(),
-                        receiver
-                    )
-                },
             )
         }
-
-    /** Initializes the internal [DynamicTypeEvaluator] if there are pending receivers. */
-    private fun initEvaluator() {
-        if (state.value.pending.isEmpty()) return
-        evaluator =
-            DynamicTypeEvaluator(
-                /* platformDataSourcesInitiallyEnabled = */ true,
-                sensorGateway,
-                stateStore,
-            )
-        for (receiver in state.value.pending) receiver.bind()
-        for (receiver in state.value.pending) receiver.startEvaluation()
-        evaluator.enablePlatformDataSources()
     }
 
-    /** Monitors [state] changes and updates [data]. */
-    private fun monitorState(coroutineScope: CoroutineScope) {
-        state
-            .onEach {
-                if (it.invalid.isNotEmpty()) _data.value = INVALID_DATA
-                else if (it.pending.isEmpty() && it.preUpdateCount == 0) _data.value = it.data
-            }
-            .launchIn(coroutineScope)
+    private suspend fun MutableStateFlow<State>.addReceiver(
+        text: WireComplicationText?,
+        setter: WireComplicationData.Builder.(WireComplicationText) -> WireComplicationData.Builder,
+    ) {
+        val expression = text?.expression ?: return
+        val executor = currentCoroutineContext().asExecutor()
+        update {
+            it.withPendingReceiver(
+                ComplicationEvaluationResultReceiver<String>(
+                    this,
+                    setter = { value ->
+                        setter(
+                            if (keepExpression) {
+                                WireComplicationText(value, expression)
+                            } else {
+                                WireComplicationText(value)
+                            }
+                        )
+                    },
+                    binder = { receiver ->
+                        value.evaluator.bind(expression, ULocale.getDefault(), executor, receiver)
+                    },
+                )
+            )
+        }
     }
 
     /**
      * Holds the state of the continuously evaluated [WireComplicationData] and the various
      * [ComplicationEvaluationResultReceiver] that are evaluating it.
      */
-    private class State(
+    private inner class State(
         val data: WireComplicationData,
-        val pending: Set<ComplicationEvaluationResultReceiver<out Any>> = setOf(),
-        val invalid: Set<ComplicationEvaluationResultReceiver<out Any>> = setOf(),
-        val complete: Set<ComplicationEvaluationResultReceiver<out Any>> = setOf(),
+        val pendingReceivers: Set<ComplicationEvaluationResultReceiver<out Any>> = setOf(),
+        val invalidReceivers: Set<ComplicationEvaluationResultReceiver<out Any>> = setOf(),
+        val completeReceivers: Set<ComplicationEvaluationResultReceiver<out Any>> = setOf(),
         val preUpdateCount: Int = 0,
-    ) {
-        val all = pending + invalid + complete
+    ) : AutoCloseable {
+        lateinit var evaluator: DynamicTypeEvaluator
 
         init {
             require(preUpdateCount >= 0) {
@@ -279,38 +172,92 @@
             }
         }
 
-        fun withPreUpdate() =
-            State(
-                data,
-                pending = pending,
-                invalid = invalid,
-                complete = complete,
-                preUpdateCount + 1,
+        fun withPendingReceiver(receiver: ComplicationEvaluationResultReceiver<out Any>) =
+            copy(pendingReceivers = pendingReceivers + receiver)
+
+        fun withPreUpdate() = copy(preUpdateCount = preUpdateCount + 1)
+
+        fun withInvalidReceiver(receiver: ComplicationEvaluationResultReceiver<out Any>) =
+            copy(
+                pendingReceivers = pendingReceivers - receiver,
+                invalidReceivers = invalidReceivers + receiver,
+                completeReceivers = completeReceivers - receiver,
+                preUpdateCount = preUpdateCount - 1,
             )
 
-        fun withInvalid(receiver: ComplicationEvaluationResultReceiver<out Any>) =
-            State(
-                data,
-                pending = pending - receiver,
-                invalid = invalid + receiver,
-                complete = complete - receiver,
-                preUpdateCount - 1,
-            )
-
-        fun withUpdate(
+        fun withUpdatedData(
             data: WireComplicationData,
             receiver: ComplicationEvaluationResultReceiver<out Any>,
         ) =
-            State(
+            copy(
                 data,
-                pending = pending - receiver,
-                invalid = invalid - receiver,
-                complete = complete + receiver,
-                preUpdateCount - 1,
+                pendingReceivers = pendingReceivers - receiver,
+                invalidReceivers = invalidReceivers - receiver,
+                completeReceivers = completeReceivers + receiver,
+                preUpdateCount = preUpdateCount - 1,
+            )
+
+        /**
+         * Initializes the internal [DynamicTypeEvaluator] if there are pending receivers.
+         *
+         * Should be called after all receivers were added.
+         */
+        suspend fun initEvaluation() {
+            if (pendingReceivers.isEmpty()) return
+            require(!this::evaluator.isInitialized) { "initEvaluator must be called exactly once." }
+            evaluator =
+                DynamicTypeEvaluator(
+                    /* platformDataSourcesInitiallyEnabled = */ true,
+                    stateStore,
+                    sensorGateway,
+                )
+            try {
+                for (receiver in pendingReceivers) receiver.bind()
+                // TODO(b/270697243): Remove this invoke once DynamicTypeEvaluator is thread safe.
+                Dispatchers.Main.immediate.invoke {
+                    // These need to be called on the main thread.
+                    for (receiver in pendingReceivers) receiver.startEvaluation()
+                    evaluator.enablePlatformDataSources()
+                }
+            } catch (e: Throwable) {
+                // Cleanup on initialization failure.
+                close()
+                throw e
+            }
+        }
+
+        override fun close() {
+            // TODO(b/270697243): Remove this launch once DynamicTypeEvaluator is thread safe.
+            CoroutineScope(Dispatchers.Main.immediate).launch {
+                // These need to be called on the main thread.
+                for (receiver in pendingReceivers + invalidReceivers + completeReceivers) {
+                    receiver.close()
+                }
+                if (this@State::evaluator.isInitialized) evaluator.close()
+            }
+        }
+
+        private fun copy(
+            data: WireComplicationData = this.data,
+            pendingReceivers: Set<ComplicationEvaluationResultReceiver<out Any>> =
+                this.pendingReceivers,
+            invalidReceivers: Set<ComplicationEvaluationResultReceiver<out Any>> =
+                this.invalidReceivers,
+            completeReceivers: Set<ComplicationEvaluationResultReceiver<out Any>> =
+                this.completeReceivers,
+            preUpdateCount: Int = this.preUpdateCount,
+        ) =
+            State(
+                data = data,
+                pendingReceivers = pendingReceivers,
+                invalidReceivers = invalidReceivers,
+                completeReceivers = completeReceivers,
+                preUpdateCount = preUpdateCount,
             )
     }
 
     private inner class ComplicationEvaluationResultReceiver<T : Any>(
+        private val state: MutableStateFlow<State>,
         private val setter: WireComplicationData.Builder.(T) -> WireComplicationData.Builder,
         private val binder: (ComplicationEvaluationResultReceiver<T>) -> BoundDynamicType,
     ) : DynamicTypeValueReceiver<T>, AutoCloseable {
@@ -321,10 +268,14 @@
             boundDynamicType = binder(this)
         }
 
+        // TODO(b/270697243): Remove this annotation once DynamicTypeEvaluator is thread safe.
+        @MainThread
         fun startEvaluation() {
             boundDynamicType.startEvaluation()
         }
 
+        // TODO(b/270697243): Remove this annotation once DynamicTypeEvaluator is thread safe.
+        @MainThread
         override fun close() {
             boundDynamicType.close()
         }
@@ -335,12 +286,15 @@
 
         override fun onData(newData: T) {
             state.update {
-                it.withUpdate(setter(WireComplicationData.Builder(it.data), newData).build(), this)
+                it.withUpdatedData(
+                    setter(WireComplicationData.Builder(it.data), newData).build(),
+                    this
+                )
             }
         }
 
         override fun onInvalidated() {
-            state.update { it.withInvalid(this) }
+            state.update { it.withInvalidReceiver(this) }
         }
     }
 
@@ -359,5 +313,15 @@
     }
 }
 
-internal fun CoroutineContext.asExecutor(): Executor =
-    (get(ContinuationInterceptor) as CoroutineDispatcher).asExecutor()
+/**
+ * Replacement for CoroutineDispatcher.asExecutor extension due to
+ * https://github.com/Kotlin/kotlinx.coroutines/pull/3683.
+ */
+internal fun CoroutineContext.asExecutor() = Executor { runnable ->
+    val dispatcher = this[ContinuationInterceptor] as CoroutineDispatcher
+    if (dispatcher.isDispatchNeeded(this)) {
+        dispatcher.dispatch(this, runnable)
+    } else {
+        runnable.run()
+    }
+}
diff --git a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluatorTest.kt b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluatorTest.kt
index 279a8b0..dbb6d56 100644
--- a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluatorTest.kt
+++ b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/ComplicationDataExpressionEvaluatorTest.kt
@@ -19,9 +19,6 @@
 import android.support.wearable.complications.ComplicationData as WireComplicationData
 import android.support.wearable.complications.ComplicationText as WireComplicationText
 import android.util.Log
-import androidx.core.content.ContextCompat
-import androidx.core.util.Consumer
-import androidx.test.core.app.ApplicationProvider.getApplicationContext
 import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat
 import androidx.wear.protolayout.expression.DynamicBuilders.DynamicString
 import androidx.wear.protolayout.expression.StateEntryBuilders.StateEntryValue
@@ -33,46 +30,30 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.firstOrNull
 import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.runBlocking
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.kotlin.any
-import org.mockito.kotlin.mock
-import org.mockito.kotlin.never
-import org.mockito.kotlin.times
-import org.mockito.kotlin.verify
 import org.robolectric.shadows.ShadowLog
-import org.robolectric.shadows.ShadowLooper.runUiThreadTasks
 
 @RunWith(SharedRobolectricTestRunner::class)
 class ComplicationDataExpressionEvaluatorTest {
     @get:Rule val expect = Expect.create()
 
-    private val listener = mock<Consumer<WireComplicationData>>()
-
     @Before
     fun setup() {
         ShadowLog.setLoggable("ComplicationData", Log.DEBUG)
     }
 
     @Test
-    fun data_notInitialized_setToNull() {
-        ComplicationDataExpressionEvaluator(DATA_WITH_NO_EXPRESSION).use { evaluator ->
-            assertThat(evaluator.data.value).isNull()
-        }
-    }
+    fun evaluate_noExpression_returnsUnevaluated() = runBlocking {
+        val evaluator = ComplicationDataExpressionEvaluator()
 
-    @Test
-    fun data_noExpression_setToUnevaluated() {
-        ComplicationDataExpressionEvaluator(DATA_WITH_NO_EXPRESSION).use { evaluator ->
-            evaluator.init()
-            runUiThreadTasks()
-
-            assertThat(evaluator.data.value).isEqualTo(DATA_WITH_NO_EXPRESSION)
-        }
+        assertThat(evaluator.evaluate(DATA_WITH_NO_EXPRESSION).firstOrNull())
+            .isEqualTo(DATA_WITH_NO_EXPRESSION)
     }
 
     /**
@@ -209,38 +190,34 @@
     }
 
     @Test
-    fun data_withExpression_setToEvaluated() {
+    fun evaluate_withExpression_returnsEvaluated() = runBlocking {
         for (scenario in DataWithExpressionScenario.values()) {
             // Defensive copy due to in-place evaluation.
             val expressed = WireComplicationData.Builder(scenario.expressed).build()
             val stateStore = ObservableStateStore(mapOf())
-            ComplicationDataExpressionEvaluator(expressed, stateStore).use { evaluator ->
-                val allEvaluations =
-                    evaluator.data
-                        .filterNotNull()
-                        .shareIn(
-                            CoroutineScope(Dispatchers.Main),
-                            SharingStarted.Eagerly,
-                            replay = 10,
-                        )
-                evaluator.init()
-                runUiThreadTasks() // Ensures data sharing started.
+            val evaluator = ComplicationDataExpressionEvaluator(stateStore)
+            val allEvaluations =
+                evaluator
+                    .evaluate(expressed)
+                    .shareIn(
+                        CoroutineScope(Dispatchers.Main.immediate),
+                        SharingStarted.Eagerly,
+                        replay = 10,
+                    )
 
-                for (state in scenario.states) {
-                    stateStore.setStateEntryValues(state)
-                    runUiThreadTasks() // Ensures data sharing ended.
-                }
-
-                expect
-                    .withMessage(scenario.name)
-                    .that(allEvaluations.replayCache)
-                    .isEqualTo(scenario.evaluated)
+            for (state in scenario.states) {
+                stateStore.setStateEntryValues(state)
             }
+
+            expect
+                .withMessage(scenario.name)
+                .that(allEvaluations.replayCache)
+                .isEqualTo(scenario.evaluated)
         }
     }
 
     @Test
-    fun data_keepExpression_doesNotTrimUnevaluatedExpression() {
+    fun evaluate_keepExpression_doesNotTrimUnevaluatedExpression() = runBlocking {
         val expressed =
             WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
                 .setRangedValueExpression(DynamicFloat.constant(1f))
@@ -250,39 +227,30 @@
                 .setShortTitle(WireComplicationText(DynamicString.constant("Short Title")))
                 .setContentDescription(WireComplicationText(DynamicString.constant("Description")))
                 .build()
-        ComplicationDataExpressionEvaluator(expressed, keepExpression = true).use { evaluator ->
-            evaluator.init()
-            runUiThreadTasks()
+        val evaluator = ComplicationDataExpressionEvaluator(keepExpression = true)
 
-            assertThat(evaluator.data.value)
-                .isEqualTo(
-                    WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
-                        .setRangedValue(1f)
-                        .setRangedValueExpression(DynamicFloat.constant(1f))
-                        .setLongText(
-                            WireComplicationText("Long Text", DynamicString.constant("Long Text"))
-                        )
-                        .setLongTitle(
-                            WireComplicationText("Long Title", DynamicString.constant("Long Title"))
-                        )
-                        .setShortText(
-                            WireComplicationText("Short Text", DynamicString.constant("Short Text"))
-                        )
-                        .setShortTitle(
-                            WireComplicationText(
-                                "Short Title",
-                                DynamicString.constant("Short Title")
-                            )
-                        )
-                        .setContentDescription(
-                            WireComplicationText(
-                                "Description",
-                                DynamicString.constant("Description")
-                            )
-                        )
-                        .build()
-                )
-        }
+        assertThat(evaluator.evaluate(expressed).firstOrNull())
+            .isEqualTo(
+                WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
+                    .setRangedValue(1f)
+                    .setRangedValueExpression(DynamicFloat.constant(1f))
+                    .setLongText(
+                        WireComplicationText("Long Text", DynamicString.constant("Long Text"))
+                    )
+                    .setLongTitle(
+                        WireComplicationText("Long Title", DynamicString.constant("Long Title"))
+                    )
+                    .setShortText(
+                        WireComplicationText("Short Text", DynamicString.constant("Short Text"))
+                    )
+                    .setShortTitle(
+                        WireComplicationText("Short Title", DynamicString.constant("Short Title"))
+                    )
+                    .setContentDescription(
+                        WireComplicationText("Description", DynamicString.constant("Description"))
+                    )
+                    .build()
+            )
     }
 
     enum class HasExpressionDataWithExpressionScenario(val data: WireComplicationData) {
@@ -330,29 +298,6 @@
         assertThat(hasExpression(DATA_WITH_NO_EXPRESSION)).isFalse()
     }
 
-    @Test
-    fun compat_notInitialized_listenerNotInvoked() {
-        ComplicationDataExpressionEvaluator.Compat.Builder(DATA_WITH_NO_EXPRESSION, listener)
-            .build()
-            .use {
-                runUiThreadTasks()
-
-                verify(listener, never()).accept(any())
-            }
-    }
-
-    @Test
-    fun compat_noExpression_listenerInvokedWithData() {
-        ComplicationDataExpressionEvaluator.Compat.Builder(DATA_WITH_NO_EXPRESSION, listener)
-            .build()
-            .use { evaluator ->
-                evaluator.init(ContextCompat.getMainExecutor(getApplicationContext()))
-                runUiThreadTasks()
-
-                verify(listener, times(1)).accept(DATA_WITH_NO_EXPRESSION)
-            }
-    }
-
     private companion object {
         val DATA_WITH_NO_EXPRESSION =
             WireComplicationData.Builder(WireComplicationData.TYPE_NO_DATA)
diff --git a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceControlServiceTest.kt b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceControlServiceTest.kt
index fb730b0..8efea62 100644
--- a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceControlServiceTest.kt
+++ b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceControlServiceTest.kt
@@ -538,4 +538,15 @@
 
         assertThat(instance.userStyleSchema.mSchema).isEmpty()
     }
+
+    @Test
+    public fun createWatchFaceService_throwsOnInvalidClass() {
+        assertThat(WatchFaceControlService()
+            .createWatchFaceService(
+                ComponentName(
+                    ApplicationProvider.getApplicationContext(),
+                    WatchFaceControlServiceTest::class.java
+                )
+            )).isNull()
+    }
 }
diff --git a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt
index f941c63c..1608c29 100644
--- a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt
+++ b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt
@@ -86,6 +86,7 @@
 import org.junit.Assert.fail
 import org.junit.Assume
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -545,6 +546,7 @@
         bitmap!!.assertAgainstGolden(screenshotRule, "placeholderComplications")
     }
 
+    @Ignore("b/274813981")
     @SuppressLint("NewApi")
     @Test
     public fun testSmallImageComplications() {
@@ -692,6 +694,7 @@
         bitmap!!.assertAgainstGolden(screenshotRule, "left_complication_pressed")
     }
 
+    @Ignore("b/274981990")
     @SuppressLint("NewApi")
     @Test
     public fun testHighlightRightComplicationInScreenshot() {
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
index 3715ca5..c3c095e 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
@@ -51,6 +51,7 @@
 import androidx.wear.watchface.complications.data.toApiComplicationData
 import androidx.wear.watchface.control.HeadlessWatchFaceImpl
 import androidx.wear.watchface.control.RemoteWatchFaceView
+import androidx.wear.watchface.control.WatchFaceControlService
 import androidx.wear.watchface.control.data.ComplicationRenderParams
 import androidx.wear.watchface.control.data.HeadlessWatchFaceInstanceParams
 import androidx.wear.watchface.control.data.WatchFaceRenderParams
@@ -178,6 +179,57 @@
             return pendingEditorDelegateCB!!
         }
 
+        @UiThread
+        internal fun createWatchFaceServiceOld(
+            componentName: ComponentName
+        ): WatchFaceService {
+            // Attempt to construct the class for the specified watchFaceName, failing if it either
+            // doesn't exist or isn't a [WatchFaceService].
+            val watchFaceServiceClass =
+                Class.forName(componentName.className)
+                    ?: throw IllegalArgumentException("Can't create ${componentName.className}")
+            if (!WatchFaceService::class.java.isAssignableFrom(watchFaceServiceClass)) {
+                throw IllegalArgumentException(
+                    "${componentName.className} is not a WatchFaceService"
+                )
+            } else {
+                return watchFaceServiceClass.getConstructor().newInstance() as WatchFaceService
+            }
+        }
+
+        @SuppressLint("NewApi")
+        @Suppress("DEPRECATION") // queryIntentServices
+        @UiThread
+        internal fun createWatchFaceService(
+            componentName: ComponentName,
+            context: Context
+        ): WatchFaceService {
+            // Resolve the WatchFaceControlService and construct WatchFaceService using its API
+            val services = context.packageManager.queryIntentServices(Intent(
+                WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE).apply {
+                setPackage(context.packageName)
+            }, 0)
+
+            if (services.size != 1)
+                throw IllegalArgumentException(
+                    "WatchFaceControlService cannot be uniquely resolved (${services.size}) for " +
+                        context.packageName
+                )
+
+            val watchFaceControlServiceClass =
+                Class.forName(services[0].serviceInfo.name)
+                    ?: throw IllegalArgumentException(
+                        "Can't find ${services[0].serviceInfo.name}"
+                    )
+
+            val watchFaceControlService =
+                watchFaceControlServiceClass.getConstructor().newInstance()
+                    as WatchFaceControlService
+
+            return watchFaceControlService.createWatchFaceService(componentName)
+                ?: throw IllegalArgumentException("Can't create ${componentName.className}")
+        }
+
         /**
          * For use by on watch face editors.
          *
@@ -191,24 +243,18 @@
             params: HeadlessWatchFaceInstanceParams,
             context: Context
         ): EditorDelegate {
-            // Attempt to construct the class for the specified watchFaceName, failing if it either
-            // doesn't exist or isn't a [WatchFaceService].
-            val watchFaceServiceClass =
-                Class.forName(componentName.className)
-                    ?: throw IllegalArgumentException("Can't create ${componentName.className}")
-            if (!WatchFaceService::class.java.isAssignableFrom(WatchFaceService::class.java)) {
-                throw IllegalArgumentException(
-                    "${componentName.className} is not a WatchFaceService"
-                )
+            val watchFaceService = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+                createWatchFaceService(componentName, context)
             } else {
-                val watchFaceService =
-                    watchFaceServiceClass.getConstructor().newInstance() as WatchFaceService
-                watchFaceService.setContext(context)
-                val engine =
-                    watchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
-                val headlessWatchFaceImpl = engine.createHeadlessInstance(params)
-                return engine.deferredWatchFaceImpl.await().WFEditorDelegate(headlessWatchFaceImpl)
+                createWatchFaceServiceOld(componentName)
+            }.apply {
+                setContext(context)
             }
+
+            val engine =
+                watchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+            val headlessWatchFaceImpl = engine.createHeadlessInstance(params)
+            return engine.deferredWatchFaceImpl.await().WFEditorDelegate(headlessWatchFaceImpl)
         }
     }
 
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 a196c02..4122a7d 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
@@ -1078,6 +1078,9 @@
 
             iWatchFaceService = IWatchFaceService.Stub.asInterface(binder)
 
+            // A ParameterlessEngine doesn't exist in WSL flow.
+            InteractiveInstanceManager.setParameterlessEngine(null)
+
             try {
                 // Note if the implementation doesn't support getVersion this will return zero
                 // rather than throwing an exception.
@@ -1346,7 +1349,7 @@
                     return
                 }
 
-                val pendingWallpaperInstance =
+                var pendingWallpaperInstance =
                     InteractiveInstanceManager.takePendingWallpaperInteractiveWatchFaceInstance()
 
                 // In a direct boot scenario attempt to load the previously serialized parameters.
@@ -1376,16 +1379,32 @@
                                 ?.let {
                                     Log.e(
                                         TAG,
-                                        "takePendingWallpaperInteractiveWatchFaceInstance failed"
+                                        "takePendingWallpaperInteractiveWatchFaceInstance failed",
+                                        e
                                     )
                                     it.callback.onInteractiveWatchFaceCrashed(CrashInfoParcel(e))
                                 }
                         } finally {
                             asyncTraceEvent.close()
                         }
+
+                        return
                     }
                 }
 
+                if (pendingWallpaperInstance == null) {
+                    // In this case we don't have any watchface parameters, probably because a WSL
+                    // watchface has been upgraded to an AndroidX one. The system has either just
+                    // racily attempted to connect (in which case we should carry on normally) or it
+                    // probably will connect at a later time. In the latter case we should
+                    // register a parameterless engine to allow the subsequent connection to
+                    // succeed.
+                    pendingWallpaperInstance = InteractiveInstanceManager
+                        .setParameterlessEngineOrTakePendingWallpaperInteractiveWatchFaceInstance(
+                            this
+                        )
+                }
+
                 // If there's a pending WallpaperInteractiveWatchFaceInstance then create it.
                 if (pendingWallpaperInstance != null) {
                     val asyncTraceEvent =
@@ -1402,7 +1421,7 @@
                             )
                             instance
                         } catch (e: Exception) {
-                            Log.e(TAG, "createInteractiveInstance failed")
+                            Log.e(TAG, "createInteractiveInstance failed", e)
                             pendingWallpaperInstance.callback.onInteractiveWatchFaceCrashed(
                                 CrashInfoParcel(e)
                             )
@@ -1434,6 +1453,29 @@
                 }
             }
 
+        /** Attaches to a parameterlessEngine if we're completely uninitialized. */
+        @SuppressWarnings("NewApi")
+        internal fun attachToParameterlessEngine(
+            pendingWallpaperInstance:
+            InteractiveInstanceManager.PendingWallpaperInteractiveWatchFaceInstance
+        ) {
+            uiThreadCoroutineScope.launch {
+                try {
+                    pendingWallpaperInstance.callback.onInteractiveWatchFaceCreated(
+                        createInteractiveInstance(
+                            pendingWallpaperInstance.params,
+                            "attachToParameterlessEngine"
+                        )
+                    )
+                } catch (e: Exception) {
+                    Log.e(TAG, "attachToParameterlessEngine failed", e)
+                    pendingWallpaperInstance.callback.onInteractiveWatchFaceCrashed(
+                        CrashInfoParcel(e)
+                    )
+                }
+            }
+        }
+
         @UiThread
         internal fun ambientTickUpdate(): Unit =
             TraceEvent("EngineWrapper.ambientTickUpdate").use {
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
index bcbafd1..1dbbc9a 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
@@ -20,6 +20,7 @@
 import androidx.annotation.UiThread
 import androidx.annotation.VisibleForTesting
 import androidx.wear.watchface.IndentingPrintWriter
+import androidx.wear.watchface.WatchFaceService
 import androidx.wear.watchface.control.data.WallpaperInteractiveWatchFaceInstanceParams
 import androidx.wear.watchface.utility.TraceEvent
 
@@ -53,6 +54,7 @@
         private var pendingWallpaperInteractiveWatchFaceInstance:
             PendingWallpaperInteractiveWatchFaceInstance? =
             null
+        private var parameterlessEngine: WatchFaceService.EngineWrapper? = null
 
         @VisibleForTesting
         fun getInstances() =
@@ -70,6 +72,54 @@
             }
         }
 
+        /**
+         * We either return the pendingWallpaperInteractiveWatchFaceInstance if there is one or
+         * set parameterlessEngine. A parameterless engine, is one that's been created without any
+         * start up params. Typically this can only happen if a WSL watchface is upgraded to an
+         * androidx one, so WallpaperManager knows about it but WearServices/WSL does not.
+         */
+        @SuppressLint("SyntheticAccessor")
+        fun setParameterlessEngineOrTakePendingWallpaperInteractiveWatchFaceInstance(
+            parameterlessEngine: WatchFaceService.EngineWrapper?
+        ): PendingWallpaperInteractiveWatchFaceInstance? {
+            synchronized(pendingWallpaperInteractiveWatchFaceInstanceLock) {
+                require(this.parameterlessEngine == null || parameterlessEngine == null) {
+                    "Already have a parameterlessEngine registered"
+                }
+
+                if (pendingWallpaperInteractiveWatchFaceInstance == null) {
+                    this.parameterlessEngine = parameterlessEngine
+                    return null
+                } else {
+                    val returnValue = pendingWallpaperInteractiveWatchFaceInstance
+                    pendingWallpaperInteractiveWatchFaceInstance = null
+                    return returnValue
+                }
+            }
+        }
+
+        /**
+         * A parameterless engine, is one that's been created without any start up params. Typically
+         * this can only happen if a WSL watchface is upgraded to an androidx one, so
+         * WallpaperManager knows about it but WearServices/WSL does not.
+         */
+        @SuppressLint("SyntheticAccessor")
+        fun setParameterlessEngine(parameterlessEngine: WatchFaceService.EngineWrapper?) {
+            synchronized(pendingWallpaperInteractiveWatchFaceInstanceLock) {
+                require(this.parameterlessEngine == null || parameterlessEngine == null) {
+                    "Already have a parameterlessEngine registered"
+                }
+                this.parameterlessEngine = parameterlessEngine
+            }
+        }
+
+        @SuppressLint("SyntheticAccessor")
+        fun getParameterlessEngine(): WatchFaceService.EngineWrapper? {
+            synchronized(pendingWallpaperInteractiveWatchFaceInstanceLock) {
+                return parameterlessEngine
+            }
+        }
+
         @SuppressLint("SyntheticAccessor")
         fun getAndRetainInstance(instanceId: String): InteractiveWatchFaceImpl? {
             synchronized(pendingWallpaperInteractiveWatchFaceInstanceLock) {
@@ -121,12 +171,22 @@
             val impl =
                 synchronized(pendingWallpaperInteractiveWatchFaceInstanceLock) {
                     val instance = instances[value.params.instanceId]
+
                     if (instance == null) {
+                        parameterlessEngine?.let {
+                            parameterlessEngine = null
+                            it.attachToParameterlessEngine(value)
+                            return null
+                        }
+
                         TraceEvent("Set pendingWallpaperInteractiveWatchFaceInstance").use {
                             pendingWallpaperInteractiveWatchFaceInstance = value
                         }
                         return null
                     }
+                    if (instance.impl.engine == parameterlessEngine) {
+                        parameterlessEngine = null
+                    }
                     instance.impl
                 }
 
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
index f0d28c5..d16421b 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
@@ -54,7 +54,6 @@
  *
  */
 @RequiresApi(27)
-@VisibleForTesting
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public open class WatchFaceControlService : Service() {
     private var watchFaceInstanceServiceStub: IWatchFaceInstanceServiceStub? = null
@@ -79,7 +78,7 @@
     open fun createWatchFaceService(watchFaceName: ComponentName): WatchFaceService? {
         return try {
             val watchFaceServiceClass = Class.forName(watchFaceName.className) ?: return null
-            if (!WatchFaceService::class.java.isAssignableFrom(WatchFaceService::class.java)) {
+            if (!WatchFaceService::class.java.isAssignableFrom(watchFaceServiceClass)) {
                 return null
             }
             watchFaceServiceClass.getConstructor().newInstance() as WatchFaceService
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/AsyncWatchFaceInitTest.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/AsyncWatchFaceInitTest.kt
index a48ba9c..01c3acb 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/AsyncWatchFaceInitTest.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/AsyncWatchFaceInitTest.kt
@@ -183,6 +183,7 @@
 
     @After
     fun tearDown() {
+        assertThat(InteractiveInstanceManager.getParameterlessEngine()).isNull()
         InteractiveInstanceManager.releaseInstance(initParams.instanceId)
         assertThat(InteractiveInstanceManager.getInstances()).isEmpty()
     }
@@ -228,6 +229,9 @@
         runPostedTasksFor(0)
 
         assertThat(pendingException.message).startsWith("WatchFace already exists!")
+
+        // Tidy up.
+        InteractiveInstanceManager.setParameterlessEngine(null)
     }
 
     @Test
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
index 140549e..97ada96 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
@@ -39,6 +39,8 @@
 import android.provider.Settings
 import android.support.wearable.complications.ComplicationData as WireComplicationData
 import android.support.wearable.complications.ComplicationText as WireComplicationText
+import android.content.IntentFilter
+import android.content.pm.ServiceInfo
 import android.support.wearable.watchface.Constants
 import android.support.wearable.watchface.IWatchFaceService
 import android.support.wearable.watchface.WatchFaceStyle
@@ -74,6 +76,7 @@
 import androidx.wear.watchface.control.IPendingInteractiveWatchFace
 import androidx.wear.watchface.control.IWatchfaceListener
 import androidx.wear.watchface.control.InteractiveInstanceManager
+import androidx.wear.watchface.control.WatchFaceControlService
 import androidx.wear.watchface.control.data.CrashInfoParcel
 import androidx.wear.watchface.control.data.HeadlessWatchFaceInstanceParams
 import androidx.wear.watchface.control.data.WallpaperInteractiveWatchFaceInstanceParams
@@ -751,6 +754,7 @@
         }
 
         assertThat(InteractiveInstanceManager.getInstances()).isEmpty()
+        assertThat(InteractiveInstanceManager.getParameterlessEngine()).isNull()
         validateMockitoUsage()
     }
 
@@ -3756,6 +3760,21 @@
     @Config(sdk = [Build.VERSION_CODES.R])
     public fun directBoot() {
         val instanceId = "DirectBootInstance"
+        val params = WallpaperInteractiveWatchFaceInstanceParams(
+            instanceId,
+            DeviceConfig(false, false, 0, 0),
+            WatchUiState(false, 0),
+            UserStyle(
+                hashMapOf(
+                    colorStyleSetting to blueStyleOption,
+                    watchHandStyleSetting to gothicStyleOption
+                )
+            )
+                .toWireFormat(),
+            null,
+            null,
+            null
+        )
         testWatchFaceService =
             TestWatchFaceService(
                 WatchFaceType.ANALOG,
@@ -3772,21 +3791,7 @@
                 watchState,
                 handler,
                 null,
-                WallpaperInteractiveWatchFaceInstanceParams(
-                    instanceId,
-                    DeviceConfig(false, false, 0, 0),
-                    WatchUiState(false, 0),
-                    UserStyle(
-                            hashMapOf(
-                                colorStyleSetting to blueStyleOption,
-                                watchHandStyleSetting to gothicStyleOption
-                            )
-                        )
-                        .toWireFormat(),
-                    null,
-                    null,
-                    null
-                ),
+                params,
                 choreographer
             )
 
@@ -3794,15 +3799,34 @@
         engineWrapper.onSurfaceChanged(surfaceHolder, 0, 100, 100)
 
         // This increments refcount to 2
-        val instance = InteractiveInstanceManager.getAndRetainInstance(instanceId)
+        val instance =
+            InteractiveInstanceManager
+                .getExistingInstanceOrSetPendingWallpaperInteractiveWatchFaceInstance(
+                    InteractiveInstanceManager.PendingWallpaperInteractiveWatchFaceInstance(
+                        params,
+                        object : IPendingInteractiveWatchFace.Stub() {
+                            override fun getApiVersion() = IPendingInteractiveWatchFace.API_VERSION
+
+                            override fun onInteractiveWatchFaceCreated(
+                                iInteractiveWatchFace: IInteractiveWatchFace
+                            ) {
+                                fail("Shouldn't get called")
+                            }
+
+                            override fun onInteractiveWatchFaceCrashed(
+                                exception: CrashInfoParcel?
+                            ) {
+                                fail("WatchFace crashed: $exception")
+                            }
+                        }
+                    )
+                )
         assertThat(instance).isNotNull()
         watchFaceImpl = engineWrapper.getWatchFaceImplOrNull()!!
         val userStyle = watchFaceImpl.currentUserStyleRepository.userStyle.value
         assertThat(userStyle[colorStyleSetting]).isEqualTo(blueStyleOption)
         assertThat(userStyle[watchHandStyleSetting]).isEqualTo(gothicStyleOption)
         instance!!.release()
-
-        InteractiveInstanceManager.releaseInstance(instanceId)
     }
 
     @Test
@@ -6523,6 +6547,131 @@
         assertThat(HeadlessWatchFaceImpl.headlessInstances).isEmpty()
     }
 
+    @Config(minSdk = 30)
+    @SuppressLint("NewApi")
+    @Test
+    public fun createHeadlessSessionDelegate_customControlServiceIsUsed() {
+        val context = ApplicationProvider.getApplicationContext<Context>()
+        val componentName = ComponentName("non existing package", "class")
+        lateinit var delegate: WatchFace.EditorDelegate
+
+        val shadowPackageManager = shadowOf(context.packageManager)
+        val controlServiceComponent = ComponentName(
+            context, TestWatchFaceControlService::class.java
+        )
+        shadowPackageManager.addOrUpdateService(ServiceInfo().apply {
+            packageName = controlServiceComponent.packageName
+            name = controlServiceComponent.className
+        })
+        shadowPackageManager.addIntentFilterForService(
+            controlServiceComponent,
+            IntentFilter(WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE)
+        )
+        // Remove default WatchFaceControlService
+        shadowPackageManager.removeService(
+            ComponentName(context, WatchFaceControlService::class.java)
+        )
+
+        // Allows us to programmatically control tasks.
+        TestNopCanvasWatchFaceService.handler = this.handler
+
+        CoroutineScope(handler.asCoroutineDispatcher().immediate).launch {
+            delegate =
+                WatchFace.createHeadlessSessionDelegate(
+                    componentName,
+                    HeadlessWatchFaceInstanceParams(
+                        componentName,
+                        DeviceConfig(false, false, 100, 200),
+                        100,
+                        100,
+                        null
+                    ),
+                    context
+                )
+        }
+
+        assertThat(delegate).isNotNull()
+
+        // Run all pending tasks.
+        while (pendingTasks.isNotEmpty()) {
+            pendingTasks.remove().runnable.run()
+        }
+
+        delegate.onDestroy()
+    }
+
+    @Test
+    public fun attachToExistingParameterlessEngine() {
+        // Construct a parameterless engine.
+        testWatchFaceService =
+            TestWatchFaceService(
+                WatchFaceType.DIGITAL,
+                emptyList(),
+                { _, currentUserStyleRepository, watchState ->
+                    renderer =
+                        TestRenderer(
+                            surfaceHolder,
+                            currentUserStyleRepository,
+                            watchState,
+                            INTERACTIVE_UPDATE_RATE_MS
+                        )
+                    renderer
+                },
+                UserStyleSchema(emptyList()),
+                watchState,
+                handler,
+                null,
+                null,
+                choreographer,
+                mockSystemTimeMillis = looperTimeMillis,
+                complicationCache = null
+            )
+
+        engineWrapper = testWatchFaceService.onCreateEngine() as WatchFaceService.EngineWrapper
+        engineWrapper.onCreate(surfaceHolder)
+        engineWrapper.onSurfaceChanged(surfaceHolder, 0, 100, 100)
+        engineWrapper.onVisibilityChanged(true)
+
+        val callback = object : IPendingInteractiveWatchFace.Stub() {
+            override fun getApiVersion() = IPendingInteractiveWatchFace.API_VERSION
+
+            override fun onInteractiveWatchFaceCreated(
+                iInteractiveWatchFace: IInteractiveWatchFace
+            ) {
+                interactiveWatchFaceInstance = iInteractiveWatchFace
+            }
+
+            override fun onInteractiveWatchFaceCrashed(exception: CrashInfoParcel?) {
+                fail("WatchFace crashed: $exception")
+            }
+        }
+
+        InteractiveInstanceManager
+            .getExistingInstanceOrSetPendingWallpaperInteractiveWatchFaceInstance(
+                InteractiveInstanceManager.PendingWallpaperInteractiveWatchFaceInstance(
+                    WallpaperInteractiveWatchFaceInstanceParams(
+                        INTERACTIVE_INSTANCE_ID,
+                        DeviceConfig(false, false, 0, 0),
+                        WatchUiState(false, 0),
+                        UserStyle(emptyMap()).toWireFormat(),
+                        null,
+                        null,
+                        null
+                    ),
+                    callback
+                )
+            )
+
+        runBlocking {
+            watchFaceImpl = engineWrapper.deferredWatchFaceImpl.awaitWithTimeout()
+            engineWrapper.deferredValidation.awaitWithTimeout()
+        }
+
+        assertThat(this::interactiveWatchFaceInstance.isInitialized).isTrue()
+        assertThat(watchFaceImpl.renderer.watchState.watchFaceInstanceId.value)
+            .isEqualTo(INTERACTIVE_INSTANCE_ID)
+    }
+
     private fun getLeftShortTextComplicationDataText(): CharSequence {
         val complication =
             complicationSlotsManager[LEFT_COMPLICATION_ID]!!.complicationData.value
@@ -6606,3 +6755,10 @@
             override fun getSystemTimeZoneId() = ZoneId.of("UTC")
         }
 }
+
+@RequiresApi(27)
+class TestWatchFaceControlService : WatchFaceControlService() {
+    override fun createWatchFaceService(watchFaceName: ComponentName): WatchFaceService? {
+        return TestNopCanvasWatchFaceService()
+    }
+}
diff --git a/ads/ads-identifier-common/api/current.txt b/window/extensions/core/core/api/1.0.0-beta02.txt
similarity index 100%
copy from ads/ads-identifier-common/api/current.txt
copy to window/extensions/core/core/api/1.0.0-beta02.txt
diff --git a/window/extensions/core/core/api/current.ignore b/window/extensions/core/core/api/current.ignore
new file mode 100644
index 0000000..e18e594
--- /dev/null
+++ b/window/extensions/core/core/api/current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+RemovedPackage: androidx.window.extensions.core.util.function:
+    Removed package androidx.window.extensions.core.util.function
diff --git a/window/extensions/core/core/api/current.txt b/window/extensions/core/core/api/current.txt
index c1191a1..e6f50d0 100644
--- a/window/extensions/core/core/api/current.txt
+++ b/window/extensions/core/core/api/current.txt
@@ -1,17 +1 @@
 // Signature format: 4.0
-package androidx.window.extensions.core.util.function {
-
-  @java.lang.FunctionalInterface public interface Consumer<T> {
-    method public void accept(T!);
-  }
-
-  @java.lang.FunctionalInterface public interface Function<T, R> {
-    method public R! apply(T!);
-  }
-
-  @java.lang.FunctionalInterface public interface Predicate<T> {
-    method public boolean test(T!);
-  }
-
-}
-
diff --git a/ads/ads-identifier-common/api/current.txt b/window/extensions/core/core/api/public_plus_experimental_1.0.0-beta02.txt
similarity index 100%
copy from ads/ads-identifier-common/api/current.txt
copy to window/extensions/core/core/api/public_plus_experimental_1.0.0-beta02.txt
diff --git a/window/extensions/core/core/api/public_plus_experimental_current.txt b/window/extensions/core/core/api/public_plus_experimental_current.txt
index c1191a1..e6f50d0 100644
--- a/window/extensions/core/core/api/public_plus_experimental_current.txt
+++ b/window/extensions/core/core/api/public_plus_experimental_current.txt
@@ -1,17 +1 @@
 // Signature format: 4.0
-package androidx.window.extensions.core.util.function {
-
-  @java.lang.FunctionalInterface public interface Consumer<T> {
-    method public void accept(T!);
-  }
-
-  @java.lang.FunctionalInterface public interface Function<T, R> {
-    method public R! apply(T!);
-  }
-
-  @java.lang.FunctionalInterface public interface Predicate<T> {
-    method public boolean test(T!);
-  }
-
-}
-
diff --git a/ads/ads-identifier-common/api/res-current.txt b/window/extensions/core/core/api/res-1.0.0-beta02.txt
similarity index 100%
copy from ads/ads-identifier-common/api/res-current.txt
copy to window/extensions/core/core/api/res-1.0.0-beta02.txt
diff --git a/window/extensions/core/core/api/restricted_1.0.0-beta02.txt b/window/extensions/core/core/api/restricted_1.0.0-beta02.txt
new file mode 100644
index 0000000..1763913
--- /dev/null
+++ b/window/extensions/core/core/api/restricted_1.0.0-beta02.txt
@@ -0,0 +1,17 @@
+// Signature format: 4.0
+package androidx.window.extensions.core.util.function {
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.FunctionalInterface public interface Consumer<T> {
+    method public void accept(T!);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.FunctionalInterface public interface Function<T, R> {
+    method public R! apply(T!);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.FunctionalInterface public interface Predicate<T> {
+    method public boolean test(T!);
+  }
+
+}
+
diff --git a/window/extensions/core/core/api/restricted_current.txt b/window/extensions/core/core/api/restricted_current.txt
index c1191a1..1763913 100644
--- a/window/extensions/core/core/api/restricted_current.txt
+++ b/window/extensions/core/core/api/restricted_current.txt
@@ -1,15 +1,15 @@
 // Signature format: 4.0
 package androidx.window.extensions.core.util.function {
 
-  @java.lang.FunctionalInterface public interface Consumer<T> {
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.FunctionalInterface public interface Consumer<T> {
     method public void accept(T!);
   }
 
-  @java.lang.FunctionalInterface public interface Function<T, R> {
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.FunctionalInterface public interface Function<T, R> {
     method public R! apply(T!);
   }
 
-  @java.lang.FunctionalInterface public interface Predicate<T> {
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.FunctionalInterface public interface Predicate<T> {
     method public boolean test(T!);
   }
 
diff --git a/window/extensions/core/core/build.gradle b/window/extensions/core/core/build.gradle
index a1d7e3b..9104d8c 100644
--- a/window/extensions/core/core/build.gradle
+++ b/window/extensions/core/core/build.gradle
@@ -25,6 +25,7 @@
 
 dependencies {
     api(libs.kotlinStdlib)
+    implementation("androidx.annotation:annotation:1.6.0")
 
     testImplementation(libs.kotlinTest)
     testImplementation(libs.kotlinTestAnnotationsCommon)
diff --git a/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Consumer.java b/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Consumer.java
index 8fee672..8e3a170 100644
--- a/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Consumer.java
+++ b/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Consumer.java
@@ -16,6 +16,10 @@
 
 package androidx.window.extensions.core.util.function;
 
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+
+import androidx.annotation.RestrictTo;
+
 /**
  * Represents a function that accepts an argument and produces no result.
  * It is used internally to avoid using Java 8 functional interface that leads to desugaring and
@@ -25,6 +29,7 @@
  *
  * @see java.util.function.Consumer
  */
+@RestrictTo(LIBRARY_GROUP_PREFIX)
 @FunctionalInterface
 public interface Consumer<T> {
     /**
diff --git a/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Function.java b/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Function.java
index 4bc7bcb..6f9d058 100644
--- a/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Function.java
+++ b/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Function.java
@@ -16,6 +16,10 @@
 
 package androidx.window.extensions.core.util.function;
 
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+
+import androidx.annotation.RestrictTo;
+
 /**
  * Represents a function that accepts an argument and produces a result.
  * It is used internally to avoid using Java 8 functional interface that leads to desugaring and
@@ -26,6 +30,7 @@
  *
  * @see java.util.function.Function
  */
+@RestrictTo(LIBRARY_GROUP_PREFIX)
 @FunctionalInterface
 public interface Function<T, R> {
     /**
diff --git a/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Predicate.java b/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Predicate.java
index 4942523..fdd870f 100644
--- a/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Predicate.java
+++ b/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Predicate.java
@@ -16,6 +16,10 @@
 
 package androidx.window.extensions.core.util.function;
 
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+
+import androidx.annotation.RestrictTo;
+
 /**
  * Represents a predicate boolean-valued function of one argument.
  * It is used internally to avoid using Java 8 functional interface that leads to desugaring and
@@ -25,6 +29,7 @@
  *
  * @see java.util.function.Predicate
  */
+@RestrictTo(LIBRARY_GROUP_PREFIX)
 @FunctionalInterface
 public interface Predicate<T> {
     /**
diff --git a/window/extensions/extensions/api/1.1.0-beta02.txt b/window/extensions/extensions/api/1.1.0-beta02.txt
new file mode 100644
index 0000000..6e04de6
--- /dev/null
+++ b/window/extensions/extensions/api/1.1.0-beta02.txt
@@ -0,0 +1,210 @@
+// Signature format: 4.0
+package androidx.window.extensions {
+
+  public interface WindowExtensions {
+    method public default androidx.window.extensions.embedding.ActivityEmbeddingComponent? getActivityEmbeddingComponent();
+    method public default int getVendorApiLevel();
+    method public default androidx.window.extensions.area.WindowAreaComponent? getWindowAreaComponent();
+    method public androidx.window.extensions.layout.WindowLayoutComponent? getWindowLayoutComponent();
+  }
+
+  public class WindowExtensionsProvider {
+    method public static androidx.window.extensions.WindowExtensions getWindowExtensions();
+  }
+
+}
+
+package androidx.window.extensions.area {
+
+  public interface WindowAreaComponent {
+    method public void addRearDisplayStatusListener(androidx.window.extensions.core.util.function.Consumer<java.lang.Integer!>);
+    method public void endRearDisplaySession();
+    method public void removeRearDisplayStatusListener(androidx.window.extensions.core.util.function.Consumer<java.lang.Integer!>);
+    method public void startRearDisplaySession(android.app.Activity, androidx.window.extensions.core.util.function.Consumer<java.lang.Integer!>);
+    field public static final int SESSION_STATE_ACTIVE = 1; // 0x1
+    field public static final int SESSION_STATE_INACTIVE = 0; // 0x0
+    field public static final int STATUS_AVAILABLE = 2; // 0x2
+    field public static final int STATUS_UNAVAILABLE = 1; // 0x1
+    field public static final int STATUS_UNSUPPORTED = 0; // 0x0
+  }
+
+}
+
+package androidx.window.extensions.embedding {
+
+  public interface ActivityEmbeddingComponent {
+    method public void clearSplitAttributesCalculator();
+    method public void clearSplitInfoCallback();
+    method public boolean isActivityEmbedded(android.app.Activity);
+    method public void setEmbeddingRules(java.util.Set<androidx.window.extensions.embedding.EmbeddingRule!>);
+    method public void setSplitAttributesCalculator(androidx.window.extensions.core.util.function.Function<androidx.window.extensions.embedding.SplitAttributesCalculatorParams!,androidx.window.extensions.embedding.SplitAttributes!>);
+    method @Deprecated public void setSplitInfoCallback(java.util.function.Consumer<java.util.List<androidx.window.extensions.embedding.SplitInfo!>!>);
+    method public default void setSplitInfoCallback(androidx.window.extensions.core.util.function.Consumer<java.util.List<androidx.window.extensions.embedding.SplitInfo!>!>);
+  }
+
+  public class ActivityRule extends androidx.window.extensions.embedding.EmbeddingRule {
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesActivity(android.app.Activity);
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesIntent(android.content.Intent);
+    method public boolean shouldAlwaysExpand();
+  }
+
+  public static final class ActivityRule.Builder {
+    ctor @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.N) public ActivityRule.Builder(java.util.function.Predicate<android.app.Activity!>, java.util.function.Predicate<android.content.Intent!>);
+    ctor public ActivityRule.Builder(androidx.window.extensions.core.util.function.Predicate<android.app.Activity!>, androidx.window.extensions.core.util.function.Predicate<android.content.Intent!>);
+    method public androidx.window.extensions.embedding.ActivityRule build();
+    method public androidx.window.extensions.embedding.ActivityRule.Builder setShouldAlwaysExpand(boolean);
+    method public androidx.window.extensions.embedding.ActivityRule.Builder setTag(String);
+  }
+
+  public class ActivityStack {
+    method public java.util.List<android.app.Activity!> getActivities();
+    method public boolean isEmpty();
+  }
+
+  public abstract class EmbeddingRule {
+    method public String? getTag();
+  }
+
+  public class SplitAttributes {
+    method public int getLayoutDirection();
+    method public androidx.window.extensions.embedding.SplitAttributes.SplitType getSplitType();
+  }
+
+  public static final class SplitAttributes.Builder {
+    ctor public SplitAttributes.Builder();
+    method public androidx.window.extensions.embedding.SplitAttributes build();
+    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);
+  }
+
+  public static final class SplitAttributes.LayoutDirection {
+    field public static final int BOTTOM_TO_TOP = 5; // 0x5
+    field public static final int LEFT_TO_RIGHT = 0; // 0x0
+    field public static final int LOCALE = 3; // 0x3
+    field public static final int RIGHT_TO_LEFT = 1; // 0x1
+    field public static final int TOP_TO_BOTTOM = 4; // 0x4
+  }
+
+  public static class SplitAttributes.SplitType {
+  }
+
+  public static final class SplitAttributes.SplitType.ExpandContainersSplitType extends androidx.window.extensions.embedding.SplitAttributes.SplitType {
+    ctor public SplitAttributes.SplitType.ExpandContainersSplitType();
+  }
+
+  public static final class SplitAttributes.SplitType.HingeSplitType extends androidx.window.extensions.embedding.SplitAttributes.SplitType {
+    ctor public SplitAttributes.SplitType.HingeSplitType(androidx.window.extensions.embedding.SplitAttributes.SplitType);
+    method public androidx.window.extensions.embedding.SplitAttributes.SplitType getFallbackSplitType();
+  }
+
+  public static final class SplitAttributes.SplitType.RatioSplitType extends androidx.window.extensions.embedding.SplitAttributes.SplitType {
+    ctor public SplitAttributes.SplitType.RatioSplitType(@FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) float);
+    method @FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) public float getRatio();
+    method public static androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType splitEqually();
+  }
+
+  public class SplitAttributesCalculatorParams {
+    method public boolean areDefaultConstraintsSatisfied();
+    method public androidx.window.extensions.embedding.SplitAttributes getDefaultSplitAttributes();
+    method public android.content.res.Configuration getParentConfiguration();
+    method public androidx.window.extensions.layout.WindowLayoutInfo getParentWindowLayoutInfo();
+    method public android.view.WindowMetrics getParentWindowMetrics();
+    method public String? getSplitRuleTag();
+  }
+
+  public class SplitInfo {
+    method public androidx.window.extensions.embedding.ActivityStack getPrimaryActivityStack();
+    method public androidx.window.extensions.embedding.ActivityStack getSecondaryActivityStack();
+    method public androidx.window.extensions.embedding.SplitAttributes getSplitAttributes();
+    method @Deprecated public float getSplitRatio();
+  }
+
+  public class SplitPairRule extends androidx.window.extensions.embedding.SplitRule {
+    method public int getFinishPrimaryWithSecondary();
+    method public int getFinishSecondaryWithPrimary();
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesActivityIntentPair(android.app.Activity, android.content.Intent);
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesActivityPair(android.app.Activity, android.app.Activity);
+    method public boolean shouldClearTop();
+  }
+
+  public static final class SplitPairRule.Builder {
+    ctor @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.N) public SplitPairRule.Builder(java.util.function.Predicate<android.util.Pair<android.app.Activity!,android.app.Activity!>!>, java.util.function.Predicate<android.util.Pair<android.app.Activity!,android.content.Intent!>!>, java.util.function.Predicate<android.view.WindowMetrics!>);
+    ctor public SplitPairRule.Builder(androidx.window.extensions.core.util.function.Predicate<android.util.Pair<android.app.Activity!,android.app.Activity!>!>, androidx.window.extensions.core.util.function.Predicate<android.util.Pair<android.app.Activity!,android.content.Intent!>!>, androidx.window.extensions.core.util.function.Predicate<android.view.WindowMetrics!>);
+    method public androidx.window.extensions.embedding.SplitPairRule build();
+    method public androidx.window.extensions.embedding.SplitPairRule.Builder setDefaultSplitAttributes(androidx.window.extensions.embedding.SplitAttributes);
+    method public androidx.window.extensions.embedding.SplitPairRule.Builder setFinishPrimaryWithSecondary(int);
+    method public androidx.window.extensions.embedding.SplitPairRule.Builder setFinishSecondaryWithPrimary(int);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPairRule.Builder setLayoutDirection(int);
+    method public androidx.window.extensions.embedding.SplitPairRule.Builder setShouldClearTop(boolean);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPairRule.Builder setShouldFinishPrimaryWithSecondary(boolean);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPairRule.Builder setShouldFinishSecondaryWithPrimary(boolean);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPairRule.Builder setSplitRatio(@FloatRange(from=0.0, to=1.0) float);
+    method public androidx.window.extensions.embedding.SplitPairRule.Builder setTag(String);
+  }
+
+  public class SplitPlaceholderRule extends androidx.window.extensions.embedding.SplitRule {
+    method public int getFinishPrimaryWithPlaceholder();
+    method @Deprecated public int getFinishPrimaryWithSecondary();
+    method public android.content.Intent getPlaceholderIntent();
+    method public boolean isSticky();
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesActivity(android.app.Activity);
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesIntent(android.content.Intent);
+  }
+
+  public static final class SplitPlaceholderRule.Builder {
+    ctor @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.N) public SplitPlaceholderRule.Builder(android.content.Intent, java.util.function.Predicate<android.app.Activity!>, java.util.function.Predicate<android.content.Intent!>, java.util.function.Predicate<android.view.WindowMetrics!>);
+    ctor public SplitPlaceholderRule.Builder(android.content.Intent, androidx.window.extensions.core.util.function.Predicate<android.app.Activity!>, androidx.window.extensions.core.util.function.Predicate<android.content.Intent!>, androidx.window.extensions.core.util.function.Predicate<android.view.WindowMetrics!>);
+    method public androidx.window.extensions.embedding.SplitPlaceholderRule build();
+    method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setDefaultSplitAttributes(androidx.window.extensions.embedding.SplitAttributes);
+    method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithPlaceholder(int);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithSecondary(int);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setLayoutDirection(int);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setSplitRatio(@FloatRange(from=0.0, to=1.0) float);
+    method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setSticky(boolean);
+    method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setTag(String);
+  }
+
+  public abstract class SplitRule extends androidx.window.extensions.embedding.EmbeddingRule {
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean checkParentMetrics(android.view.WindowMetrics);
+    method public androidx.window.extensions.embedding.SplitAttributes getDefaultSplitAttributes();
+    method @Deprecated public int getLayoutDirection();
+    method @Deprecated public float getSplitRatio();
+    field public static final int FINISH_ADJACENT = 2; // 0x2
+    field public static final int FINISH_ALWAYS = 1; // 0x1
+    field public static final int FINISH_NEVER = 0; // 0x0
+  }
+
+}
+
+package androidx.window.extensions.layout {
+
+  public interface DisplayFeature {
+    method public android.graphics.Rect getBounds();
+  }
+
+  public class FoldingFeature implements androidx.window.extensions.layout.DisplayFeature {
+    ctor public FoldingFeature(android.graphics.Rect, int, int);
+    method public android.graphics.Rect getBounds();
+    method public int getState();
+    method public int getType();
+    field public static final int STATE_FLAT = 1; // 0x1
+    field public static final int STATE_HALF_OPENED = 2; // 0x2
+    field public static final int TYPE_FOLD = 1; // 0x1
+    field public static final int TYPE_HINGE = 2; // 0x2
+  }
+
+  public interface WindowLayoutComponent {
+    method @Deprecated public void addWindowLayoutInfoListener(android.app.Activity, java.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo!>);
+    method @Deprecated public default void addWindowLayoutInfoListener(@UiContext android.content.Context, 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 @Deprecated public void removeWindowLayoutInfoListener(java.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo!>);
+    method public default void removeWindowLayoutInfoListener(androidx.window.extensions.core.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo!>);
+  }
+
+  public class WindowLayoutInfo {
+    ctor public WindowLayoutInfo(java.util.List<androidx.window.extensions.layout.DisplayFeature!>);
+    method public java.util.List<androidx.window.extensions.layout.DisplayFeature!> getDisplayFeatures();
+  }
+
+}
+
diff --git a/window/extensions/extensions/api/public_plus_experimental_1.1.0-beta02.txt b/window/extensions/extensions/api/public_plus_experimental_1.1.0-beta02.txt
new file mode 100644
index 0000000..6e04de6
--- /dev/null
+++ b/window/extensions/extensions/api/public_plus_experimental_1.1.0-beta02.txt
@@ -0,0 +1,210 @@
+// Signature format: 4.0
+package androidx.window.extensions {
+
+  public interface WindowExtensions {
+    method public default androidx.window.extensions.embedding.ActivityEmbeddingComponent? getActivityEmbeddingComponent();
+    method public default int getVendorApiLevel();
+    method public default androidx.window.extensions.area.WindowAreaComponent? getWindowAreaComponent();
+    method public androidx.window.extensions.layout.WindowLayoutComponent? getWindowLayoutComponent();
+  }
+
+  public class WindowExtensionsProvider {
+    method public static androidx.window.extensions.WindowExtensions getWindowExtensions();
+  }
+
+}
+
+package androidx.window.extensions.area {
+
+  public interface WindowAreaComponent {
+    method public void addRearDisplayStatusListener(androidx.window.extensions.core.util.function.Consumer<java.lang.Integer!>);
+    method public void endRearDisplaySession();
+    method public void removeRearDisplayStatusListener(androidx.window.extensions.core.util.function.Consumer<java.lang.Integer!>);
+    method public void startRearDisplaySession(android.app.Activity, androidx.window.extensions.core.util.function.Consumer<java.lang.Integer!>);
+    field public static final int SESSION_STATE_ACTIVE = 1; // 0x1
+    field public static final int SESSION_STATE_INACTIVE = 0; // 0x0
+    field public static final int STATUS_AVAILABLE = 2; // 0x2
+    field public static final int STATUS_UNAVAILABLE = 1; // 0x1
+    field public static final int STATUS_UNSUPPORTED = 0; // 0x0
+  }
+
+}
+
+package androidx.window.extensions.embedding {
+
+  public interface ActivityEmbeddingComponent {
+    method public void clearSplitAttributesCalculator();
+    method public void clearSplitInfoCallback();
+    method public boolean isActivityEmbedded(android.app.Activity);
+    method public void setEmbeddingRules(java.util.Set<androidx.window.extensions.embedding.EmbeddingRule!>);
+    method public void setSplitAttributesCalculator(androidx.window.extensions.core.util.function.Function<androidx.window.extensions.embedding.SplitAttributesCalculatorParams!,androidx.window.extensions.embedding.SplitAttributes!>);
+    method @Deprecated public void setSplitInfoCallback(java.util.function.Consumer<java.util.List<androidx.window.extensions.embedding.SplitInfo!>!>);
+    method public default void setSplitInfoCallback(androidx.window.extensions.core.util.function.Consumer<java.util.List<androidx.window.extensions.embedding.SplitInfo!>!>);
+  }
+
+  public class ActivityRule extends androidx.window.extensions.embedding.EmbeddingRule {
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesActivity(android.app.Activity);
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesIntent(android.content.Intent);
+    method public boolean shouldAlwaysExpand();
+  }
+
+  public static final class ActivityRule.Builder {
+    ctor @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.N) public ActivityRule.Builder(java.util.function.Predicate<android.app.Activity!>, java.util.function.Predicate<android.content.Intent!>);
+    ctor public ActivityRule.Builder(androidx.window.extensions.core.util.function.Predicate<android.app.Activity!>, androidx.window.extensions.core.util.function.Predicate<android.content.Intent!>);
+    method public androidx.window.extensions.embedding.ActivityRule build();
+    method public androidx.window.extensions.embedding.ActivityRule.Builder setShouldAlwaysExpand(boolean);
+    method public androidx.window.extensions.embedding.ActivityRule.Builder setTag(String);
+  }
+
+  public class ActivityStack {
+    method public java.util.List<android.app.Activity!> getActivities();
+    method public boolean isEmpty();
+  }
+
+  public abstract class EmbeddingRule {
+    method public String? getTag();
+  }
+
+  public class SplitAttributes {
+    method public int getLayoutDirection();
+    method public androidx.window.extensions.embedding.SplitAttributes.SplitType getSplitType();
+  }
+
+  public static final class SplitAttributes.Builder {
+    ctor public SplitAttributes.Builder();
+    method public androidx.window.extensions.embedding.SplitAttributes build();
+    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);
+  }
+
+  public static final class SplitAttributes.LayoutDirection {
+    field public static final int BOTTOM_TO_TOP = 5; // 0x5
+    field public static final int LEFT_TO_RIGHT = 0; // 0x0
+    field public static final int LOCALE = 3; // 0x3
+    field public static final int RIGHT_TO_LEFT = 1; // 0x1
+    field public static final int TOP_TO_BOTTOM = 4; // 0x4
+  }
+
+  public static class SplitAttributes.SplitType {
+  }
+
+  public static final class SplitAttributes.SplitType.ExpandContainersSplitType extends androidx.window.extensions.embedding.SplitAttributes.SplitType {
+    ctor public SplitAttributes.SplitType.ExpandContainersSplitType();
+  }
+
+  public static final class SplitAttributes.SplitType.HingeSplitType extends androidx.window.extensions.embedding.SplitAttributes.SplitType {
+    ctor public SplitAttributes.SplitType.HingeSplitType(androidx.window.extensions.embedding.SplitAttributes.SplitType);
+    method public androidx.window.extensions.embedding.SplitAttributes.SplitType getFallbackSplitType();
+  }
+
+  public static final class SplitAttributes.SplitType.RatioSplitType extends androidx.window.extensions.embedding.SplitAttributes.SplitType {
+    ctor public SplitAttributes.SplitType.RatioSplitType(@FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) float);
+    method @FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) public float getRatio();
+    method public static androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType splitEqually();
+  }
+
+  public class SplitAttributesCalculatorParams {
+    method public boolean areDefaultConstraintsSatisfied();
+    method public androidx.window.extensions.embedding.SplitAttributes getDefaultSplitAttributes();
+    method public android.content.res.Configuration getParentConfiguration();
+    method public androidx.window.extensions.layout.WindowLayoutInfo getParentWindowLayoutInfo();
+    method public android.view.WindowMetrics getParentWindowMetrics();
+    method public String? getSplitRuleTag();
+  }
+
+  public class SplitInfo {
+    method public androidx.window.extensions.embedding.ActivityStack getPrimaryActivityStack();
+    method public androidx.window.extensions.embedding.ActivityStack getSecondaryActivityStack();
+    method public androidx.window.extensions.embedding.SplitAttributes getSplitAttributes();
+    method @Deprecated public float getSplitRatio();
+  }
+
+  public class SplitPairRule extends androidx.window.extensions.embedding.SplitRule {
+    method public int getFinishPrimaryWithSecondary();
+    method public int getFinishSecondaryWithPrimary();
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesActivityIntentPair(android.app.Activity, android.content.Intent);
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesActivityPair(android.app.Activity, android.app.Activity);
+    method public boolean shouldClearTop();
+  }
+
+  public static final class SplitPairRule.Builder {
+    ctor @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.N) public SplitPairRule.Builder(java.util.function.Predicate<android.util.Pair<android.app.Activity!,android.app.Activity!>!>, java.util.function.Predicate<android.util.Pair<android.app.Activity!,android.content.Intent!>!>, java.util.function.Predicate<android.view.WindowMetrics!>);
+    ctor public SplitPairRule.Builder(androidx.window.extensions.core.util.function.Predicate<android.util.Pair<android.app.Activity!,android.app.Activity!>!>, androidx.window.extensions.core.util.function.Predicate<android.util.Pair<android.app.Activity!,android.content.Intent!>!>, androidx.window.extensions.core.util.function.Predicate<android.view.WindowMetrics!>);
+    method public androidx.window.extensions.embedding.SplitPairRule build();
+    method public androidx.window.extensions.embedding.SplitPairRule.Builder setDefaultSplitAttributes(androidx.window.extensions.embedding.SplitAttributes);
+    method public androidx.window.extensions.embedding.SplitPairRule.Builder setFinishPrimaryWithSecondary(int);
+    method public androidx.window.extensions.embedding.SplitPairRule.Builder setFinishSecondaryWithPrimary(int);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPairRule.Builder setLayoutDirection(int);
+    method public androidx.window.extensions.embedding.SplitPairRule.Builder setShouldClearTop(boolean);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPairRule.Builder setShouldFinishPrimaryWithSecondary(boolean);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPairRule.Builder setShouldFinishSecondaryWithPrimary(boolean);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPairRule.Builder setSplitRatio(@FloatRange(from=0.0, to=1.0) float);
+    method public androidx.window.extensions.embedding.SplitPairRule.Builder setTag(String);
+  }
+
+  public class SplitPlaceholderRule extends androidx.window.extensions.embedding.SplitRule {
+    method public int getFinishPrimaryWithPlaceholder();
+    method @Deprecated public int getFinishPrimaryWithSecondary();
+    method public android.content.Intent getPlaceholderIntent();
+    method public boolean isSticky();
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesActivity(android.app.Activity);
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesIntent(android.content.Intent);
+  }
+
+  public static final class SplitPlaceholderRule.Builder {
+    ctor @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.N) public SplitPlaceholderRule.Builder(android.content.Intent, java.util.function.Predicate<android.app.Activity!>, java.util.function.Predicate<android.content.Intent!>, java.util.function.Predicate<android.view.WindowMetrics!>);
+    ctor public SplitPlaceholderRule.Builder(android.content.Intent, androidx.window.extensions.core.util.function.Predicate<android.app.Activity!>, androidx.window.extensions.core.util.function.Predicate<android.content.Intent!>, androidx.window.extensions.core.util.function.Predicate<android.view.WindowMetrics!>);
+    method public androidx.window.extensions.embedding.SplitPlaceholderRule build();
+    method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setDefaultSplitAttributes(androidx.window.extensions.embedding.SplitAttributes);
+    method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithPlaceholder(int);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithSecondary(int);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setLayoutDirection(int);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setSplitRatio(@FloatRange(from=0.0, to=1.0) float);
+    method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setSticky(boolean);
+    method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setTag(String);
+  }
+
+  public abstract class SplitRule extends androidx.window.extensions.embedding.EmbeddingRule {
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean checkParentMetrics(android.view.WindowMetrics);
+    method public androidx.window.extensions.embedding.SplitAttributes getDefaultSplitAttributes();
+    method @Deprecated public int getLayoutDirection();
+    method @Deprecated public float getSplitRatio();
+    field public static final int FINISH_ADJACENT = 2; // 0x2
+    field public static final int FINISH_ALWAYS = 1; // 0x1
+    field public static final int FINISH_NEVER = 0; // 0x0
+  }
+
+}
+
+package androidx.window.extensions.layout {
+
+  public interface DisplayFeature {
+    method public android.graphics.Rect getBounds();
+  }
+
+  public class FoldingFeature implements androidx.window.extensions.layout.DisplayFeature {
+    ctor public FoldingFeature(android.graphics.Rect, int, int);
+    method public android.graphics.Rect getBounds();
+    method public int getState();
+    method public int getType();
+    field public static final int STATE_FLAT = 1; // 0x1
+    field public static final int STATE_HALF_OPENED = 2; // 0x2
+    field public static final int TYPE_FOLD = 1; // 0x1
+    field public static final int TYPE_HINGE = 2; // 0x2
+  }
+
+  public interface WindowLayoutComponent {
+    method @Deprecated public void addWindowLayoutInfoListener(android.app.Activity, java.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo!>);
+    method @Deprecated public default void addWindowLayoutInfoListener(@UiContext android.content.Context, 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 @Deprecated public void removeWindowLayoutInfoListener(java.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo!>);
+    method public default void removeWindowLayoutInfoListener(androidx.window.extensions.core.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo!>);
+  }
+
+  public class WindowLayoutInfo {
+    ctor public WindowLayoutInfo(java.util.List<androidx.window.extensions.layout.DisplayFeature!>);
+    method public java.util.List<androidx.window.extensions.layout.DisplayFeature!> getDisplayFeatures();
+  }
+
+}
+
diff --git a/ads/ads-identifier-common/api/res-current.txt b/window/extensions/extensions/api/res-1.1.0-beta02.txt
similarity index 100%
copy from ads/ads-identifier-common/api/res-current.txt
copy to window/extensions/extensions/api/res-1.1.0-beta02.txt
diff --git a/window/extensions/extensions/api/restricted_1.1.0-beta02.txt b/window/extensions/extensions/api/restricted_1.1.0-beta02.txt
new file mode 100644
index 0000000..6e04de6
--- /dev/null
+++ b/window/extensions/extensions/api/restricted_1.1.0-beta02.txt
@@ -0,0 +1,210 @@
+// Signature format: 4.0
+package androidx.window.extensions {
+
+  public interface WindowExtensions {
+    method public default androidx.window.extensions.embedding.ActivityEmbeddingComponent? getActivityEmbeddingComponent();
+    method public default int getVendorApiLevel();
+    method public default androidx.window.extensions.area.WindowAreaComponent? getWindowAreaComponent();
+    method public androidx.window.extensions.layout.WindowLayoutComponent? getWindowLayoutComponent();
+  }
+
+  public class WindowExtensionsProvider {
+    method public static androidx.window.extensions.WindowExtensions getWindowExtensions();
+  }
+
+}
+
+package androidx.window.extensions.area {
+
+  public interface WindowAreaComponent {
+    method public void addRearDisplayStatusListener(androidx.window.extensions.core.util.function.Consumer<java.lang.Integer!>);
+    method public void endRearDisplaySession();
+    method public void removeRearDisplayStatusListener(androidx.window.extensions.core.util.function.Consumer<java.lang.Integer!>);
+    method public void startRearDisplaySession(android.app.Activity, androidx.window.extensions.core.util.function.Consumer<java.lang.Integer!>);
+    field public static final int SESSION_STATE_ACTIVE = 1; // 0x1
+    field public static final int SESSION_STATE_INACTIVE = 0; // 0x0
+    field public static final int STATUS_AVAILABLE = 2; // 0x2
+    field public static final int STATUS_UNAVAILABLE = 1; // 0x1
+    field public static final int STATUS_UNSUPPORTED = 0; // 0x0
+  }
+
+}
+
+package androidx.window.extensions.embedding {
+
+  public interface ActivityEmbeddingComponent {
+    method public void clearSplitAttributesCalculator();
+    method public void clearSplitInfoCallback();
+    method public boolean isActivityEmbedded(android.app.Activity);
+    method public void setEmbeddingRules(java.util.Set<androidx.window.extensions.embedding.EmbeddingRule!>);
+    method public void setSplitAttributesCalculator(androidx.window.extensions.core.util.function.Function<androidx.window.extensions.embedding.SplitAttributesCalculatorParams!,androidx.window.extensions.embedding.SplitAttributes!>);
+    method @Deprecated public void setSplitInfoCallback(java.util.function.Consumer<java.util.List<androidx.window.extensions.embedding.SplitInfo!>!>);
+    method public default void setSplitInfoCallback(androidx.window.extensions.core.util.function.Consumer<java.util.List<androidx.window.extensions.embedding.SplitInfo!>!>);
+  }
+
+  public class ActivityRule extends androidx.window.extensions.embedding.EmbeddingRule {
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesActivity(android.app.Activity);
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesIntent(android.content.Intent);
+    method public boolean shouldAlwaysExpand();
+  }
+
+  public static final class ActivityRule.Builder {
+    ctor @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.N) public ActivityRule.Builder(java.util.function.Predicate<android.app.Activity!>, java.util.function.Predicate<android.content.Intent!>);
+    ctor public ActivityRule.Builder(androidx.window.extensions.core.util.function.Predicate<android.app.Activity!>, androidx.window.extensions.core.util.function.Predicate<android.content.Intent!>);
+    method public androidx.window.extensions.embedding.ActivityRule build();
+    method public androidx.window.extensions.embedding.ActivityRule.Builder setShouldAlwaysExpand(boolean);
+    method public androidx.window.extensions.embedding.ActivityRule.Builder setTag(String);
+  }
+
+  public class ActivityStack {
+    method public java.util.List<android.app.Activity!> getActivities();
+    method public boolean isEmpty();
+  }
+
+  public abstract class EmbeddingRule {
+    method public String? getTag();
+  }
+
+  public class SplitAttributes {
+    method public int getLayoutDirection();
+    method public androidx.window.extensions.embedding.SplitAttributes.SplitType getSplitType();
+  }
+
+  public static final class SplitAttributes.Builder {
+    ctor public SplitAttributes.Builder();
+    method public androidx.window.extensions.embedding.SplitAttributes build();
+    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);
+  }
+
+  public static final class SplitAttributes.LayoutDirection {
+    field public static final int BOTTOM_TO_TOP = 5; // 0x5
+    field public static final int LEFT_TO_RIGHT = 0; // 0x0
+    field public static final int LOCALE = 3; // 0x3
+    field public static final int RIGHT_TO_LEFT = 1; // 0x1
+    field public static final int TOP_TO_BOTTOM = 4; // 0x4
+  }
+
+  public static class SplitAttributes.SplitType {
+  }
+
+  public static final class SplitAttributes.SplitType.ExpandContainersSplitType extends androidx.window.extensions.embedding.SplitAttributes.SplitType {
+    ctor public SplitAttributes.SplitType.ExpandContainersSplitType();
+  }
+
+  public static final class SplitAttributes.SplitType.HingeSplitType extends androidx.window.extensions.embedding.SplitAttributes.SplitType {
+    ctor public SplitAttributes.SplitType.HingeSplitType(androidx.window.extensions.embedding.SplitAttributes.SplitType);
+    method public androidx.window.extensions.embedding.SplitAttributes.SplitType getFallbackSplitType();
+  }
+
+  public static final class SplitAttributes.SplitType.RatioSplitType extends androidx.window.extensions.embedding.SplitAttributes.SplitType {
+    ctor public SplitAttributes.SplitType.RatioSplitType(@FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) float);
+    method @FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) public float getRatio();
+    method public static androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType splitEqually();
+  }
+
+  public class SplitAttributesCalculatorParams {
+    method public boolean areDefaultConstraintsSatisfied();
+    method public androidx.window.extensions.embedding.SplitAttributes getDefaultSplitAttributes();
+    method public android.content.res.Configuration getParentConfiguration();
+    method public androidx.window.extensions.layout.WindowLayoutInfo getParentWindowLayoutInfo();
+    method public android.view.WindowMetrics getParentWindowMetrics();
+    method public String? getSplitRuleTag();
+  }
+
+  public class SplitInfo {
+    method public androidx.window.extensions.embedding.ActivityStack getPrimaryActivityStack();
+    method public androidx.window.extensions.embedding.ActivityStack getSecondaryActivityStack();
+    method public androidx.window.extensions.embedding.SplitAttributes getSplitAttributes();
+    method @Deprecated public float getSplitRatio();
+  }
+
+  public class SplitPairRule extends androidx.window.extensions.embedding.SplitRule {
+    method public int getFinishPrimaryWithSecondary();
+    method public int getFinishSecondaryWithPrimary();
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesActivityIntentPair(android.app.Activity, android.content.Intent);
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesActivityPair(android.app.Activity, android.app.Activity);
+    method public boolean shouldClearTop();
+  }
+
+  public static final class SplitPairRule.Builder {
+    ctor @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.N) public SplitPairRule.Builder(java.util.function.Predicate<android.util.Pair<android.app.Activity!,android.app.Activity!>!>, java.util.function.Predicate<android.util.Pair<android.app.Activity!,android.content.Intent!>!>, java.util.function.Predicate<android.view.WindowMetrics!>);
+    ctor public SplitPairRule.Builder(androidx.window.extensions.core.util.function.Predicate<android.util.Pair<android.app.Activity!,android.app.Activity!>!>, androidx.window.extensions.core.util.function.Predicate<android.util.Pair<android.app.Activity!,android.content.Intent!>!>, androidx.window.extensions.core.util.function.Predicate<android.view.WindowMetrics!>);
+    method public androidx.window.extensions.embedding.SplitPairRule build();
+    method public androidx.window.extensions.embedding.SplitPairRule.Builder setDefaultSplitAttributes(androidx.window.extensions.embedding.SplitAttributes);
+    method public androidx.window.extensions.embedding.SplitPairRule.Builder setFinishPrimaryWithSecondary(int);
+    method public androidx.window.extensions.embedding.SplitPairRule.Builder setFinishSecondaryWithPrimary(int);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPairRule.Builder setLayoutDirection(int);
+    method public androidx.window.extensions.embedding.SplitPairRule.Builder setShouldClearTop(boolean);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPairRule.Builder setShouldFinishPrimaryWithSecondary(boolean);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPairRule.Builder setShouldFinishSecondaryWithPrimary(boolean);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPairRule.Builder setSplitRatio(@FloatRange(from=0.0, to=1.0) float);
+    method public androidx.window.extensions.embedding.SplitPairRule.Builder setTag(String);
+  }
+
+  public class SplitPlaceholderRule extends androidx.window.extensions.embedding.SplitRule {
+    method public int getFinishPrimaryWithPlaceholder();
+    method @Deprecated public int getFinishPrimaryWithSecondary();
+    method public android.content.Intent getPlaceholderIntent();
+    method public boolean isSticky();
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesActivity(android.app.Activity);
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean matchesIntent(android.content.Intent);
+  }
+
+  public static final class SplitPlaceholderRule.Builder {
+    ctor @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.N) public SplitPlaceholderRule.Builder(android.content.Intent, java.util.function.Predicate<android.app.Activity!>, java.util.function.Predicate<android.content.Intent!>, java.util.function.Predicate<android.view.WindowMetrics!>);
+    ctor public SplitPlaceholderRule.Builder(android.content.Intent, androidx.window.extensions.core.util.function.Predicate<android.app.Activity!>, androidx.window.extensions.core.util.function.Predicate<android.content.Intent!>, androidx.window.extensions.core.util.function.Predicate<android.view.WindowMetrics!>);
+    method public androidx.window.extensions.embedding.SplitPlaceholderRule build();
+    method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setDefaultSplitAttributes(androidx.window.extensions.embedding.SplitAttributes);
+    method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithPlaceholder(int);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithSecondary(int);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setLayoutDirection(int);
+    method @Deprecated public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setSplitRatio(@FloatRange(from=0.0, to=1.0) float);
+    method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setSticky(boolean);
+    method public androidx.window.extensions.embedding.SplitPlaceholderRule.Builder setTag(String);
+  }
+
+  public abstract class SplitRule extends androidx.window.extensions.embedding.EmbeddingRule {
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.N) public boolean checkParentMetrics(android.view.WindowMetrics);
+    method public androidx.window.extensions.embedding.SplitAttributes getDefaultSplitAttributes();
+    method @Deprecated public int getLayoutDirection();
+    method @Deprecated public float getSplitRatio();
+    field public static final int FINISH_ADJACENT = 2; // 0x2
+    field public static final int FINISH_ALWAYS = 1; // 0x1
+    field public static final int FINISH_NEVER = 0; // 0x0
+  }
+
+}
+
+package androidx.window.extensions.layout {
+
+  public interface DisplayFeature {
+    method public android.graphics.Rect getBounds();
+  }
+
+  public class FoldingFeature implements androidx.window.extensions.layout.DisplayFeature {
+    ctor public FoldingFeature(android.graphics.Rect, int, int);
+    method public android.graphics.Rect getBounds();
+    method public int getState();
+    method public int getType();
+    field public static final int STATE_FLAT = 1; // 0x1
+    field public static final int STATE_HALF_OPENED = 2; // 0x2
+    field public static final int TYPE_FOLD = 1; // 0x1
+    field public static final int TYPE_HINGE = 2; // 0x2
+  }
+
+  public interface WindowLayoutComponent {
+    method @Deprecated public void addWindowLayoutInfoListener(android.app.Activity, java.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo!>);
+    method @Deprecated public default void addWindowLayoutInfoListener(@UiContext android.content.Context, 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 @Deprecated public void removeWindowLayoutInfoListener(java.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo!>);
+    method public default void removeWindowLayoutInfoListener(androidx.window.extensions.core.util.function.Consumer<androidx.window.extensions.layout.WindowLayoutInfo!>);
+  }
+
+  public class WindowLayoutInfo {
+    ctor public WindowLayoutInfo(java.util.List<androidx.window.extensions.layout.DisplayFeature!>);
+    method public java.util.List<androidx.window.extensions.layout.DisplayFeature!> getDisplayFeatures();
+  }
+
+}
+
diff --git a/window/extensions/extensions/build.gradle b/window/extensions/extensions/build.gradle
index 6d72848..8830606 100644
--- a/window/extensions/extensions/build.gradle
+++ b/window/extensions/extensions/build.gradle
@@ -24,9 +24,9 @@
 
 dependencies {
     api(libs.kotlinStdlib)
-    implementation("androidx.annotation:annotation:1.3.0")
+    implementation("androidx.annotation:annotation:1.6.0")
     implementation("androidx.annotation:annotation-experimental:1.1.0")
-    implementation(project(":window:extensions:core:core"))
+    implementation("androidx.window.extensions.core:core:1.0.0-beta01")
 
     testImplementation(libs.robolectric)
     testImplementation(libs.testExtJunit)
diff --git a/window/window-core/api/1.1.0-beta02.txt b/window/window-core/api/1.1.0-beta02.txt
new file mode 100644
index 0000000..624b2df
--- /dev/null
+++ b/window/window-core/api/1.1.0-beta02.txt
@@ -0,0 +1,38 @@
+// Signature format: 4.0
+package androidx.window.core.layout {
+
+  public final class WindowHeightSizeClass {
+    field public static final androidx.window.core.layout.WindowHeightSizeClass COMPACT;
+    field public static final androidx.window.core.layout.WindowHeightSizeClass.Companion Companion;
+    field public static final androidx.window.core.layout.WindowHeightSizeClass EXPANDED;
+    field public static final androidx.window.core.layout.WindowHeightSizeClass MEDIUM;
+  }
+
+  public static final class WindowHeightSizeClass.Companion {
+  }
+
+  public final class WindowSizeClass {
+    method public static androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
+    method public androidx.window.core.layout.WindowHeightSizeClass getWindowHeightSizeClass();
+    method public androidx.window.core.layout.WindowWidthSizeClass getWindowWidthSizeClass();
+    property public final androidx.window.core.layout.WindowHeightSizeClass windowHeightSizeClass;
+    property public final androidx.window.core.layout.WindowWidthSizeClass windowWidthSizeClass;
+    field public static final androidx.window.core.layout.WindowSizeClass.Companion Companion;
+  }
+
+  public static final class WindowSizeClass.Companion {
+    method public androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
+  }
+
+  public final class WindowWidthSizeClass {
+    field public static final androidx.window.core.layout.WindowWidthSizeClass COMPACT;
+    field public static final androidx.window.core.layout.WindowWidthSizeClass.Companion Companion;
+    field public static final androidx.window.core.layout.WindowWidthSizeClass EXPANDED;
+    field public static final androidx.window.core.layout.WindowWidthSizeClass MEDIUM;
+  }
+
+  public static final class WindowWidthSizeClass.Companion {
+  }
+
+}
+
diff --git a/window/window-core/api/public_plus_experimental_1.1.0-beta02.txt b/window/window-core/api/public_plus_experimental_1.1.0-beta02.txt
new file mode 100644
index 0000000..624b2df
--- /dev/null
+++ b/window/window-core/api/public_plus_experimental_1.1.0-beta02.txt
@@ -0,0 +1,38 @@
+// Signature format: 4.0
+package androidx.window.core.layout {
+
+  public final class WindowHeightSizeClass {
+    field public static final androidx.window.core.layout.WindowHeightSizeClass COMPACT;
+    field public static final androidx.window.core.layout.WindowHeightSizeClass.Companion Companion;
+    field public static final androidx.window.core.layout.WindowHeightSizeClass EXPANDED;
+    field public static final androidx.window.core.layout.WindowHeightSizeClass MEDIUM;
+  }
+
+  public static final class WindowHeightSizeClass.Companion {
+  }
+
+  public final class WindowSizeClass {
+    method public static androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
+    method public androidx.window.core.layout.WindowHeightSizeClass getWindowHeightSizeClass();
+    method public androidx.window.core.layout.WindowWidthSizeClass getWindowWidthSizeClass();
+    property public final androidx.window.core.layout.WindowHeightSizeClass windowHeightSizeClass;
+    property public final androidx.window.core.layout.WindowWidthSizeClass windowWidthSizeClass;
+    field public static final androidx.window.core.layout.WindowSizeClass.Companion Companion;
+  }
+
+  public static final class WindowSizeClass.Companion {
+    method public androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
+  }
+
+  public final class WindowWidthSizeClass {
+    field public static final androidx.window.core.layout.WindowWidthSizeClass COMPACT;
+    field public static final androidx.window.core.layout.WindowWidthSizeClass.Companion Companion;
+    field public static final androidx.window.core.layout.WindowWidthSizeClass EXPANDED;
+    field public static final androidx.window.core.layout.WindowWidthSizeClass MEDIUM;
+  }
+
+  public static final class WindowWidthSizeClass.Companion {
+  }
+
+}
+
diff --git a/ads/ads-identifier/api/res-current.txt b/window/window-core/api/res-1.1.0-beta02.txt
similarity index 100%
copy from ads/ads-identifier/api/res-current.txt
copy to window/window-core/api/res-1.1.0-beta02.txt
diff --git a/window/window-core/api/restricted_1.1.0-beta02.txt b/window/window-core/api/restricted_1.1.0-beta02.txt
new file mode 100644
index 0000000..624b2df
--- /dev/null
+++ b/window/window-core/api/restricted_1.1.0-beta02.txt
@@ -0,0 +1,38 @@
+// Signature format: 4.0
+package androidx.window.core.layout {
+
+  public final class WindowHeightSizeClass {
+    field public static final androidx.window.core.layout.WindowHeightSizeClass COMPACT;
+    field public static final androidx.window.core.layout.WindowHeightSizeClass.Companion Companion;
+    field public static final androidx.window.core.layout.WindowHeightSizeClass EXPANDED;
+    field public static final androidx.window.core.layout.WindowHeightSizeClass MEDIUM;
+  }
+
+  public static final class WindowHeightSizeClass.Companion {
+  }
+
+  public final class WindowSizeClass {
+    method public static androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
+    method public androidx.window.core.layout.WindowHeightSizeClass getWindowHeightSizeClass();
+    method public androidx.window.core.layout.WindowWidthSizeClass getWindowWidthSizeClass();
+    property public final androidx.window.core.layout.WindowHeightSizeClass windowHeightSizeClass;
+    property public final androidx.window.core.layout.WindowWidthSizeClass windowWidthSizeClass;
+    field public static final androidx.window.core.layout.WindowSizeClass.Companion Companion;
+  }
+
+  public static final class WindowSizeClass.Companion {
+    method public androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
+  }
+
+  public final class WindowWidthSizeClass {
+    field public static final androidx.window.core.layout.WindowWidthSizeClass COMPACT;
+    field public static final androidx.window.core.layout.WindowWidthSizeClass.Companion Companion;
+    field public static final androidx.window.core.layout.WindowWidthSizeClass EXPANDED;
+    field public static final androidx.window.core.layout.WindowWidthSizeClass MEDIUM;
+  }
+
+  public static final class WindowWidthSizeClass.Companion {
+  }
+
+}
+
diff --git a/window/window-java/api/1.1.0-beta02.txt b/window/window-java/api/1.1.0-beta02.txt
new file mode 100644
index 0000000..39c35ac
--- /dev/null
+++ b/window/window-java/api/1.1.0-beta02.txt
@@ -0,0 +1,12 @@
+// Signature format: 4.0
+package androidx.window.java.layout {
+
+  public final class WindowInfoTrackerCallbackAdapter implements androidx.window.layout.WindowInfoTracker {
+    ctor public WindowInfoTrackerCallbackAdapter(androidx.window.layout.WindowInfoTracker tracker);
+    method public void addWindowLayoutInfoListener(android.app.Activity activity, java.util.concurrent.Executor executor, androidx.core.util.Consumer<androidx.window.layout.WindowLayoutInfo> consumer);
+    method public void addWindowLayoutInfoListener(@UiContext android.content.Context context, java.util.concurrent.Executor executor, androidx.core.util.Consumer<androidx.window.layout.WindowLayoutInfo> consumer);
+    method public void removeWindowLayoutInfoListener(androidx.core.util.Consumer<androidx.window.layout.WindowLayoutInfo> consumer);
+  }
+
+}
+
diff --git a/window/window-java/api/public_plus_experimental_1.1.0-beta02.txt b/window/window-java/api/public_plus_experimental_1.1.0-beta02.txt
new file mode 100644
index 0000000..d621966
--- /dev/null
+++ b/window/window-java/api/public_plus_experimental_1.1.0-beta02.txt
@@ -0,0 +1,22 @@
+// Signature format: 4.0
+package androidx.window.java.embedding {
+
+  @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);
+    method public void removeSplitListener(androidx.core.util.Consumer<java.util.List<androidx.window.embedding.SplitInfo>> consumer);
+  }
+
+}
+
+package androidx.window.java.layout {
+
+  public final class WindowInfoTrackerCallbackAdapter implements androidx.window.layout.WindowInfoTracker {
+    ctor public WindowInfoTrackerCallbackAdapter(androidx.window.layout.WindowInfoTracker tracker);
+    method public void addWindowLayoutInfoListener(android.app.Activity activity, java.util.concurrent.Executor executor, androidx.core.util.Consumer<androidx.window.layout.WindowLayoutInfo> consumer);
+    method public void addWindowLayoutInfoListener(@UiContext android.content.Context context, java.util.concurrent.Executor executor, androidx.core.util.Consumer<androidx.window.layout.WindowLayoutInfo> consumer);
+    method public void removeWindowLayoutInfoListener(androidx.core.util.Consumer<androidx.window.layout.WindowLayoutInfo> consumer);
+  }
+
+}
+
diff --git a/ads/ads-identifier/api/res-current.txt b/window/window-java/api/res-1.1.0-beta02.txt
similarity index 100%
copy from ads/ads-identifier/api/res-current.txt
copy to window/window-java/api/res-1.1.0-beta02.txt
diff --git a/window/window-java/api/restricted_1.1.0-beta02.txt b/window/window-java/api/restricted_1.1.0-beta02.txt
new file mode 100644
index 0000000..39c35ac
--- /dev/null
+++ b/window/window-java/api/restricted_1.1.0-beta02.txt
@@ -0,0 +1,12 @@
+// Signature format: 4.0
+package androidx.window.java.layout {
+
+  public final class WindowInfoTrackerCallbackAdapter implements androidx.window.layout.WindowInfoTracker {
+    ctor public WindowInfoTrackerCallbackAdapter(androidx.window.layout.WindowInfoTracker tracker);
+    method public void addWindowLayoutInfoListener(android.app.Activity activity, java.util.concurrent.Executor executor, androidx.core.util.Consumer<androidx.window.layout.WindowLayoutInfo> consumer);
+    method public void addWindowLayoutInfoListener(@UiContext android.content.Context context, java.util.concurrent.Executor executor, androidx.core.util.Consumer<androidx.window.layout.WindowLayoutInfo> consumer);
+    method public void removeWindowLayoutInfoListener(androidx.core.util.Consumer<androidx.window.layout.WindowLayoutInfo> consumer);
+  }
+
+}
+
diff --git a/window/window-rxjava2/api/1.1.0-beta02.txt b/window/window-rxjava2/api/1.1.0-beta02.txt
new file mode 100644
index 0000000..5250696
--- /dev/null
+++ b/window/window-rxjava2/api/1.1.0-beta02.txt
@@ -0,0 +1,12 @@
+// Signature format: 4.0
+package androidx.window.rxjava2.layout {
+
+  public final class WindowInfoTrackerRx {
+    method public static io.reactivex.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+    method public static io.reactivex.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+  }
+
+}
+
diff --git a/window/window-rxjava2/api/public_plus_experimental_1.1.0-beta02.txt b/window/window-rxjava2/api/public_plus_experimental_1.1.0-beta02.txt
new file mode 100644
index 0000000..5250696
--- /dev/null
+++ b/window/window-rxjava2/api/public_plus_experimental_1.1.0-beta02.txt
@@ -0,0 +1,12 @@
+// Signature format: 4.0
+package androidx.window.rxjava2.layout {
+
+  public final class WindowInfoTrackerRx {
+    method public static io.reactivex.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+    method public static io.reactivex.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+  }
+
+}
+
diff --git a/ads/ads-identifier-common/api/res-current.txt b/window/window-rxjava2/api/res-1.1.0-beta02.txt
similarity index 100%
copy from ads/ads-identifier-common/api/res-current.txt
copy to window/window-rxjava2/api/res-1.1.0-beta02.txt
diff --git a/window/window-rxjava2/api/restricted_1.1.0-beta02.txt b/window/window-rxjava2/api/restricted_1.1.0-beta02.txt
new file mode 100644
index 0000000..5250696
--- /dev/null
+++ b/window/window-rxjava2/api/restricted_1.1.0-beta02.txt
@@ -0,0 +1,12 @@
+// Signature format: 4.0
+package androidx.window.rxjava2.layout {
+
+  public final class WindowInfoTrackerRx {
+    method public static io.reactivex.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+    method public static io.reactivex.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+  }
+
+}
+
diff --git a/window/window-rxjava3/api/1.1.0-beta02.txt b/window/window-rxjava3/api/1.1.0-beta02.txt
new file mode 100644
index 0000000..23510cc
--- /dev/null
+++ b/window/window-rxjava3/api/1.1.0-beta02.txt
@@ -0,0 +1,12 @@
+// Signature format: 4.0
+package androidx.window.rxjava3.layout {
+
+  public final class WindowInfoTrackerRx {
+    method public static io.reactivex.rxjava3.core.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.rxjava3.core.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+    method public static io.reactivex.rxjava3.core.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.rxjava3.core.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+  }
+
+}
+
diff --git a/window/window-rxjava3/api/public_plus_experimental_1.1.0-beta02.txt b/window/window-rxjava3/api/public_plus_experimental_1.1.0-beta02.txt
new file mode 100644
index 0000000..23510cc
--- /dev/null
+++ b/window/window-rxjava3/api/public_plus_experimental_1.1.0-beta02.txt
@@ -0,0 +1,12 @@
+// Signature format: 4.0
+package androidx.window.rxjava3.layout {
+
+  public final class WindowInfoTrackerRx {
+    method public static io.reactivex.rxjava3.core.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.rxjava3.core.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+    method public static io.reactivex.rxjava3.core.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.rxjava3.core.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+  }
+
+}
+
diff --git a/ads/ads-identifier-common/api/res-current.txt b/window/window-rxjava3/api/res-1.1.0-beta02.txt
similarity index 100%
copy from ads/ads-identifier-common/api/res-current.txt
copy to window/window-rxjava3/api/res-1.1.0-beta02.txt
diff --git a/window/window-rxjava3/api/restricted_1.1.0-beta02.txt b/window/window-rxjava3/api/restricted_1.1.0-beta02.txt
new file mode 100644
index 0000000..23510cc
--- /dev/null
+++ b/window/window-rxjava3/api/restricted_1.1.0-beta02.txt
@@ -0,0 +1,12 @@
+// Signature format: 4.0
+package androidx.window.rxjava3.layout {
+
+  public final class WindowInfoTrackerRx {
+    method public static io.reactivex.rxjava3.core.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.rxjava3.core.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+    method public static io.reactivex.rxjava3.core.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.rxjava3.core.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+  }
+
+}
+
diff --git a/window/window-testing/api/1.1.0-beta02.txt b/window/window-testing/api/1.1.0-beta02.txt
new file mode 100644
index 0000000..12a7d20
--- /dev/null
+++ b/window/window-testing/api/1.1.0-beta02.txt
@@ -0,0 +1,24 @@
+// Signature format: 4.0
+package androidx.window.testing.layout {
+
+  public final class DisplayFeatureTesting {
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+  }
+
+  public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
+    ctor public WindowLayoutInfoPublisherRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void overrideWindowLayoutInfo(androidx.window.layout.WindowLayoutInfo info);
+  }
+
+  public final class WindowLayoutInfoTesting {
+    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
+    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo();
+  }
+
+}
+
diff --git a/window/window-testing/api/public_plus_experimental_1.1.0-beta02.txt b/window/window-testing/api/public_plus_experimental_1.1.0-beta02.txt
new file mode 100644
index 0000000..16174f8
--- /dev/null
+++ b/window/window-testing/api/public_plus_experimental_1.1.0-beta02.txt
@@ -0,0 +1,68 @@
+// Signature format: 4.0
+package androidx.window.testing.embedding {
+
+  @androidx.window.core.ExperimentalWindowApi public final class ActivityEmbeddingTestRule implements org.junit.rules.TestRule {
+    ctor public ActivityEmbeddingTestRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void overrideIsActivityEmbedded(android.app.Activity activity, boolean isActivityEmbedded);
+    method public void overrideSplitInfo(android.app.Activity activity, java.util.List<androidx.window.embedding.SplitInfo> splitInfoList);
+    method public void overrideSplitSupportStatus(androidx.window.embedding.SplitController.SplitSupportStatus status);
+  }
+
+  public final class TestActivityStack {
+    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess, optional boolean isEmpty);
+    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess);
+    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.ActivityStack createTestActivityStack();
+  }
+
+  public final class TestSplitAttributesCalculatorParams {
+    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied, optional String? splitRuleTag);
+    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied);
+    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo);
+    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration);
+    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics);
+  }
+
+  public final class TestSplitInfo {
+    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack, optional androidx.window.embedding.SplitAttributes splitAttributes);
+    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack);
+    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack);
+    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo();
+  }
+
+}
+
+package androidx.window.testing.layout {
+
+  public final class DisplayFeatureTesting {
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size);
+    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center);
+    method @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
+  }
+
+  @androidx.window.core.ExperimentalWindowApi public final class StubWindowMetricsCalculatorRule implements org.junit.rules.TestRule {
+    ctor public StubWindowMetricsCalculatorRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+  }
+
+  public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
+    ctor public WindowLayoutInfoPublisherRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void overrideWindowLayoutInfo(androidx.window.layout.WindowLayoutInfo info);
+  }
+
+  public final class WindowLayoutInfoTesting {
+    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
+    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo();
+  }
+
+}
+
diff --git a/ads/ads-identifier-common/api/res-current.txt b/window/window-testing/api/res-1.1.0-beta02.txt
similarity index 100%
copy from ads/ads-identifier-common/api/res-current.txt
copy to window/window-testing/api/res-1.1.0-beta02.txt
diff --git a/window/window-testing/api/restricted_1.1.0-beta02.txt b/window/window-testing/api/restricted_1.1.0-beta02.txt
new file mode 100644
index 0000000..12a7d20
--- /dev/null
+++ b/window/window-testing/api/restricted_1.1.0-beta02.txt
@@ -0,0 +1,24 @@
+// Signature format: 4.0
+package androidx.window.testing.layout {
+
+  public final class DisplayFeatureTesting {
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+  }
+
+  public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
+    ctor public WindowLayoutInfoPublisherRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void overrideWindowLayoutInfo(androidx.window.layout.WindowLayoutInfo info);
+  }
+
+  public final class WindowLayoutInfoTesting {
+    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
+    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo();
+  }
+
+}
+
diff --git a/window/window/api/1.1.0-beta02.txt b/window/window/api/1.1.0-beta02.txt
new file mode 100644
index 0000000..5617dbb
--- /dev/null
+++ b/window/window/api/1.1.0-beta02.txt
@@ -0,0 +1,337 @@
+// Signature format: 4.0
+package androidx.window {
+
+  public final class WindowProperties {
+    field public static final androidx.window.WindowProperties INSTANCE;
+    field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE";
+    field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED";
+  }
+
+}
+
+package androidx.window.embedding {
+
+  public final class ActivityEmbeddingController {
+    method public static androidx.window.embedding.ActivityEmbeddingController getInstance(android.content.Context context);
+    method public boolean isActivityEmbedded(android.app.Activity activity);
+    field public static final androidx.window.embedding.ActivityEmbeddingController.Companion Companion;
+  }
+
+  public static final class ActivityEmbeddingController.Companion {
+    method public androidx.window.embedding.ActivityEmbeddingController getInstance(android.content.Context context);
+  }
+
+  public final class ActivityFilter {
+    ctor public ActivityFilter(android.content.ComponentName componentName, String? intentAction);
+    method public android.content.ComponentName getComponentName();
+    method public String? getIntentAction();
+    method public boolean matchesActivity(android.app.Activity activity);
+    method public boolean matchesIntent(android.content.Intent intent);
+    property public final android.content.ComponentName componentName;
+    property public final String? intentAction;
+  }
+
+  public final class ActivityRule extends androidx.window.embedding.EmbeddingRule {
+    method public boolean getAlwaysExpand();
+    method public java.util.Set<androidx.window.embedding.ActivityFilter> getFilters();
+    property public final boolean alwaysExpand;
+    property public final java.util.Set<androidx.window.embedding.ActivityFilter> filters;
+  }
+
+  public static final class ActivityRule.Builder {
+    ctor public ActivityRule.Builder(java.util.Set<androidx.window.embedding.ActivityFilter> filters);
+    method public androidx.window.embedding.ActivityRule build();
+    method public androidx.window.embedding.ActivityRule.Builder setAlwaysExpand(boolean alwaysExpand);
+    method public androidx.window.embedding.ActivityRule.Builder setTag(String? tag);
+  }
+
+  public final class ActivityStack {
+    method public operator boolean contains(android.app.Activity activity);
+    method public boolean isEmpty();
+    property public final boolean isEmpty;
+  }
+
+  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;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio ALWAYS_DISALLOW;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio.Companion Companion;
+  }
+
+  public static final class EmbeddingAspectRatio.Companion {
+    method public androidx.window.embedding.EmbeddingAspectRatio ratio(@FloatRange(from=1.0, fromInclusive=false) float ratio);
+  }
+
+  public abstract class EmbeddingRule {
+    method public final String? getTag();
+    property public final String? tag;
+  }
+
+  public final class RuleController {
+    method public void addRule(androidx.window.embedding.EmbeddingRule rule);
+    method public void clearRules();
+    method public static androidx.window.embedding.RuleController getInstance(android.content.Context context);
+    method public java.util.Set<androidx.window.embedding.EmbeddingRule> getRules();
+    method public static java.util.Set<androidx.window.embedding.EmbeddingRule> parseRules(android.content.Context context, @XmlRes int staticRuleResourceId);
+    method public void removeRule(androidx.window.embedding.EmbeddingRule rule);
+    method public void setRules(java.util.Set<? extends androidx.window.embedding.EmbeddingRule> rules);
+    field public static final androidx.window.embedding.RuleController.Companion Companion;
+  }
+
+  public static final class RuleController.Companion {
+    method public androidx.window.embedding.RuleController getInstance(android.content.Context context);
+    method public java.util.Set<androidx.window.embedding.EmbeddingRule> parseRules(android.content.Context context, @XmlRes int staticRuleResourceId);
+  }
+
+  public final class SplitAttributes {
+    method public androidx.window.embedding.SplitAttributes.LayoutDirection getLayoutDirection();
+    method public androidx.window.embedding.SplitAttributes.SplitType getSplitType();
+    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;
+  }
+
+  public static final class SplitAttributes.Builder {
+    ctor public SplitAttributes.Builder();
+    method public androidx.window.embedding.SplitAttributes build();
+    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);
+  }
+
+  public static final class SplitAttributes.Companion {
+  }
+
+  public static final class SplitAttributes.LayoutDirection {
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection BOTTOM_TO_TOP;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection.Companion Companion;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection LEFT_TO_RIGHT;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection LOCALE;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection RIGHT_TO_LEFT;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection TOP_TO_BOTTOM;
+  }
+
+  public static final class SplitAttributes.LayoutDirection.Companion {
+  }
+
+  public static final class SplitAttributes.SplitType {
+    method public static androidx.window.embedding.SplitAttributes.SplitType ratio(@FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) float ratio);
+    field public static final androidx.window.embedding.SplitAttributes.SplitType.Companion Companion;
+    field public static final androidx.window.embedding.SplitAttributes.SplitType SPLIT_TYPE_EQUAL;
+    field public static final androidx.window.embedding.SplitAttributes.SplitType SPLIT_TYPE_EXPAND;
+    field public static final androidx.window.embedding.SplitAttributes.SplitType SPLIT_TYPE_HINGE;
+  }
+
+  public static final class SplitAttributes.SplitType.Companion {
+    method public androidx.window.embedding.SplitAttributes.SplitType ratio(@FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) float ratio);
+  }
+
+  public final class SplitController {
+    method public static androidx.window.embedding.SplitController getInstance(android.content.Context context);
+    method public androidx.window.embedding.SplitController.SplitSupportStatus getSplitSupportStatus();
+    method public kotlinx.coroutines.flow.Flow<java.util.List<androidx.window.embedding.SplitInfo>> splitInfoList(android.app.Activity activity);
+    property public final androidx.window.embedding.SplitController.SplitSupportStatus splitSupportStatus;
+    field public static final androidx.window.embedding.SplitController.Companion Companion;
+  }
+
+  public static final class SplitController.Companion {
+    method public androidx.window.embedding.SplitController getInstance(android.content.Context context);
+  }
+
+  public static final class SplitController.SplitSupportStatus {
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus.Companion Companion;
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus SPLIT_AVAILABLE;
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus SPLIT_ERROR_PROPERTY_NOT_DECLARED;
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus SPLIT_UNAVAILABLE;
+  }
+
+  public static final class SplitController.SplitSupportStatus.Companion {
+  }
+
+  public final class SplitInfo {
+    method public operator boolean contains(android.app.Activity activity);
+    method public androidx.window.embedding.ActivityStack getPrimaryActivityStack();
+    method public androidx.window.embedding.ActivityStack getSecondaryActivityStack();
+    method public androidx.window.embedding.SplitAttributes getSplitAttributes();
+    property public final androidx.window.embedding.ActivityStack primaryActivityStack;
+    property public final androidx.window.embedding.ActivityStack secondaryActivityStack;
+    property public final androidx.window.embedding.SplitAttributes splitAttributes;
+  }
+
+  public final class SplitPairFilter {
+    ctor public SplitPairFilter(android.content.ComponentName primaryActivityName, android.content.ComponentName secondaryActivityName, String? secondaryActivityIntentAction);
+    method public android.content.ComponentName getPrimaryActivityName();
+    method public String? getSecondaryActivityIntentAction();
+    method public android.content.ComponentName getSecondaryActivityName();
+    method public boolean matchesActivityIntentPair(android.app.Activity primaryActivity, android.content.Intent secondaryActivityIntent);
+    method public boolean matchesActivityPair(android.app.Activity primaryActivity, android.app.Activity secondaryActivity);
+    property public final android.content.ComponentName primaryActivityName;
+    property public final String? secondaryActivityIntentAction;
+    property public final android.content.ComponentName secondaryActivityName;
+  }
+
+  public final class SplitPairRule extends androidx.window.embedding.SplitRule {
+    method public boolean getClearTop();
+    method public java.util.Set<androidx.window.embedding.SplitPairFilter> getFilters();
+    method public androidx.window.embedding.SplitRule.FinishBehavior getFinishPrimaryWithSecondary();
+    method public androidx.window.embedding.SplitRule.FinishBehavior getFinishSecondaryWithPrimary();
+    property public final boolean clearTop;
+    property public final java.util.Set<androidx.window.embedding.SplitPairFilter> filters;
+    property public final androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithSecondary;
+    property public final androidx.window.embedding.SplitRule.FinishBehavior finishSecondaryWithPrimary;
+  }
+
+  public static final class SplitPairRule.Builder {
+    ctor public SplitPairRule.Builder(java.util.Set<androidx.window.embedding.SplitPairFilter> filters);
+    method public androidx.window.embedding.SplitPairRule build();
+    method public androidx.window.embedding.SplitPairRule.Builder setClearTop(boolean clearTop);
+    method public androidx.window.embedding.SplitPairRule.Builder setDefaultSplitAttributes(androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+    method public androidx.window.embedding.SplitPairRule.Builder setFinishPrimaryWithSecondary(androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithSecondary);
+    method public androidx.window.embedding.SplitPairRule.Builder setFinishSecondaryWithPrimary(androidx.window.embedding.SplitRule.FinishBehavior finishSecondaryWithPrimary);
+    method public androidx.window.embedding.SplitPairRule.Builder setMaxAspectRatioInLandscape(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPairRule.Builder setMaxAspectRatioInPortrait(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPairRule.Builder setMinHeightDp(@IntRange(from=0L) int minHeightDp);
+    method public androidx.window.embedding.SplitPairRule.Builder setMinSmallestWidthDp(@IntRange(from=0L) int minSmallestWidthDp);
+    method public androidx.window.embedding.SplitPairRule.Builder setMinWidthDp(@IntRange(from=0L) int minWidthDp);
+    method public androidx.window.embedding.SplitPairRule.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();
+    method public android.content.Intent getPlaceholderIntent();
+    method public boolean isSticky();
+    property public final java.util.Set<androidx.window.embedding.ActivityFilter> filters;
+    property public final androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithPlaceholder;
+    property public final boolean isSticky;
+    property public final android.content.Intent placeholderIntent;
+  }
+
+  public static final class SplitPlaceholderRule.Builder {
+    ctor public SplitPlaceholderRule.Builder(java.util.Set<androidx.window.embedding.ActivityFilter> filters, android.content.Intent placeholderIntent);
+    method public androidx.window.embedding.SplitPlaceholderRule build();
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setDefaultSplitAttributes(androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithPlaceholder(androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithPlaceholder);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMaxAspectRatioInLandscape(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMaxAspectRatioInPortrait(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMinHeightDp(@IntRange(from=0L) int minHeightDp);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMinSmallestWidthDp(@IntRange(from=0L) int minSmallestWidthDp);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMinWidthDp(@IntRange(from=0L) int minWidthDp);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setSticky(boolean isSticky);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setTag(String? tag);
+  }
+
+  public class SplitRule extends androidx.window.embedding.EmbeddingRule {
+    method public final androidx.window.embedding.SplitAttributes getDefaultSplitAttributes();
+    method public final androidx.window.embedding.EmbeddingAspectRatio getMaxAspectRatioInLandscape();
+    method public final androidx.window.embedding.EmbeddingAspectRatio getMaxAspectRatioInPortrait();
+    method public final int getMinHeightDp();
+    method public final int getMinSmallestWidthDp();
+    method public final int getMinWidthDp();
+    property public final androidx.window.embedding.SplitAttributes defaultSplitAttributes;
+    property public final androidx.window.embedding.EmbeddingAspectRatio maxAspectRatioInLandscape;
+    property public final androidx.window.embedding.EmbeddingAspectRatio maxAspectRatioInPortrait;
+    property public final int minHeightDp;
+    property public final int minSmallestWidthDp;
+    property public final int minWidthDp;
+    field public static final androidx.window.embedding.SplitRule.Companion Companion;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT;
+    field public static final int SPLIT_MIN_DIMENSION_ALWAYS_ALLOW = 0; // 0x0
+    field public static final int SPLIT_MIN_DIMENSION_DP_DEFAULT = 600; // 0x258
+  }
+
+  public static final class SplitRule.Companion {
+  }
+
+  public static final class SplitRule.FinishBehavior {
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior ADJACENT;
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior ALWAYS;
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior.Companion Companion;
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior NEVER;
+  }
+
+  public static final class SplitRule.FinishBehavior.Companion {
+  }
+
+}
+
+package androidx.window.layout {
+
+  public interface DisplayFeature {
+    method public android.graphics.Rect getBounds();
+    property public abstract android.graphics.Rect bounds;
+  }
+
+  public interface FoldingFeature extends androidx.window.layout.DisplayFeature {
+    method public androidx.window.layout.FoldingFeature.OcclusionType getOcclusionType();
+    method public androidx.window.layout.FoldingFeature.Orientation getOrientation();
+    method public androidx.window.layout.FoldingFeature.State getState();
+    method public boolean isSeparating();
+    property public abstract boolean isSeparating;
+    property public abstract androidx.window.layout.FoldingFeature.OcclusionType occlusionType;
+    property public abstract androidx.window.layout.FoldingFeature.Orientation orientation;
+    property public abstract androidx.window.layout.FoldingFeature.State state;
+  }
+
+  public static final class FoldingFeature.OcclusionType {
+    field public static final androidx.window.layout.FoldingFeature.OcclusionType.Companion Companion;
+    field public static final androidx.window.layout.FoldingFeature.OcclusionType FULL;
+    field public static final androidx.window.layout.FoldingFeature.OcclusionType NONE;
+  }
+
+  public static final class FoldingFeature.OcclusionType.Companion {
+  }
+
+  public static final class FoldingFeature.Orientation {
+    field public static final androidx.window.layout.FoldingFeature.Orientation.Companion Companion;
+    field public static final androidx.window.layout.FoldingFeature.Orientation HORIZONTAL;
+    field public static final androidx.window.layout.FoldingFeature.Orientation VERTICAL;
+  }
+
+  public static final class FoldingFeature.Orientation.Companion {
+  }
+
+  public static final class FoldingFeature.State {
+    field public static final androidx.window.layout.FoldingFeature.State.Companion Companion;
+    field public static final androidx.window.layout.FoldingFeature.State FLAT;
+    field public static final androidx.window.layout.FoldingFeature.State HALF_OPENED;
+  }
+
+  public static final class FoldingFeature.State.Companion {
+  }
+
+  public interface WindowInfoTracker {
+    method public default static androidx.window.layout.WindowInfoTracker getOrCreate(android.content.Context context);
+    method public kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(android.app.Activity activity);
+    field public static final androidx.window.layout.WindowInfoTracker.Companion Companion;
+  }
+
+  public static final class WindowInfoTracker.Companion {
+    method public androidx.window.layout.WindowInfoTracker getOrCreate(android.content.Context context);
+  }
+
+  public final class WindowLayoutInfo {
+    method public java.util.List<androidx.window.layout.DisplayFeature> getDisplayFeatures();
+    property public final java.util.List<androidx.window.layout.DisplayFeature> displayFeatures;
+  }
+
+  public final class WindowMetrics {
+    method public android.graphics.Rect getBounds();
+    property public final android.graphics.Rect bounds;
+  }
+
+  public interface WindowMetricsCalculator {
+    method public androidx.window.layout.WindowMetrics computeCurrentWindowMetrics(android.app.Activity activity);
+    method public default androidx.window.layout.WindowMetrics computeCurrentWindowMetrics(@UiContext android.content.Context context);
+    method public androidx.window.layout.WindowMetrics computeMaximumWindowMetrics(android.app.Activity activity);
+    method public default androidx.window.layout.WindowMetrics computeMaximumWindowMetrics(@UiContext android.content.Context context);
+    method public default static androidx.window.layout.WindowMetricsCalculator getOrCreate();
+    field public static final androidx.window.layout.WindowMetricsCalculator.Companion Companion;
+  }
+
+  public static final class WindowMetricsCalculator.Companion {
+    method public androidx.window.layout.WindowMetricsCalculator getOrCreate();
+  }
+
+}
+
diff --git a/window/window/api/public_plus_experimental_1.1.0-beta02.txt b/window/window/api/public_plus_experimental_1.1.0-beta02.txt
new file mode 100644
index 0000000..0ac0175
--- /dev/null
+++ b/window/window/api/public_plus_experimental_1.1.0-beta02.txt
@@ -0,0 +1,367 @@
+// Signature format: 4.0
+package androidx.window {
+
+  public final class WindowProperties {
+    field public static final androidx.window.WindowProperties INSTANCE;
+    field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE";
+    field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED";
+  }
+
+}
+
+package androidx.window.core {
+
+  @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.WARNING) @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalWindowApi {
+  }
+
+}
+
+package androidx.window.embedding {
+
+  public final class ActivityEmbeddingController {
+    method public static androidx.window.embedding.ActivityEmbeddingController getInstance(android.content.Context context);
+    method public boolean isActivityEmbedded(android.app.Activity activity);
+    field public static final androidx.window.embedding.ActivityEmbeddingController.Companion Companion;
+  }
+
+  public static final class ActivityEmbeddingController.Companion {
+    method public androidx.window.embedding.ActivityEmbeddingController getInstance(android.content.Context context);
+  }
+
+  public final class ActivityFilter {
+    ctor public ActivityFilter(android.content.ComponentName componentName, String? intentAction);
+    method public android.content.ComponentName getComponentName();
+    method public String? getIntentAction();
+    method public boolean matchesActivity(android.app.Activity activity);
+    method public boolean matchesIntent(android.content.Intent intent);
+    property public final android.content.ComponentName componentName;
+    property public final String? intentAction;
+  }
+
+  public final class ActivityRule extends androidx.window.embedding.EmbeddingRule {
+    method public boolean getAlwaysExpand();
+    method public java.util.Set<androidx.window.embedding.ActivityFilter> getFilters();
+    property public final boolean alwaysExpand;
+    property public final java.util.Set<androidx.window.embedding.ActivityFilter> filters;
+  }
+
+  public static final class ActivityRule.Builder {
+    ctor public ActivityRule.Builder(java.util.Set<androidx.window.embedding.ActivityFilter> filters);
+    method public androidx.window.embedding.ActivityRule build();
+    method public androidx.window.embedding.ActivityRule.Builder setAlwaysExpand(boolean alwaysExpand);
+    method public androidx.window.embedding.ActivityRule.Builder setTag(String? tag);
+  }
+
+  public final class ActivityStack {
+    method public operator boolean contains(android.app.Activity activity);
+    method public boolean isEmpty();
+    property public final boolean isEmpty;
+  }
+
+  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;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio ALWAYS_DISALLOW;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio.Companion Companion;
+  }
+
+  public static final class EmbeddingAspectRatio.Companion {
+    method public androidx.window.embedding.EmbeddingAspectRatio ratio(@FloatRange(from=1.0, fromInclusive=false) float ratio);
+  }
+
+  public abstract class EmbeddingRule {
+    method public final String? getTag();
+    property public final String? tag;
+  }
+
+  public final class RuleController {
+    method public void addRule(androidx.window.embedding.EmbeddingRule rule);
+    method public void clearRules();
+    method public static androidx.window.embedding.RuleController getInstance(android.content.Context context);
+    method public java.util.Set<androidx.window.embedding.EmbeddingRule> getRules();
+    method public static java.util.Set<androidx.window.embedding.EmbeddingRule> parseRules(android.content.Context context, @XmlRes int staticRuleResourceId);
+    method public void removeRule(androidx.window.embedding.EmbeddingRule rule);
+    method public void setRules(java.util.Set<? extends androidx.window.embedding.EmbeddingRule> rules);
+    field public static final androidx.window.embedding.RuleController.Companion Companion;
+  }
+
+  public static final class RuleController.Companion {
+    method public androidx.window.embedding.RuleController getInstance(android.content.Context context);
+    method public java.util.Set<androidx.window.embedding.EmbeddingRule> parseRules(android.content.Context context, @XmlRes int staticRuleResourceId);
+  }
+
+  public final class SplitAttributes {
+    method public androidx.window.embedding.SplitAttributes.LayoutDirection getLayoutDirection();
+    method public androidx.window.embedding.SplitAttributes.SplitType getSplitType();
+    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;
+  }
+
+  public static final class SplitAttributes.Builder {
+    ctor public SplitAttributes.Builder();
+    method public androidx.window.embedding.SplitAttributes build();
+    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);
+  }
+
+  public static final class SplitAttributes.Companion {
+  }
+
+  public static final class SplitAttributes.LayoutDirection {
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection BOTTOM_TO_TOP;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection.Companion Companion;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection LEFT_TO_RIGHT;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection LOCALE;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection RIGHT_TO_LEFT;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection TOP_TO_BOTTOM;
+  }
+
+  public static final class SplitAttributes.LayoutDirection.Companion {
+  }
+
+  public static final class SplitAttributes.SplitType {
+    method public static androidx.window.embedding.SplitAttributes.SplitType ratio(@FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) float ratio);
+    field public static final androidx.window.embedding.SplitAttributes.SplitType.Companion Companion;
+    field public static final androidx.window.embedding.SplitAttributes.SplitType SPLIT_TYPE_EQUAL;
+    field public static final androidx.window.embedding.SplitAttributes.SplitType SPLIT_TYPE_EXPAND;
+    field public static final androidx.window.embedding.SplitAttributes.SplitType SPLIT_TYPE_HINGE;
+  }
+
+  public static final class SplitAttributes.SplitType.Companion {
+    method public androidx.window.embedding.SplitAttributes.SplitType ratio(@FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) float ratio);
+  }
+
+  @androidx.window.core.ExperimentalWindowApi public final class SplitAttributesCalculatorParams {
+    method public boolean getAreDefaultConstraintsSatisfied();
+    method public androidx.window.embedding.SplitAttributes getDefaultSplitAttributes();
+    method public android.content.res.Configuration getParentConfiguration();
+    method public androidx.window.layout.WindowLayoutInfo getParentWindowLayoutInfo();
+    method public androidx.window.layout.WindowMetrics getParentWindowMetrics();
+    method public String? getSplitRuleTag();
+    property public final boolean areDefaultConstraintsSatisfied;
+    property public final androidx.window.embedding.SplitAttributes defaultSplitAttributes;
+    property public final android.content.res.Configuration parentConfiguration;
+    property public final androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo;
+    property public final androidx.window.layout.WindowMetrics parentWindowMetrics;
+    property public final String? splitRuleTag;
+  }
+
+  public final class SplitController {
+    method @Deprecated @androidx.window.core.ExperimentalWindowApi public void addSplitListener(android.app.Activity activity, java.util.concurrent.Executor executor, androidx.core.util.Consumer<java.util.List<androidx.window.embedding.SplitInfo>> consumer);
+    method @androidx.window.core.ExperimentalWindowApi public void clearSplitAttributesCalculator();
+    method public static androidx.window.embedding.SplitController getInstance(android.content.Context context);
+    method public androidx.window.embedding.SplitController.SplitSupportStatus getSplitSupportStatus();
+    method @androidx.window.core.ExperimentalWindowApi public boolean isSplitAttributesCalculatorSupported();
+    method @Deprecated @androidx.window.core.ExperimentalWindowApi public boolean isSplitSupported();
+    method @Deprecated @androidx.window.core.ExperimentalWindowApi public void removeSplitListener(androidx.core.util.Consumer<java.util.List<androidx.window.embedding.SplitInfo>> consumer);
+    method @androidx.window.core.ExperimentalWindowApi 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);
+    property public final androidx.window.embedding.SplitController.SplitSupportStatus splitSupportStatus;
+    field public static final androidx.window.embedding.SplitController.Companion Companion;
+  }
+
+  public static final class SplitController.Companion {
+    method public androidx.window.embedding.SplitController getInstance(android.content.Context context);
+  }
+
+  public static final class SplitController.SplitSupportStatus {
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus.Companion Companion;
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus SPLIT_AVAILABLE;
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus SPLIT_ERROR_PROPERTY_NOT_DECLARED;
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus SPLIT_UNAVAILABLE;
+  }
+
+  public static final class SplitController.SplitSupportStatus.Companion {
+  }
+
+  public final class SplitInfo {
+    method public operator boolean contains(android.app.Activity activity);
+    method public androidx.window.embedding.ActivityStack getPrimaryActivityStack();
+    method public androidx.window.embedding.ActivityStack getSecondaryActivityStack();
+    method public androidx.window.embedding.SplitAttributes getSplitAttributes();
+    property public final androidx.window.embedding.ActivityStack primaryActivityStack;
+    property public final androidx.window.embedding.ActivityStack secondaryActivityStack;
+    property public final androidx.window.embedding.SplitAttributes splitAttributes;
+  }
+
+  public final class SplitPairFilter {
+    ctor public SplitPairFilter(android.content.ComponentName primaryActivityName, android.content.ComponentName secondaryActivityName, String? secondaryActivityIntentAction);
+    method public android.content.ComponentName getPrimaryActivityName();
+    method public String? getSecondaryActivityIntentAction();
+    method public android.content.ComponentName getSecondaryActivityName();
+    method public boolean matchesActivityIntentPair(android.app.Activity primaryActivity, android.content.Intent secondaryActivityIntent);
+    method public boolean matchesActivityPair(android.app.Activity primaryActivity, android.app.Activity secondaryActivity);
+    property public final android.content.ComponentName primaryActivityName;
+    property public final String? secondaryActivityIntentAction;
+    property public final android.content.ComponentName secondaryActivityName;
+  }
+
+  public final class SplitPairRule extends androidx.window.embedding.SplitRule {
+    method public boolean getClearTop();
+    method public java.util.Set<androidx.window.embedding.SplitPairFilter> getFilters();
+    method public androidx.window.embedding.SplitRule.FinishBehavior getFinishPrimaryWithSecondary();
+    method public androidx.window.embedding.SplitRule.FinishBehavior getFinishSecondaryWithPrimary();
+    property public final boolean clearTop;
+    property public final java.util.Set<androidx.window.embedding.SplitPairFilter> filters;
+    property public final androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithSecondary;
+    property public final androidx.window.embedding.SplitRule.FinishBehavior finishSecondaryWithPrimary;
+  }
+
+  public static final class SplitPairRule.Builder {
+    ctor public SplitPairRule.Builder(java.util.Set<androidx.window.embedding.SplitPairFilter> filters);
+    method public androidx.window.embedding.SplitPairRule build();
+    method public androidx.window.embedding.SplitPairRule.Builder setClearTop(boolean clearTop);
+    method public androidx.window.embedding.SplitPairRule.Builder setDefaultSplitAttributes(androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+    method public androidx.window.embedding.SplitPairRule.Builder setFinishPrimaryWithSecondary(androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithSecondary);
+    method public androidx.window.embedding.SplitPairRule.Builder setFinishSecondaryWithPrimary(androidx.window.embedding.SplitRule.FinishBehavior finishSecondaryWithPrimary);
+    method public androidx.window.embedding.SplitPairRule.Builder setMaxAspectRatioInLandscape(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPairRule.Builder setMaxAspectRatioInPortrait(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPairRule.Builder setMinHeightDp(@IntRange(from=0L) int minHeightDp);
+    method public androidx.window.embedding.SplitPairRule.Builder setMinSmallestWidthDp(@IntRange(from=0L) int minSmallestWidthDp);
+    method public androidx.window.embedding.SplitPairRule.Builder setMinWidthDp(@IntRange(from=0L) int minWidthDp);
+    method public androidx.window.embedding.SplitPairRule.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();
+    method public android.content.Intent getPlaceholderIntent();
+    method public boolean isSticky();
+    property public final java.util.Set<androidx.window.embedding.ActivityFilter> filters;
+    property public final androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithPlaceholder;
+    property public final boolean isSticky;
+    property public final android.content.Intent placeholderIntent;
+  }
+
+  public static final class SplitPlaceholderRule.Builder {
+    ctor public SplitPlaceholderRule.Builder(java.util.Set<androidx.window.embedding.ActivityFilter> filters, android.content.Intent placeholderIntent);
+    method public androidx.window.embedding.SplitPlaceholderRule build();
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setDefaultSplitAttributes(androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithPlaceholder(androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithPlaceholder);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMaxAspectRatioInLandscape(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMaxAspectRatioInPortrait(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMinHeightDp(@IntRange(from=0L) int minHeightDp);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMinSmallestWidthDp(@IntRange(from=0L) int minSmallestWidthDp);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMinWidthDp(@IntRange(from=0L) int minWidthDp);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setSticky(boolean isSticky);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setTag(String? tag);
+  }
+
+  public class SplitRule extends androidx.window.embedding.EmbeddingRule {
+    method public final androidx.window.embedding.SplitAttributes getDefaultSplitAttributes();
+    method public final androidx.window.embedding.EmbeddingAspectRatio getMaxAspectRatioInLandscape();
+    method public final androidx.window.embedding.EmbeddingAspectRatio getMaxAspectRatioInPortrait();
+    method public final int getMinHeightDp();
+    method public final int getMinSmallestWidthDp();
+    method public final int getMinWidthDp();
+    property public final androidx.window.embedding.SplitAttributes defaultSplitAttributes;
+    property public final androidx.window.embedding.EmbeddingAspectRatio maxAspectRatioInLandscape;
+    property public final androidx.window.embedding.EmbeddingAspectRatio maxAspectRatioInPortrait;
+    property public final int minHeightDp;
+    property public final int minSmallestWidthDp;
+    property public final int minWidthDp;
+    field public static final androidx.window.embedding.SplitRule.Companion Companion;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT;
+    field public static final int SPLIT_MIN_DIMENSION_ALWAYS_ALLOW = 0; // 0x0
+    field public static final int SPLIT_MIN_DIMENSION_DP_DEFAULT = 600; // 0x258
+  }
+
+  public static final class SplitRule.Companion {
+  }
+
+  public static final class SplitRule.FinishBehavior {
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior ADJACENT;
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior ALWAYS;
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior.Companion Companion;
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior NEVER;
+  }
+
+  public static final class SplitRule.FinishBehavior.Companion {
+  }
+
+}
+
+package androidx.window.layout {
+
+  public interface DisplayFeature {
+    method public android.graphics.Rect getBounds();
+    property public abstract android.graphics.Rect bounds;
+  }
+
+  public interface FoldingFeature extends androidx.window.layout.DisplayFeature {
+    method public androidx.window.layout.FoldingFeature.OcclusionType getOcclusionType();
+    method public androidx.window.layout.FoldingFeature.Orientation getOrientation();
+    method public androidx.window.layout.FoldingFeature.State getState();
+    method public boolean isSeparating();
+    property public abstract boolean isSeparating;
+    property public abstract androidx.window.layout.FoldingFeature.OcclusionType occlusionType;
+    property public abstract androidx.window.layout.FoldingFeature.Orientation orientation;
+    property public abstract androidx.window.layout.FoldingFeature.State state;
+  }
+
+  public static final class FoldingFeature.OcclusionType {
+    field public static final androidx.window.layout.FoldingFeature.OcclusionType.Companion Companion;
+    field public static final androidx.window.layout.FoldingFeature.OcclusionType FULL;
+    field public static final androidx.window.layout.FoldingFeature.OcclusionType NONE;
+  }
+
+  public static final class FoldingFeature.OcclusionType.Companion {
+  }
+
+  public static final class FoldingFeature.Orientation {
+    field public static final androidx.window.layout.FoldingFeature.Orientation.Companion Companion;
+    field public static final androidx.window.layout.FoldingFeature.Orientation HORIZONTAL;
+    field public static final androidx.window.layout.FoldingFeature.Orientation VERTICAL;
+  }
+
+  public static final class FoldingFeature.Orientation.Companion {
+  }
+
+  public static final class FoldingFeature.State {
+    field public static final androidx.window.layout.FoldingFeature.State.Companion Companion;
+    field public static final androidx.window.layout.FoldingFeature.State FLAT;
+    field public static final androidx.window.layout.FoldingFeature.State HALF_OPENED;
+  }
+
+  public static final class FoldingFeature.State.Companion {
+  }
+
+  public interface WindowInfoTracker {
+    method public default static androidx.window.layout.WindowInfoTracker getOrCreate(android.content.Context context);
+    method @androidx.window.core.ExperimentalWindowApi public default kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(@UiContext android.content.Context context);
+    method public kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(android.app.Activity activity);
+    field public static final androidx.window.layout.WindowInfoTracker.Companion Companion;
+  }
+
+  public static final class WindowInfoTracker.Companion {
+    method public androidx.window.layout.WindowInfoTracker getOrCreate(android.content.Context context);
+  }
+
+  public final class WindowLayoutInfo {
+    method public java.util.List<androidx.window.layout.DisplayFeature> getDisplayFeatures();
+    property public final java.util.List<androidx.window.layout.DisplayFeature> displayFeatures;
+  }
+
+  public final class WindowMetrics {
+    method public android.graphics.Rect getBounds();
+    method @RequiresApi(android.os.Build.VERSION_CODES.R) @androidx.window.core.ExperimentalWindowApi public androidx.core.view.WindowInsetsCompat getWindowInsets();
+    property public final android.graphics.Rect bounds;
+  }
+
+  public interface WindowMetricsCalculator {
+    method public androidx.window.layout.WindowMetrics computeCurrentWindowMetrics(android.app.Activity activity);
+    method public default androidx.window.layout.WindowMetrics computeCurrentWindowMetrics(@UiContext android.content.Context context);
+    method public androidx.window.layout.WindowMetrics computeMaximumWindowMetrics(android.app.Activity activity);
+    method public default androidx.window.layout.WindowMetrics computeMaximumWindowMetrics(@UiContext android.content.Context context);
+    method public default static androidx.window.layout.WindowMetricsCalculator getOrCreate();
+    field public static final androidx.window.layout.WindowMetricsCalculator.Companion Companion;
+  }
+
+  public static final class WindowMetricsCalculator.Companion {
+    method public androidx.window.layout.WindowMetricsCalculator getOrCreate();
+  }
+
+}
+
diff --git a/window/window/api/res-1.1.0-beta02.txt b/window/window/api/res-1.1.0-beta02.txt
new file mode 100644
index 0000000..185352b
--- /dev/null
+++ b/window/window/api/res-1.1.0-beta02.txt
@@ -0,0 +1,21 @@
+attr activityAction
+attr activityName
+attr alwaysExpand
+attr animationBackgroundColor
+attr clearTop
+attr finishPrimaryWithPlaceholder
+attr finishPrimaryWithSecondary
+attr finishSecondaryWithPrimary
+attr placeholderActivityName
+attr primaryActivityName
+attr secondaryActivityAction
+attr secondaryActivityName
+attr splitLayoutDirection
+attr splitMaxAspectRatioInLandscape
+attr splitMaxAspectRatioInPortrait
+attr splitMinHeightDp
+attr splitMinSmallestWidthDp
+attr splitMinWidthDp
+attr splitRatio
+attr stickyPlaceholder
+attr tag
diff --git a/window/window/api/restricted_1.1.0-beta02.txt b/window/window/api/restricted_1.1.0-beta02.txt
new file mode 100644
index 0000000..5617dbb
--- /dev/null
+++ b/window/window/api/restricted_1.1.0-beta02.txt
@@ -0,0 +1,337 @@
+// Signature format: 4.0
+package androidx.window {
+
+  public final class WindowProperties {
+    field public static final androidx.window.WindowProperties INSTANCE;
+    field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE";
+    field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED";
+  }
+
+}
+
+package androidx.window.embedding {
+
+  public final class ActivityEmbeddingController {
+    method public static androidx.window.embedding.ActivityEmbeddingController getInstance(android.content.Context context);
+    method public boolean isActivityEmbedded(android.app.Activity activity);
+    field public static final androidx.window.embedding.ActivityEmbeddingController.Companion Companion;
+  }
+
+  public static final class ActivityEmbeddingController.Companion {
+    method public androidx.window.embedding.ActivityEmbeddingController getInstance(android.content.Context context);
+  }
+
+  public final class ActivityFilter {
+    ctor public ActivityFilter(android.content.ComponentName componentName, String? intentAction);
+    method public android.content.ComponentName getComponentName();
+    method public String? getIntentAction();
+    method public boolean matchesActivity(android.app.Activity activity);
+    method public boolean matchesIntent(android.content.Intent intent);
+    property public final android.content.ComponentName componentName;
+    property public final String? intentAction;
+  }
+
+  public final class ActivityRule extends androidx.window.embedding.EmbeddingRule {
+    method public boolean getAlwaysExpand();
+    method public java.util.Set<androidx.window.embedding.ActivityFilter> getFilters();
+    property public final boolean alwaysExpand;
+    property public final java.util.Set<androidx.window.embedding.ActivityFilter> filters;
+  }
+
+  public static final class ActivityRule.Builder {
+    ctor public ActivityRule.Builder(java.util.Set<androidx.window.embedding.ActivityFilter> filters);
+    method public androidx.window.embedding.ActivityRule build();
+    method public androidx.window.embedding.ActivityRule.Builder setAlwaysExpand(boolean alwaysExpand);
+    method public androidx.window.embedding.ActivityRule.Builder setTag(String? tag);
+  }
+
+  public final class ActivityStack {
+    method public operator boolean contains(android.app.Activity activity);
+    method public boolean isEmpty();
+    property public final boolean isEmpty;
+  }
+
+  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;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio ALWAYS_DISALLOW;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio.Companion Companion;
+  }
+
+  public static final class EmbeddingAspectRatio.Companion {
+    method public androidx.window.embedding.EmbeddingAspectRatio ratio(@FloatRange(from=1.0, fromInclusive=false) float ratio);
+  }
+
+  public abstract class EmbeddingRule {
+    method public final String? getTag();
+    property public final String? tag;
+  }
+
+  public final class RuleController {
+    method public void addRule(androidx.window.embedding.EmbeddingRule rule);
+    method public void clearRules();
+    method public static androidx.window.embedding.RuleController getInstance(android.content.Context context);
+    method public java.util.Set<androidx.window.embedding.EmbeddingRule> getRules();
+    method public static java.util.Set<androidx.window.embedding.EmbeddingRule> parseRules(android.content.Context context, @XmlRes int staticRuleResourceId);
+    method public void removeRule(androidx.window.embedding.EmbeddingRule rule);
+    method public void setRules(java.util.Set<? extends androidx.window.embedding.EmbeddingRule> rules);
+    field public static final androidx.window.embedding.RuleController.Companion Companion;
+  }
+
+  public static final class RuleController.Companion {
+    method public androidx.window.embedding.RuleController getInstance(android.content.Context context);
+    method public java.util.Set<androidx.window.embedding.EmbeddingRule> parseRules(android.content.Context context, @XmlRes int staticRuleResourceId);
+  }
+
+  public final class SplitAttributes {
+    method public androidx.window.embedding.SplitAttributes.LayoutDirection getLayoutDirection();
+    method public androidx.window.embedding.SplitAttributes.SplitType getSplitType();
+    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;
+  }
+
+  public static final class SplitAttributes.Builder {
+    ctor public SplitAttributes.Builder();
+    method public androidx.window.embedding.SplitAttributes build();
+    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);
+  }
+
+  public static final class SplitAttributes.Companion {
+  }
+
+  public static final class SplitAttributes.LayoutDirection {
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection BOTTOM_TO_TOP;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection.Companion Companion;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection LEFT_TO_RIGHT;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection LOCALE;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection RIGHT_TO_LEFT;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection TOP_TO_BOTTOM;
+  }
+
+  public static final class SplitAttributes.LayoutDirection.Companion {
+  }
+
+  public static final class SplitAttributes.SplitType {
+    method public static androidx.window.embedding.SplitAttributes.SplitType ratio(@FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) float ratio);
+    field public static final androidx.window.embedding.SplitAttributes.SplitType.Companion Companion;
+    field public static final androidx.window.embedding.SplitAttributes.SplitType SPLIT_TYPE_EQUAL;
+    field public static final androidx.window.embedding.SplitAttributes.SplitType SPLIT_TYPE_EXPAND;
+    field public static final androidx.window.embedding.SplitAttributes.SplitType SPLIT_TYPE_HINGE;
+  }
+
+  public static final class SplitAttributes.SplitType.Companion {
+    method public androidx.window.embedding.SplitAttributes.SplitType ratio(@FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) float ratio);
+  }
+
+  public final class SplitController {
+    method public static androidx.window.embedding.SplitController getInstance(android.content.Context context);
+    method public androidx.window.embedding.SplitController.SplitSupportStatus getSplitSupportStatus();
+    method public kotlinx.coroutines.flow.Flow<java.util.List<androidx.window.embedding.SplitInfo>> splitInfoList(android.app.Activity activity);
+    property public final androidx.window.embedding.SplitController.SplitSupportStatus splitSupportStatus;
+    field public static final androidx.window.embedding.SplitController.Companion Companion;
+  }
+
+  public static final class SplitController.Companion {
+    method public androidx.window.embedding.SplitController getInstance(android.content.Context context);
+  }
+
+  public static final class SplitController.SplitSupportStatus {
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus.Companion Companion;
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus SPLIT_AVAILABLE;
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus SPLIT_ERROR_PROPERTY_NOT_DECLARED;
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus SPLIT_UNAVAILABLE;
+  }
+
+  public static final class SplitController.SplitSupportStatus.Companion {
+  }
+
+  public final class SplitInfo {
+    method public operator boolean contains(android.app.Activity activity);
+    method public androidx.window.embedding.ActivityStack getPrimaryActivityStack();
+    method public androidx.window.embedding.ActivityStack getSecondaryActivityStack();
+    method public androidx.window.embedding.SplitAttributes getSplitAttributes();
+    property public final androidx.window.embedding.ActivityStack primaryActivityStack;
+    property public final androidx.window.embedding.ActivityStack secondaryActivityStack;
+    property public final androidx.window.embedding.SplitAttributes splitAttributes;
+  }
+
+  public final class SplitPairFilter {
+    ctor public SplitPairFilter(android.content.ComponentName primaryActivityName, android.content.ComponentName secondaryActivityName, String? secondaryActivityIntentAction);
+    method public android.content.ComponentName getPrimaryActivityName();
+    method public String? getSecondaryActivityIntentAction();
+    method public android.content.ComponentName getSecondaryActivityName();
+    method public boolean matchesActivityIntentPair(android.app.Activity primaryActivity, android.content.Intent secondaryActivityIntent);
+    method public boolean matchesActivityPair(android.app.Activity primaryActivity, android.app.Activity secondaryActivity);
+    property public final android.content.ComponentName primaryActivityName;
+    property public final String? secondaryActivityIntentAction;
+    property public final android.content.ComponentName secondaryActivityName;
+  }
+
+  public final class SplitPairRule extends androidx.window.embedding.SplitRule {
+    method public boolean getClearTop();
+    method public java.util.Set<androidx.window.embedding.SplitPairFilter> getFilters();
+    method public androidx.window.embedding.SplitRule.FinishBehavior getFinishPrimaryWithSecondary();
+    method public androidx.window.embedding.SplitRule.FinishBehavior getFinishSecondaryWithPrimary();
+    property public final boolean clearTop;
+    property public final java.util.Set<androidx.window.embedding.SplitPairFilter> filters;
+    property public final androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithSecondary;
+    property public final androidx.window.embedding.SplitRule.FinishBehavior finishSecondaryWithPrimary;
+  }
+
+  public static final class SplitPairRule.Builder {
+    ctor public SplitPairRule.Builder(java.util.Set<androidx.window.embedding.SplitPairFilter> filters);
+    method public androidx.window.embedding.SplitPairRule build();
+    method public androidx.window.embedding.SplitPairRule.Builder setClearTop(boolean clearTop);
+    method public androidx.window.embedding.SplitPairRule.Builder setDefaultSplitAttributes(androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+    method public androidx.window.embedding.SplitPairRule.Builder setFinishPrimaryWithSecondary(androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithSecondary);
+    method public androidx.window.embedding.SplitPairRule.Builder setFinishSecondaryWithPrimary(androidx.window.embedding.SplitRule.FinishBehavior finishSecondaryWithPrimary);
+    method public androidx.window.embedding.SplitPairRule.Builder setMaxAspectRatioInLandscape(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPairRule.Builder setMaxAspectRatioInPortrait(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPairRule.Builder setMinHeightDp(@IntRange(from=0L) int minHeightDp);
+    method public androidx.window.embedding.SplitPairRule.Builder setMinSmallestWidthDp(@IntRange(from=0L) int minSmallestWidthDp);
+    method public androidx.window.embedding.SplitPairRule.Builder setMinWidthDp(@IntRange(from=0L) int minWidthDp);
+    method public androidx.window.embedding.SplitPairRule.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();
+    method public android.content.Intent getPlaceholderIntent();
+    method public boolean isSticky();
+    property public final java.util.Set<androidx.window.embedding.ActivityFilter> filters;
+    property public final androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithPlaceholder;
+    property public final boolean isSticky;
+    property public final android.content.Intent placeholderIntent;
+  }
+
+  public static final class SplitPlaceholderRule.Builder {
+    ctor public SplitPlaceholderRule.Builder(java.util.Set<androidx.window.embedding.ActivityFilter> filters, android.content.Intent placeholderIntent);
+    method public androidx.window.embedding.SplitPlaceholderRule build();
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setDefaultSplitAttributes(androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithPlaceholder(androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithPlaceholder);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMaxAspectRatioInLandscape(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMaxAspectRatioInPortrait(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMinHeightDp(@IntRange(from=0L) int minHeightDp);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMinSmallestWidthDp(@IntRange(from=0L) int minSmallestWidthDp);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMinWidthDp(@IntRange(from=0L) int minWidthDp);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setSticky(boolean isSticky);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setTag(String? tag);
+  }
+
+  public class SplitRule extends androidx.window.embedding.EmbeddingRule {
+    method public final androidx.window.embedding.SplitAttributes getDefaultSplitAttributes();
+    method public final androidx.window.embedding.EmbeddingAspectRatio getMaxAspectRatioInLandscape();
+    method public final androidx.window.embedding.EmbeddingAspectRatio getMaxAspectRatioInPortrait();
+    method public final int getMinHeightDp();
+    method public final int getMinSmallestWidthDp();
+    method public final int getMinWidthDp();
+    property public final androidx.window.embedding.SplitAttributes defaultSplitAttributes;
+    property public final androidx.window.embedding.EmbeddingAspectRatio maxAspectRatioInLandscape;
+    property public final androidx.window.embedding.EmbeddingAspectRatio maxAspectRatioInPortrait;
+    property public final int minHeightDp;
+    property public final int minSmallestWidthDp;
+    property public final int minWidthDp;
+    field public static final androidx.window.embedding.SplitRule.Companion Companion;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT;
+    field public static final int SPLIT_MIN_DIMENSION_ALWAYS_ALLOW = 0; // 0x0
+    field public static final int SPLIT_MIN_DIMENSION_DP_DEFAULT = 600; // 0x258
+  }
+
+  public static final class SplitRule.Companion {
+  }
+
+  public static final class SplitRule.FinishBehavior {
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior ADJACENT;
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior ALWAYS;
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior.Companion Companion;
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior NEVER;
+  }
+
+  public static final class SplitRule.FinishBehavior.Companion {
+  }
+
+}
+
+package androidx.window.layout {
+
+  public interface DisplayFeature {
+    method public android.graphics.Rect getBounds();
+    property public abstract android.graphics.Rect bounds;
+  }
+
+  public interface FoldingFeature extends androidx.window.layout.DisplayFeature {
+    method public androidx.window.layout.FoldingFeature.OcclusionType getOcclusionType();
+    method public androidx.window.layout.FoldingFeature.Orientation getOrientation();
+    method public androidx.window.layout.FoldingFeature.State getState();
+    method public boolean isSeparating();
+    property public abstract boolean isSeparating;
+    property public abstract androidx.window.layout.FoldingFeature.OcclusionType occlusionType;
+    property public abstract androidx.window.layout.FoldingFeature.Orientation orientation;
+    property public abstract androidx.window.layout.FoldingFeature.State state;
+  }
+
+  public static final class FoldingFeature.OcclusionType {
+    field public static final androidx.window.layout.FoldingFeature.OcclusionType.Companion Companion;
+    field public static final androidx.window.layout.FoldingFeature.OcclusionType FULL;
+    field public static final androidx.window.layout.FoldingFeature.OcclusionType NONE;
+  }
+
+  public static final class FoldingFeature.OcclusionType.Companion {
+  }
+
+  public static final class FoldingFeature.Orientation {
+    field public static final androidx.window.layout.FoldingFeature.Orientation.Companion Companion;
+    field public static final androidx.window.layout.FoldingFeature.Orientation HORIZONTAL;
+    field public static final androidx.window.layout.FoldingFeature.Orientation VERTICAL;
+  }
+
+  public static final class FoldingFeature.Orientation.Companion {
+  }
+
+  public static final class FoldingFeature.State {
+    field public static final androidx.window.layout.FoldingFeature.State.Companion Companion;
+    field public static final androidx.window.layout.FoldingFeature.State FLAT;
+    field public static final androidx.window.layout.FoldingFeature.State HALF_OPENED;
+  }
+
+  public static final class FoldingFeature.State.Companion {
+  }
+
+  public interface WindowInfoTracker {
+    method public default static androidx.window.layout.WindowInfoTracker getOrCreate(android.content.Context context);
+    method public kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(android.app.Activity activity);
+    field public static final androidx.window.layout.WindowInfoTracker.Companion Companion;
+  }
+
+  public static final class WindowInfoTracker.Companion {
+    method public androidx.window.layout.WindowInfoTracker getOrCreate(android.content.Context context);
+  }
+
+  public final class WindowLayoutInfo {
+    method public java.util.List<androidx.window.layout.DisplayFeature> getDisplayFeatures();
+    property public final java.util.List<androidx.window.layout.DisplayFeature> displayFeatures;
+  }
+
+  public final class WindowMetrics {
+    method public android.graphics.Rect getBounds();
+    property public final android.graphics.Rect bounds;
+  }
+
+  public interface WindowMetricsCalculator {
+    method public androidx.window.layout.WindowMetrics computeCurrentWindowMetrics(android.app.Activity activity);
+    method public default androidx.window.layout.WindowMetrics computeCurrentWindowMetrics(@UiContext android.content.Context context);
+    method public androidx.window.layout.WindowMetrics computeMaximumWindowMetrics(android.app.Activity activity);
+    method public default androidx.window.layout.WindowMetrics computeMaximumWindowMetrics(@UiContext android.content.Context context);
+    method public default static androidx.window.layout.WindowMetricsCalculator getOrCreate();
+    field public static final androidx.window.layout.WindowMetricsCalculator.Companion Companion;
+  }
+
+  public static final class WindowMetricsCalculator.Companion {
+    method public androidx.window.layout.WindowMetricsCalculator getOrCreate();
+  }
+
+}
+
diff --git a/window/window/build.gradle b/window/window/build.gradle
index 23e165d..32d38d8 100644
--- a/window/window/build.gradle
+++ b/window/window/build.gradle
@@ -48,10 +48,10 @@
     implementation("androidx.annotation:annotation:1.3.0")
     implementation("androidx.collection:collection:1.1.0")
     implementation("androidx.core:core:1.8.0")
-    implementation(project(":window:extensions:core:core"))
 
+    implementation("androidx.window.extensions.core:core:1.0.0-beta01")
     compileOnly(project(":window:sidecar:sidecar"))
-    compileOnly(project(":window:extensions:extensions"))
+    compileOnly("androidx.window.extensions:extensions:1.1.0-beta01")
 
     testImplementation(libs.testCore)
     testImplementation(libs.testRunner)
@@ -61,9 +61,9 @@
     testImplementation(libs.mockitoCore4)
     testImplementation(libs.mockitoKotlin4)
     testImplementation(libs.kotlinCoroutinesTest)
+    testImplementation("androidx.window.extensions.core:core:1.0.0-beta01")
     testImplementation(compileOnly(project(":window:sidecar:sidecar")))
-    testImplementation(compileOnly(project(":window:extensions:extensions")))
-    testImplementation(implementation(project(":window:extensions:core:core")))
+    testImplementation(compileOnly("androidx.window.extensions:extensions:1.1.0-beta01"))
 
     androidTestImplementation(libs.testCore)
     androidTestImplementation(libs.kotlinTestJunit)
@@ -77,9 +77,9 @@
     androidTestImplementation(libs.multidex)
     androidTestImplementation(libs.truth)
     androidTestImplementation(libs.junit) // Needed for Assert.assertThrows
-    androidTestImplementation(compileOnly(project(":window:extensions:extensions")))
+    androidTestImplementation("androidx.window.extensions.core:core:1.0.0-beta01")
     androidTestImplementation(compileOnly(project(":window:sidecar:sidecar")))
-    androidTestImplementation(implementation(project(":window:extensions:core:core")))
+    androidTestImplementation(compileOnly("androidx.window.extensions:extensions:1.1.0-beta01"))
 }
 
 androidx {
diff --git a/work/work-runtime/api/current.txt b/work/work-runtime/api/current.txt
index 3e5a864..2b6df42d 100644
--- a/work/work-runtime/api/current.txt
+++ b/work/work-runtime/api/current.txt
@@ -336,6 +336,9 @@
   }
 
   public final class WorkInfo {
+    ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis, optional androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo, optional long earliestPossibleRuntimeMillis);
+    ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis, optional androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo);
+    ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount);
@@ -343,23 +346,37 @@
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags);
     method public androidx.work.Constraints getConstraints();
+    method public long getEarliestPossibleRuntimeMillis();
     method public int getGeneration();
     method public java.util.UUID getId();
+    method public long getInitialDelayMillis();
     method public androidx.work.Data getOutputData();
+    method public androidx.work.WorkInfo.PeriodicityInfo? getPeriodicityInfo();
     method public androidx.work.Data getProgress();
     method @IntRange(from=0L) public int getRunAttemptCount();
     method public androidx.work.WorkInfo.State getState();
     method public java.util.Set<java.lang.String> getTags();
     property public final androidx.work.Constraints constraints;
+    property public final long earliestPossibleRuntimeMillis;
     property public final int generation;
     property public final java.util.UUID id;
+    property public final long initialDelayMillis;
     property public final androidx.work.Data outputData;
+    property public final androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo;
     property public final androidx.work.Data progress;
     property @IntRange(from=0L) public final int runAttemptCount;
     property public final androidx.work.WorkInfo.State state;
     property public final java.util.Set<java.lang.String> tags;
   }
 
+  public static final class WorkInfo.PeriodicityInfo {
+    ctor public WorkInfo.PeriodicityInfo(long repeatIntervalMillis, long flexIntervalMillis);
+    method public long getFlexIntervalMillis();
+    method public long getRepeatIntervalMillis();
+    property public final long flexIntervalMillis;
+    property public final long repeatIntervalMillis;
+  }
+
   public enum WorkInfo.State {
     method public final boolean isFinished();
     method public static androidx.work.WorkInfo.State valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
diff --git a/work/work-runtime/api/public_plus_experimental_current.txt b/work/work-runtime/api/public_plus_experimental_current.txt
index 3e5a864..2b6df42d 100644
--- a/work/work-runtime/api/public_plus_experimental_current.txt
+++ b/work/work-runtime/api/public_plus_experimental_current.txt
@@ -336,6 +336,9 @@
   }
 
   public final class WorkInfo {
+    ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis, optional androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo, optional long earliestPossibleRuntimeMillis);
+    ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis, optional androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo);
+    ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount);
@@ -343,23 +346,37 @@
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags);
     method public androidx.work.Constraints getConstraints();
+    method public long getEarliestPossibleRuntimeMillis();
     method public int getGeneration();
     method public java.util.UUID getId();
+    method public long getInitialDelayMillis();
     method public androidx.work.Data getOutputData();
+    method public androidx.work.WorkInfo.PeriodicityInfo? getPeriodicityInfo();
     method public androidx.work.Data getProgress();
     method @IntRange(from=0L) public int getRunAttemptCount();
     method public androidx.work.WorkInfo.State getState();
     method public java.util.Set<java.lang.String> getTags();
     property public final androidx.work.Constraints constraints;
+    property public final long earliestPossibleRuntimeMillis;
     property public final int generation;
     property public final java.util.UUID id;
+    property public final long initialDelayMillis;
     property public final androidx.work.Data outputData;
+    property public final androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo;
     property public final androidx.work.Data progress;
     property @IntRange(from=0L) public final int runAttemptCount;
     property public final androidx.work.WorkInfo.State state;
     property public final java.util.Set<java.lang.String> tags;
   }
 
+  public static final class WorkInfo.PeriodicityInfo {
+    ctor public WorkInfo.PeriodicityInfo(long repeatIntervalMillis, long flexIntervalMillis);
+    method public long getFlexIntervalMillis();
+    method public long getRepeatIntervalMillis();
+    property public final long flexIntervalMillis;
+    property public final long repeatIntervalMillis;
+  }
+
   public enum WorkInfo.State {
     method public final boolean isFinished();
     method public static androidx.work.WorkInfo.State valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
diff --git a/work/work-runtime/api/restricted_current.txt b/work/work-runtime/api/restricted_current.txt
index 3e5a864..2b6df42d 100644
--- a/work/work-runtime/api/restricted_current.txt
+++ b/work/work-runtime/api/restricted_current.txt
@@ -336,6 +336,9 @@
   }
 
   public final class WorkInfo {
+    ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis, optional androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo, optional long earliestPossibleRuntimeMillis);
+    ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis, optional androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo);
+    ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints, optional long initialDelayMillis);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation, optional androidx.work.Constraints constraints);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount, optional int generation);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData, optional androidx.work.Data progress, optional int runAttemptCount);
@@ -343,23 +346,37 @@
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags, optional androidx.work.Data outputData);
     ctor public WorkInfo(java.util.UUID id, androidx.work.WorkInfo.State state, java.util.Set<java.lang.String> tags);
     method public androidx.work.Constraints getConstraints();
+    method public long getEarliestPossibleRuntimeMillis();
     method public int getGeneration();
     method public java.util.UUID getId();
+    method public long getInitialDelayMillis();
     method public androidx.work.Data getOutputData();
+    method public androidx.work.WorkInfo.PeriodicityInfo? getPeriodicityInfo();
     method public androidx.work.Data getProgress();
     method @IntRange(from=0L) public int getRunAttemptCount();
     method public androidx.work.WorkInfo.State getState();
     method public java.util.Set<java.lang.String> getTags();
     property public final androidx.work.Constraints constraints;
+    property public final long earliestPossibleRuntimeMillis;
     property public final int generation;
     property public final java.util.UUID id;
+    property public final long initialDelayMillis;
     property public final androidx.work.Data outputData;
+    property public final androidx.work.WorkInfo.PeriodicityInfo? periodicityInfo;
     property public final androidx.work.Data progress;
     property @IntRange(from=0L) public final int runAttemptCount;
     property public final androidx.work.WorkInfo.State state;
     property public final java.util.Set<java.lang.String> tags;
   }
 
+  public static final class WorkInfo.PeriodicityInfo {
+    ctor public WorkInfo.PeriodicityInfo(long repeatIntervalMillis, long flexIntervalMillis);
+    method public long getFlexIntervalMillis();
+    method public long getRepeatIntervalMillis();
+    property public final long flexIntervalMillis;
+    property public final long repeatIntervalMillis;
+  }
+
   public enum WorkInfo.State {
     method public final boolean isFinished();
     method public static androidx.work.WorkInfo.State valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/WorkUpdateTest.kt b/work/work-runtime/src/androidTest/java/androidx/work/WorkUpdateTest.kt
index daa5863..2084fdd 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/WorkUpdateTest.kt
+++ b/work/work-runtime/src/androidTest/java/androidx/work/WorkUpdateTest.kt
@@ -438,7 +438,7 @@
         val spec = workManager.workDatabase.workSpecDao().getWorkSpec(request.stringId)!!
         val delta = spec.calculateNextRunTime() - System.currentTimeMillis()
         assertThat(delta).isGreaterThan(0)
-        workManager.workDatabase.workSpecDao().setLastEnqueuedTime(
+        workManager.workDatabase.workSpecDao().setLastEnqueueTime(
             request.stringId,
             spec.lastEnqueueTime - delta
         )
@@ -458,7 +458,7 @@
             .setInitialDelay(10, TimeUnit.MINUTES).build()
         val enqueueTime = System.currentTimeMillis()
         workManager.enqueue(request).result.get()
-        workManager.workDatabase.workSpecDao().setLastEnqueuedTime(
+        workManager.workDatabase.workSpecDao().setLastEnqueueTime(
             request.stringId,
             enqueueTime - TimeUnit.MINUTES.toMillis(5)
         )
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java b/work/work-runtime/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java
index d64b7d8..8b4a84c 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java
+++ b/work/work-runtime/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java
@@ -123,6 +123,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 
+import java.time.Duration;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
@@ -1179,7 +1180,7 @@
 
     @Test
     @MediumTest
-    public void testGetWorkInfoByIdSyncConstraints() throws Exception {
+    public void testGetWorkInfoByIdSync_constraints() throws Exception {
         Constraints constraints = new Constraints.Builder()
                 .setRequiresCharging(true)
                 .setRequiredNetworkType(CONNECTED)
@@ -1197,6 +1198,44 @@
 
     @Test
     @MediumTest
+    public void testGetWorkInfoByIdSync_oneTime_schedules() throws Exception {
+        OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class)
+                .setInitialState(SUCCEEDED)
+                .setInitialDelay(1234, TimeUnit.MILLISECONDS)
+                .build();
+        insertWorkSpecAndTags(work);
+
+        WorkInfo workInfo = mWorkManagerImpl.getWorkInfoById(work.getId()).get();
+        assertThat(workInfo.getId().toString(), is(work.getStringId()));
+        assertThat(workInfo.getInitialDelayMillis(), equalTo(1234L));
+        assertThat(workInfo.getPeriodicityInfo(), is(nullValue()));
+    }
+
+    @Test
+    @MediumTest
+    @SdkSuppress(minSdkVersion = 26)
+    public void testGetWorkInfoByIdSync_periodic_schedules() throws Exception {
+        Duration repeatInterval = Duration.ofMinutes(60);
+        Duration flexInterval = Duration.ofMinutes(30);
+
+        PeriodicWorkRequest work =
+                new PeriodicWorkRequest.Builder(TestWorker.class, repeatInterval, flexInterval)
+                        .setInitialState(SUCCEEDED)
+                        .setInitialDelay(1234, TimeUnit.MILLISECONDS)
+                        .build();
+        insertWorkSpecAndTags(work);
+
+        WorkInfo workInfo = mWorkManagerImpl.getWorkInfoById(work.getId()).get();
+        assertThat(workInfo.getId().toString(), is(work.getStringId()));
+        assertThat(workInfo.getInitialDelayMillis(), equalTo(1234L));
+        assertThat(workInfo.getPeriodicityInfo().getRepeatIntervalMillis(), equalTo(
+                repeatInterval.toMillis()));
+        assertThat(workInfo.getPeriodicityInfo().getFlexIntervalMillis(), equalTo(
+                flexInterval.toMillis()));
+    }
+
+    @Test
+    @MediumTest
     public void testGetWorkInfoByIdSync_returnsNullIfNotInDatabase()
             throws ExecutionException, InterruptedException {
 
@@ -1265,6 +1304,79 @@
     }
 
     @Test
+    @SmallTest
+    public void testGetWorkInfoById_earliestPossibleRunTime_notEnqueued()
+            throws ExecutionException, InterruptedException {
+        OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class).build();
+        work.getWorkSpec().state = RUNNING;
+        work.getWorkSpec().lastEnqueueTime = 1000L;
+        insertWorkSpecAndTags(work);
+
+        WorkInfo info = mWorkManagerImpl.getWorkInfoById(work.getId()).get();
+
+        assertThat(info.getState(), equalTo(RUNNING));
+        assertThat(info.getEarliestPossibleRuntimeMillis(), equalTo(Long.MAX_VALUE));
+    }
+
+    @Test
+    @SmallTest
+    public void testGetWorkInfoById_earliestPossibleRunTime_enqueued()
+            throws ExecutionException, InterruptedException {
+        OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class).build();
+        work.getWorkSpec().lastEnqueueTime = 1000L;
+        insertWorkSpecAndTags(work);
+
+        WorkInfo info = mWorkManagerImpl.getWorkInfoById(work.getId()).get();
+
+        assertThat(info.getState(), equalTo(ENQUEUED));
+        assertThat(info.getEarliestPossibleRuntimeMillis(),
+                equalTo(1000L));
+    }
+
+    @Test
+    @SmallTest
+    @SdkSuppress(minSdkVersion = 26)
+    public void testGetWorkInfoById_earliestPossibleRunTime_onetime_initialDelay()
+            throws ExecutionException, InterruptedException {
+        OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class).setInitialDelay(
+                Duration.ofMillis(2000)).build();
+        work.getWorkSpec().lastEnqueueTime = 1000L;
+        insertWorkSpecAndTags(work);
+
+        WorkInfo info = mWorkManagerImpl.getWorkInfoById(work.getId()).get();
+
+        assertThat(info.getState(), equalTo(ENQUEUED));
+        assertThat(info.getEarliestPossibleRuntimeMillis(),
+                equalTo(3000L));
+    }
+
+    @Test
+    @SmallTest
+    @SdkSuppress(minSdkVersion = 26)
+    public void testGetWorkInfoById_earliestPossibleRunTime_periodic_period()
+            throws ExecutionException, InterruptedException {
+        Duration period = Duration.ofMinutes(15);
+        Duration initialDelay = Duration.ofMillis(2000);
+        Duration lastEnqueueTime = Duration.ofMillis(1000L);
+
+        PeriodicWorkRequest work0 = new PeriodicWorkRequest.Builder(
+                TestWorker.class, period)
+                .setInitialDelay(initialDelay)
+                .build();
+
+        work0.getWorkSpec().lastEnqueueTime = lastEnqueueTime.toMillis();
+        work0.getWorkSpec().setPeriodCount(3);
+        insertWorkSpecAndTags(work0);
+
+        WorkInfo info = mWorkManagerImpl.getWorkInfoById(work0.getId()).get();
+
+        assertThat(info.getState(), equalTo(ENQUEUED));
+        assertThat(info.getEarliestPossibleRuntimeMillis(),
+                equalTo(lastEnqueueTime.plus(period).toMillis()));
+        assertThat(info.getInitialDelayMillis(), equalTo(initialDelay.toMillis()));
+    }
+
+    @Test
     @MediumTest
     public void testGetWorkInfosByTagSync() throws ExecutionException, InterruptedException {
         final String firstTag = "first_tag";
@@ -1801,7 +1913,7 @@
             }
         };
         InstantWorkTaskExecutor workTaskExecutor = new InstantWorkTaskExecutor();
-        Processor processor = new Processor(mContext,  mConfiguration, workTaskExecutor, mDatabase);
+        Processor processor = new Processor(mContext, mConfiguration, workTaskExecutor, mDatabase);
         WorkLauncherImpl launcher = new WorkLauncherImpl(processor, workTaskExecutor);
 
         Trackers trackers = mWorkManagerImpl.getTrackers();
@@ -1811,7 +1923,7 @@
                         mWorkManagerImpl.getConfiguration(),
                         trackers,
                         processor, launcher);
-        mWorkManagerImpl =  createWorkManager(mContext, mConfiguration, workTaskExecutor,
+        mWorkManagerImpl = createWorkManager(mContext, mConfiguration, workTaskExecutor,
                 mDatabase, trackers, processor, schedulers(scheduler));
 
         WorkManagerImpl.setDelegate(mWorkManagerImpl);
@@ -2033,6 +2145,10 @@
 
     @NonNull
     private static WorkInfo createWorkInfo(UUID id, WorkInfo.State state, List<String> tags) {
-        return new WorkInfo(id, state, new HashSet<>(tags), Data.EMPTY, Data.EMPTY, 0, 0);
+        return new WorkInfo(
+                id, state, new HashSet<>(tags), Data.EMPTY, Data.EMPTY, 0, 0,
+                Constraints.NONE, 0, null,
+                Long.MAX_VALUE // Documented error value.
+        );
     }
 }
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/impl/WorkerWrapperTest.java b/work/work-runtime/src/androidTest/java/androidx/work/impl/WorkerWrapperTest.java
index df87e7e..0e16ded 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/impl/WorkerWrapperTest.java
+++ b/work/work-runtime/src/androidTest/java/androidx/work/impl/WorkerWrapperTest.java
@@ -520,7 +520,7 @@
         mDatabase.beginTransaction();
         try {
             mWorkSpecDao.insertWorkSpec(retryWork.getWorkSpec());
-            mWorkSpecDao.setLastEnqueuedTime(retryWork.getStringId(), future);
+            mWorkSpecDao.setLastEnqueueTime(retryWork.getStringId(), future);
             mWorkSpecDao.incrementWorkSpecRunAttemptCount(retryWork.getStringId());
             mDatabase.setTransactionSuccessful();
         } finally {
diff --git a/work/work-runtime/src/main/java/androidx/work/WorkInfo.kt b/work/work-runtime/src/main/java/androidx/work/WorkInfo.kt
index 3923f2b..67c79d0 100644
--- a/work/work-runtime/src/main/java/androidx/work/WorkInfo.kt
+++ b/work/work-runtime/src/main/java/androidx/work/WorkInfo.kt
@@ -72,6 +72,39 @@
      * [Constraints] of this worker.
      */
     val constraints: Constraints = Constraints.NONE,
+
+    /** The initial delay for this work set in the [WorkRequest] */
+    val initialDelayMillis: Long = 0,
+
+    /**
+     * For periodic work, the period and flex duration set in the [PeriodicWorkRequest].
+     *
+     * Null if this is onetime work.
+     */
+    val periodicityInfo: PeriodicityInfo? = null,
+
+    /**
+     * The earliest time this work is eligible to run next, if this work is [State.ENQUEUED].
+     *
+     * This is the earliest [System.currentTimeMillis] time that WorkManager would consider running
+     * this work, regardless of any other system. It only represents the time that the
+     * initialDelay, periodic configuration, and backoff criteria are considered to be met.
+     *
+     * Work will almost never run at this time in the real world. This method is intended for use
+     * in scheduling tests or to check set schedules in app. Work run times are dependent on
+     * many factors like the underlying system scheduler, doze and power saving modes of the OS, and
+     * meeting any configured constraints. This is expected and is not considered a bug.
+     *
+     * The returned value may be in the past if the work was not able to run at that time. It will
+     * be eligible to run any time after that time.
+     *
+     * Defaults to [Long.MAX_VALUE] for all other states, including if the work is currently
+     * [State.RUNNING] or [State.BLOCKED] on prerequisite work.
+     *
+     * Even if this value is set, the work may not be registered with the system scheduler if
+     * there are limited scheduling slots or other factors.
+     */
+    val earliestPossibleRuntimeMillis: Long = Long.MAX_VALUE,
 ) {
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
@@ -83,6 +116,9 @@
         if (state != workInfo.state) return false
         if (outputData != workInfo.outputData) return false
         if (constraints != workInfo.constraints) return false
+        if (initialDelayMillis != workInfo.initialDelayMillis) return false
+        if (periodicityInfo != workInfo.periodicityInfo) return false
+        if (earliestPossibleRuntimeMillis != workInfo.earliestPossibleRuntimeMillis) return false
         return if (tags != workInfo.tags) false else progress == workInfo.progress
     }
 
@@ -95,13 +131,19 @@
         result = 31 * result + runAttemptCount
         result = 31 * result + generation
         result = 31 * result + constraints.hashCode()
+        result = 31 * result + initialDelayMillis.hashCode()
+        result = 31 * result + periodicityInfo.hashCode()
+        result = 31 * result + earliestPossibleRuntimeMillis.hashCode()
         return result
     }
 
     override fun toString(): String {
         return ("WorkInfo{id='$id', state=$state, " +
             "outputData=$outputData, tags=$tags, progress=$progress, " +
-            "runAttemptCount=$runAttemptCount, generation=$generation, constraints=$constraints}")
+            "runAttemptCount=$runAttemptCount, generation=$generation, " +
+            "constraints=$constraints}, initialDelayMillis=$initialDelayMillis, " +
+            "periodicityInfo=$periodicityInfo, " +
+            "earliestPossibleRunTimeMillis=$earliestPossibleRuntimeMillis")
     }
 
     /**
@@ -151,4 +193,35 @@
         val isFinished: Boolean
             get() = this == SUCCEEDED || this == FAILED || this == CANCELLED
     }
-}
\ No newline at end of file
+
+    /** A periodic work's interval and flex duration */
+    class PeriodicityInfo(
+        /**
+         * The periodic work's configured repeat interval in millis, as configured in
+         * [PeriodicWorkRequest.Builder]
+         */
+        val repeatIntervalMillis: Long,
+        /**
+         * The duration in millis in which this work repeats from the end of the `repeatInterval`,
+         * as configured in [PeriodicWorkRequest.Builder].
+         */
+        val flexIntervalMillis: Long
+    ) {
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (other == null || javaClass != other.javaClass) return false
+            val period = other as PeriodicityInfo
+            return period.repeatIntervalMillis == repeatIntervalMillis &&
+                period.flexIntervalMillis == flexIntervalMillis
+        }
+
+        override fun hashCode(): Int {
+            return 31 * repeatIntervalMillis.hashCode() + flexIntervalMillis.hashCode()
+        }
+
+        override fun toString(): String {
+            return "PeriodicityInfo{repeatIntervalMillis=$repeatIntervalMillis, " +
+                "flexIntervalMillis=$flexIntervalMillis}"
+        }
+    }
+}
diff --git a/work/work-runtime/src/main/java/androidx/work/WorkRequest.kt b/work/work-runtime/src/main/java/androidx/work/WorkRequest.kt
index b9688ec3..60dd8fe 100644
--- a/work/work-runtime/src/main/java/androidx/work/WorkRequest.kt
+++ b/work/work-runtime/src/main/java/androidx/work/WorkRequest.kt
@@ -306,16 +306,16 @@
         }
 
         /**
-         * Sets the period start time for this work. Used in testing only.
+         * Sets the enqueue time for this work. Used in testing only.
          *
-         * @param periodStartTime the period start time in `timeUnit` units
+         * @param lastEnqueueTime The enqueue time in `timeUnit` units
          * @param timeUnit The [TimeUnit] for `periodStartTime`
          * @return The current [Builder]
          */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         @VisibleForTesting
-        fun setLastEnqueueTime(periodStartTime: Long, timeUnit: TimeUnit): B {
-            workSpec.lastEnqueueTime = timeUnit.toMillis(periodStartTime)
+        fun setLastEnqueueTime(lastEnqueueTime: Long, timeUnit: TimeUnit): B {
+            workSpec.lastEnqueueTime = timeUnit.toMillis(lastEnqueueTime)
             return thisObject
         }
 
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/WorkerWrapper.java b/work/work-runtime/src/main/java/androidx/work/impl/WorkerWrapper.java
index c44cedd..bb3f931 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/WorkerWrapper.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/WorkerWrapper.java
@@ -533,7 +533,7 @@
         mWorkDatabase.beginTransaction();
         try {
             mWorkSpecDao.setState(ENQUEUED, mWorkSpecId);
-            mWorkSpecDao.setLastEnqueuedTime(mWorkSpecId, System.currentTimeMillis());
+            mWorkSpecDao.setLastEnqueueTime(mWorkSpecId, System.currentTimeMillis());
             mWorkSpecDao.markWorkSpecScheduled(mWorkSpecId, SCHEDULE_NOT_REQUESTED_YET);
             mWorkDatabase.setTransactionSuccessful();
         } finally {
@@ -545,11 +545,11 @@
     private void resetPeriodicAndResolve() {
         mWorkDatabase.beginTransaction();
         try {
-            // The system clock may have been changed such that the periodStartTime was in the past.
+            // The system clock may have been changed such that the lastEnqueueTime was in the past.
             // Therefore we always use the current time to determine the next run time of a Worker.
             // This way, the Schedulers will correctly schedule the next instance of the
             // PeriodicWork in the future. This happens in calculateNextRunTime() in WorkSpec.
-            mWorkSpecDao.setLastEnqueuedTime(mWorkSpecId, System.currentTimeMillis());
+            mWorkSpecDao.setLastEnqueueTime(mWorkSpecId, System.currentTimeMillis());
             mWorkSpecDao.setState(ENQUEUED, mWorkSpecId);
             mWorkSpecDao.resetWorkSpecRunAttemptCount(mWorkSpecId);
             mWorkSpecDao.incrementPeriodCount(mWorkSpecId);
@@ -579,7 +579,7 @@
                     Logger.get().info(TAG,
                             "Setting status to enqueued for " + dependentWorkId);
                     mWorkSpecDao.setState(ENQUEUED, dependentWorkId);
-                    mWorkSpecDao.setLastEnqueuedTime(dependentWorkId, currentTimeMillis);
+                    mWorkSpecDao.setLastEnqueueTime(dependentWorkId, currentTimeMillis);
                 }
             }
 
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/model/WorkSpec.kt b/work/work-runtime/src/main/java/androidx/work/impl/model/WorkSpec.kt
index 1d6a6b8..efe451d 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/model/WorkSpec.kt
+++ b/work/work-runtime/src/main/java/androidx/work/impl/model/WorkSpec.kt
@@ -272,44 +272,18 @@
      * @return UTC time at which this [WorkSpec] should be allowed to run.
      */
     fun calculateNextRunTime(): Long {
-        return if (isBackedOff) {
-            val isLinearBackoff = backoffPolicy == BackoffPolicy.LINEAR
-            val delay = if (isLinearBackoff) backoffDelayDuration * runAttemptCount else Math.scalb(
-                backoffDelayDuration.toFloat(),
-                runAttemptCount - 1
-            )
-                .toLong()
-            lastEnqueueTime + delay.coerceAtMost(WorkRequest.MAX_BACKOFF_MILLIS)
-        } else if (isPeriodic) {
-            val start = if (periodCount == 0) lastEnqueueTime + initialDelay else lastEnqueueTime
-            val isFlexApplicable = flexDuration != intervalDuration
-            if (isFlexApplicable) {
-                // To correctly emulate flex, we need to set it
-                // to now, so the PeriodicWorkRequest has an initial delay of
-                // initialDelay + (interval - flex).
-
-                // The subsequent runs will only add the interval duration and no flex.
-                // This gives us the following behavior:
-                // 1 => now + (interval - flex) + initialDelay = firstRunTime
-                // 2 => firstRunTime + 2 * interval - flex
-                // 3 => firstRunTime + 3 * interval - flex
-                val offset = if (periodCount == 0) -1 * flexDuration else 0
-                start + intervalDuration + offset
-            } else {
-                // Don't use flexDuration for determining next run time for PeriodicWork
-                // This is because intervalDuration could equal flexDuration.
-
-                // The first run of a periodic work request is immediate in JobScheduler, and we
-                // need to emulate this behavior.
-                val offset = if (periodCount == 0) 0 else intervalDuration
-                start + offset
-            }
-        } else if (lastEnqueueTime == 0L) {
-            // If never enqueued, we aren't scheduled to run.
-            Long.MAX_VALUE / 2 // 100 million years.
-        } else {
-            lastEnqueueTime + initialDelay
-        }
+        return calculateNextRunTime(
+            isBackedOff = isBackedOff,
+            runAttemptCount = runAttemptCount,
+            backoffPolicy = backoffPolicy,
+            backoffDelayDuration = backoffDelayDuration,
+            lastEnqueueTime = lastEnqueueTime,
+            periodCount = periodCount,
+            isPeriodic = isPeriodic,
+            initialDelay = initialDelay,
+            flexDuration = flexDuration,
+            intervalDuration = intervalDuration
+        )
     }
 
     /**
@@ -336,7 +310,7 @@
     )
 
     /**
-     * A POJO containing the ID, state, output, tags, and run attempt count of a WorkSpec.
+     * A POJO containing externally queryable info for the WorkSpec.
      */
     data class WorkInfoPojo(
         @ColumnInfo(name = "id")
@@ -348,15 +322,36 @@
         @ColumnInfo(name = "output")
         val output: Data,
 
-        @ColumnInfo(name = "run_attempt_count")
-        val runAttemptCount: Int,
+        @ColumnInfo(name = "initial_delay")
+        val initialDelay: Long = 0,
 
-        @ColumnInfo(name = "generation")
-        val generation: Int,
+        @ColumnInfo(name = "interval_duration")
+        val intervalDuration: Long = 0,
+
+        @ColumnInfo(name = "flex_duration")
+        val flexDuration: Long = 0,
 
         @Embedded
         val constraints: Constraints,
 
+        @ColumnInfo(name = "run_attempt_count")
+        val runAttemptCount: Int,
+
+        @ColumnInfo(name = "backoff_policy")
+        var backoffPolicy: BackoffPolicy = BackoffPolicy.EXPONENTIAL,
+
+        @ColumnInfo(name = "backoff_delay_duration")
+        var backoffDelayDuration: Long = WorkRequest.DEFAULT_BACKOFF_DELAY_MILLIS,
+
+        @ColumnInfo(name = "last_enqueue_time")
+        var lastEnqueueTime: Long = 0,
+
+        @ColumnInfo(name = "period_count", defaultValue = "0")
+        var periodCount: Int = 0,
+
+        @ColumnInfo(name = "generation")
+        val generation: Int,
+
         @Relation(
             parentColumn = "id",
             entityColumn = "work_spec_id",
@@ -375,6 +370,11 @@
         )
         val progress: List<Data>,
     ) {
+        val isPeriodic: Boolean
+            get() = intervalDuration != 0L
+        val isBackedOff: Boolean
+            get() = state == WorkInfo.State.ENQUEUED && runAttemptCount > 0
+
         /**
          * Converts this POJO to a [WorkInfo].
          *
@@ -390,9 +390,35 @@
                 progress,
                 runAttemptCount,
                 generation,
-                constraints
+                constraints,
+                initialDelay,
+                getPeriodicityOrNull(),
+                calculateNextRunTimeMillis()
             )
         }
+
+        private fun getPeriodicityOrNull() = if (intervalDuration != 0L)
+            WorkInfo.PeriodicityInfo(
+                intervalDuration,
+                flexDuration
+            ) else null
+
+        private fun calculateNextRunTimeMillis(): Long {
+            return if (state == WorkInfo.State.ENQUEUED)
+                calculateNextRunTime(
+                    isBackedOff = isBackedOff,
+                    runAttemptCount = runAttemptCount,
+                    backoffPolicy = backoffPolicy,
+                    backoffDelayDuration = backoffDelayDuration,
+                    lastEnqueueTime = lastEnqueueTime,
+                    periodCount = periodCount,
+                    isPeriodic = isPeriodic,
+                    initialDelay = initialDelay,
+                    flexDuration = flexDuration,
+                    intervalDuration = intervalDuration
+                )
+            else Long.MAX_VALUE
+        }
     }
 
     companion object {
@@ -403,6 +429,60 @@
         val WORK_INFO_MAPPER: Function<List<WorkInfoPojo>, List<WorkInfo>> = Function { input ->
             input?.map { it.toWorkInfo() }
         }
+
+        fun calculateNextRunTime(
+            isBackedOff: Boolean,
+            runAttemptCount: Int,
+            backoffPolicy: BackoffPolicy,
+            backoffDelayDuration: Long,
+            lastEnqueueTime: Long,
+            periodCount: Int,
+            isPeriodic: Boolean,
+            initialDelay: Long,
+            flexDuration: Long,
+            intervalDuration: Long
+        ): Long {
+            return if (isBackedOff) {
+                val isLinearBackoff = backoffPolicy == BackoffPolicy.LINEAR
+                val delay =
+                    if (isLinearBackoff) backoffDelayDuration * runAttemptCount else Math.scalb(
+                        backoffDelayDuration.toFloat(),
+                        runAttemptCount - 1
+                    )
+                        .toLong()
+                lastEnqueueTime + delay.coerceAtMost(WorkRequest.MAX_BACKOFF_MILLIS)
+            } else if (isPeriodic) {
+                val start =
+                    if (periodCount == 0) lastEnqueueTime + initialDelay else lastEnqueueTime
+                val isFlexApplicable = flexDuration != intervalDuration
+                if (isFlexApplicable) {
+                    // To correctly emulate flex, we need to set it
+                    // to now, so the PeriodicWorkRequest has an initial delay of
+                    // initialDelay + (interval - flex).
+
+                    // The subsequent runs will only add the interval duration and no flex.
+                    // This gives us the following behavior:
+                    // 1 => now + (interval - flex) + initialDelay = firstRunTime
+                    // 2 => firstRunTime + 2 * interval - flex
+                    // 3 => firstRunTime + 3 * interval - flex
+                    val offset = if (periodCount == 0) -1 * flexDuration else 0
+                    start + intervalDuration + offset
+                } else {
+                    // Don't use flexDuration for determining next run time for PeriodicWork
+                    // This is because intervalDuration could equal flexDuration.
+
+                    // The first run of a periodic work request is immediate in JobScheduler, and we
+                    // need to emulate this behavior.
+                    val offset = if (periodCount == 0) 0 else intervalDuration
+                    start + offset
+                }
+            } else if (lastEnqueueTime == 0L) {
+                // If never enqueued, we aren't scheduled to run.
+                Long.MAX_VALUE // 200 million years.
+            } else {
+                lastEnqueueTime + initialDelay
+            }
+        }
     }
 }
 
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/model/WorkSpecDao.kt b/work/work-runtime/src/main/java/androidx/work/impl/model/WorkSpecDao.kt
index ac78f50..6f440fe 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/model/WorkSpecDao.kt
+++ b/work/work-runtime/src/main/java/androidx/work/impl/model/WorkSpecDao.kt
@@ -120,7 +120,7 @@
      * @param enqueueTime The time when the period started.
      */
     @Query("UPDATE workspec SET last_enqueue_time=:enqueueTime WHERE id=:id")
-    fun setLastEnqueuedTime(id: String, enqueueTime: Long)
+    fun setLastEnqueueTime(id: String, enqueueTime: Long)
 
     /**
      * Increment run attempt count of a [WorkSpec].
@@ -463,7 +463,8 @@
     .flowOn(dispatcher)
 
 private const val WORK_INFO_COLUMNS = "id, state, output, run_attempt_count, generation" +
-    ", $CONSTRAINTS_COLUMNS"
+    ", $CONSTRAINTS_COLUMNS, initial_delay, interval_duration, flex_duration, backoff_policy" +
+    ", backoff_delay_duration, last_enqueue_time, period_count"
 
 @Language("sql")
 private const val WORK_INFO_BY_IDS = "SELECT $WORK_INFO_COLUMNS FROM workspec WHERE id IN (:ids)"
diff --git a/work/work-testing/src/main/java/androidx/work/testing/TestScheduler.kt b/work/work-testing/src/main/java/androidx/work/testing/TestScheduler.kt
index fd6d17b..4bccdaa 100644
--- a/work/work-testing/src/main/java/androidx/work/testing/TestScheduler.kt
+++ b/work/work-testing/src/main/java/androidx/work/testing/TestScheduler.kt
@@ -186,7 +186,7 @@
     val now = System.currentTimeMillis()
     val timeOffset = workSpec.calculateNextRunTime() - now
     if (timeOffset > 0) {
-        dao.setLastEnqueuedTime(id, workSpec.lastEnqueueTime - timeOffset)
+        dao.setLastEnqueueTime(id, workSpec.lastEnqueueTime - timeOffset)
     }
     return dao.getWorkSpec(id)
         ?: throw IllegalStateException("WorkSpec is already deleted from WM's db")